mirror of
https://github.com/Karaka-Management/oms-Workflow.git
synced 2026-01-11 06:48:41 +00:00
continue implementation drafting
This commit is contained in:
parent
06c7709042
commit
e4f9e7447a
|
|
@ -14,6 +14,6 @@ declare(strict_types=1);
|
|||
|
||||
return [
|
||||
'/.*/' => [
|
||||
'callback' => ['\Modules\Workflow\Controller\CliController:findWorkflow'],
|
||||
'callback' => ['\Modules\Workflow\Controller\CliController:runWorkflowFromHook'],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
12
Admin/Routes/Cli.php
Normal file
12
Admin/Routes/Cli.php
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
use phpOMS\Router\RouteVerb;
|
||||
|
||||
return [
|
||||
'^.*/workflow/instance.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\CliController:cliWorkflowInstanceCreate',
|
||||
'verb' => RouteVerb::PUT,
|
||||
],
|
||||
]
|
||||
];
|
||||
54
Admin/Routes/Web/Api.php
Normal file
54
Admin/Routes/Web/Api.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package Modules
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://karaka.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
use Modules\Workflow\Controller\ApiController;
|
||||
use Modules\Workflow\Models\PermissionCategory;
|
||||
use phpOMS\Account\PermissionType;
|
||||
use phpOMS\Router\RouteVerb;
|
||||
|
||||
return [
|
||||
'^.*/workflow/instance/export.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\ApiController:apiWorkflowExport',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => ApiController::NAME,
|
||||
'type' => PermissionType::READ,
|
||||
'state' => PermissionCategory::WORKFLOW,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/workflow/template.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\ApiController:apiWorkflowTemplateCreate',
|
||||
'verb' => RouteVerb::PUT,
|
||||
'permission' => [
|
||||
'module' => ApiController::NAME,
|
||||
'type' => PermissionType::CREATE,
|
||||
'state' => PermissionCategory::TEMPLATE,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/workflow/instance.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\ApiController:apiWorkflowInstanceCreate',
|
||||
'verb' => RouteVerb::PUT,
|
||||
'permission' => [
|
||||
'module' => ApiController::NAME,
|
||||
'type' => PermissionType::CREATE,
|
||||
'state' => PermissionCategory::WORKFLOW,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
@ -20,7 +20,7 @@ use phpOMS\Router\RouteVerb;
|
|||
return [
|
||||
'^.*/workflow/template/list.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewWorkflowTemplates',
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewWorkflowTemplateList',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
|
|
@ -29,7 +29,7 @@ return [
|
|||
],
|
||||
],
|
||||
],
|
||||
'^.*/workflow/template/single.*$' => [
|
||||
'^.*/workflow/template/profile.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewWorkflowTemplate',
|
||||
'verb' => RouteVerb::GET,
|
||||
|
|
@ -53,7 +53,18 @@ return [
|
|||
],
|
||||
'^.*/workflow/dashboard.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewWorkflowDashboard',
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewDashboard',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
'type' => PermissionType::CREATE,
|
||||
'state' => PermissionCategory::WORKFLOW,
|
||||
],
|
||||
],
|
||||
],
|
||||
'^.*/workflow/instance/list.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewInstanceList',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
|
|
@ -62,9 +73,9 @@ return [
|
|||
],
|
||||
],
|
||||
],
|
||||
'^.*/workflow/single.*$' => [
|
||||
'^.*/workflow/instance/profile.*$' => [
|
||||
[
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewWorkflowSingle',
|
||||
'dest' => '\Modules\Workflow\Controller\BackendController:viewInstance',
|
||||
'verb' => RouteVerb::GET,
|
||||
'permission' => [
|
||||
'module' => BackendController::NAME,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use Modules\Media\Models\NullCollection;
|
|||
use Modules\Media\Models\NullMedia;
|
||||
use Modules\Workflow\Models\PermissionCategory;
|
||||
use Modules\Workflow\Models\WorkflowInstance;
|
||||
use Modules\Workflow\Models\WorkflowInstanceAbstract;
|
||||
use Modules\Workflow\Models\WorkflowInstanceMapper;
|
||||
use Modules\Workflow\Models\WorkflowTemplate;
|
||||
use Modules\Workflow\Models\WorkflowTemplateMapper;
|
||||
|
|
@ -34,10 +35,10 @@ use phpOMS\Message\RequestAbstract;
|
|||
use phpOMS\Message\ResponseAbstract;
|
||||
use phpOMS\Model\Message\FormValidation;
|
||||
use phpOMS\System\MimeType;
|
||||
use phpOMS\System\SystemUtils;
|
||||
use phpOMS\Utils\Parser\Markdown\Markdown;
|
||||
use phpOMS\Utils\StringUtils;
|
||||
use phpOMS\Views\View;
|
||||
use phpOMS\DataStorage\Database\Schema\Builder as SchemaBuilder;
|
||||
|
||||
/**
|
||||
* Workflow controller class.
|
||||
|
|
@ -71,7 +72,7 @@ final class ApiController extends Controller
|
|||
return;
|
||||
}
|
||||
|
||||
/** @var WorkflowInstance $instance */
|
||||
/** @var WorkflowInstanceAbstract $instance */
|
||||
$instance = WorkflowInstanceMapper::get()
|
||||
->with('template')
|
||||
->with('template/source')
|
||||
|
|
@ -206,7 +207,7 @@ final class ApiController extends Controller
|
|||
/**
|
||||
* Create view from template
|
||||
*
|
||||
* @param WorkflowInstance $instance Instance to create view from
|
||||
* @param WorkflowInstanceAbstract $instance Instance to create view from
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
*
|
||||
|
|
@ -216,7 +217,7 @@ final class ApiController extends Controller
|
|||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createView(WorkflowInstance $instance, RequestAbstract $request, ResponseAbstract $response) : View
|
||||
private function createView(WorkflowInstanceAbstract $instance, RequestAbstract $request, ResponseAbstract $response) : View
|
||||
{
|
||||
/** @var array<string, \Modules\Media\Models\Media|\Modules\Media\Models\Media[]> $tcoll */
|
||||
$tcoll = [];
|
||||
|
|
@ -308,9 +309,8 @@ final class ApiController extends Controller
|
|||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function apiTemplateCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
|
||||
public function apiWorkflowTemplateCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
|
||||
{
|
||||
$dbFiles = $request->getDataJson('media-list') ?? [];
|
||||
$uploadedFiles = $request->getFiles() ?? [];
|
||||
$files = [];
|
||||
|
||||
|
|
@ -328,6 +328,7 @@ final class ApiController extends Controller
|
|||
return;
|
||||
}
|
||||
|
||||
/** @var \Modules\Media\Models\Media[] $uploaded */
|
||||
$uploaded = $this->app->moduleManager->get('Media')->uploadFiles(
|
||||
$request->getDataList('names') ?? [],
|
||||
$request->getDataList('filenames') ?? [],
|
||||
|
|
@ -337,11 +338,11 @@ final class ApiController extends Controller
|
|||
);
|
||||
|
||||
foreach ($uploaded as $upload) {
|
||||
$files[] = new NullMedia($upload->getId());
|
||||
}
|
||||
if ($upload instanceof NullMedia) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($dbFiles as $db) {
|
||||
$files[] = new NullMedia($db);
|
||||
$files[] = $upload;
|
||||
}
|
||||
|
||||
/** @var Collection $collection */
|
||||
|
|
@ -365,8 +366,22 @@ final class ApiController extends Controller
|
|||
CollectionMapper::create()->execute($collection);
|
||||
|
||||
$template = $this->createTemplateFromRequest($request, $collection->getId());
|
||||
$this->createDatabaseForTemplate($template);
|
||||
|
||||
$this->createModel($request->header->account, $template, WorkflowTemplateMapper::class, 'template', $request->getOrigin());
|
||||
|
||||
// replace placeholders
|
||||
foreach ($uploaded as $upload) {
|
||||
if ($upload instanceof NullMedia) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $upload->getAbsolutePath();
|
||||
$content = \file_get_contents($path);
|
||||
$content = \str_replace('{workflow_id}', (string) $template->getId(), $content);
|
||||
\file_put_contents($path, $content);
|
||||
}
|
||||
|
||||
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Template', 'Template successfully created', $template);
|
||||
}
|
||||
|
||||
|
|
@ -403,7 +418,7 @@ final class ApiController extends Controller
|
|||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return Template
|
||||
* @return WorkflowTemplate
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
|
|
@ -425,7 +440,46 @@ final class ApiController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
* Method to create database for template.
|
||||
*
|
||||
* @param WorkflowTemplate $template Workflow template
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createDatabaseForTemplate(WorkflowTemplate $template) : void
|
||||
{
|
||||
/** @var \Modules\Media\Models\Collection $collection */
|
||||
$collection = CollectionMapper::get()
|
||||
->with('sources')
|
||||
->where('id', $template->source->getId())
|
||||
->execute();
|
||||
|
||||
$files = $collection->getSources();
|
||||
foreach ($files as $file) {
|
||||
if (!StringUtils::endsWith($file->getPath(), 'db.json')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\is_file($file->getPath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$content = \file_get_contents($file->getPath());
|
||||
if ($content === false) {
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$definitions = \json_decode($content, true);
|
||||
foreach ($definitions as $definition) {
|
||||
SchemaBuilder::createFromSchema($definition, $this->app->dbPool->get('schema'))->execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method which creates a workflow instance
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
|
|
@ -433,12 +487,75 @@ final class ApiController extends Controller
|
|||
*
|
||||
* @return void
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function apiWorkflowInstanceCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : void
|
||||
{
|
||||
if (!empty($val = $this->validateInstanceCreate($request))) {
|
||||
$response->set('instance_create', new FormValidation($val));
|
||||
$response->header->status = RequestStatusCode::R_400;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$instance = $this->createInstanceFromRequest($request);
|
||||
|
||||
$this->createModel($request->header->account, $instance, WorkflowInstanceMapper::class, 'instance', $request->getOrigin());
|
||||
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Instance', 'Instance successfully created', $instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate template create request
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return array<string, bool>
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function validateInstanceCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['j'] = empty($request->getData('j')))) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create interface from request.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return WorkflowInstanceAbstract
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createInstanceFromRequest(RequestAbstract $request) : WorkflowInstanceAbstract
|
||||
{
|
||||
$template = WorkflowTemplateMapper::get()
|
||||
->where('id', (int) $request->getData('j'))
|
||||
->execute();
|
||||
|
||||
$controller = null;
|
||||
|
||||
$files = $template->source->getSources();
|
||||
foreach ($files as $tMedia) {
|
||||
$lowerPath = \strtolower($tMedia->getPath());
|
||||
|
||||
switch (true) {
|
||||
case StringUtils::endsWith($lowerPath, 'WorkflowController.php'):
|
||||
require_once $lowerPath;
|
||||
|
||||
$controller = new WorkflowController($this->app, $template);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$instance = $controller->createInstanceFromRequest($request);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -459,34 +576,4 @@ final class ApiController extends Controller
|
|||
public function apiWorkflowImport(HttpRequest $request, HttpResponse $response, $data = null) : void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Api method to make a call to the cli app
|
||||
*
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @todo maybe this needs to be moved to the admin module if there every is another hook which uses .* regex-match and is forwarded to the cli application
|
||||
*/
|
||||
public function cliEventCall(...$data) : void
|
||||
{
|
||||
$count = \count($data);
|
||||
|
||||
// @todo: if no Cli is available do it in the web app (maybe first web request and if this is also not allowed run it in here)
|
||||
/*
|
||||
SystemUtils::runProc(
|
||||
'php',
|
||||
__DIR__ . '/../../../cli.php' . ' '
|
||||
. 'post:/admin/event' . ' '
|
||||
. '-g ' . \escapeshellarg($data[$count - 2]) . ' '
|
||||
. '-i ' . \escapeshellarg($data[$count - 1]) . ' '
|
||||
. '-d ' . \escapeshellarg(\json_encode($data)),
|
||||
true
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ declare(strict_types=1);
|
|||
namespace Modules\Workflow\Controller;
|
||||
|
||||
use Modules\Media\Models\CollectionMapper;
|
||||
use Modules\Media\Models\NullMedia;
|
||||
use Modules\Workflow\Models\WorkflowInstanceMapper;
|
||||
use Modules\Workflow\Models\WorkflowStatus;
|
||||
use Modules\Workflow\Models\WorkflowTemplateMapper;
|
||||
use phpOMS\Contract\RenderableInterface;
|
||||
use phpOMS\Message\RequestAbstract;
|
||||
|
|
@ -31,6 +34,60 @@ use phpOMS\Views\View;
|
|||
*/
|
||||
final class BackendController extends Controller
|
||||
{
|
||||
/**
|
||||
* Api method to make a call to the cli app
|
||||
*
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function runWorkflowFromHook(...$data) : void
|
||||
{
|
||||
$workflows = WorkflowTemplateMapper::getAll()->where('status', WorkflowStatus::ACTIVE)->execute();
|
||||
foreach ($workflows as $workflow) {
|
||||
$hooks = $workflow->getHooks();
|
||||
|
||||
foreach ($hooks as $hook) {
|
||||
$triggerIsRegex = \stripos($data[':triggerGroup'], '/') === 0;
|
||||
$matched = false;
|
||||
|
||||
if ($triggerIsRegex) {
|
||||
$matched = \preg_match($data[':triggerGroup'], $hook) === 1;
|
||||
} else {
|
||||
$matched = $data[':triggerGroup'] === $hook;
|
||||
}
|
||||
|
||||
if (!$matched && \stripos($hook, '/') === 0) {
|
||||
$matched = \preg_match($hook, $data[':triggerGroup']) === 1;
|
||||
}
|
||||
|
||||
if ($matched) {
|
||||
$this->runWorkflow($workflow, $hook, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Api method to make a call to the cli app
|
||||
*
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function runWorkflow(WorkflowTemplate $workflow, string $hook, array $data) : void
|
||||
{
|
||||
include $workflow->media->getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
|
|
@ -43,7 +100,7 @@ final class BackendController extends Controller
|
|||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewWorkflowTemplates(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
public function viewWorkflowTemplateList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/workflow-template-list');
|
||||
|
|
@ -84,29 +141,25 @@ final class BackendController extends Controller
|
|||
public function viewWorkflowTemplate(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/task-single');
|
||||
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1005501001, $request, $response));
|
||||
|
||||
return $view;
|
||||
}
|
||||
$template = WorkflowTemplateMapper::get()
|
||||
->with('createdBy')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->execute();
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewWorkflowSingle(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/task-create');
|
||||
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1005501001, $request, $response));
|
||||
require_once $template->findFile('WorkflowController.php')->getPath();
|
||||
|
||||
/** @var WorkflowControllerInterface $controller */
|
||||
$controller = new WorkflowController($this->app, $template);
|
||||
|
||||
// @todo load template specific data and pass it to the view
|
||||
|
||||
if (!(($list = $template->findFile('template-profile.tpl.php')) instanceof NullMedia)) {
|
||||
$view->setTemplate('/' . \substr($list->getPath(), 0, -8));
|
||||
} else {
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/workflow-profile');
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
|
@ -144,7 +197,7 @@ final class BackendController extends Controller
|
|||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewWorkflowDashboard(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
public function viewDashboard(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/workflow-dashboard');
|
||||
|
|
@ -152,4 +205,79 @@ final class BackendController extends Controller
|
|||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewInstanceList(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1005501001, $request, $response));
|
||||
|
||||
$template = WorkflowTemplateMapper::get()
|
||||
->with('createdBy')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->execute();
|
||||
|
||||
require_once $template->findFile('WorkflowController.php')->getPath();
|
||||
|
||||
/** @var WorkflowControllerInterface $controller */
|
||||
$controller = new WorkflowController($this->app, $template);
|
||||
|
||||
$view->addData('instances', $controller->getInstanceListFromRequest($request));
|
||||
|
||||
if (!(($list = $template->findFile('instance-list.tpl.php')) instanceof NullMedia)) {
|
||||
$view->setTemplate('/' . \substr($list->getPath(), 0, -8));
|
||||
} else {
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/workflow-instance-list');
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Routing end-point for application behaviour.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return RenderableInterface
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function viewInstance(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
$view->addData('nav', $this->app->moduleManager->get('Navigation')->createNavigationMid(1005501001, $request, $response));
|
||||
|
||||
$template = WorkflowTemplateMapper::get()
|
||||
->where('template', (int) $request->getData('template'))
|
||||
->execute();
|
||||
|
||||
require_once $template->findFile('WorkflowController.php')->getPath();
|
||||
|
||||
/** @var WorkflowControllerInterface $controller */
|
||||
$controller = new WorkflowController($this->app, $template);
|
||||
|
||||
$view->addData('instance', $controller->getInstanceFromRequest($request));
|
||||
|
||||
if (!(($instance = $template->findFile('instance-profile.tpl.php')) instanceof NullMedia)) {
|
||||
$view->setTemplate('/' . \substr($instance->getPath(), 0, -8));
|
||||
} else {
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Backend/workflow-instance');
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,17 @@ declare(strict_types=1);
|
|||
|
||||
namespace Modules\Workflow\Controller;
|
||||
|
||||
use Modules\Workflow\Models\WorkflowInstanceAbstract;
|
||||
use Modules\Workflow\Models\WorkflowStatus;
|
||||
use Modules\Workflow\Models\WorkflowTemplate;
|
||||
use Modules\Workflow\Models\WorkflowTemplateMapper;
|
||||
use phpOMS\System\SystemUtils;
|
||||
use phpOMS\Contract\RenderableInterface;
|
||||
use phpOMS\Message\Http\RequestStatusCode;
|
||||
use phpOMS\Message\RequestAbstract;
|
||||
use phpOMS\Message\ResponseAbstract;
|
||||
use phpOMS\Model\Message\FormValidation;
|
||||
use phpOMS\Utils\StringUtils;
|
||||
use phpOMS\Views\View;
|
||||
|
||||
/**
|
||||
* Workflow controller class.
|
||||
|
|
@ -40,7 +47,7 @@ final class CliController extends Controller
|
|||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function findWorkflow(...$data) : void
|
||||
public function runWorkflowFromHook(...$data) : void
|
||||
{
|
||||
$workflows = WorkflowTemplateMapper::getAll()->where('status', WorkflowStatus::ACTIVE)->execute();
|
||||
foreach ($workflows as $workflow) {
|
||||
|
|
@ -82,4 +89,88 @@ final class CliController extends Controller
|
|||
{
|
||||
include $workflow->media->getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method which creates a workflow instance
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
* @param ResponseAbstract $response Response
|
||||
* @param mixed $data Generic data
|
||||
*
|
||||
* @return RenderableInterface Response can be rendered
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function cliWorkflowInstanceCreate(RequestAbstract $request, ResponseAbstract $response, $data = null) : RenderableInterface
|
||||
{
|
||||
$view = new View($this->app->l11nManager, $request, $response);
|
||||
|
||||
if (!empty($val = $this->validateInstanceCreate($request))) {
|
||||
$response->set('instance_create', new FormValidation($val));
|
||||
$response->header->status = RequestStatusCode::R_400;
|
||||
}
|
||||
|
||||
$instance = $this->createInstanceFromRequest($request);
|
||||
|
||||
$view->setTemplate('/Modules/Workflow/Theme/Cli/empty-command');
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate template create request
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return array<string, bool>
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function validateInstanceCreate(RequestAbstract $request) : array
|
||||
{
|
||||
$val = [];
|
||||
if (($val['j'] = empty($request->getData('j')))) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create instance from request.
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return WorkflowInstanceAbstract
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function createInstanceFromRequest(RequestAbstract $request) : WorkflowInstanceAbstract
|
||||
{
|
||||
$template = WorkflowTemplateMapper::get()
|
||||
->where('id', (int) $request->getData('j'))
|
||||
->execute();
|
||||
|
||||
$controller = null;
|
||||
$mapper = null;
|
||||
|
||||
$files = $template->source->getSources();
|
||||
foreach ($files as $tMedia) {
|
||||
$lowerPath = \strtolower($tMedia->getPath());
|
||||
|
||||
switch (true) {
|
||||
case StringUtils::endsWith($lowerPath, 'WorkflowController.php'):
|
||||
require_once $lowerPath;
|
||||
|
||||
$controller = new WorkflowController($this->app, $template);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$instance = $controller->createInstanceFromRequest($request);
|
||||
$controller->createInstanceDbModel($instance);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
74
Models/WorkflowControllerInterface.php
Normal file
74
Models/WorkflowControllerInterface.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package Modules\Workflow\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://karaka.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Workflow\Models;
|
||||
|
||||
use phpOMS\Contract\RenderableInterface;
|
||||
use phpOMS\Message\RequestAbstract;
|
||||
use phpOMS\Message\ResponseAbstract;
|
||||
|
||||
/**
|
||||
* Controller interface.
|
||||
*
|
||||
* @package Modules\Workflow\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://karaka.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
interface WorkflowControllerInterface
|
||||
{
|
||||
/**
|
||||
* Create instance from request
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return WorkflowInstanceAbstract
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function createInstanceFromRequest(RequestAbstract $request) : WorkflowInstanceAbstract;
|
||||
|
||||
/**
|
||||
* Create list of all instances for this workflow from a request
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInstanceListFromRequest(RequestAbstract $request) : array;
|
||||
|
||||
/**
|
||||
* Change workflow instance state
|
||||
*
|
||||
* @param RequestAbstract $request Request
|
||||
*
|
||||
* @return WorkflowInstanceAbstract
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function apiChangeState(RequestAbstract $request, ResponseAbstract $response, $data = null) : void;
|
||||
|
||||
/**
|
||||
* Store instance model in the database
|
||||
*
|
||||
* @param WorkflowInstanceAbstract $instance Instance
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function createInstanceDbModel(WorkflowInstanceAbstract $instance) : void;
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ use Modules\Admin\Models\NullAccount;
|
|||
* @link https://karaka.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class WorkflowInstance
|
||||
class WorkflowInstanceAbstract
|
||||
{
|
||||
/**
|
||||
* ID.
|
||||
|
|
@ -35,6 +35,14 @@ class WorkflowInstance
|
|||
*/
|
||||
protected int $id = 0;
|
||||
|
||||
/**
|
||||
* Title.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $title = '';
|
||||
|
||||
/**
|
||||
* Template.
|
||||
*
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package Modules\Workflow\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://karaka.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Workflow\Models;
|
||||
|
||||
/**
|
||||
* Workflow interface.
|
||||
*
|
||||
* @package Modules\Workflow\Models
|
||||
* @license OMS License 1.0
|
||||
* @link https://karaka.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
interface WorkflowInterface
|
||||
{
|
||||
}
|
||||
|
|
@ -17,7 +17,10 @@ namespace Modules\Workflow\Models;
|
|||
use Modules\Admin\Models\Account;
|
||||
use Modules\Admin\Models\NullAccount;
|
||||
use Modules\Media\Models\Collection;
|
||||
use Modules\Media\Models\Media;
|
||||
use Modules\Media\Models\NullCollection;
|
||||
use Modules\Media\Models\NullMedia;
|
||||
use phpOMS\Utils\StringUtils;
|
||||
|
||||
/**
|
||||
* Workflow template class.
|
||||
|
|
@ -116,4 +119,16 @@ class WorkflowTemplate
|
|||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function findFile(string $name) : Media
|
||||
{
|
||||
$files = $this->source->getSources();
|
||||
foreach ($files as $file) {
|
||||
if (StringUtils::endsWith($file->getPath(), $name)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return new NullMedia();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,43 +13,4 @@
|
|||
declare(strict_types=1);
|
||||
|
||||
/** @var \phpOMS\Views\View $this */
|
||||
echo $this->getData('nav')->render(); ?>
|
||||
|
||||
<section class="box w-50 floatLeft">
|
||||
<header><h1><?= $this->getHtml('Task'); ?></h1></header>
|
||||
|
||||
<div class="inner">
|
||||
<form id="fTask" method="POST" action="<?= \phpOMS\Uri\UriFactory::build('{/api}task/create'); ?>">
|
||||
<table class="layout wf-100">
|
||||
<tbody>
|
||||
<tr><td colspan="2"><label for="iReceiver"><?= $this->getHtml('To'); ?></label>
|
||||
<tr><td><span class="input"><button type="button" data-action='[{"type": "popup", "tpl": "acc-grp-tpl", "aniIn": "fadeIn", "aniOut": "fadeOut", "stay": 5000}]' formaction=""><i class="fa fa-book"></i></button><input type="number" min="1" id="iReceiver" name="receiver" placeholder=" Guest" required></span><td><button><?= $this->getHtml('Add', '0', '0'); ?></button>
|
||||
<tr><td colspan="2"><label for="iObserver"><?= $this->getHtml('CC'); ?></label>
|
||||
<tr><td><span class="input"><button type="button" formaction=""><i class="fa fa-book"></i></button><input type="number" min="1" id="iObserver" name="observer" placeholder=" Guest" required></span><td><button><?= $this->getHtml('Add', '0', '0'); ?></button>
|
||||
<tr><td colspan="2"><label for="iDue"><?= $this->getHtml('Due'); ?></label>
|
||||
<tr><td><input type="datetime-local" id="iDue" name="due" value="<?= $this->printHtml((new \DateTime('NOW'))->format('Y-m-d\TH:i:s')); ?>"><td>
|
||||
<tr><td colspan="2"><label for="iTitle"><?= $this->getHtml('Title'); ?></label>
|
||||
<tr><td><input type="text" id="iTitle" name="title" placeholder=" <?= $this->getHtml('Title'); ?>"><td>
|
||||
<tr><td colspan="2"><label for="iMessage"><?= $this->getHtml('Message'); ?></label>
|
||||
<tr><td><textarea id="iMessage" name="description" placeholder=""></textarea><td>
|
||||
<tr><td colspan="2"><input type="submit" value="<?= $this->getHtml('Create', '0', '0'); ?>"><input type="hidden" name="type" value="<?= $this->printHtml(\Modules\Tasks\Models\TaskType::SINGLE); ?>">
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="box w-50 floatLeft">
|
||||
<header><h1><?= $this->getHtml('Media'); ?></h1></header>
|
||||
|
||||
<div class="inner">
|
||||
<form>
|
||||
<table class="layout wf-100">
|
||||
<tbody>
|
||||
<tr><td colspan="2"><label for="iMedia"><?= $this->getHtml('Media'); ?></label>
|
||||
<tr><td><input type="text" id="iMedia" placeholder=" File"><td><button><?= $this->getHtml('Select'); ?></button>
|
||||
<tr><td colspan="2"><label for="iUpload"><?= $this->getHtml('Upload'); ?></label>
|
||||
<tr><td><input type="file" id="iUpload" form="fTask"><input form="fTask" type="hidden" name="type"><td>
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
echo $this->getData('nav')->render();
|
||||
|
|
|
|||
48
Theme/Backend/workflow-dashboard.tpl.php
Executable file → Normal file
48
Theme/Backend/workflow-dashboard.tpl.php
Executable file → Normal file
|
|
@ -12,49 +12,5 @@
|
|||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
use Modules\Workflow\Models\WorkflowStatus;
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
*/
|
||||
$workflows = [];
|
||||
|
||||
echo $this->getData('nav')->render(); ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Workflow'); ?><i class="fa fa-download floatRight download btn"></i></div>
|
||||
<table class="default">
|
||||
<thead>
|
||||
<td><?= $this->getHtml('Status'); ?>
|
||||
<td><?= $this->getHtml('Next'); ?>
|
||||
<td class="full"><?= $this->getHtml('Title'); ?>
|
||||
<td><?= $this->getHtml('Creator'); ?>
|
||||
<td><?= $this->getHtml('Created'); ?>
|
||||
<tfoot>
|
||||
<tbody>
|
||||
<?php $c = 0;
|
||||
foreach ($workflows as $key => $workflow) : ++$c;
|
||||
|
||||
$url = \phpOMS\Uri\UriFactory::build('{/prefix}task/single?{?}&id=' . $workflow->getId());
|
||||
$color = 'darkred';
|
||||
|
||||
if ($workflow->getStatus() === WorkflowStatus::DONE) { $color = 'green'; }
|
||||
elseif ($workflow->getStatus() === WorkflowStatus::OPEN) { $color = 'darkblue'; }
|
||||
elseif ($workflow->getStatus() === WorkflowStatus::WORKING) { $color = 'purple'; }
|
||||
elseif ($workflow->getStatus() === WorkflowStatus::CANCELED) { $color = 'red'; }
|
||||
elseif ($workflow->getStatus() === WorkflowStatus::SUSPENDED) { $color = 'yellow'; } ?>
|
||||
<tr>
|
||||
<td data-label="<?= $this->getHtml('Status'); ?>"><a href="<?= $url; ?>"><span class="tag <?= $this->printHtml($color); ?>"><?= $this->getHtml('S' . $workflow->getStatus()); ?></span></a>
|
||||
<td data-label="<?= $this->getHtml('Next'); ?>"><a href="<?= $url; ?>"><?= $this->printHtml($workflow->getDue()->format('Y-m-d H:i')); ?></a>
|
||||
<td data-label="<?= $this->getHtml('Title'); ?>"><a href="<?= $url; ?>"><?= $this->printHtml($workflow->getTitle()); ?></a>
|
||||
<td data-label="<?= $this->getHtml('Creator'); ?>"><a href="<?= $url; ?>"><?= $this->printHtml($workflow->createdBy->getId()); ?></a>
|
||||
<td data-label="<?= $this->getHtml('Created'); ?>"><a href="<?= $url; ?>"><?= $this->printHtml($workflow->createdAt->format('Y-m-d H:i')); ?></a>
|
||||
<?php endforeach; if ($c == 0) : ?>
|
||||
<tr><td colspan="6" class="empty"><?= $this->getHtml('Empty', '0', '0'); ?>
|
||||
<?php endif; ?>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
/** @var \phpOMS\Views\View $this */
|
||||
echo $this->getData('nav')->render();
|
||||
|
|
|
|||
7
Admin/Hooks/Web/Api.php → Theme/Backend/workflow-instance-list.tpl.php
Normal file → Executable file
7
Admin/Hooks/Web/Api.php → Theme/Backend/workflow-instance-list.tpl.php
Normal file → Executable file
|
|
@ -12,8 +12,5 @@
|
|||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'/.*/' => [
|
||||
'callback' => ['\Modules\Workflow\Controller\ApiController:cliEventCall'],
|
||||
],
|
||||
];
|
||||
/** @var \phpOMS\Views\View $this */
|
||||
echo $this->getData('nav')->render();
|
||||
16
Theme/Backend/workflow-instance.tpl.php
Executable file
16
Theme/Backend/workflow-instance.tpl.php
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package Modules\Workflow
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://karaka.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
/** @var \phpOMS\Views\View $this */
|
||||
echo $this->getData('nav')->render();
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Karaka
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package Modules\Workflow
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://karaka.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @var \phpOMS\Views\View $this
|
||||
* @var \Modules\Tasks\Models\Task $task
|
||||
*/
|
||||
$task = $this->getData('task');
|
||||
$elements = $task->getTaskElements();
|
||||
$cElements = \count($elements);
|
||||
|
||||
echo $this->getData('nav')->render(); ?>
|
||||
|
||||
<section class="box w-50">
|
||||
<header><h1><?= $this->printHtml($task->getTitle()); ?></h1></header>
|
||||
<div class="inner">
|
||||
<div class="floatRight">Due <?= $this->printHtml($task->getDue()->format('Y-m-d H:i')); ?></div>
|
||||
<div>Created <?= $this->printHtml($task->createdAt->format('Y-m-d H:i')); ?></div>
|
||||
<blockquote>
|
||||
<?= $this->printHtml($task->description); ?>
|
||||
</blockquote>
|
||||
<div>Created <?= $this->printHtml($task->createdBy->getId()); ?></div>
|
||||
<div>Status <?= $this->printHtml($task->getStatus()); ?></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php $c = 0;
|
||||
foreach ($elements as $key => $element) : ++$c;
|
||||
if ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::DONE) { $color = 'green'; }
|
||||
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::OPEN) { $color = 'darkblue'; }
|
||||
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::WORKING) { $color = 'purple'; }
|
||||
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::CANCELED) { $color = 'red'; }
|
||||
elseif ($element->getStatus() === \Modules\Tasks\Models\TaskStatus::SUSPENDED) { $color = 'yellow'; } ?>
|
||||
<section class="box w-50">
|
||||
<div class="floatRight"><span class="tag <?= $this->printHtml($color); ?>"><?= $this->getHtml('S' . $element->getStatus()); ?></span></div>
|
||||
<div><?= $this->printHtml($element->createdBy->getId()); ?> - <?= $this->printHtml($element->createdAt->format('Y-m-d H:i')); ?></div>
|
||||
</section>
|
||||
<?php if ($element->description !== '') : ?>
|
||||
<section class="box w-50">
|
||||
<div class="inner">
|
||||
<blockquote>
|
||||
<?= $this->printHtml($element->description); ?>
|
||||
</blockquote>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
<section class="box w-50">
|
||||
<?php if ($element->getStatus() !== \Modules\Tasks\Models\TaskStatus::CANCELED ||
|
||||
$element->getStatus() !== \Modules\Tasks\Models\TaskStatus::DONE ||
|
||||
$element->getStatus() !== \Modules\Tasks\Models\TaskStatus::SUSPENDED || $c != $cElements
|
||||
) : ?>
|
||||
<div class="floatRight">Due <?= $this->printHtml($element->getDue()->format('Y-m-d H:i')); ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($element->getForwarded() !== 0) : ?>
|
||||
<div>Forwarded <?= $this->printHtml($element->getForwarded()); ?></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<section class="box w-50">
|
||||
<div class="inner">
|
||||
<form>
|
||||
<table class="layout wf-100">
|
||||
<tr><td><label for="iMessage"><?= $this->getHtml('Message'); ?></label>
|
||||
<tr><td><textarea></textarea>
|
||||
<tr><td><label for="iDue"><?= $this->getHtml('Due'); ?></label>
|
||||
<tr><td><input type="datetime-local">
|
||||
<tr><td><label for="iReceiver"><?= $this->getHtml('Status'); ?></label>
|
||||
<tr><td><select>
|
||||
<option>
|
||||
</select>
|
||||
<tr><td><label for="iReceiver"><?= $this->getHtml('To'); ?></label>
|
||||
<tr><td><input type="text" id="iReceiver" placeholder=" Guest">
|
||||
<tr><td><input type="submit" value="<?= $this->getHtml('Create', '0', '0'); ?>"><input type="hidden" name="type" value="1">
|
||||
</table>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
Loading…
Reference in New Issue
Block a user