new: [workflow:editor] Initial work on the workflow editor

pull/8530/head
Sami Mokaddem 2022-05-03 22:59:42 +02:00
parent 5698b91e28
commit f6213e86f8
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
10 changed files with 896 additions and 0 deletions

View File

@ -0,0 +1,33 @@
<?php
App::uses('AppController', 'Controller');
class WorkflowsController extends AppController
{
public $components = array(
);
public function index()
{
return $this->RestResponse->viewData([]);
}
public function add()
{
}
public function edit()
{
}
public function delete()
{
}
public function editor()
{
}
}

View File

@ -0,0 +1,11 @@
<div id="<?= h($block['id']) ?>" class="sidebar-workflow-block" style="user-select: none;">
<div class="icon">
<?php if (!empty($block['icon'])) : ?>
<i class="<?= $this->FontAwesome->getClass($block['icon']) ?> fa-fw <?= $block['icon_class'] ?? '' ?>"></i>
<?php endif; ?>
</div>
<div>
<strong style="font-size: large;"><?= h($block['name']) ?></strong>
<div class="muted"><?= h($block['description']) ?></div>
</div>
</div>

View File

View File

View File

@ -0,0 +1,266 @@
<?php
$blocks_trigger = [
[
'id' => 'publish',
'name' => 'Publish',
'icon' => 'upload',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'inputs' => 0,
],
[
'id' => 'new-attribute',
'name' => 'New Attribute',
'icon' => 'cube',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'inputs' => 0,
'disabled' => true,
],
[
'id' => 'new-object',
'name' => 'New Object',
'icon' => 'cubes',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'inputs' => 0,
'disabled' => true,
],
[
'id' => 'email-sent',
'name' => 'Email sent',
'icon' => 'envelope',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'inputs' => 0,
'disabled' => true,
],
[
'id' => 'user-new',
'name' => 'New User',
'icon' => 'user-plus',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'inputs' => 0,
'disabled' => true,
],
[
'id' => 'feed-pull',
'name' => 'Feed pull',
'icon' => 'arrow-alt-circle-down',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'inputs' => 0,
'disabled' => true,
],
];
$blocks_condition = [
[
'id' => 'if',
'name' => 'IF',
'icon' => 'code-branch',
'description' => 'IF conditions',
'outputs' => 2,
'html_template' => 'IF',
],
];
$blocks_action = [
[
'id' => 'add-tag',
'name' => 'Add Tag',
'icon' => 'tag',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'params' => [
[
'type' => 'input',
'label' => 'Tag name',
'default' => 'tlp:red',
'placeholder' => __('Enter tag name')
],
],
'outputs' => 0,
],
[
'id' => 'enrich-attribute',
'name' => 'Enrich Attribute',
'icon' => 'asterisk',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'outputs' => 0,
],
[
'id' => 'slack-message',
'name' => 'Slack Message',
'icon' => 'slack',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'params' => [
[
'type' => 'select',
'label' => 'Channel name',
'default' => 'team-4_3_misp',
'options' => [
'team-4_3_misp',
'team-4_0_elite_as_one',
],
],
],
'outputs' => 0,
],
[
'id' => 'send-email',
'name' => 'Send Email',
'icon' => 'envelope',
'description' => 'Lorem ipsum dolor, sit amet consectetur adipisicing elit.',
'params' => [
[
'type' => 'select',
'label' => 'Email template',
'default' => 'default',
'options' => [
'default',
'TLP marking',
],
],
],
'outputs' => 0,
],
[
'name' => 'Do nothing',
'id' => 'dev-null',
'icon' => 'ban',
'description' => 'Essentially a /dev/null',
'outputs' => 0,
],
[
'name' => 'Push to ZMQ',
'id' => 'push-zmq',
'icon' => 'wifi',
'icon_class' => 'fa-rotate-90',
'description' => 'Push to the ZMQ channel',
'params' => [
[
'type' => 'input',
'label' => 'ZMQ Topic',
'default' => 'from-misp-workflow',
],
],
'outputs' => 0,
],
];
$blocks_all = array_merge($blocks_trigger, $blocks_condition, $blocks_action);
$workflows = [
['id' => 1, 'name' => 'Publish workflow', 'data' => []],
['id' => 2, 'name' => 'My test worklow1', 'data' => []],
];
?>
<div class="root-container">
<div class="main-container">
<div class="side-panel">
<h2>Workflows</h2>
<div class="workflow-selector-container">
<select type="text" placeholder="Load a workflow" class="chosen-container workflows" autocomplete="off">
<?php foreach ($workflows as $workflow) : ?>
<option val="<?= h($workflow['name']) ?>"><?= h($workflow['name']) ?></option>
<?php endforeach; ?>
</select>
<div class="btn-group" style="margin-left: 3px;">
<a class="btn btn-primary" href="#"><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>
<ul class="dropdown-menu">
<li><a href="#"><i class="fa-fw <?= $this->FontAwesome->getClass('file-export') ?>"></i> <?= __('Export workflow') ?></a></li>
<li><a href="#"><i class="fa-fw <?= $this->FontAwesome->getClass('file-import') ?>"></i> <?= __('Import workflow') ?></a></li>
</ul>
</div>
</div>
<div class="" style="margin-left: 3px;">
<a class="btn btn-primary" href="#"><i class="fa-fw <?= $this->FontAwesome->getClass('save') ?>"></i> <?= __('Save') ?></a>
<a class="btn btn-danger" href="#"><i class="fa-fw <?= $this->FontAwesome->getClass('trash') ?>"></i> <?= __('Delete') ?></a>
</div>
<h2>Blocks</h2>
<select type="text" placeholder="Search for a block" class="chosen-container blocks" autocomplete="off">
<?php foreach ($blocks_all as $block) : ?>
<option val="<?= h($block['name']) ?>"><?= h($block['name']) ?></option>
<?php endforeach; ?>
</select>
<ul class="nav nav-tabs" id="block-tabs">
<li class="active"><a href="#container-triggers">
<i class="<?= $this->FontAwesome->getClass('flag') ?>"></i>
Triggers
</a></li>
<li><a href="#container-conditions">
<i class="<?= $this->FontAwesome->getClass('code-branch') ?>"></i>
conditions
</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 ($blocks_trigger as $block) : ?>
<?= $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
<?php endforeach; ?>
</div>
<div class="tab-pane" id="container-conditions">
<?php foreach ($blocks_condition as $block) : ?>
<?= $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
<?php endforeach; ?>
</div>
<div class="tab-pane" id="container-actions">
<?php foreach ($blocks_action as $block) : ?>
<?= $this->element('Workflows/sidebar-block', ['block' => $block]) ?>
<?php endforeach; ?>
</div>
</div>
</div>
<div class="canvas">
<div id="drawflow"></div>
</div>
</div>
<div class="properties-container">
</div>
</div>
<div id="block-modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3>Block options</h3>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<button class="btn btn-primary">Save changes</button>
</div>
</div>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<?php
echo $this->element('genericElements/assetLoader', [
'css' => ['drawflow.min'],
'js' => ['jquery-ui', 'drawflow.min', 'doT'],
]);
echo $this->element('genericElements/assetLoader', [
'css' => ['workflows-editor'],
'js' => ['workflows-editor/workflows-editor'],
]);
?>
<script>
var $root_container = $('.root-container')
var $side_panel = $('.root-container .side-panel')
var $canvas = $('.root-container .canvas')
var $chosenWorkflows = $('.root-container .side-panel .chosen-container.workflows')
var $chosenBlocks = $('.root-container .side-panel .chosen-container.blocks')
var $drawflow = $('#drawflow')
var editor = false
var all_blocks = <?= json_encode($blocks_all) ?>;
$(document).ready(function() {
initDrawflow()
})
</script>

View File

1
app/webroot/css/drawflow.min.css vendored Normal file
View File

@ -0,0 +1 @@
.drawflow,.drawflow .parent-node{position:relative}.parent-drawflow{display:flex;overflow:hidden;touch-action:none;outline:0}.drawflow{width:100%;height:100%;user-select:none;perspective:0}.drawflow .drawflow-node{display:flex;align-items:center;position:absolute;background:#0ff;width:160px;min-height:40px;border-radius:4px;border:2px solid #000;color:#000;z-index:2;padding:15px}.drawflow .drawflow-node.selected{background:red}.drawflow .drawflow-node:hover{cursor:move}.drawflow .drawflow-node .inputs,.drawflow .drawflow-node .outputs{width:0}.drawflow .drawflow-node .drawflow_content_node{width:100%;display:block}.drawflow .drawflow-node .input,.drawflow .drawflow-node .output{position:relative;width:20px;height:20px;background:#fff;border-radius:50%;border:2px solid #000;cursor:crosshair;z-index:1;margin-bottom:5px}.drawflow .drawflow-node .input{left:-27px;top:2px;background:#ff0}.drawflow .drawflow-node .output{right:-3px;top:2px}.drawflow svg{z-index:0;position:absolute;overflow:visible!important}.drawflow .connection{position:absolute;pointer-events:none;aspect-ratio:1/1}.drawflow .connection .main-path{fill:none;stroke-width:5px;stroke:#4682b4;pointer-events:all}.drawflow .connection .main-path:hover{stroke:#1266ab;cursor:pointer}.drawflow .connection .main-path.selected{stroke:#43b993}.drawflow .connection .point{cursor:move;stroke:#000;stroke-width:2;fill:#fff;pointer-events:all}.drawflow .connection .point.selected,.drawflow .connection .point:hover{fill:#1266ab}.drawflow .main-path{fill:none;stroke-width:5px;stroke:#4682b4}.drawflow-delete{position:absolute;display:block;width:30px;height:30px;background:#000;color:#fff;z-index:4;border:2px solid #fff;line-height:30px;font-weight:700;text-align:center;border-radius:50%;font-family:monospace;cursor:pointer}.drawflow>.drawflow-delete{margin-left:-15px;margin-top:15px}.parent-node .drawflow-delete{right:-15px;top:-15px}

View File

@ -0,0 +1,392 @@
.root-container {
display: flex;
height: calc(100vh - 2*42px); /* total space available minus header and footer */
margin-top: -20px;
}
.main-container {
display: flex;
height: 100%;
width: 100%;
}
.side-panel {
z-index: 18;
position: absolute;
box-sizing: border-box;
height: calc(100% - 2*42px);
width: var(--editor-sidepanel-width);
box-shadow: 2px 0px 6px #cecece;
background-color: #fff;
padding: 0.5em 0.25em;
display: flex;
flex-direction: column;
}
.side-panel #block-tabs {
margin-bottom: 10px;
}
.side-panel .workflow-selector-container {
display: flex;
}
.side-panel .workflow-selector-container > select {
flex-grow: 1;
}
.side-panel .workflow-selector-container > button {
margin-left: 0.25em;
}
.side-panel .chosen-container.workflows {
margin-bottom: 0;
}
.side-panel .chosen-container.blocks {
width: 100%;
margin-bottom: 1.25em;
}
.side-panel #block-tabs {
margin-top: 1.25em;
}
.side-panel .tab-pane.active {
overflow-y: auto;
}
.sidebar-workflow-block {
display: flex;
width: 300px;
background-color: #fff;
border-radius: 5px;
margin: 0.75em 0.5em;
padding: 0.25em;
transition-property: box-shadow;
transition-duration: .1s;
box-shadow: 0px 3px 6px 2px #33333311;
}
.sidebar-workflow-block:hover {
box-shadow: 0px 3px 6px 2px #33333333;
cursor: pointer;
}
.sidebar-workflow-block.disabled {
cursor: not-allowed !important;
filter: blur(1px);
}
.sidebar-workflow-block > .icon {
width: 1.25em;
align-items: flex-start;
display: flex;
font-size: large;
padding: 0 0.25em;
}
.canvas {
height: 100%;
width: calc(100% - var(--editor-sidepanel-width));
margin-left: var(--editor-sidepanel-width);
}
.canvas-workflow-block {
display: flex;
width: 300px;
background-color: #fff;
border-radius: 5px;
padding: 0.25em 0.75em;
box-shadow: 0px 3px 6px 2px #33333333;
}
.canvas-workflow-block.small {
height: 50px;
}
.canvas-workflow-block br {
width: 100%;
height: 1px;
background-color: #e9e9e9;
}
.canvas-workflow-block>.icon {
width: 1.25em;
align-items: flex-start;
display: flex;
font-size: large;
padding: 0 0.25em;
}
#drawflow {
height: 100%;
width: 100%;
}
/* Canvas blocks */
.default-main-container {
display: flex;
font-size: large;
padding: 0.25em 0;
border-bottom: 1px solid #efefef;
}
.canvas-workflow-block.small .default-main-container-small {
display: flex;
font-size: large;
padding: 0.25em;
height: 100%;
box-sizing: border-box;
align-items: center;
}
.default-main-container-small .then-else-container {
font-size: small;
display: flex;
flex-direction: column;
margin-left: auto;
font-family: monospace;
margin-top: -1px;
margin-right: 2px;
}
.default-main-container .description {
user-select: none;
margin: 0.25em 0.5em;
}
/* Drawflow CSS */
:root {
--editor-sidepanel-width: 340px;
--dfBackgroundColor: #ffffff;
--dfBackgroundSize: 15px;
--dfBackgroundImage: linear-gradient(to right, #66666615 1px, transparent 1px),
linear-gradient(to bottom, #66666615 1px, transparent 1px);
--dfNodeType: flex;
--dfNodeTypeFloat: none;
--dfNodeBackgroundColor: #ffffff;
--dfNodeTextColor: #000000;
--dfNodeBorderSize: 0px;
--dfNodeBorderColor: #000000;
--dfNodeBorderRadius: 4px;
--dfNodeMinHeight: 40px;
--dfNodeMinWidth: 160px;
--dfNodePadding: 0px;
--dfNodePaddingTop: 15px;
--dfNodePaddingBottom: 15px;
--dfNodeHoverBackgroundColor: #ffffff;
--dfNodeHoverTextColor: #000000;
--dfNodeHoverBoxShadowHL: 0px;
--dfNodeHoverBoxShadowVL: 2px;
--dfNodeHoverBoxShadowBR: 6px;
--dfNodeHoverBoxShadowS: 0px;
--dfNodeHoverBoxShadowColor: #0088cc99;
--dfNodeSelectedBackgroundColor: #ffffff;
--dfNodeSelectedTextColor: #000000;
--dfNodeSelectedBoxShadowHL: 0px;
--dfNodeSelectedBoxShadowVL: 2px;
--dfNodeSelectedBoxShadowBR: 6px;
--dfNodeSelectedBoxShadowS: 0px;
--dfNodeSelectedBoxShadowColor: #0088cc99;
--dfInputBackgroundColor: #ffffff;
--dfInputBorderSize: 2px;
--dfInputBorderColor: #5a5a5a;
--dfInputBorderRadius: 50px;
--dfInputLeft: -7px;
--dfInputHeight: 10px;
--dfInputWidth: 10px;
--dfInputHeightHover: 16px;
--dfInputWidthHover: 16px;
--dfInputLeftHover: -10px;
--dfInputHoverBackgroundColor: #ffffff;
--dfInputHoverBorderSize: 2px;
--dfInputHoverBorderColor: #5a5a5a;
--dfInputHoverBorderRadius: 50px;
--dfOutputBackgroundColor: #ffffff;
--dfOutputBorderSize: 2px;
--dfOutputBorderColor: #5a5a5a;
--dfOutputBorderRadius: 50px;
--dfOutputRight: 6px;
--dfOutputHeight: 10px;
--dfOutputWidth: 10px;
--dfOutputHoverBackgroundColor: #ffffff;
--dfOutputHoverBorderSize: 2px;
--dfOutputHoverBorderColor: #5a5a5a;
--dfOutputHoverBorderRadius: 50px;
--dfOutputHeightHover: 16px;
--dfOutputWidthHover: 16px;
--dfOutputRightHover: 9px;
--dfLineWidth: 5px;
--dfLineColor: #4682b4;
--dfLineHoverColor: #4682b4;
--dfLineSelectedColor: #43b993;
--dfRerouteBorderWidth: 2px;
--dfRerouteBorderColor: #000000;
--dfRerouteBackgroundColor: #ffffff;
--dfRerouteHoverBorderWidth: 2px;
--dfRerouteHoverBorderColor: #000000;
--dfRerouteHoverBackgroundColor: #ffffff;
--dfDeleteDisplay: block;
--dfDeleteColor: #ffffff;
--dfDeleteBackgroundColor: #e37b78;
--dfDeleteBorderSize: 2px;
--dfDeleteBorderColor: #ffffff;
--dfDeleteBorderRadius: 50px;
--dfDeleteTop: -11px;
--dfDeleteRight: -11px;
--dfDeleteHeight: 15px;
--dfDeleteWidth: 15px;
--dfDeleteLineHeight: 16px;
--dfDeleteHoverColor: #ffffff;
--dfDeleteHoverBackgroundColor: #da4e49;
--dfDeleteHoverBorderSize: 2px;
--dfDeleteHoverBorderColor: #ffffff;
--dfDeleteHoverBorderRadius: 50px;
}
#drawflow {
background: var(--dfBackgroundColor);
background-size: var(--dfBackgroundSize) var(--dfBackgroundSize);
background-image: var(--dfBackgroundImage);
}
.drawflow .drawflow-node {
display: var(--dfNodeType);
background: var(--dfNodeBackgroundColor);
color: var(--dfNodeTextColor);
border: var(--dfNodeBorderSize) solid var(--dfNodeBorderColor);
border-radius: var(--dfNodeBorderRadius);
min-height: var(--dfNodeMinHeight);
width: auto;
min-width: var(--dfNodeMinWidth);
padding-top: var(--dfNodePaddingTop);
padding-bottom: var(--dfNodePaddingBottom);
padding: var(--dfNodePadding);
-webkit-box-shadow: var(--dfNodeBoxShadowHL) var(--dfNodeBoxShadowVL) var(--dfNodeBoxShadowBR) var(--dfNodeBoxShadowS) var(--dfNodeBoxShadowColor);
box-shadow: var(--dfNodeBoxShadowHL) var(--dfNodeBoxShadowVL) var(--dfNodeBoxShadowBR) var(--dfNodeBoxShadowS) var(--dfNodeBoxShadowColor);
}
.drawflow .drawflow-node:hover {
-webkit-box-shadow: var(--dfNodeHoverBoxShadowHL) var(--dfNodeHoverBoxShadowVL) var(--dfNodeHoverBoxShadowBR) var(--dfNodeHoverBoxShadowS) var(--dfNodeHoverBoxShadowColor);
box-shadow: var(--dfNodeHoverBoxShadowHL) var(--dfNodeHoverBoxShadowVL) var(--dfNodeHoverBoxShadowBR) var(--dfNodeHoverBoxShadowS) var(--dfNodeHoverBoxShadowColor);
}
.drawflow .drawflow-node.selected {
background: var(--dfNodeSelectedBackgroundColor);
color: var(--dfNodeSelectedTextColor);
-webkit-box-shadow: var(--dfNodeSelectedBoxShadowHL) var(--dfNodeSelectedBoxShadowVL) var(--dfNodeSelectedBoxShadowBR) var(--dfNodeSelectedBoxShadowS) var(--dfNodeSelectedBoxShadowColor);
box-shadow: var(--dfNodeSelectedBoxShadowHL) var(--dfNodeSelectedBoxShadowVL) var(--dfNodeSelectedBoxShadowBR) var(--dfNodeSelectedBoxShadowS) var(--dfNodeSelectedBoxShadowColor);
}
.drawflow .drawflow-node .input {
left: var(--dfInputLeft);
background: var(--dfInputBackgroundColor);
border: var(--dfInputBorderSize) solid var(--dfInputBorderColor);
border-radius: var(--dfInputBorderRadius);
height: var(--dfInputHeight);
width: var(--dfInputWidth);
transition-property: height,width,left;
transition-duration: .1s;
}
.drawflow .drawflow-node .input:hover {
background: var(--dfInputHoverBackgroundColor);
border: var(--dfInputHoverBorderSize) solid var(--dfInputHoverBorderColor);
border-radius: var(--dfInputHoverBorderRadius);
height: var(--dfInputHeightHover);
width: var(--dfInputWidthHover);
left: var(--dfInputLeftHover);
}
.drawflow .drawflow-node .outputs {
float: var(--dfNodeTypeFloat);
}
.drawflow .drawflow-node .output {
right: var(--dfOutputRight);
background: var(--dfOutputBackgroundColor);
border: var(--dfOutputBorderSize) solid var(--dfOutputBorderColor);
border-radius: var(--dfOutputBorderRadius);
height: var(--dfOutputHeight);
width: var(--dfOutputWidth);
transition-property: height,width,right;
transition-duration: .1s;
}
.drawflow .drawflow-node .output:hover {
background: var(--dfOutputHoverBackgroundColor);
border: var(--dfOutputHoverBorderSize) solid var(--dfOutputHoverBorderColor);
border-radius: var(--dfOutputHoverBorderRadius);
height: var(--dfOutputHeightHover);
width: var(--dfOutputWidthHover);
right: var(--dfOutputRightHover);
}
.drawflow .connection .main-path {
stroke-width: var(--dfLineWidth);
stroke: var(--dfLineColor);
}
.drawflow .connection .main-path:hover {
stroke: var(--dfLineHoverColor);
}
.drawflow .connection .main-path.selected {
stroke: var(--dfLineSelectedColor);
}
.drawflow .connection .point {
stroke: var(--dfRerouteBorderColor);
stroke-width: var(--dfRerouteBorderWidth);
fill: var(--dfRerouteBackgroundColor);
}
.drawflow .connection .point:hover {
stroke: var(--dfRerouteHoverBorderColor);
stroke-width: var(--dfRerouteHoverBorderWidth);
fill: var(--dfRerouteHoverBackgroundColor);
}
.drawflow-delete {
display: var(--dfDeleteDisplay);
color: var(--dfDeleteColor);
background: var(--dfDeleteBackgroundColor);
border: var(--dfDeleteBorderSize) solid var(--dfDeleteBorderColor);
border-radius: var(--dfDeleteBorderRadius);
}
.parent-node .drawflow-delete {
top: var(--dfDeleteTop);
right: var(--dfDeleteRight);
height: var(--dfDeleteHeight);
width: var(--dfDeleteWidth);
line-height: var(--dfDeleteLineHeight);
}
.drawflow-delete:hover {
color: var(--dfDeleteHoverColor);
background: var(--dfDeleteHoverBackgroundColor);
border: var(--dfDeleteHoverBorderSize) solid var(--dfDeleteHoverBorderColor);
border-radius: var(--dfDeleteHoverBorderRadius);
}

1
app/webroot/js/drawflow.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,192 @@
var dotBlockDefault = doT.template(' \
<div class="canvas-workflow-block"> \
<div style="width: 100%;"> \
<div class="default-main-container"> \
<i class="fas fa-fw fa-{{=it.icon}} {{=it.icon_class}}"></i> \
<strong style="margin-left: 0.25em;"> \
{{=it.name}} \
</strong> \
<span style="margin-left: auto;"> \
<a href="#block-modal" role="button" class="btn btn-mini" data-toggle="modal"><i class="fas fa-ellipsis-h"></i></a> \
</span> \
</div> \
<div class="muted" class="description">{{=it.description}}</div> \
{{=it.block_param_html}} \
</div> \
</div>')
var dotIF = doT.template(' \
<div class="canvas-workflow-block small"> \
<div style="width: 100%; height: 100%;"> \
<div class="default-main-container-small"> \
<i class="fas fa-fw fa-{{=it.icon}} {{=it.icon_class}}"></i> \
<strong style="margin-left: 0.25em;"> \
{{=it.name}} \
</strong> \
<div class="then-else-container"> \
<span>then</span> \
<span>else</span> \
</div> \
</div> \
</div> \
</div>')
function sanitizeObject(obj) {
var newObj = {}
for (var key of Object.keys(obj)) {
var newVal = $('</p>').text(obj[key]).html()
newObj[key] = newVal
}
return newObj
}
function initDrawflow() {
editor = new Drawflow($drawflow[0]);
editor.start();
$('#block-tabs a').click(function (e) {
e.preventDefault();
$(this).tab('show');
})
$chosenWorkflows.chosen()
.on('change', function (evt, param) {
// console.log(param);
});
$chosenBlocks.chosen()
.on('change', function (evt, param) {
// console.log(param);
});
$('.sidebar-workflow-block').each(function () {
var $block = $(this)
all_blocks.forEach(function (block) {
if ($block[0].id == block['id']) {
$block.data('block', block)
}
});
})
$('.sidebar-workflow-block').each(function() {
if ($(this).data('block').disabled) {
$(this).addClass('disabled')
}
$(this).draggable({
helper: "clone",
scroll: false,
disabled: $(this).data('block').disabled,
start: function (event, ui) {
$(this).addClass('disabled')
},
stop: function (event, ui) {
$(this).removeClass('disabled')
// addNode($(this).data('block'), ui.position)
}
});
})
$canvas.droppable({
drop: function (event, ui) {
// console.log(event)
// console.log(ui)
addNode(ui.draggable.data('block'), ui.position)
}
});
}
function getTemplateForBlock(block) {
var html = block.name
if (block.html !== undefined) {
html = block.html
} else {
if (block.html_template !== undefined) {
html = window['dot' + block.html_template](block)
} else {
html = dotBlockDefault(block)
}
}
return html
}
function genBlockParamHtml(block) {
if (!block.params) {
return ''
}
var html = ''
block.params.forEach(function(param) {
paramHtml = ''
switch (param.type) {
case 'input':
paramHtml = genInput(param)[0].outerHTML
break;
case 'select':
paramHtml = genSelect(param)[0].outerHTML
break;
default:
break;
}
html += paramHtml
})
return html
}
function genSelect(options) {
var $select = $('<select>').css({
width: '100%',
})
options.options.forEach(function(option) {
var optionValue = ''
var optionName = ''
if (typeof option === 'string') {
optionValue = option
optionName = option
} else {
optionValue = option.value
optionName = option.name
}
var $option = $('<option>')
.val(optionValue)
.text(optionName)
$select.append($option)
})
if (options.default !== undefined) {
$select.val(options.default)
}
return $select
}
function genInput(options) {
var $input = $('<input>')
$input.attr('type', 'text')
if (options.default !== undefined) {
// $input.val(options.default)
$input.attr('value', options.default) // wtf why does it not work?!
}
if (options.placeholder !== undefined) {
$input.attr('placeholder', options.placeholder)
}
return $input
}
function addNode(block, position) {
var canvasPosition = $canvas[0].getBoundingClientRect()
var adjsutedPosition = {
left: position.left - canvasPosition.left,
top: position.top,
}
block['block_param_html'] = genBlockParamHtml(block)
var html = getTemplateForBlock(block)
editor.addNode(
block.name,
block.inputs === undefined ? 1 : block.inputs,
block.outputs === undefined ? 1 : block.outputs,
adjsutedPosition.left,
adjsutedPosition.top,
block.class === undefined ? '' : block.class,
block,
html
);
}