mirror of https://github.com/MISP/MISP
chg: [worflow] Started removing feature from initial design
- Multiple workflows per trigger - Custom Workflow per user - Workflow import/export - Blocking & Parallel path from triggerspull/8530/head
parent
52e0a059f1
commit
d180c2cc17
|
@ -24,7 +24,6 @@ class WorkflowsController extends AppController
|
|||
$params = [
|
||||
'filters' => ['name', 'uuid'],
|
||||
'quickFilters' => ['name', 'uuid'],
|
||||
'contain' => ['Organisation']
|
||||
];
|
||||
$this->CRUD->index($params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
|
@ -35,19 +34,16 @@ class WorkflowsController extends AppController
|
|||
|
||||
public function rebuildRedis()
|
||||
{
|
||||
$this->Workflow->rebuildRedis($this->Auth->user());
|
||||
$this->Workflow->rebuildRedis();
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$currentUser = $this->Auth->user();
|
||||
$params = [
|
||||
'beforeSave' => function ($data) use ($currentUser) {
|
||||
'beforeSave' => function ($data) {
|
||||
if (empty($data['Workflow']['uuid'])) {
|
||||
$data['Workflow']['uuid'] = CakeText::uuid();
|
||||
}
|
||||
$data['Workflow']['user_id'] = $currentUser['id'];
|
||||
$data['Workflow']['org_id'] = $currentUser['org_id'];
|
||||
if (!isset($data['Workflow']['description'])) {
|
||||
$data['Workflow']['description'] = '';
|
||||
}
|
||||
|
@ -72,18 +68,18 @@ class WorkflowsController extends AppController
|
|||
public function edit($id)
|
||||
{
|
||||
$this->set('id', $id);
|
||||
$savedWorkflow = $this->Workflow->fetchWorkflow($this->Auth->user(), $id);
|
||||
$savedWorkflow = $this->Workflow->fetchWorkflow($id);
|
||||
if ($this->request->is('post') || $this->request->is('put')) {
|
||||
$newWorkflow = $this->request->data;
|
||||
$newWorkflow['Workflow']['data'] = JsonTool::decode($newWorkflow['Workflow']['data']);
|
||||
$newWorkflow = $this->__applyDataFromSavedWorkflow($newWorkflow, $savedWorkflow);
|
||||
$errors = $this->Workflow->editWorkflow($this->Auth->user(), $newWorkflow);
|
||||
$errors = $this->Workflow->editWorkflow($newWorkflow);
|
||||
$redirectTarget = ['action' => 'view', $id];
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($errors, null, 'edit', $this->Workflow->id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Workflow saved.');
|
||||
$savedWorkflow =$this->Workflow->fetchWorkflow($this->Auth->user(), $id);
|
||||
$savedWorkflow =$this->Workflow->fetchWorkflow($id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $savedWorkflow, 'edit', false, $redirectTarget);
|
||||
}
|
||||
} else {
|
||||
|
@ -98,7 +94,6 @@ class WorkflowsController extends AppController
|
|||
public function delete($id)
|
||||
{
|
||||
$params = [
|
||||
'conditions' => $this->Workflow->buildACLConditions($this->Auth->user()),
|
||||
];
|
||||
$this->CRUD->delete($id, $params);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
|
@ -109,8 +104,6 @@ class WorkflowsController extends AppController
|
|||
public function view($id)
|
||||
{
|
||||
$this->CRUD->view($id, [
|
||||
'conditions' => $this->Workflow->buildACLConditions($this->Auth->user()),
|
||||
'contain' => ['Organisation', 'User']
|
||||
]);
|
||||
if ($this->IndexFilter->isRest()) {
|
||||
return $this->restResponsePayload;
|
||||
|
@ -121,59 +114,90 @@ class WorkflowsController extends AppController
|
|||
|
||||
public function enable($id)
|
||||
{
|
||||
$errors = $this->Workflow->toggleWorkflow($this->Auth->user(), $id, true);
|
||||
$errors = $this->Workflow->toggleWorkflow($id, true);
|
||||
$redirectTarget = ['action' => 'index'];
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($errors, null, 'edit', $this->Workflow->id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Workflow enabled.');
|
||||
$savedWorkflow = $this->Workflow->fetchWorkflow($this->Auth->user(), $id);
|
||||
$savedWorkflow = $this->Workflow->fetchWorkflow($id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $savedWorkflow, 'edit', false, $redirectTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public function disable($id)
|
||||
{
|
||||
$errors = $this->Workflow->toggleWorkflow($this->Auth->user(), $id, false);
|
||||
$errors = $this->Workflow->toggleWorkflow($id, false);
|
||||
$redirectTarget = ['action' => 'index'];
|
||||
if (!empty($errors)) {
|
||||
return $this->__getFailResponseBasedOnContext($errors, null, 'edit', $this->Workflow->id, $redirectTarget);
|
||||
} else {
|
||||
$successMessage = __('Workflow disabled.');
|
||||
$savedWorkflow = $this->Workflow->fetchWorkflow($this->Auth->user(), $id);
|
||||
$savedWorkflow = $this->Workflow->fetchWorkflow($id);
|
||||
return $this->__getSuccessResponseBasedOnContext($successMessage, $savedWorkflow, 'edit', false, $redirectTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public function editor($id = false)
|
||||
public function editor($trigger_id)
|
||||
{
|
||||
$modules = $this->Workflow->getModulesByType();
|
||||
$workflow = $this->Workflow->fetchWorkflow($this->Auth->user(), $id);
|
||||
$modules = $this->Workflow->attachNotificationToModules($this->Auth->user(), $modules, $workflow);
|
||||
$workflows = $this->Workflow->fetchWorkflows($this->Auth->user());
|
||||
$trigger_ids = Hash::extract($modules['blocks_trigger'], '{n}.id');
|
||||
if (!in_array($trigger_id, $trigger_ids)) {
|
||||
return $this->__getFailResponseBasedOnContext(
|
||||
[__('Unkown trigger %s', $trigger_id)],
|
||||
null,
|
||||
'add',
|
||||
$trigger_id,
|
||||
['controller' => 'workflows', 'action' => 'triggers']
|
||||
);
|
||||
}
|
||||
$workflow = $this->Workflow->fetchWorkflowByTrigger($trigger_id, false);
|
||||
if (empty($workflow)) { // Workflow do not exists yet. Create it.
|
||||
$this->Workflow->create();
|
||||
$savedWorkflow = $this->Workflow->save([
|
||||
'name' => sprintf('Workflow for trigger %s', $trigger_id),
|
||||
'trigger_id' => $trigger_id,
|
||||
]);
|
||||
if (empty($savedWorkflow)) {
|
||||
return $this->__getFailResponseBasedOnContext(
|
||||
[__('Could not create workflow for trigger %s', $trigger_id), $this->validationErrors],
|
||||
null,
|
||||
'add',
|
||||
$trigger_id,
|
||||
['controller' => 'workflows', 'action' => 'editor']
|
||||
);
|
||||
}
|
||||
$workflow = $savedWorkflow;
|
||||
}
|
||||
$modules = $this->Workflow->attachNotificationToModules($modules, $workflow);
|
||||
$this->set('selectedWorkflow', $workflow);
|
||||
$this->set('workflows', $workflows);
|
||||
$this->set('modules', $modules);
|
||||
}
|
||||
|
||||
public function triggers()
|
||||
{
|
||||
$triggers = $this->Workflow->getModulesByType('trigger');
|
||||
$triggers = $this->Workflow->attachWorkflowToTriggers($triggers);
|
||||
$data = $triggers;
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($data, $this->response->type());
|
||||
}
|
||||
$this->set('data', $data);
|
||||
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'index_trigger']);
|
||||
}
|
||||
|
||||
public function moduleIndex()
|
||||
{
|
||||
$modules = $this->Workflow->getModulesByType();
|
||||
$this->Module = ClassRegistry::init('Module');
|
||||
$mispModules = $this->Module->getModules('Enrichment');
|
||||
$mispModules = $this->Module->getModules('Action');
|
||||
$this->set('module_service_error', !is_array($mispModules));
|
||||
// FIXME: Apply ACL to filter out module not available to users
|
||||
$filters = $this->IndexFilter->harvestParameters(['type']);
|
||||
$moduleType = $filters['type'] ?? 'trigger';
|
||||
if ($moduleType == 'trigger') {
|
||||
$triggers = $modules['blocks_trigger'];
|
||||
$triggers = $this->Workflow->attachWorkflowsToTriggers($this->Auth->user(), $triggers, true);
|
||||
$data = $triggers;
|
||||
} elseif ($moduleType == 'all') {
|
||||
$moduleType = $filters['type'] ?? 'action';
|
||||
if ($moduleType == 'all') {
|
||||
$data = array_merge(
|
||||
$modules["blocks_trigger"],
|
||||
$modules["blocks_logic"],
|
||||
$modules["blocks_action"]
|
||||
$modules["blocks_action"],
|
||||
$modules["blocks_logic"]
|
||||
);
|
||||
} else {
|
||||
$data = $modules["blocks_{$moduleType}"];
|
||||
|
@ -192,8 +216,9 @@ class WorkflowsController extends AppController
|
|||
if (empty($module)) {
|
||||
throw new NotFoundException(__('Invalid trigger ID'));
|
||||
}
|
||||
if ($module['module_type'] == 'trigger') {
|
||||
$module = $this->Workflow->attachWorkflowsToTriggers($this->Auth->user(), [$module], true)[0];
|
||||
$is_trigger = $module['module_type'] == 'trigger';
|
||||
if ($is_trigger) {
|
||||
$module = $this->Workflow->attachWorkflowToTriggers([$module])[0];
|
||||
}
|
||||
if ($this->_isRest()) {
|
||||
return $this->RestResponse->viewData($module, $this->response->type());
|
||||
|
@ -220,7 +245,7 @@ class WorkflowsController extends AppController
|
|||
|
||||
public function export($id)
|
||||
{
|
||||
$workflow = $this->Workflow->fetchWorkflow($this->Auth->user(), $id);
|
||||
$workflow = $this->Workflow->fetchWorkflow($id);
|
||||
$content = JsonTool::encode($workflow, JSON_PRETTY_PRINT);
|
||||
$this->response->body($content);
|
||||
$this->response->type('json');
|
||||
|
@ -234,7 +259,7 @@ class WorkflowsController extends AppController
|
|||
if (empty($trigger)) {
|
||||
throw new NotFoundException(__('Invalid trigger ID'));
|
||||
}
|
||||
$trigger = $this->Workflow->attachWorkflowsToTriggers($this->Auth->user(), [$trigger], true)[0];
|
||||
$trigger = $this->Workflow->attachWorkflowToTriggers([$trigger])[0];
|
||||
$workflow_order = [];
|
||||
if (!empty($trigger['Workflows']['blocking'])) {
|
||||
$workflow_order = Hash::extract($trigger['Workflows']['blocking'], '{n}.Workflow.id');
|
||||
|
@ -291,7 +316,7 @@ class WorkflowsController extends AppController
|
|||
return $this->RestResponse->saveFailResponse('Workflow', $action, $id, $message, false, $data);
|
||||
} else {
|
||||
$this->Flash->error($message);
|
||||
$this->redirect($this->referer());
|
||||
$this->redirect($redirect);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,7 +325,7 @@ class WorkflowsController extends AppController
|
|||
if (!isset($newReport['Workflow'])) {
|
||||
$newReport = ['Workflow' => $newWorkflow];
|
||||
}
|
||||
$ignoreFieldList = ['id', 'uuid', 'org_id', 'user_id'];
|
||||
$ignoreFieldList = ['id', 'uuid'];
|
||||
foreach (Workflow::CAPTURE_FIELDS as $field) {
|
||||
if (!in_array($field, $ignoreFieldList) && isset($newWorkflow['Workflow'][$field])) {
|
||||
$savedWorkflow['Workflow'][$field] = $newWorkflow['Workflow'][$field];
|
||||
|
|
|
@ -15,14 +15,6 @@ class Workflow extends AppModel
|
|||
];
|
||||
|
||||
public $belongsTo = [
|
||||
'User' => [
|
||||
'className' => 'User',
|
||||
'foreignKey' => 'user_id',
|
||||
],
|
||||
'Organisation' => [
|
||||
'className' => 'Organisation',
|
||||
'foreignKey' => 'org_id'
|
||||
]
|
||||
];
|
||||
|
||||
public $validate = [
|
||||
|
@ -55,8 +47,6 @@ class Workflow extends AppModel
|
|||
];
|
||||
|
||||
public $defaultContain = [
|
||||
// 'Organisation',
|
||||
// 'User'
|
||||
];
|
||||
|
||||
private $loaded_modules = [];
|
||||
|
@ -81,6 +71,11 @@ class Workflow extends AppModel
|
|||
public function beforeValidate($options = array())
|
||||
{
|
||||
parent::beforeValidate();
|
||||
if (empty($this->data['Workflow']['uuid'])) {
|
||||
$this->data['Workflow']['uuid'] = CakeText::uuid();
|
||||
} else {
|
||||
$this->data['Workflow']['uuid'] = strtolower($this->data['Workflow']['uuid']);
|
||||
}
|
||||
if (empty($this->data['Workflow']['data'])) {
|
||||
$this->data['Workflow']['data'] = [];
|
||||
}
|
||||
|
@ -147,10 +142,10 @@ class Workflow extends AppModel
|
|||
return !empty($this->__getWorkflowsIDPerTrigger($trigger_id));
|
||||
}
|
||||
|
||||
public function rebuildRedis($user)
|
||||
public function rebuildRedis()
|
||||
{
|
||||
$redis = $this->setupRedisWithException();
|
||||
$workflows = $this->fetchWorkflows($user);
|
||||
$workflows = $this->fetchWorkflows();
|
||||
$keys = $redis->keys(Workflow::REDIS_KEY_WORKFLOW_NAMESPACE . ':*');
|
||||
$redis->delete($keys);
|
||||
foreach ($workflows as $wokflow) {
|
||||
|
@ -285,79 +280,60 @@ class Workflow extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* buildACLConditions Generate ACL conditions for viewing the workflow
|
||||
* attachWorkflowToTriggers Collect the workflows listening to this trigger
|
||||
*
|
||||
* @param array $user
|
||||
* @return array
|
||||
*/
|
||||
public function buildACLConditions(array $user)
|
||||
{
|
||||
$conditions = [];
|
||||
if (!$user['Role']['perm_site_admin']) {
|
||||
$conditions['Workflow.org_id'] = $user['org_id'];
|
||||
}
|
||||
return $conditions;
|
||||
}
|
||||
|
||||
public function canEdit(array $user, array $workflow)
|
||||
{
|
||||
if ($user['Role']['perm_site_admin']) {
|
||||
return true;
|
||||
}
|
||||
if (empty($workflow['Workflow'])) {
|
||||
return __('Could not find associated workflow');
|
||||
}
|
||||
if ($workflow['Workflow']['user_id'] != $user['id']) {
|
||||
return __('Only the creator user of the workflow can modify it');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* attachWorkflowsToTriggers Collect the workflows listening to this trigger
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $triggers
|
||||
* @param bool $group_per_blocking Whether or not the workflows should be grouped together if they have a blocking path set
|
||||
* @return array
|
||||
*/
|
||||
public function attachWorkflowsToTriggers(array $user, array $triggers, bool $group_per_blocking=true): array
|
||||
public function attachWorkflowToTriggers(array $triggers): array
|
||||
{
|
||||
$all_workflow_ids = [];
|
||||
$workflows_per_trigger = [];
|
||||
$ordered_workflows_per_trigger = [];
|
||||
foreach ($triggers as $trigger) {
|
||||
$workflow_ids_for_trigger = $this->__getWorkflowsIDPerTrigger($trigger['id']);
|
||||
$workflows_per_trigger[$trigger['id']] = $workflow_ids_for_trigger;
|
||||
$ordered_workflows_per_trigger[$trigger['id']] = $this->__getOrderedWorkflowsPerTrigger($trigger['id']);
|
||||
foreach ($workflow_ids_for_trigger as $id) {
|
||||
$all_workflow_ids[$id] = true;
|
||||
}
|
||||
}
|
||||
$all_workflow_ids = array_keys($all_workflow_ids);
|
||||
$workflows = $this->fetchWorkflows($user, [
|
||||
// $all_workflow_ids = [];
|
||||
// $workflows_per_trigger = [];
|
||||
// $ordered_workflows_per_trigger = [];
|
||||
// foreach ($triggers as $trigger) {
|
||||
// $workflow_ids_for_trigger = $this->__getWorkflowsIDPerTrigger($trigger['id']);
|
||||
// $workflows_per_trigger[$trigger['id']] = $workflow_ids_for_trigger;
|
||||
// $ordered_workflows_per_trigger[$trigger['id']] = $this->__getOrderedWorkflowsPerTrigger($trigger['id']);
|
||||
// foreach ($workflow_ids_for_trigger as $id) {
|
||||
// $all_workflow_ids[$id] = true;
|
||||
// }
|
||||
// }
|
||||
// $all_workflow_ids = array_keys($all_workflow_ids);
|
||||
// $workflows = $this->fetchWorkflows([
|
||||
// 'conditions' => [
|
||||
// 'Workflow.id' => $all_workflow_ids,
|
||||
// ],
|
||||
// 'fields' => ['*'],
|
||||
// ]);
|
||||
// $workflows = Hash::combine($workflows, '{n}.Workflow.id', '{n}');
|
||||
// foreach ($triggers as $i => $trigger) {
|
||||
// $workflow_ids = $workflows_per_trigger[$trigger['id']];
|
||||
// $ordered_workflow_ids = $ordered_workflows_per_trigger[$trigger['id']];
|
||||
// $triggers[$i]['Workflows'] = [];
|
||||
// foreach ($workflow_ids as $workflow_id) {
|
||||
// $triggers[$i]['Workflows'][] = $workflows[$workflow_id];
|
||||
// }
|
||||
// if (!empty($group_per_blocking)) {
|
||||
// $triggers[$i]['GroupedWorkflows'] = $this->groupWorkflowsPerBlockingType($triggers[$i]['Workflows'], $trigger['id'], $ordered_workflow_ids);
|
||||
// }
|
||||
// }
|
||||
// return $triggers;
|
||||
$workflows = $this->fetchWorkflows([
|
||||
'conditions' => [
|
||||
'Workflow.id' => $all_workflow_ids,
|
||||
'Workflow.trigger_id' => Hash::extract($triggers, '{n}.id'),
|
||||
],
|
||||
'fields' => ['*'],
|
||||
'contain' => ['Organisation' => ['fields' => ['*']]],
|
||||
]);
|
||||
$workflows = Hash::combine($workflows, '{n}.Workflow.id', '{n}');
|
||||
$workflows_per_trigger = Hash::combine($workflows, '{n}.Workflow.trigger_id', '{n}');
|
||||
foreach ($triggers as $i => $trigger) {
|
||||
$workflow_ids = $workflows_per_trigger[$trigger['id']];
|
||||
$ordered_workflow_ids = $ordered_workflows_per_trigger[$trigger['id']];
|
||||
$triggers[$i]['Workflows'] = [];
|
||||
foreach ($workflow_ids as $workflow_id) {
|
||||
$triggers[$i]['Workflows'][] = $workflows[$workflow_id];
|
||||
}
|
||||
if (!empty($group_per_blocking)) {
|
||||
$triggers[$i]['GroupedWorkflows'] = $this->groupWorkflowsPerBlockingType($triggers[$i]['Workflows'], $trigger['id'], $ordered_workflow_ids);
|
||||
if (!empty($workflows_per_trigger[$trigger['id']])) {
|
||||
$triggers[$i]['Workflow'] = $workflows_per_trigger[$trigger['id']]['Workflow'];
|
||||
}
|
||||
}
|
||||
return $triggers;
|
||||
}
|
||||
|
||||
public function fetchWorkflowsForTrigger($user, $trigger_id, $filterDisabled=false): array
|
||||
public function fetchWorkflowsForTrigger($trigger_id, $filterDisabled=false): array
|
||||
{
|
||||
$workflow_ids_for_trigger = $this->__getWorkflowsIDPerTrigger($trigger_id);
|
||||
$conditions = [
|
||||
|
@ -366,10 +342,9 @@ class Workflow extends AppModel
|
|||
if (!empty($filterDisabled)) {
|
||||
$conditions['Workflow.enabled'] = true;
|
||||
}
|
||||
$workflows = $this->fetchWorkflows($user, [
|
||||
$workflows = $this->fetchWorkflows([
|
||||
'conditions' => $conditions,
|
||||
'fields' => ['*'],
|
||||
'contain' => ['Organisation' => ['fields' => ['*']], 'User' => ['Role']],
|
||||
]);
|
||||
return $workflows;
|
||||
}
|
||||
|
@ -377,16 +352,15 @@ class Workflow extends AppModel
|
|||
/**
|
||||
* getExecutionOrderForTrigger Generate the execution order for the provided trigger
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $trigger
|
||||
* @return array
|
||||
*/
|
||||
public function getExecutionOrderForTrigger(array $user, array $trigger, $filterDisabled=false): array
|
||||
public function getExecutionOrderForTrigger(array $trigger, $filterDisabled=false): array
|
||||
{
|
||||
if (empty($trigger)) {
|
||||
return ['blocking' => [], 'non-blocking' => [] ];
|
||||
}
|
||||
$workflows = $this->fetchWorkflowsForTrigger($user, $trigger['id'], $filterDisabled);
|
||||
$workflows = $this->fetchWorkflowsForTrigger($trigger['id'], $filterDisabled);
|
||||
$ordered_workflow_ids = $this->__getOrderedWorkflowsPerTrigger($trigger['id']);
|
||||
return $this->groupWorkflowsPerBlockingType($workflows, $trigger['id'], $ordered_workflow_ids);
|
||||
}
|
||||
|
@ -469,7 +443,6 @@ class Workflow extends AppModel
|
|||
{
|
||||
$this->loadAllWorkflowModules();
|
||||
|
||||
$user = ['Role' => ['perm_site_admin' => true]];
|
||||
if (empty($this->loaded_modules['trigger'][$trigger_id])) {
|
||||
throw new TriggerNotFoundException(__('Unknown trigger `%s`', $trigger_id));
|
||||
}
|
||||
|
@ -479,13 +452,13 @@ class Workflow extends AppModel
|
|||
}
|
||||
|
||||
$blockingPathExecutionSuccess = true;
|
||||
$workflowExecutionOrder = $this->getExecutionOrderForTrigger($user, $trigger, true);
|
||||
$workflowExecutionOrder = $this->getExecutionOrderForTrigger($trigger, true);
|
||||
$orderedBlockingWorkflows = $workflowExecutionOrder['blocking'];
|
||||
$orderedDeferredWorkflows = $workflowExecutionOrder['non-blocking'];
|
||||
foreach ($orderedBlockingWorkflows as $workflow) {
|
||||
$walkResult = [];
|
||||
$continueExecution = $this->walkGraph($workflow, $trigger_id, 'blocking', $data, $blockingErrors, $walkResult);
|
||||
$this->loadLog()->createLogEntry($this->User->getAuthUser($workflow['User']['id']), 'walkGraph', 'Workflow', $workflow['Workflow']['id'], __('Executed blocking path for trigger `%s`', $trigger_id), $this->digestExecutionResult($walkResult));
|
||||
// $this->loadLog()->createLogEntry($this->User->getAuthUser($workflow['User']['id']), 'walkGraph', 'Workflow', $workflow['Workflow']['id'], __('Executed blocking path for trigger `%s`', $trigger_id), $this->digestExecutionResult($walkResult));
|
||||
if (!$continueExecution) {
|
||||
$blockingPathExecutionSuccess = false;
|
||||
break;
|
||||
|
@ -495,7 +468,7 @@ class Workflow extends AppModel
|
|||
$deferredErrors = [];
|
||||
$walkResult = [];
|
||||
$this->walkGraph($workflow, $trigger_id, 'non-blocking', $data, $deferredErrors, $walkResult);
|
||||
$this->loadLog()->createLogEntry($this->User->getAuthUser($workflow['User']['id']), 'walkGraph', 'Workflow', $workflow['Workflow']['id'], __('Executed non-blocking path for trigger `%s`', $trigger_id), $this->digestExecutionResult($walkResult));
|
||||
// $this->loadLog()->createLogEntry($this->User->getAuthUser($workflow['User']['id']), 'walkGraph', 'Workflow', $workflow['Workflow']['id'], __('Executed non-blocking path for trigger `%s`', $trigger_id), $this->digestExecutionResult($walkResult));
|
||||
}
|
||||
return $blockingPathExecutionSuccess;
|
||||
}
|
||||
|
@ -527,7 +500,7 @@ class Workflow extends AppModel
|
|||
'Executed nodes' => [],
|
||||
'Nodes that stopped execution' => [],
|
||||
];
|
||||
$workflowUser = $this->User->getAuthUser($workflow['Workflow']['user_id'], true);
|
||||
// $workflowUser = $this->User->getAuthUser($workflow['Workflow']['user_id'], true);
|
||||
$roamingData = $this->workflowGraphTool->getRoamingData($workflowUser, $data);
|
||||
$graphData = !empty($workflow['Workflow']) ? $workflow['Workflow']['data'] : $workflow['data'];
|
||||
$startNode = $this->workflowGraphTool->getNodeIdForTrigger($graphData, $trigger_id);
|
||||
|
@ -604,7 +577,7 @@ class Workflow extends AppModel
|
|||
return $moduleClass;
|
||||
}
|
||||
|
||||
public function attachNotificationToModules(array $user, array $modules, array $workflow): array
|
||||
public function attachNotificationToModules(array $modules, array $workflow): array
|
||||
{
|
||||
foreach ($modules as $moduleType => $modulesByType) {
|
||||
foreach ($modulesByType as $i => $module) {
|
||||
|
@ -615,24 +588,6 @@ class Workflow extends AppModel
|
|||
];
|
||||
}
|
||||
}
|
||||
$triggers = $modules['blocks_trigger'];
|
||||
foreach ($triggers as $i => $trigger) {
|
||||
$blockingExecutionOrder = $this->getExecutionOrderForTrigger($user, $trigger, true)['blocking'];
|
||||
$blockingExecutionOrderIDs = Hash::extract($blockingExecutionOrder, '{n}.Workflow.id');
|
||||
$indexInExecutionPath = array_search($workflow['Workflow']['id'], $blockingExecutionOrderIDs);
|
||||
$effectiveBlockingExecutionOrder = array_slice($blockingExecutionOrder, 0, $indexInExecutionPath);
|
||||
$details = [];
|
||||
foreach ($effectiveBlockingExecutionOrder as $workflow) {
|
||||
$details[] = sprintf('[%s] %s', h($workflow['Workflow']['id']), h($workflow['Workflow']['name']));
|
||||
}
|
||||
if (!empty($effectiveBlockingExecutionOrder)) {
|
||||
$modules['blocks_trigger'][$i]['notifications']['warning'][] = [
|
||||
'text' => __('%s blocking worflows are executed before this trigger.', count($effectiveBlockingExecutionOrder)),
|
||||
'description' => __('The blocking path of this trigger might not be executed. If any of the blocking workflows stop the propagation, the blocking path of this trigger will not be executed. Nevertheless, the deferred path will always be executed.'),
|
||||
'details' => $details,
|
||||
];
|
||||
}
|
||||
}
|
||||
return $modules;
|
||||
}
|
||||
|
||||
|
@ -650,8 +605,7 @@ class Workflow extends AppModel
|
|||
$this->loaded_modules[$type] = $classModuleFromFiles['classConfigs'];
|
||||
$this->loaded_classes[$type] = $classModuleFromFiles['instancedClasses'];
|
||||
}
|
||||
$superUser = ['Role' => ['perm_site_admin' => true]];
|
||||
$modules_from_service = $this->__getModulesFromModuleService($superUser) ?? [];
|
||||
$modules_from_service = $this->__getModulesFromModuleService() ?? [];
|
||||
$misp_module_class = $this->__getClassForMispModule($modules_from_service);
|
||||
$misp_module_configs = [];
|
||||
foreach ($misp_module_class as $i => $module_class) {
|
||||
|
@ -687,12 +641,12 @@ class Workflow extends AppModel
|
|||
/* FIXME: end */
|
||||
}
|
||||
|
||||
private function __getEnabledModulesFromModuleService($user)
|
||||
private function __getEnabledModulesFromModuleService()
|
||||
{
|
||||
if (empty($this->Module)) {
|
||||
$this->Module = ClassRegistry::init('Module');
|
||||
}
|
||||
$enabledModules = $this->Module->getEnabledModules($user, null, 'Action');
|
||||
$enabledModules = $this->Module->getEnabledModules(null, 'Action');
|
||||
$misp_module_config = empty($enabledModules) ? false : $enabledModules;
|
||||
return $misp_module_config;
|
||||
}
|
||||
|
@ -848,8 +802,8 @@ class Workflow extends AppModel
|
|||
'blocks_action' => $blocks_action,
|
||||
];
|
||||
if (!empty($module_type)) {
|
||||
if (!empty($modules[$module_type])) {
|
||||
return $modules['block_' . $module_type];
|
||||
if (!empty($modules['blocks_' . $module_type])) {
|
||||
return $modules['blocks_' . $module_type];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
@ -890,17 +844,15 @@ class Workflow extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* fetchWorkflows ACL-aware method. Basically find with ACL
|
||||
* fetchWorkflows
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $options
|
||||
* @param bool $full
|
||||
* @return array
|
||||
*/
|
||||
public function fetchWorkflows(array $user, array $options = array(), $full = false)
|
||||
public function fetchWorkflows(array $options = array(), $full = false)
|
||||
{
|
||||
$params = array(
|
||||
'conditions' => $this->buildACLConditions($user),
|
||||
'contain' => $this->defaultContain,
|
||||
'recursive' => -1
|
||||
);
|
||||
|
@ -927,29 +879,55 @@ class Workflow extends AppModel
|
|||
}
|
||||
|
||||
/**
|
||||
* fetchWorkflow ACL-aware method. Basically find with ACL
|
||||
* fetchWorkflow
|
||||
*
|
||||
* @param array $user
|
||||
* @param int|string $id
|
||||
* @param bool $throwErrors
|
||||
* @throws NotFoundException
|
||||
* @return array
|
||||
*/
|
||||
public function fetchWorkflow(array $user, $id, bool $throwErrors = true)
|
||||
public function fetchWorkflow($id, bool $throwErrors = true): array
|
||||
{
|
||||
$options = [];
|
||||
if (is_numeric($id)) {
|
||||
$options = ['conditions' => ["Workflow.id" => $id]];
|
||||
$options = ['conditions' => ['Workflow.id' => $id]];
|
||||
} elseif (Validation::uuid($id)) {
|
||||
$options = ['conditions' => ["Workflow.uuid" => $id]];
|
||||
$options = ['conditions' => ['Workflow.uuid' => $id]];
|
||||
} else {
|
||||
if ($throwErrors) {
|
||||
throw new NotFoundException(__('Invalid workflow'));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
$workflow = $this->fetchWorkflows($user, $options);
|
||||
$workflow = $this->fetchWorkflows($options);
|
||||
if (empty($workflow)) {
|
||||
throw new NotFoundException(__('Invalid workflow'));
|
||||
if ($throwErrors) {
|
||||
throw new NotFoundException(__('Invalid workflow'));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return $workflow[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* fetchWorkflowByTrigger
|
||||
*
|
||||
* @param int|string $id
|
||||
* @param bool $throwErrors
|
||||
* @throws NotFoundException
|
||||
* @return array
|
||||
*/
|
||||
public function fetchWorkflowByTrigger($trigger_id, bool $throwErrors = true): array
|
||||
{
|
||||
$options = ['conditions' => [
|
||||
'Workflow.trigger_id' => $trigger_id,
|
||||
]];
|
||||
$workflow = $this->fetchWorkflows($options);
|
||||
if (empty($workflow)) {
|
||||
if ($throwErrors) {
|
||||
throw new NotFoundException(__('Invalid workflow'));
|
||||
}
|
||||
return [];
|
||||
}
|
||||
return $workflow[0];
|
||||
}
|
||||
|
@ -957,18 +935,17 @@ class Workflow extends AppModel
|
|||
/**
|
||||
* editWorkflow Edit a worflow
|
||||
*
|
||||
* @param array $user
|
||||
* @param array $workflow
|
||||
* @return array Any errors preventing the edition
|
||||
*/
|
||||
public function editWorkflow(array $user, array $workflow)
|
||||
public function editWorkflow(array $workflow)
|
||||
{
|
||||
$errors = array();
|
||||
if (!isset($workflow['Workflow']['uuid'])) {
|
||||
$errors[] = __('Workflow doesn\'t have an UUID');
|
||||
return $errors;
|
||||
}
|
||||
$existingWorkflow = $this->fetchWorkflow($user, $workflow['Workflow']['id']);
|
||||
$existingWorkflow = $this->fetchWorkflow($workflow['Workflow']['id']);
|
||||
$workflow['Workflow']['id'] = $existingWorkflow['Workflow']['id'];
|
||||
unset($workflow['Workflow']['timestamp']);
|
||||
$errors = $this->__saveAndReturnErrors($workflow, ['fieldList' => self::CAPTURE_FIELDS], $errors);
|
||||
|
@ -978,16 +955,15 @@ class Workflow extends AppModel
|
|||
/**
|
||||
* fetchWorkflow ACL-aware method. Basically find with ACL
|
||||
*
|
||||
* @param array $user
|
||||
* @param int|string $id
|
||||
* @param bool $enable
|
||||
* @param bool $throwErrors
|
||||
* @return array
|
||||
*/
|
||||
public function toggleWorkflow(array $user, $id, $enable=true, bool $throwErrors=true)
|
||||
public function toggleWorkflow($id, $enable=true, bool $throwErrors=true)
|
||||
{
|
||||
$errors = array();
|
||||
$workflow = $this->fetchWorkflow($user, $id, $throwErrors);
|
||||
$workflow = $this->fetchWorkflow($id, $throwErrors);
|
||||
$workflow['Workflow']['enabled'] = $enable;
|
||||
$errors = $this->__saveAndReturnErrors($workflow, ['fieldList' => ['enabled']], $errors);
|
||||
return $errors;
|
||||
|
|
|
@ -8,7 +8,7 @@ class Module_email_sent extends WorkflowBaseModule
|
|||
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
|
||||
public $icon = 'envelope';
|
||||
public $inputs = 0;
|
||||
public $outputs = 2;
|
||||
public $outputs = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ class Module_feed_pull extends WorkflowBaseModule
|
|||
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
|
||||
public $icon = 'arrow-alt-circle-down';
|
||||
public $inputs = 0;
|
||||
public $outputs = 2;
|
||||
public $outputs = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ class Module_new_attribute extends WorkflowBaseModule
|
|||
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
|
||||
public $icon = 'cube';
|
||||
public $inputs = 0;
|
||||
public $outputs = 2;
|
||||
public $outputs = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ class Module_new_object extends WorkflowBaseModule
|
|||
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
|
||||
public $icon = 'cubes';
|
||||
public $inputs = 0;
|
||||
public $outputs = 2;
|
||||
public $outputs = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ class Module_new_user extends WorkflowBaseModule
|
|||
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
|
||||
public $icon = 'user-plus';
|
||||
public $inputs = 0;
|
||||
public $outputs = 2;
|
||||
public $outputs = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ class Module_publish extends WorkflowBaseModule
|
|||
public $description = 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.';
|
||||
public $icon = 'upload';
|
||||
public $inputs = 0;
|
||||
public $outputs = 2;
|
||||
public $outputs = 1;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
|
|
@ -85,7 +85,7 @@ echo $this->element('genericElements/assetLoader', [
|
|||
});
|
||||
var centroidX = sumX / nodes.length
|
||||
var centroidY = sumY / nodes.length
|
||||
var calc_zoom = Math.min(editor_bcr.width / maxX, editor_bcr.height / maxY) // Zoom out if needed
|
||||
var calc_zoom = Math.min(Math.min(editor_bcr.width / maxX, editor_bcr.height / maxY), 1) // Zoom out if needed
|
||||
editor.translate_to(
|
||||
offset_x - centroidX + offset_block_x,
|
||||
offset_y - centroidY - offset_block_y
|
||||
|
|
|
@ -1615,8 +1615,8 @@ $divider = $this->element('/genericElements/SideMenu/side_menu_divider');
|
|||
case 'workflows':
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'index',
|
||||
'url' => '/workflows/index',
|
||||
'text' => __('List Workflows')
|
||||
'url' => '/workflows/triggers',
|
||||
'text' => __('List Triggers')
|
||||
));
|
||||
echo $this->element('/genericElements/SideMenu/side_menu_link', array(
|
||||
'element_id' => 'index_trigger',
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<?php
|
||||
$allModules = [];
|
||||
foreach ($modules as $moduleType => $module) {
|
||||
$allModules = array_merge($allModules, $module);
|
||||
}
|
||||
$usableModules = [
|
||||
'blocks_action' => $modules['blocks_action'],
|
||||
'blocks_logic' => $modules['blocks_logic'],
|
||||
];
|
||||
$allModules = array_merge($usableModules['blocks_action'], $usableModules['blocks_logic']);
|
||||
$triggerModules = $modules['blocks_trigger'];
|
||||
?>
|
||||
<div class="root-container">
|
||||
<div class="main-container">
|
||||
|
@ -11,18 +13,13 @@ foreach ($modules as $moduleType => $module) {
|
|||
<i class="fa-fw <?= $this->FontAwesome->getClass('caret-left') ?>"></i>
|
||||
<?= __('Workflow index') ?>
|
||||
</a>
|
||||
<h3>Workflows</h3>
|
||||
<div class="workflow-selector-container">
|
||||
<select type="text" placeholder="Load a workflow" class="chosen-container workflows" autocomplete="off">
|
||||
<?php foreach ($workflows as $workflow) : ?>
|
||||
<option value="<?= h($workflow['Workflow']['id']) ?>" <?= $selectedWorkflow['Workflow']['id'] == $workflow['Workflow']['id'] ? 'selected' : '' ?>><?= h($workflow['Workflow']['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<h3>
|
||||
<span style="font-weight:normal;"><?= __('Workflows:') ?></span>
|
||||
<strong><?= h($selectedWorkflow['Workflow']['trigger_id']) ?></strong>
|
||||
</h3>
|
||||
<div class="" style="margin-top: 0.5em;">
|
||||
<div class="btn-group" style="margin-left: 3px;">
|
||||
<a class="btn btn-primary" href="<?= $baseurl . '/workflows/add' ?>"><i class="fa-fw <?= $this->FontAwesome->getClass('plus') ?>"></i> <?= __('New') ?></a>
|
||||
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#"><span class="caret"></span></a>
|
||||
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#"><?= __('More Actions') ?> <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="importWorkflow" href="<?= $baseurl . '/workflows/import/' ?>"><i class="fa-fw <?= $this->FontAwesome->getClass('file-import') ?>"></i> <?= __('Import workflow') ?></a></li>
|
||||
<li><a id="exportWorkflow" href="<?= $baseurl . '/workflows/export/' . h($selectedWorkflow['Workflow']['id']) ?>"><i class="fa-fw <?= $this->FontAwesome->getClass('file-export') ?>"></i> <?= __('Export workflow') ?></a></li>
|
||||
|
@ -45,23 +42,19 @@ foreach ($modules as $moduleType => $module) {
|
|||
</select>
|
||||
|
||||
<ul class="nav nav-tabs" id="block-tabs">
|
||||
<li class="active"><a href="#container-triggers">
|
||||
<i class="<?= $this->FontAwesome->getClass('flag') ?>"></i>
|
||||
Triggers
|
||||
<li class="active"><a href="#container-actions">
|
||||
<i class="<?= $this->FontAwesome->getClass('play') ?>"></i>
|
||||
Actions
|
||||
</a></li>
|
||||
<li><a href="#container-logic">
|
||||
<i class="<?= $this->FontAwesome->getClass('code-branch') ?>"></i>
|
||||
Logic
|
||||
</a></li>
|
||||
<li><a href="#container-actions">
|
||||
<i class="<?= $this->FontAwesome->getClass('play') ?>"></i>
|
||||
Actions
|
||||
</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="container-triggers">
|
||||
<?php foreach ($modules['blocks_trigger'] as $block) : ?>
|
||||
<div class="tab-pane active" id="container-actions">
|
||||
<?php foreach ($modules['blocks_action'] as $block) : ?>
|
||||
<?= $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
@ -70,11 +63,6 @@ foreach ($modules as $moduleType => $module) {
|
|||
<?= $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div class="tab-pane" id="container-actions">
|
||||
<?php foreach ($modules['blocks_action'] as $block) : ?>
|
||||
<?= $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -138,14 +126,14 @@ echo $this->element('genericElements/assetLoader', [
|
|||
var $exportWorkflowButton = $('#exportWorkflow')
|
||||
var $saveWorkflowButton = $('#saveWorkflow')
|
||||
var $lastModifiedField = $('#lastModifiedField')
|
||||
var $blockContainerTriggers = $('#container-triggers')
|
||||
var $blockContainerLogic = $('#container-logic')
|
||||
var $blockContainerAction = $('#container-actions')
|
||||
var editor = false
|
||||
var all_blocks = <?= json_encode($allModules) ?>;
|
||||
var all_blocks_by_id = <?= json_encode(Hash::combine($allModules, '{n}.id', '{n}')) ?>;
|
||||
var all_triggers_by_id = <?= json_encode(Hash::combine($triggerModules, '{n}.id', '{n}')) ?>;
|
||||
var workflow = false
|
||||
<?php if (!empty($workflow)) : ?>
|
||||
<?php if (!empty($selectedWorkflow)) : ?>
|
||||
var workflow = <?= json_encode($selectedWorkflow) ?>;
|
||||
<?php endif; ?>
|
||||
|
||||
|
|
|
@ -15,12 +15,10 @@
|
|||
'data_path' => 'description',
|
||||
],
|
||||
[
|
||||
'name' => __('Workflow Execution Order'),
|
||||
'requirement' => $indexType == 'trigger',
|
||||
'element' => 'custom',
|
||||
'function' => function ($row) {
|
||||
return $this->element('Workflows/executionOrder', ['trigger' => $row]);
|
||||
}
|
||||
'name' => __('Module Type'),
|
||||
'sort' => 'module_type',
|
||||
'class' => 'short',
|
||||
'data_path' => 'module_type',
|
||||
],
|
||||
[
|
||||
'name' => __('Is misp-module'),
|
||||
|
@ -54,26 +52,21 @@
|
|||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
[
|
||||
'url' => $baseurl . '/workflows/moduleIndex/type:trigger',
|
||||
'text' => __('Triggers'),
|
||||
'active' => $indexType === 'trigger',
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/workflows/moduleIndex/type:all',
|
||||
'text' => __('All'),
|
||||
'active' => $indexType === 'all',
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/workflows/moduleIndex/type:logic',
|
||||
'text' => __('Logic'),
|
||||
'active' => $indexType === 'logic',
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/workflows/moduleIndex/type:action',
|
||||
'text' => __('Action'),
|
||||
'active' => $indexType === 'action',
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/workflows/moduleIndex/type:logic',
|
||||
'text' => __('Logic'),
|
||||
'active' => $indexType === 'logic',
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
|
|
|
@ -1,18 +1,8 @@
|
|||
<?php
|
||||
$append = [];
|
||||
if ($data['module_type'] == 'trigger') {
|
||||
$append[] = [
|
||||
'element' => 'Workflows/executionOrderWidget',
|
||||
'element_params' => [
|
||||
'trigger' => $data
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
echo $this->element(
|
||||
'genericElements/SingleViews/single_view',
|
||||
[
|
||||
'title' => 'Workflow module view',
|
||||
'title' => $data['module_type'] == 'trigger' ? __('Trigger module view') : __('Workflow module view'),
|
||||
'data' => $data,
|
||||
'fields' => [
|
||||
[
|
||||
|
@ -57,16 +47,32 @@ echo $this->element(
|
|||
'path' => 'params',
|
||||
],
|
||||
[
|
||||
'key' => __('Workflow Execution Order'),
|
||||
'requirement' => $data['module_type'] == 'trigger',
|
||||
'key' => __('Run counter'),
|
||||
'path' => 'Workflow.counter',
|
||||
'type' => 'custom',
|
||||
'function' => function ($row) {
|
||||
return $this->element('Workflows/executionOrder', ['trigger' => $row]);
|
||||
return h($row['Workflow']['counter']);
|
||||
}
|
||||
],
|
||||
[
|
||||
'key' => __('Workflow Data'),
|
||||
'class' => 'restrict-height',
|
||||
'path' => 'Workflow.data',
|
||||
'type' => 'json',
|
||||
],
|
||||
],
|
||||
'append' => $append
|
||||
'append' => [
|
||||
['element' => 'Workflows/executionPath', 'element_params' => ['workflow' => $data['Workflow']]],
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
<style>
|
||||
.restrict-height>div {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
resize: both;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
$fields = [
|
||||
[
|
||||
'name' => __('Module name'),
|
||||
'sort' => 'name',
|
||||
'data_path' => 'name',
|
||||
'element' => 'custom',
|
||||
'class' => 'bold',
|
||||
'function' => function ($row) {
|
||||
return sprintf('<i class="fa-fw %s"></i> %s', $this->FontAwesome->getClass($row['icon']), h($row['name']));
|
||||
}
|
||||
],
|
||||
[
|
||||
'name' => __('Description'),
|
||||
'data_path' => 'description',
|
||||
],
|
||||
[
|
||||
'name' => __('Module Enabled'),
|
||||
'sort' => 'disabled',
|
||||
'class' => 'short',
|
||||
'data_path' => 'disabled',
|
||||
'element' => 'booleanOrNA',
|
||||
'boolean_reverse' => true
|
||||
],
|
||||
];
|
||||
|
||||
echo $this->element('genericElements/IndexTable/scaffold', [
|
||||
'scaffold_data' => [
|
||||
'data' => [
|
||||
'stupid_pagination' => true,
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
],
|
||||
'fields' => $fields,
|
||||
'icon' => 'flag',
|
||||
'title' => __('Triggers'),
|
||||
'description' => __('List the available triggers that can be listened to by workflows'),
|
||||
'actions' => [
|
||||
[
|
||||
'title' => __('Enable'),
|
||||
'icon' => 'play',
|
||||
'postLink' => true,
|
||||
'url' => $baseurl . '/workflows/enableModule',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'postLinkConfirm' => __('Are you sure you want to enable this module?'),
|
||||
'complex_requirement' => array(
|
||||
'function' => function ($row, $options) use ($isSiteAdmin) {
|
||||
return $isSiteAdmin && $options['datapath']['disabled'];
|
||||
},
|
||||
'options' => array(
|
||||
'datapath' => array(
|
||||
'disabled' => 'disabled'
|
||||
)
|
||||
)
|
||||
),
|
||||
],
|
||||
[
|
||||
'title' => __('Disable'),
|
||||
'icon' => 'stop',
|
||||
'postLink' => true,
|
||||
'url' => $baseurl . '/workflows/disableModule',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'postLinkConfirm' => __('Are you sure you want to disable this module?'),
|
||||
'complex_requirement' => array(
|
||||
'function' => function ($row, $options) use ($isSiteAdmin) {
|
||||
return $isSiteAdmin && !$options['datapath']['disabled'];
|
||||
},
|
||||
'options' => array(
|
||||
'datapath' => array(
|
||||
'disabled' => 'disabled'
|
||||
)
|
||||
)
|
||||
),
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/workflows/editor',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'icon' => 'code',
|
||||
'dbclickAction' => true,
|
||||
],
|
||||
[
|
||||
'url' => $baseurl . '/workflows/moduleView',
|
||||
'url_params_data_paths' => ['id'],
|
||||
'icon' => 'eye',
|
||||
],
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
|
@ -21,13 +21,6 @@ echo $this->element(
|
|||
'key' => __('Timestamp'),
|
||||
'path' => 'Workflow.timestamp',
|
||||
],
|
||||
[
|
||||
'key' => __('Owner Organisation'),
|
||||
'path' => 'Workflow.org_id',
|
||||
'pathName' => 'Organisation.name',
|
||||
'type' => 'model',
|
||||
'model' => 'organisations'
|
||||
],
|
||||
[
|
||||
'key' => __('Description'),
|
||||
'path' => 'Workflow.description'
|
||||
|
|
|
@ -224,6 +224,12 @@
|
|||
/* No special format for default block types */
|
||||
}
|
||||
|
||||
.drawflow .drawflow-node.block-type-trigger {
|
||||
border-left-color: #73a2c9;
|
||||
border-left-width: 0.25rem;
|
||||
border-left-style: solid;
|
||||
}
|
||||
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output::before {
|
||||
display: none;
|
||||
transform: translateX(calc(50% - 8px));
|
||||
|
@ -256,29 +262,19 @@
|
|||
}
|
||||
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_1 {
|
||||
background-color: #ea5b57;
|
||||
background-color: #73a2c9;
|
||||
}
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_1::before {
|
||||
content: 'blocking';
|
||||
content: 'Listen';
|
||||
top: -26px;
|
||||
}
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_1::after {
|
||||
content: "\f251";
|
||||
content: "\f025";
|
||||
font-size: 9px;
|
||||
}
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_1:hover::after {
|
||||
font-size: 14px;
|
||||
}
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_2 {
|
||||
background-color: #468847;
|
||||
}
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_2::before {
|
||||
content: 'deferred';
|
||||
top: 26px;
|
||||
}
|
||||
.drawflow .drawflow-node.block-type-trigger > .outputs > .output_2::after {
|
||||
content: "\f074";
|
||||
}
|
||||
|
||||
.drawflow .drawflow-node.block-type-IF > .outputs > .output::before {
|
||||
display: none;
|
||||
|
|
|
@ -20,7 +20,22 @@ var dotBlock_default = doT.template(' \
|
|||
</div> \
|
||||
</div>')
|
||||
|
||||
var dotBlock_trigger = dotBlock_default
|
||||
var dotBlock_trigger = doT.template(' \
|
||||
<div class="canvas-workflow-block" data-nodeuid="{{=it.node_uid}}"> \
|
||||
<div style="width: 100%;"> \
|
||||
<div class="default-main-container" style="border:none;"> \
|
||||
<i class="fa-fw fa-{{=it.icon}} {{=it.icon_class}}"></i> \
|
||||
<strong style="margin-left: 0.25em;"> \
|
||||
{{=it.name}} \
|
||||
</strong> \
|
||||
<span style="margin-left: auto;"> \
|
||||
<span class="block-notification-container"> \
|
||||
{{=it._block_notification_html}} \
|
||||
</span> \
|
||||
</span> \
|
||||
</div> \
|
||||
</div> \
|
||||
</div>')
|
||||
|
||||
var dotBlock_IF = doT.template(' \
|
||||
<div class="canvas-workflow-block" data-nodeuid="{{=it.node_uid}}"> \
|
||||
|
@ -99,11 +114,9 @@ function initDrawflow() {
|
|||
|
||||
editor.on('nodeCreated', function() {
|
||||
invalidateContentCache()
|
||||
toggleTriggersDraggableState()
|
||||
})
|
||||
editor.on('nodeRemoved', function () {
|
||||
invalidateContentCache()
|
||||
toggleTriggersDraggableState()
|
||||
})
|
||||
editor.on('nodeDataChanged', invalidateContentCache)
|
||||
editor.on('nodeMoved', invalidateContentCache)
|
||||
|
@ -120,7 +133,7 @@ function initDrawflow() {
|
|||
}
|
||||
})
|
||||
editor.on('keydown', function (evt) {
|
||||
if (evt.keyCode == 67) {
|
||||
if (evt.keyCode == 67 && $drawflow.is(evt.target)) {
|
||||
editor.fitCanvas()
|
||||
}
|
||||
})
|
||||
|
@ -227,6 +240,14 @@ function initDrawflow() {
|
|||
fetchAndLoadWorkflow().then(function() {
|
||||
graphPooler.start(undefined)
|
||||
editor.fitCanvas()
|
||||
// block contextual menu for trigger blocks
|
||||
$canvas.find('.canvas-workflow-block').on('contextmenu', function (evt) {
|
||||
var selectedNode = getSelectedBlock()
|
||||
if (selectedNode !== undefined && selectedNode.data.module_type == 'trigger') {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
}
|
||||
})
|
||||
})
|
||||
$saveWorkflowButton.click(saveWorkflow)
|
||||
$importWorkflowButton.click(importWorkflow)
|
||||
|
@ -301,16 +322,11 @@ function revalidateContentCache() {
|
|||
|
||||
|
||||
function addNode(block, position) {
|
||||
var module = all_blocks_by_id[block.id]
|
||||
var module = all_blocks_by_id[block.id] || all_triggers_by_id[block.id]
|
||||
if (!module) {
|
||||
console.error('Tried to add node for unknown module ' + block.data.id + ' (' + block.id + ')')
|
||||
return '';
|
||||
}
|
||||
|
||||
if (editor.registeredTriggers[module.id]) {
|
||||
console.info('Tried to add node for a trigger already registered: ' + module.id)
|
||||
return '';
|
||||
}
|
||||
|
||||
var node_uid = uid() // only used for UI purposes
|
||||
block['node_uid'] = node_uid
|
||||
|
@ -340,50 +356,6 @@ function addNode(block, position) {
|
|||
);
|
||||
}
|
||||
|
||||
function toggleTriggersDraggableState() {
|
||||
if (editor.isLoading) {
|
||||
return
|
||||
}
|
||||
var data = Object.values(getEditorData())
|
||||
editor.registeredTriggers = {}
|
||||
data.forEach(function(node) {
|
||||
if (node.data.module_type == 'trigger') {
|
||||
editor.registeredTriggers[node.data.id] = true
|
||||
}
|
||||
})
|
||||
$blockContainerTriggers.find('.sidebar-workflow-block')
|
||||
.filter(function () {
|
||||
return !$(this).hasClass('ui-draggable-dragging')
|
||||
&& !$(this).data('block').disabled
|
||||
})
|
||||
.draggable('option', { disabled: false })
|
||||
.removeClass(['disabled', 'disabled-one-instance'])
|
||||
.attr('title', '')
|
||||
|
||||
data.forEach(function(node) {
|
||||
if (node.data.module_type == 'trigger') {
|
||||
$blockContainerTriggers.find('#'+node.data.id + '.sidebar-workflow-block')
|
||||
.filter(function () {
|
||||
return !$(this).hasClass('ui-draggable-dragging')
|
||||
|| $(this).data('block').disabled
|
||||
|| editor.registeredTriggers[$(this).data('block').id] !== undefined
|
||||
})
|
||||
.draggable('option', { disabled: true })
|
||||
.addClass(['disabled', 'disabled-one-instance'])
|
||||
.attr('title', 'Only one instance of this trigger is allowed per workflow')
|
||||
.each(function() {
|
||||
var block_id = $(this).data('block').id
|
||||
$chosenBlocks.find('option')
|
||||
.filter(function() {
|
||||
return $(this).val() == block_id
|
||||
})
|
||||
.prop('disabled', true)
|
||||
$chosenBlocks.chosen('destroy').chosen()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getEditorData(cleanInvalidParams) {
|
||||
var data = {} // Make sure nodes are index by their internal IDs
|
||||
var editorExport = editor.export().drawflow.Home.data
|
||||
|
@ -416,7 +388,6 @@ function fetchAndLoadWorkflow() {
|
|||
lastModified = workflow.timestamp + '000'
|
||||
loadWorkflow(workflow)
|
||||
editor.isLoading = false
|
||||
toggleTriggersDraggableState()
|
||||
revalidateContentCache()
|
||||
resolve()
|
||||
})
|
||||
|
@ -425,10 +396,21 @@ function fetchAndLoadWorkflow() {
|
|||
|
||||
function loadWorkflow(workflow) {
|
||||
editor.clear()
|
||||
if (workflow.data.length == 0) {
|
||||
console.log('stop');
|
||||
var trigger_id = workflow['trigger_id'];
|
||||
if (all_triggers_by_id[trigger_id] === undefined) {
|
||||
console.error('Unknown trigger');
|
||||
showMessage('error', 'Unknown trigger')
|
||||
}
|
||||
var trigger_block = all_triggers_by_id[trigger_id]
|
||||
addNode(trigger_block, {left: 0, top: 0})
|
||||
}
|
||||
// We cannot rely on the editor's import function as it recreates the nodes with the saved HTML instead of rebuilding them
|
||||
// We have to manually add the nodes and their connections
|
||||
Object.values(workflow.data).forEach(function (block) {
|
||||
if (!all_blocks_by_id[block.data.id]) {
|
||||
var module = all_blocks_by_id[block.data.id] || all_triggers_by_id[block.data.id]
|
||||
if (!module) {
|
||||
console.error('Tried to add node for unknown module ' + block.data.id + ' (' + block.id + ')')
|
||||
return '';
|
||||
}
|
||||
|
@ -924,7 +906,7 @@ function setParamValueForInput($input, node_data) {
|
|||
}
|
||||
|
||||
function genBlockNotificationHtml(block) {
|
||||
var module = all_blocks_by_id[block.id]
|
||||
var module = all_blocks_by_id[block.id] || all_triggers_by_id[block.id]
|
||||
if (!module) {
|
||||
console.error('Tried to get notification of unknown module ' + block.id)
|
||||
return '';
|
||||
|
|
Loading…
Reference in New Issue