@saltcorn/server 1.1.1-beta.0 → 1.1.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/help/Event types.tmd +2 -0
- package/locales/en.json +7 -1
- package/locales/pl.json +16 -1
- package/package.json +9 -9
- package/public/saltcorn.js +87 -17
- package/routes/actions.js +128 -183
- package/routes/admin.js +257 -46
- package/routes/fields.js +9 -0
- package/routes/menu.js +1 -0
- package/routes/notifications.js +20 -12
- package/routes/page.js +2 -2
- package/routes/pageedit.js +34 -1
- package/routes/utils.js +5 -0
- package/routes/view.js +14 -12
- package/routes/viewedit.js +26 -0
- package/tests/clientjs.test.js +19 -0
- package/tests/view.test.js +37 -0
package/routes/actions.js
CHANGED
|
@@ -11,9 +11,13 @@ const {
|
|
|
11
11
|
addOnDoneRedirect,
|
|
12
12
|
is_relative_url,
|
|
13
13
|
} = require("./utils.js");
|
|
14
|
-
const { ppVal } = require("@saltcorn/data/utils");
|
|
14
|
+
const { ppVal, jsIdentifierValidator } = require("@saltcorn/data/utils");
|
|
15
15
|
const { getState } = require("@saltcorn/data/db/state");
|
|
16
16
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
17
|
+
const View = require("@saltcorn/data/models/view");
|
|
18
|
+
const {
|
|
19
|
+
getForm,
|
|
20
|
+
} = require("@saltcorn/data/base-plugin/viewtemplates/viewable_fields");
|
|
17
21
|
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
18
22
|
const { getTriggerList } = require("./common_lists");
|
|
19
23
|
const TagEntry = require("@saltcorn/data/models/tag_entry");
|
|
@@ -441,6 +445,10 @@ router.post(
|
|
|
441
445
|
const tr = await Trigger.create(form.values);
|
|
442
446
|
id = tr.id;
|
|
443
447
|
}
|
|
448
|
+
Trigger.emitEvent("AppChange", `Trigger ${form.values.name}`, req.user, {
|
|
449
|
+
entity_type: "Trigger",
|
|
450
|
+
entity_name: form.values.name,
|
|
451
|
+
});
|
|
444
452
|
res.redirect(addOnDoneRedirect(`/actions/configure/${id}`, req));
|
|
445
453
|
}
|
|
446
454
|
})
|
|
@@ -482,6 +490,10 @@ router.post(
|
|
|
482
490
|
...form.values.configuration,
|
|
483
491
|
};
|
|
484
492
|
await Trigger.update(trigger.id, form.values); //{configuration: form.values});
|
|
493
|
+
Trigger.emitEvent("AppChange", `Trigger ${trigger.name}`, req.user, {
|
|
494
|
+
entity_type: "Trigger",
|
|
495
|
+
entity_name: trigger.name,
|
|
496
|
+
});
|
|
485
497
|
if (req.xhr) {
|
|
486
498
|
res.json({ success: "ok" });
|
|
487
499
|
return;
|
|
@@ -495,7 +507,7 @@ router.post(
|
|
|
495
507
|
function genWorkflowDiagram(steps) {
|
|
496
508
|
const stepNames = steps.map((s) => s.name);
|
|
497
509
|
const nodeLines = steps.map(
|
|
498
|
-
(s) => ` ${s.
|
|
510
|
+
(s) => ` ${s.mmname}["\`**${s.name}**
|
|
499
511
|
${s.action_name}\`"]:::wfstep${s.id}`
|
|
500
512
|
);
|
|
501
513
|
|
|
@@ -503,34 +515,39 @@ function genWorkflowDiagram(steps) {
|
|
|
503
515
|
const linkLines = [];
|
|
504
516
|
let step_ix = 0;
|
|
505
517
|
for (const step of steps) {
|
|
506
|
-
if (step.initial_step) linkLines.push(` _Start --> ${step.
|
|
507
|
-
if (step.
|
|
508
|
-
linkLines.push(
|
|
509
|
-
` ${step.name} --> ${step.configuration.for_loop_step_name}`
|
|
510
|
-
);
|
|
511
|
-
} else if (stepNames.includes(step.next_step)) {
|
|
518
|
+
if (step.initial_step) linkLines.push(` _Start --> ${step.mmname}`);
|
|
519
|
+
if (stepNames.includes(step.next_step)) {
|
|
512
520
|
linkLines.push(
|
|
513
|
-
` ${step.
|
|
521
|
+
` ${step.mmname}-- <i class="fas fa-plus add-btw-nodes btw-nodes-${step.id}-${step.next_step}"></i> ---${step.mmnext}`
|
|
514
522
|
);
|
|
515
523
|
} else if (step.next_step) {
|
|
516
524
|
let found = false;
|
|
517
525
|
for (const otherStep of stepNames)
|
|
518
526
|
if (step.next_step.includes(otherStep)) {
|
|
519
|
-
linkLines.push(
|
|
527
|
+
linkLines.push(
|
|
528
|
+
` ${step.mmname} --> ${WorkflowStep.mmescape(otherStep)}`
|
|
529
|
+
);
|
|
520
530
|
found = true;
|
|
521
531
|
}
|
|
522
532
|
if (!found) {
|
|
523
533
|
linkLines.push(
|
|
524
|
-
` ${step.
|
|
534
|
+
` ${step.mmname}-- <a href="/actions/stepedit/${step.trigger_id}/${step.id}">Error: missing next step in ${step.mmname}</a> ---_End_${step.mmname}`
|
|
525
535
|
);
|
|
526
536
|
nodeLines.push(
|
|
527
|
-
` _End_${step.
|
|
537
|
+
` _End_${step.mmname}:::wfadd${step.id}@{ shape: circle, label: "<i class='fas fa-plus with-link'></i>" }`
|
|
528
538
|
);
|
|
529
539
|
}
|
|
530
540
|
} else if (!step.next_step) {
|
|
531
|
-
linkLines.push(` ${step.
|
|
541
|
+
linkLines.push(` ${step.mmname} --> _End_${step.mmname}`);
|
|
532
542
|
nodeLines.push(
|
|
533
|
-
` _End_${step.
|
|
543
|
+
` _End_${step.mmname}:::wfadd${step.id}@{ shape: circle, label: "<i class='fas fa-plus with-link'></i>" }`
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
if (step.action_name === "ForLoop") {
|
|
547
|
+
linkLines.push(
|
|
548
|
+
` ${step.mmname}-.->${WorkflowStep.mmescape(
|
|
549
|
+
step.configuration.loop_body_initial_step
|
|
550
|
+
)}`
|
|
534
551
|
);
|
|
535
552
|
}
|
|
536
553
|
if (step.action_name === "EndForLoop") {
|
|
@@ -542,7 +559,7 @@ function genWorkflowDiagram(steps) {
|
|
|
542
559
|
break;
|
|
543
560
|
}
|
|
544
561
|
}
|
|
545
|
-
if (forStep) linkLines.push(` ${step.
|
|
562
|
+
if (forStep) linkLines.push(` ${step.mmname} --> ${forStep.mmname}`);
|
|
546
563
|
}
|
|
547
564
|
step_ix += 1;
|
|
548
565
|
}
|
|
@@ -581,7 +598,29 @@ const getWorkflowConfig = async (req, id, table, trigger) => {
|
|
|
581
598
|
],
|
|
582
599
|
});
|
|
583
600
|
trigCfgForm.values = trigger.configuration;
|
|
601
|
+
let copilot_form = "";
|
|
602
|
+
|
|
603
|
+
if (getState().functions.copilot_generate_workflow) {
|
|
604
|
+
copilot_form = renderForm(
|
|
605
|
+
new Form({
|
|
606
|
+
action: `/actions/gen-copilot/${id}`,
|
|
607
|
+
values: { description: trigger.description || "" },
|
|
608
|
+
submitLabel: "Generate workflow with copilot",
|
|
609
|
+
formStyle: "vert",
|
|
610
|
+
fields: [
|
|
611
|
+
{
|
|
612
|
+
name: "description",
|
|
613
|
+
label: "Description",
|
|
614
|
+
type: "String",
|
|
615
|
+
fieldview: "textarea",
|
|
616
|
+
},
|
|
617
|
+
],
|
|
618
|
+
}),
|
|
619
|
+
req.csrfToken()
|
|
620
|
+
);
|
|
621
|
+
}
|
|
584
622
|
return (
|
|
623
|
+
copilot_form +
|
|
585
624
|
pre({ class: "mermaid" }, genWorkflowDiagram(steps)) +
|
|
586
625
|
script(
|
|
587
626
|
{ defer: "defer" },
|
|
@@ -641,16 +680,6 @@ window.addEventListener('DOMContentLoaded',tryAddWFNodes)`
|
|
|
641
680
|
);
|
|
642
681
|
};
|
|
643
682
|
|
|
644
|
-
const jsIdentifierValidator = (s) => {
|
|
645
|
-
if (!s) return "An identifier is required";
|
|
646
|
-
if (s.includes(" ")) return "Spaces not allowd";
|
|
647
|
-
let badc = "'#:/\\@()[]{}\"!%^&*-+*~<>,.?|"
|
|
648
|
-
.split("")
|
|
649
|
-
.find((c) => s.includes(c));
|
|
650
|
-
|
|
651
|
-
if (badc) return `Character ${badc} not allowed`;
|
|
652
|
-
};
|
|
653
|
-
|
|
654
683
|
const getWorkflowStepForm = async (
|
|
655
684
|
trigger,
|
|
656
685
|
req,
|
|
@@ -696,23 +725,26 @@ const getWorkflowStepForm = async (
|
|
|
696
725
|
},
|
|
697
726
|
};
|
|
698
727
|
if (cfgFld.input_type === "code") cfgFld.input_type = "textarea";
|
|
699
|
-
actionConfigFields.push(cfgFld);
|
|
700
728
|
}
|
|
701
729
|
} catch {}
|
|
702
730
|
}
|
|
731
|
+
actionConfigFields.push({
|
|
732
|
+
label: "Subcontext",
|
|
733
|
+
name: "subcontext",
|
|
734
|
+
type: "String",
|
|
735
|
+
sublabel:
|
|
736
|
+
"Optional. A key on the current workflow's context, the values of which will be the called workflow's context.",
|
|
737
|
+
showIf: {
|
|
738
|
+
wf_action_name: Trigger.find({ action: "Workflow" }).map((wf) => wf.name),
|
|
739
|
+
},
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
const builtInActionExplainers = WorkflowStep.builtInActionExplainers();
|
|
703
743
|
const actionsNotRequiringRow = Trigger.action_options({
|
|
704
744
|
notRequireRow: true,
|
|
705
745
|
noMultiStep: true,
|
|
706
746
|
builtInLabel: "Workflow Actions",
|
|
707
|
-
builtIns:
|
|
708
|
-
"SetContext",
|
|
709
|
-
"TableQuery",
|
|
710
|
-
"Output",
|
|
711
|
-
"DataOutput",
|
|
712
|
-
"WaitUntil",
|
|
713
|
-
"WaitNextTick",
|
|
714
|
-
"UserForm",
|
|
715
|
-
],
|
|
747
|
+
builtIns: Object.keys(builtInActionExplainers),
|
|
716
748
|
forWorkflow: true,
|
|
717
749
|
});
|
|
718
750
|
const triggers = Trigger.find({
|
|
@@ -721,151 +753,9 @@ const getWorkflowStepForm = async (
|
|
|
721
753
|
triggers.forEach((tr) => {
|
|
722
754
|
if (tr.description) actionExplainers[tr.name] = tr.description;
|
|
723
755
|
});
|
|
724
|
-
actionExplainers
|
|
725
|
-
actionExplainers.TableQuery = "Query a table into a variable in the context";
|
|
726
|
-
actionExplainers.Output =
|
|
727
|
-
"Display a message to the user. Pause workflow until the message is read.";
|
|
728
|
-
actionExplainers.DataOutput =
|
|
729
|
-
"Display a value to the user. Arrays of objects will be displayed as tables. Pause workflow until the message is read.";
|
|
730
|
-
actionExplainers.WaitUntil = "Pause until a time in the future";
|
|
731
|
-
actionExplainers.WaitNextTick =
|
|
732
|
-
"Pause until the next scheduler invocation (at most 5 minutes)";
|
|
733
|
-
actionExplainers.UserForm =
|
|
734
|
-
"Ask a user one or more questions, pause until they are answered";
|
|
735
|
-
|
|
736
|
-
actionConfigFields.push({
|
|
737
|
-
label: "Form header",
|
|
738
|
-
sublabel: "Text shown to the user at the top of the form",
|
|
739
|
-
name: "form_header",
|
|
740
|
-
type: "String",
|
|
741
|
-
showIf: { wf_action_name: "UserForm" },
|
|
742
|
-
});
|
|
743
|
-
actionConfigFields.push({
|
|
744
|
-
label: "User ID",
|
|
745
|
-
name: "user_id_expression",
|
|
746
|
-
type: "String",
|
|
747
|
-
sublabel: "Optional. If blank assigned to user starting the workflow",
|
|
748
|
-
showIf: { wf_action_name: "UserForm" },
|
|
749
|
-
});
|
|
750
|
-
actionConfigFields.push({
|
|
751
|
-
label: "Resume at",
|
|
752
|
-
name: "resume_at",
|
|
753
|
-
sublabel:
|
|
754
|
-
"JavaScript expression for the time to resume. <code>moment</code> is in scope.",
|
|
755
|
-
type: "String",
|
|
756
|
-
showIf: { wf_action_name: "WaitUntil" },
|
|
757
|
-
});
|
|
758
|
-
actionConfigFields.push({
|
|
759
|
-
label: "Context values",
|
|
760
|
-
name: "ctx_values",
|
|
761
|
-
sublabel:
|
|
762
|
-
"JavaScript object expression for the variables to set. Example <code>{x: 5, y:y+1}</code> will set x to 5 and increment existing context variable y",
|
|
763
|
-
type: "String",
|
|
764
|
-
fieldview: "textarea",
|
|
765
|
-
class: "validate-expression",
|
|
766
|
-
default: "{}",
|
|
767
|
-
showIf: { wf_action_name: "SetContext" },
|
|
768
|
-
});
|
|
769
|
-
actionConfigFields.push({
|
|
770
|
-
label: "Output text",
|
|
771
|
-
name: "output_text",
|
|
772
|
-
sublabel:
|
|
773
|
-
"Message shown to the user. Can contain HTML tags and use interpolations {{ }} to access the context",
|
|
774
|
-
type: "String",
|
|
775
|
-
fieldview: "textarea",
|
|
776
|
-
showIf: { wf_action_name: "Output" },
|
|
777
|
-
});
|
|
778
|
-
actionConfigFields.push({
|
|
779
|
-
label: "Output expression",
|
|
780
|
-
name: "output_expr",
|
|
781
|
-
sublabel:
|
|
782
|
-
"JavaScript expression for the value to output. Typically the name of a variable",
|
|
783
|
-
type: "String",
|
|
784
|
-
class: "validate-expression",
|
|
785
|
-
showIf: { wf_action_name: "DataOutput" },
|
|
786
|
-
});
|
|
787
|
-
actionConfigFields.push({
|
|
788
|
-
label: "Markdown",
|
|
789
|
-
name: "markdown",
|
|
790
|
-
sublabel:
|
|
791
|
-
"The centents are markdown formatted and should be rendered to HTML",
|
|
792
|
-
type: "Bool",
|
|
793
|
-
showIf: { wf_action_name: "Output" },
|
|
794
|
-
});
|
|
795
|
-
actionConfigFields.push({
|
|
796
|
-
label: "Table",
|
|
797
|
-
name: "query_table",
|
|
798
|
-
type: "String",
|
|
799
|
-
required: true,
|
|
800
|
-
attributes: { options: (await Table.find()).map((t) => t.name) },
|
|
801
|
-
showIf: { wf_action_name: "TableQuery" },
|
|
802
|
-
});
|
|
803
|
-
actionConfigFields.push({
|
|
804
|
-
label: "Query",
|
|
805
|
-
name: "query_object",
|
|
806
|
-
sublabel: "Where object, example <code>{manager: 1}</code>",
|
|
807
|
-
type: "String",
|
|
808
|
-
required: true,
|
|
809
|
-
class: "validate-expression",
|
|
810
|
-
default: "{}",
|
|
811
|
-
showIf: { wf_action_name: "TableQuery" },
|
|
812
|
-
});
|
|
813
|
-
actionConfigFields.push({
|
|
814
|
-
label: "Variable",
|
|
815
|
-
name: "query_variable",
|
|
816
|
-
sublabel: "Context variable to write to query results to",
|
|
817
|
-
type: "String",
|
|
818
|
-
required: true,
|
|
819
|
-
validator: jsIdentifierValidator,
|
|
820
|
-
showIf: { wf_action_name: "TableQuery" },
|
|
821
|
-
});
|
|
756
|
+
Object.assign(actionExplainers, builtInActionExplainers);
|
|
822
757
|
actionConfigFields.push(
|
|
823
|
-
|
|
824
|
-
name: "user_form_questions",
|
|
825
|
-
showIf: { wf_action_name: "UserForm" },
|
|
826
|
-
fields: [
|
|
827
|
-
{
|
|
828
|
-
label: "Label",
|
|
829
|
-
name: "label",
|
|
830
|
-
type: "String",
|
|
831
|
-
sublabel:
|
|
832
|
-
"The text that will shown to the user above the input elements",
|
|
833
|
-
},
|
|
834
|
-
{
|
|
835
|
-
label: "Variable name",
|
|
836
|
-
name: "var_name",
|
|
837
|
-
type: "String",
|
|
838
|
-
sublabel:
|
|
839
|
-
"The answer will be set in the context with this variable name",
|
|
840
|
-
validator: jsIdentifierValidator,
|
|
841
|
-
},
|
|
842
|
-
{
|
|
843
|
-
label: "Input Type",
|
|
844
|
-
name: "qtype",
|
|
845
|
-
type: "String",
|
|
846
|
-
required: true,
|
|
847
|
-
attributes: {
|
|
848
|
-
options: [
|
|
849
|
-
"Yes/No",
|
|
850
|
-
"Checkbox",
|
|
851
|
-
"Free text",
|
|
852
|
-
"Multiple choice",
|
|
853
|
-
//"Multiple checks",
|
|
854
|
-
"Integer",
|
|
855
|
-
"Float",
|
|
856
|
-
//"File upload",
|
|
857
|
-
],
|
|
858
|
-
},
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
label: "Options",
|
|
862
|
-
name: "options",
|
|
863
|
-
type: "String",
|
|
864
|
-
sublabel: "Comma separated list of multiple choice options",
|
|
865
|
-
showIf: { qtype: ["Multiple choice", "Multiple checks"] },
|
|
866
|
-
},
|
|
867
|
-
],
|
|
868
|
-
})
|
|
758
|
+
...(await WorkflowStep.builtInActionConfigFields({ trigger }))
|
|
869
759
|
);
|
|
870
760
|
|
|
871
761
|
const form = new Form({
|
|
@@ -1278,6 +1168,10 @@ router.post(
|
|
|
1278
1168
|
await Trigger.update(trigger.id, {
|
|
1279
1169
|
configuration: { ...trigger.configuration, ...form.values },
|
|
1280
1170
|
});
|
|
1171
|
+
Trigger.emitEvent("AppChange", `Trigger ${trigger.name}`, req.user, {
|
|
1172
|
+
entity_type: "Trigger",
|
|
1173
|
+
entity_name: trigger.name,
|
|
1174
|
+
});
|
|
1281
1175
|
if (req.xhr) {
|
|
1282
1176
|
res.json({ success: "ok" });
|
|
1283
1177
|
return;
|
|
@@ -1304,6 +1198,10 @@ router.post(
|
|
|
1304
1198
|
error_catcher(async (req, res) => {
|
|
1305
1199
|
const { id } = req.params;
|
|
1306
1200
|
const trigger = await Trigger.findOne({ id });
|
|
1201
|
+
Trigger.emitEvent("AppChange", `Trigger ${trigger.name}`, req.user, {
|
|
1202
|
+
entity_type: "Trigger",
|
|
1203
|
+
entity_name: trigger.name,
|
|
1204
|
+
});
|
|
1307
1205
|
await trigger.delete();
|
|
1308
1206
|
res.redirect(`/actions/`);
|
|
1309
1207
|
})
|
|
@@ -1432,6 +1330,10 @@ router.post(
|
|
|
1432
1330
|
const { id } = req.params;
|
|
1433
1331
|
const trig = await Trigger.findOne({ id });
|
|
1434
1332
|
const newtrig = await trig.clone();
|
|
1333
|
+
Trigger.emitEvent("AppChange", `Trigger ${newtrig.name}`, req.user, {
|
|
1334
|
+
entity_type: "Trigger",
|
|
1335
|
+
entity_name: newtrig.name,
|
|
1336
|
+
});
|
|
1435
1337
|
req.flash(
|
|
1436
1338
|
"success",
|
|
1437
1339
|
req.__("Trigger %s duplicated as %s", trig.name, newtrig.name)
|
|
@@ -1561,7 +1463,11 @@ router.post(
|
|
|
1561
1463
|
res.redirect(`/actions/configure/${step.trigger_id}`);
|
|
1562
1464
|
}
|
|
1563
1465
|
}
|
|
1564
|
-
|
|
1466
|
+
Trigger.emitEvent("AppChange", `Trigger ${trigger.name}`, req.user, {
|
|
1467
|
+
entity_type: "Trigger",
|
|
1468
|
+
entity_name: trigger.name,
|
|
1469
|
+
});
|
|
1470
|
+
if (_after_step && _after_step !== "undefined") {
|
|
1565
1471
|
const astep = await WorkflowStep.findOne({
|
|
1566
1472
|
id: _after_step,
|
|
1567
1473
|
trigger_id,
|
|
@@ -1583,6 +1489,32 @@ router.post(
|
|
|
1583
1489
|
})
|
|
1584
1490
|
);
|
|
1585
1491
|
|
|
1492
|
+
router.post(
|
|
1493
|
+
"/gen-copilot/:trigger_id",
|
|
1494
|
+
isAdmin,
|
|
1495
|
+
error_catcher(async (req, res) => {
|
|
1496
|
+
const { trigger_id } = req.params;
|
|
1497
|
+
const trigger = await Trigger.findOne({ id: trigger_id });
|
|
1498
|
+
await WorkflowStep.deleteForTrigger(trigger.id);
|
|
1499
|
+
const description = req.body.description;
|
|
1500
|
+
await Trigger.update(trigger.id, { description });
|
|
1501
|
+
const steps = await getState().functions.copilot_generate_workflow.run(
|
|
1502
|
+
description,
|
|
1503
|
+
trigger.id
|
|
1504
|
+
);
|
|
1505
|
+
if (steps.length) steps[0].initial_step = true;
|
|
1506
|
+
for (const step of steps) {
|
|
1507
|
+
step.trigger_id = trigger.id;
|
|
1508
|
+
await WorkflowStep.create(step);
|
|
1509
|
+
}
|
|
1510
|
+
Trigger.emitEvent("AppChange", `Trigger ${trigger.name}`, req.user, {
|
|
1511
|
+
entity_type: "Trigger",
|
|
1512
|
+
entity_name: trigger.name,
|
|
1513
|
+
});
|
|
1514
|
+
res.redirect(`/actions/configure/${trigger.id}`);
|
|
1515
|
+
})
|
|
1516
|
+
);
|
|
1517
|
+
|
|
1586
1518
|
router.post(
|
|
1587
1519
|
"/delete-step/:step_id",
|
|
1588
1520
|
isAdmin,
|
|
@@ -1801,6 +1733,21 @@ router.post(
|
|
|
1801
1733
|
);
|
|
1802
1734
|
|
|
1803
1735
|
const getWorkflowStepUserForm = async (run, trigger, step, req) => {
|
|
1736
|
+
if (step.action_name === "EditViewForm") {
|
|
1737
|
+
const view = View.findOne({ name: step.configuration.edit_view });
|
|
1738
|
+
const table = Table.findOne({ id: view.table_id });
|
|
1739
|
+
const form = await getForm(
|
|
1740
|
+
table,
|
|
1741
|
+
view.name,
|
|
1742
|
+
view.configuration.columns,
|
|
1743
|
+
view.configuration.layout,
|
|
1744
|
+
null,
|
|
1745
|
+
req
|
|
1746
|
+
);
|
|
1747
|
+
form.action = `/actions/fill-workflow-form/${run.id}`;
|
|
1748
|
+
return form;
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1804
1751
|
let blurb = run.wait_info.output || step.configuration?.form_header || "";
|
|
1805
1752
|
if (run.wait_info.markdown && run.wait_info.output) blurb = md.render(blurb);
|
|
1806
1753
|
const form = new Form({
|
|
@@ -1933,12 +1880,10 @@ WORKFLOWS TODO
|
|
|
1933
1880
|
|
|
1934
1881
|
help file to explain steps, and context
|
|
1935
1882
|
|
|
1936
|
-
workflow actions:
|
|
1883
|
+
workflow actions: Stop, RunEditView, ReadFile, WriteFile, APIResponse
|
|
1937
1884
|
|
|
1938
|
-
Error handlers
|
|
1939
1885
|
other triggers can be steps
|
|
1940
1886
|
interactive workflows for not logged in
|
|
1941
|
-
show end node in diagram
|
|
1942
1887
|
actions can declare which variables they inject into scope
|
|
1943
1888
|
|
|
1944
1889
|
show unconnected steps
|