new: [workflow] Added toggling module state

pull/8530/head
Sami Mokaddem 2022-06-17 09:20:27 +02:00
parent 4ffebbfff4
commit 574deccac8
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
8 changed files with 147 additions and 49 deletions

View File

@ -181,31 +181,54 @@ class WorkflowsController extends AppController
$this->set('menuData', ['menuList' => 'workflows', 'menuItem' => 'view_module']);
}
public function import()
public function toggleModule($module_id, $enabled)
{
if ($this->request->is('post') || $this->request->is('put')) {
$data = $this->request->data['Workflow'];
$text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
$workflow = JsonTool::decode($text);
if ($workflow === null) {
throw new MethodNotAllowedException(__('Error while decoding JSON'));
}
$workflow['Workflow']['enabled'] = false;
$workflow['Workflow']['data'] = JsonTool::encode($workflow['Workflow']['data']);
$this->request->data = $workflow;
$this->add();
$this->request->allowMethod(['post', 'put']);
$saved = $this->Workflow->toggleModule($module_id, $enabled);
if ($saved) {
return $this->__getSuccessResponseBasedOnContext(
__('%s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
null,
'toggle_module',
$module_id,
['action' => 'moduleIndex']
);
} else {
return $this->__getFailResponseBasedOnContext(
__('Could not %s module %s', ($enabled ? 'Enabled' : 'Disabled'), $module_id),
null,
'toggle_module',
$module_id,
['action' => 'moduleIndex']
);
}
}
public function export($id)
{
$workflow = $this->Workflow->fetchWorkflow($id);
$content = JsonTool::encode($workflow, JSON_PRETTY_PRINT);
$this->response->body($content);
$this->response->type('json');
$this->response->download(sprintf('workflow_%s_%s.json', $workflow['Workflow']['name'], time()));
return $this->response;
}
// public function import()
// {
// if ($this->request->is('post') || $this->request->is('put')) {
// $data = $this->request->data['Workflow'];
// $text = FileAccessTool::getTempUploadedFile($data['submittedjson'], $data['json']);
// $workflow = JsonTool::decode($text);
// if ($workflow === null) {
// throw new MethodNotAllowedException(__('Error while decoding JSON'));
// }
// $workflow['Workflow']['enabled'] = false;
// $workflow['Workflow']['data'] = JsonTool::encode($workflow['Workflow']['data']);
// $this->request->data = $workflow;
// $this->add();
// }
// }
// public function export($id)
// {
// $workflow = $this->Workflow->fetchWorkflow($id);
// $content = JsonTool::encode($workflow, JSON_PRETTY_PRINT);
// $this->response->body($content);
// $this->response->type('json');
// $this->response->download(sprintf('workflow_%s_%s.json', $workflow['Workflow']['name'], time()));
// return $this->response;
// }
private function __getSuccessResponseBasedOnContext($message, $data = null, $action = '', $id = false, $redirect = array())
{

View File

@ -7045,6 +7045,13 @@ class Server extends AppModel
'test' => 'testForPortNumber',
'type' => 'numeric'
),
'WorkflowTriggers_publish' => array(
'level' => 1,
'description' => __('Enable/disable the `publish` trigger'),
'value' => false,
'test' => 'testBool',
'type' => 'boolean'
),
'Cortex_services_url' => array(
'level' => 1,
'description' => __('The url used to access Cortex. By default, it is accessible at http://cortex-url'),

View File

@ -61,6 +61,7 @@ class Workflow extends AppModel
const REDIS_KEY_WORKFLOW_NAMESPACE = 'workflow';
const REDIS_KEY_WORKFLOW_PER_TRIGGER = 'workflow:workflow_list:%s';
const REDIS_KEY_TRIGGER_PER_WORKFLOW = 'workflow:trigger_list:%s';
const REDIS_KEY_MODULES_ENABLED = 'workflow:modules_enabled';
const BLOCKING_PATH = 'blocking';
const NON_BLOCKING_PATH = 'non-blocking';
@ -136,8 +137,34 @@ class Workflow extends AppModel
{
$filename = sprintf('Module_%s.php', preg_replace('/[^a-zA-Z0-9_]/', '_', Inflector::underscore($trigger_id)));
$module_config = $this->__getClassFromModuleFiles('trigger', [$filename])['classConfigs'];
// FIXME: Merge global configuration!
return empty($module_config['disabled']);
$module_disabled = empty(Configure::read(sprintf('Plugin.WorkflowTriggers_%s', $trigger_id)));
return empty($module_config['disabled']) && !$module_disabled;
}
protected function getEnabledModules(): array
{
try {
$redis = $this->setupRedisWithException();
} catch (Exception $e) {
return false;
}
$list = $redis->sMembers(Workflow::REDIS_KEY_MODULES_ENABLED);
return !empty($list) ? $list : [];
}
public function toggleModule($module_id, $enable): bool
{
try {
$redis = $this->setupRedisWithException();
} catch (Exception $e) {
return false;
}
if ($enable) {
$list = $redis->sAdd(Workflow::REDIS_KEY_MODULES_ENABLED, $module_id);
} else {
$list = $redis->sRem(Workflow::REDIS_KEY_MODULES_ENABLED, $module_id);
}
return true;
}
protected function checkTriggerListenedTo($trigger_id)
@ -465,6 +492,17 @@ class Workflow extends AppModel
'warning' => [],
'info' => [],
];
if ($module['disabled']) {
$modules[$moduleType][$i]['notifications']['error'][] = [
'text' => __('Module disabled'),
'description' => __('This module is disabled and thus will not be executed.'),
'details' => [
__('Disabled modules that are blocking will also block the remaining of the execution')
],
'__show_in_sidebar' => false,
'__show_in_node' => true,
];
}
}
}
return $modules;
@ -500,24 +538,36 @@ class Workflow extends AppModel
private function __mergeGlobalConfigIntoLoadedModules()
{
/* FIXME: Delete `disabled` entry. This is for testing while we wait for module settings */
array_walk($this->loaded_modules['trigger'], function (&$trigger) {
$module_enabled = !in_array($trigger['id'], ['publish', 'new-attribute']);
$trigger['html_template'] = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
$trigger['disabled'] = $module_enabled;
$this->loaded_classes['trigger'][$trigger['id']]->disabled = $module_enabled;
$this->loaded_classes['trigger'][$trigger['id']]->html_template = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
});
array_walk($this->loaded_modules['logic'], function (&$logic) {
$module_enabled = true;
$logic['disabled'] = !$module_enabled;
$this->loaded_classes['logic'][$logic['id']]->disabled = $module_enabled;
});
array_walk($this->loaded_modules['action'], function (&$action) {
$module_enabled = !in_array($action['id'], ['push-zmq', 'slack-message', 'mattermost-message', 'add-tag', 'writeactions', 'enrich-event', 'testaction', 'stop-execution', ]);
$action['disabled'] = $module_enabled;
$this->loaded_classes['action'][$action['id']]->disabled = $module_enabled;
});
// array_walk($this->loaded_modules['logic'], function (&$logic) {
// $module_enabled = true;
// $logic['disabled'] = !$module_enabled;
// $this->loaded_classes['logic'][$logic['id']]->disabled = $module_enabled;
// });
// array_walk($this->loaded_modules['action'], function (&$action) {
// $module_enabled = !in_array($action['id'], ['push-zmq', 'slack-message', 'mattermost-message', 'add-tag', 'writeactions', 'enrich-event', 'testaction', 'stop-execution', ]);
// $action['disabled'] = $module_enabled;
// $this->loaded_classes['action'][$action['id']]->disabled = $module_enabled;
// });
/* FIXME: end */
foreach ($this->loaded_modules['trigger'] as &$trigger) {
$module_disabled = empty(Configure::read(sprintf('Plugin.WorkflowTriggers_%s', $trigger['id'])));
$trigger['html_template'] = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
$trigger['disabled'] = $module_disabled;
$this->loaded_classes['trigger'][$trigger['id']]->disabled = $module_disabled;
$this->loaded_classes['trigger'][$trigger['id']]->html_template = !empty($trigger['html_template']) ? $trigger['html_template'] : 'trigger';
}
$enabledModules = $this->getEnabledModules();
array_walk($this->loaded_modules['logic'], function (&$logic) use ($enabledModules) {
$module_disabled = !in_array($logic['id'], $enabledModules);
$logic['disabled'] = $module_disabled;
$this->loaded_classes['logic'][$logic['id']]->disabled = $module_disabled;
});
array_walk($this->loaded_modules['action'], function (&$action) use ($enabledModules) {
$module_disabled = !in_array($action['id'], $enabledModules);
$action['disabled'] = $module_disabled;
$this->loaded_classes['action'][$action['id']]->disabled = $module_disabled;
});
}
private function __getEnabledModulesFromModuleService()

View File

@ -13,6 +13,9 @@ class Module_parallel_task extends WorkflowBaseModule
public $html_template = 'parallel';
public $params = [];
private $Workflow;
private $Job;
public function __construct()
{
parent::__construct();

View File

@ -5,7 +5,7 @@ $classFromSeverity = [
'error' => 'danger',
];
?>
<div id="<?= h($block['id']) ?>" class="sidebar-workflow-block" style="user-select: none;" data-blockid="<?= h($block['id']) ?>">
<div id="<?= h($block['id']) ?>" class="sidebar-workflow-block" style="user-select: none;" data-blockid="<?= h($block['id']) ?>" title="<?= !empty($block['disabled']) ? __('This module is disabled') : '' ?>">
<div class="icon">
<?php if (!empty($block['icon'])) : ?>
<i class="<?= $this->FontAwesome->getClass($block['icon']) ?> fa-fw <?= $block['icon_class'] ?? '' ?>"></i>
@ -24,8 +24,13 @@ $classFromSeverity = [
</span>
<span class="block-notification-container">
<?php foreach (array_keys($classFromSeverity) as $severity) : ?>
<?php if (!empty($block['notifications'][$severity])) : ?>
<button class="btn btn-mini btn-<?= $classFromSeverity[$severity] ?>" type="button" title="<?= implode('&#013;', h(Hash::extract($block['notifications'][$severity], '{n}.text'))) ?>" onclick="showNotificationModalForSidebarModule(this)">
<?php
$visibleNotifications = array_filter($block['notifications'][$severity], function($notification) {
return $notification['__show_in_sidebar'];
});
?>
<?php if (!empty($visibleNotifications)) : ?>
<button class="btn btn-mini btn-<?= $classFromSeverity[$severity] ?>" type="button" title="<?= implode('&#013;', h(Hash::extract($visibleNotifications, '{n}.text'))) ?>" onclick="showNotificationModalForSidebarModule(this)">
<?php if ($severity == 'danger') : ?>
<i class="<?= $this->FontAwesome->getClass('times-circle') ?>"></i>
<?php elseif ($severity == 'warning') : ?>
@ -34,7 +39,7 @@ $classFromSeverity = [
<i class="<?= $this->FontAwesome->getClass('exclamation-circle') ?>"></i>
<?php endif; ?>
<strong>
<?= count($block['notifications'][$severity]) ?>
<?= count($visibleNotifications) ?>
</strong>
</button>
<?php endif; ?>

View File

@ -80,6 +80,9 @@
}
$url .= '/' . $url_params_values;
}
if (!empty($action['url_suffix'])) {
$url .= $action['url_suffix'];
}
if (!empty($action['url_extension'])) {
$url .= '.' . $action['url_extension'];
}

View File

@ -98,8 +98,9 @@
'title' => __('Enable'),
'icon' => 'play',
'postLink' => true,
'url' => $baseurl . '/workflows/enableModule',
'url' => $baseurl . '/workflows/toggleModule',
'url_params_data_paths' => ['id'],
'url_suffix' => '/1',
'postLinkConfirm' => __('Are you sure you want to enable this module?'),
'complex_requirement' => array(
'function' => function ($row, $options) use ($isSiteAdmin) {
@ -116,8 +117,9 @@
'title' => __('Disable'),
'icon' => 'stop',
'postLink' => true,
'url' => $baseurl . '/workflows/disableModule',
'url' => $baseurl . '/workflows/toggleModule',
'url_params_data_paths' => ['id'],
'url_suffix' => '/0',
'postLinkConfirm' => __('Are you sure you want to disable this module?'),
'complex_requirement' => array(
'function' => function ($row, $options) use ($isSiteAdmin) {

View File

@ -538,6 +538,9 @@ function loadWorkflow(workflow) {
if (block.data.module_type == 'logic') {
blockClass.push('block-type-logic')
}
if (block.data.disabled) {
blockClass.push('disabled')
}
var html = getTemplateForBlock(block.data)
editor.nodeId = block.id // force the editor to use the saved id of the block instead of generating a new one
editor.addNode(
@ -1056,8 +1059,10 @@ function genBlockNotificationHtml(block) {
var html = ''
var $notificationContainer = $('<span></span>')
severities.forEach(function(severity) {
if (module.notifications[severity] && module.notifications[severity].length > 0) {
var notificationTitles = module.notifications[severity].map(function (notification) {
var visibleNotifications = module.notifications[severity].filter(function (notification) { return notification
.__show_in_node})
if (visibleNotifications && visibleNotifications.length > 0) {
var notificationTitles = visibleNotifications.map(function (notification) {
return notification.text
}).join('&#013;')
var $notification = $('<button class="btn btn-mini" role="button" onclick="showNotificationModalForBlock(this)"></button>')
@ -1072,7 +1077,7 @@ function genBlockNotificationHtml(block) {
})
.append(
$('<i class="fas"></i>').addClass(iconBySeverity[severity]),
$('<strong></strong>').text(' '+module.notifications[severity].length)
$('<strong></strong>').text(' '+visibleNotifications.length)
)
$notificationContainer.append($notification)
}