@saltcorn/server 1.1.0-beta.14 → 1.1.0-beta.15
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/auth/admin.js +1 -1
- package/locales/en.json +4 -1
- package/package.json +9 -9
- package/public/mermaid.min.js +1077 -792
- package/public/saltcorn.js +15 -0
- package/routes/actions.js +69 -27
- package/routes/tables.js +25 -19
package/public/saltcorn.js
CHANGED
|
@@ -1208,6 +1208,21 @@ function check_unsaved_form(event, script_tag) {
|
|
|
1208
1208
|
event.returnValue = true;
|
|
1209
1209
|
}
|
|
1210
1210
|
}
|
|
1211
|
+
function check_delete_unsaved(tablename, script_tag) {
|
|
1212
|
+
const form = $(script_tag).parent().find("form");
|
|
1213
|
+
if (form.length && !form.attr("data-form-changed")) {
|
|
1214
|
+
//delete row
|
|
1215
|
+
const rec = get_form_record(form);
|
|
1216
|
+
|
|
1217
|
+
$.ajax({
|
|
1218
|
+
url: `/api/${tablename}/${rec.id}`,
|
|
1219
|
+
type: "DELETE",
|
|
1220
|
+
headers: {
|
|
1221
|
+
"CSRF-Token": _sc_globalCsrf,
|
|
1222
|
+
},
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1211
1226
|
|
|
1212
1227
|
(() => {
|
|
1213
1228
|
const e = document.querySelector("[data-sidebar-toggler]");
|
package/routes/actions.js
CHANGED
|
@@ -344,6 +344,11 @@ router.get(
|
|
|
344
344
|
isAdmin,
|
|
345
345
|
error_catcher(async (req, res) => {
|
|
346
346
|
const form = await triggerForm(req);
|
|
347
|
+
if (req.query.table) {
|
|
348
|
+
const table = Table.findOne({ name: req.query.table });
|
|
349
|
+
if (table) form.values.table_id = table.id;
|
|
350
|
+
}
|
|
351
|
+
|
|
347
352
|
send_events_page({
|
|
348
353
|
res,
|
|
349
354
|
req,
|
|
@@ -487,15 +492,16 @@ router.post(
|
|
|
487
492
|
|
|
488
493
|
function genWorkflowDiagram(steps) {
|
|
489
494
|
const stepNames = steps.map((s) => s.name);
|
|
490
|
-
const nodeLines = steps
|
|
491
|
-
.
|
|
492
|
-
(s) => ` ${s.name}["\`**${s.name}**
|
|
495
|
+
const nodeLines = steps.map(
|
|
496
|
+
(s) => ` ${s.name}["\`**${s.name}**
|
|
493
497
|
${s.action_name}\`"]:::wfstep${s.id}`
|
|
494
|
-
|
|
495
|
-
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
nodeLines.unshift(` _Start@{ shape: circle, label: "Start" }`);
|
|
496
501
|
const linkLines = [];
|
|
497
502
|
let step_ix = 0;
|
|
498
503
|
for (const step of steps) {
|
|
504
|
+
if (step.initial_step) linkLines.push(` _Start --> ${step.name}`);
|
|
499
505
|
if (step.action_name === "ForLoop") {
|
|
500
506
|
linkLines.push(
|
|
501
507
|
` ${step.name} --> ${step.configuration.for_loop_step_name}`
|
|
@@ -520,7 +526,9 @@ function genWorkflowDiagram(steps) {
|
|
|
520
526
|
}
|
|
521
527
|
step_ix += 1;
|
|
522
528
|
}
|
|
523
|
-
|
|
529
|
+
const fc =
|
|
530
|
+
"flowchart TD\n" + nodeLines.join("\n") + "\n" + linkLines.join("\n");
|
|
531
|
+
return fc;
|
|
524
532
|
}
|
|
525
533
|
|
|
526
534
|
const getWorkflowConfig = async (req, id, table, trigger) => {
|
|
@@ -644,6 +652,7 @@ const getWorkflowStepForm = async (trigger, req, step_id) => {
|
|
|
644
652
|
builtIns: [
|
|
645
653
|
"SetContext",
|
|
646
654
|
"TableQuery",
|
|
655
|
+
"Output",
|
|
647
656
|
"WaitUntil",
|
|
648
657
|
"UserForm",
|
|
649
658
|
"WaitNextTick",
|
|
@@ -683,7 +692,15 @@ const getWorkflowStepForm = async (trigger, req, step_id) => {
|
|
|
683
692
|
default: "{}",
|
|
684
693
|
showIf: { wf_action_name: "SetContext" },
|
|
685
694
|
});
|
|
686
|
-
|
|
695
|
+
actionConfigFields.push({
|
|
696
|
+
label: "Output text",
|
|
697
|
+
name: "output_text",
|
|
698
|
+
sublabel:
|
|
699
|
+
"Message shown to the user. Can contain HTML tags and use interpolations {{ }} to access the context",
|
|
700
|
+
type: "String",
|
|
701
|
+
fieldview: "textarea",
|
|
702
|
+
showIf: { wf_action_name: "Output" },
|
|
703
|
+
});
|
|
687
704
|
actionConfigFields.push({
|
|
688
705
|
label: "Table",
|
|
689
706
|
name: "query_table",
|
|
@@ -959,7 +976,9 @@ router.get(
|
|
|
959
976
|
},
|
|
960
977
|
{
|
|
961
978
|
headerTag: `<script type="module">mermaid.initialize({securityLevel: 'loose'${
|
|
962
|
-
getState().getLightDarkMode(req.user)
|
|
979
|
+
getState().getLightDarkMode(req.user) === "dark"
|
|
980
|
+
? ",theme: 'dark',"
|
|
981
|
+
: ""
|
|
963
982
|
}});</script>`,
|
|
964
983
|
},
|
|
965
984
|
],
|
|
@@ -1401,22 +1420,36 @@ router.post(
|
|
|
1401
1420
|
trigger_id,
|
|
1402
1421
|
configuration,
|
|
1403
1422
|
};
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1423
|
+
try {
|
|
1424
|
+
if (wf_step_id && wf_step_id !== "undefined") {
|
|
1425
|
+
const wfStep = new WorkflowStep({ id: wf_step_id, ...step });
|
|
1426
|
+
|
|
1427
|
+
await wfStep.update(step);
|
|
1428
|
+
if (req.xhr) res.json({ success: "ok" });
|
|
1429
|
+
else {
|
|
1430
|
+
req.flash("success", req.__("Step saved"));
|
|
1431
|
+
res.redirect(`/actions/configure/${step.trigger_id}`);
|
|
1432
|
+
}
|
|
1433
|
+
} else {
|
|
1434
|
+
//insert
|
|
1435
|
+
|
|
1436
|
+
const id = await WorkflowStep.create(step);
|
|
1437
|
+
if (req.xhr)
|
|
1438
|
+
res.json({ success: "ok", set_fields: { wf_step_id: id } });
|
|
1439
|
+
else {
|
|
1440
|
+
req.flash("success", req.__("Step saved"));
|
|
1441
|
+
res.redirect(`/actions/configure/${step.trigger_id}`);
|
|
1442
|
+
}
|
|
1413
1443
|
}
|
|
1414
|
-
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1444
|
+
} catch (e) {
|
|
1445
|
+
const emsg =
|
|
1446
|
+
e.message ===
|
|
1447
|
+
'duplicate key value violates unique constraint "workflow_steps_name_uniq"'
|
|
1448
|
+
? `A step with the name ${wf_step_name} already exists`
|
|
1449
|
+
: e.message;
|
|
1450
|
+
if (req.xhr) res.json({ error: emsg });
|
|
1418
1451
|
else {
|
|
1419
|
-
req.flash("
|
|
1452
|
+
req.flash("error", emsg);
|
|
1420
1453
|
res.redirect(`/actions/configure/${step.trigger_id}`);
|
|
1421
1454
|
}
|
|
1422
1455
|
}
|
|
@@ -1463,10 +1496,10 @@ router.get(
|
|
|
1463
1496
|
case "Error":
|
|
1464
1497
|
return run.error;
|
|
1465
1498
|
case "Waiting":
|
|
1466
|
-
if (run.wait_info?.form)
|
|
1499
|
+
if (run.wait_info?.form || run.wait_info.output)
|
|
1467
1500
|
return a(
|
|
1468
1501
|
{ href: `/actions/fill-workflow-form/${run.id}` },
|
|
1469
|
-
"Fill ",
|
|
1502
|
+
run.wait_info.output ? "Show " : "Fill ",
|
|
1470
1503
|
run.current_step
|
|
1471
1504
|
);
|
|
1472
1505
|
return run.current_step;
|
|
@@ -1599,6 +1632,9 @@ router.get(
|
|
|
1599
1632
|
run.status === "Waiting"
|
|
1600
1633
|
? tr(th("Waiting for"), td(JSON.stringify(run.wait_info)))
|
|
1601
1634
|
: null,
|
|
1635
|
+
run.status === "Error"
|
|
1636
|
+
? tr(th("Error message"), td(run.error))
|
|
1637
|
+
: null,
|
|
1602
1638
|
tr(
|
|
1603
1639
|
th("Context"),
|
|
1604
1640
|
td(pre(text(JSON.stringify(run.context, null, 2))))
|
|
@@ -1663,7 +1699,8 @@ const getWorkflowStepUserForm = async (run, trigger, step, user) => {
|
|
|
1663
1699
|
|
|
1664
1700
|
const form = new Form({
|
|
1665
1701
|
action: `/actions/fill-workflow-form/${run.id}`,
|
|
1666
|
-
blurb: step.configuration?.form_header || "",
|
|
1702
|
+
blurb: run.wait_info.output || step.configuration?.form_header || "",
|
|
1703
|
+
formStyle: run.wait_info.output ? "vert" : undefined,
|
|
1667
1704
|
fields: (step.configuration.user_form_questions || []).map((q) => ({
|
|
1668
1705
|
label: q.label,
|
|
1669
1706
|
name: q.var_name,
|
|
@@ -1697,7 +1734,7 @@ router.get(
|
|
|
1697
1734
|
|
|
1698
1735
|
const form = await getWorkflowStepUserForm(run, trigger, step, req.user);
|
|
1699
1736
|
if (req.xhr) form.xhrSubmit = true;
|
|
1700
|
-
const title = "Fill form";
|
|
1737
|
+
const title = run.wait_info.output ? "Workflow output" : "Fill form";
|
|
1701
1738
|
res.sendWrap(title, renderForm(form, req.csrfToken()));
|
|
1702
1739
|
})
|
|
1703
1740
|
);
|
|
@@ -1791,9 +1828,14 @@ WORKFLOWS TODO
|
|
|
1791
1828
|
delete is not always working?
|
|
1792
1829
|
help file to explain steps, and context
|
|
1793
1830
|
|
|
1794
|
-
|
|
1831
|
+
action explainer
|
|
1832
|
+
workflow actions: ForLoop, EndForLoop, ReadFile, WriteFile, APIResponse
|
|
1795
1833
|
|
|
1796
1834
|
interactive workflows for not logged in
|
|
1835
|
+
correctly suggest new step name
|
|
1836
|
+
show end node in diagram
|
|
1837
|
+
|
|
1838
|
+
Error handlers
|
|
1797
1839
|
|
|
1798
1840
|
show unconnected steps
|
|
1799
1841
|
why is code not initialising
|
package/routes/tables.js
CHANGED
|
@@ -56,7 +56,7 @@ const {
|
|
|
56
56
|
} = require("@saltcorn/data/models/discovery");
|
|
57
57
|
const { getState } = require("@saltcorn/data/db/state");
|
|
58
58
|
const { cardHeaderTabs } = require("@saltcorn/markup/layout_utils");
|
|
59
|
-
const { tablesList, viewsList } = require("./common_lists");
|
|
59
|
+
const { tablesList, viewsList, getTriggerList } = require("./common_lists");
|
|
60
60
|
const {
|
|
61
61
|
InvalidConfiguration,
|
|
62
62
|
removeAllWhiteSpace,
|
|
@@ -839,22 +839,6 @@ router.get(
|
|
|
839
839
|
inbound_refs.map((tnm) => link(`/table/${tnm}`, tnm)).join(", ") +
|
|
840
840
|
"<br>"
|
|
841
841
|
: "",
|
|
842
|
-
triggers.length
|
|
843
|
-
? req.__("Table triggers: ") +
|
|
844
|
-
triggers
|
|
845
|
-
.map((t) =>
|
|
846
|
-
link(
|
|
847
|
-
`/actions/configure/${
|
|
848
|
-
t.id
|
|
849
|
-
}?on_done_redirect=${encodeURIComponent(
|
|
850
|
-
`table/${table.name}`
|
|
851
|
-
)}`,
|
|
852
|
-
t.name
|
|
853
|
-
)
|
|
854
|
-
)
|
|
855
|
-
.join(", ") +
|
|
856
|
-
"<br>"
|
|
857
|
-
: "",
|
|
858
842
|
!table.external &&
|
|
859
843
|
!table.provider_name &&
|
|
860
844
|
a(
|
|
@@ -866,7 +850,8 @@ router.get(
|
|
|
866
850
|
),
|
|
867
851
|
];
|
|
868
852
|
}
|
|
869
|
-
|
|
853
|
+
let viewCard;
|
|
854
|
+
let triggerCard = "";
|
|
870
855
|
if (fields.length > 0) {
|
|
871
856
|
const views = await View.find(
|
|
872
857
|
table.id ? { table_id: table.id } : { exttable_name: table.name }
|
|
@@ -899,6 +884,25 @@ router.get(
|
|
|
899
884
|
req.__("Create view")
|
|
900
885
|
),
|
|
901
886
|
};
|
|
887
|
+
|
|
888
|
+
triggerCard = {
|
|
889
|
+
type: "card",
|
|
890
|
+
id: "table-triggers",
|
|
891
|
+
title: req.__("Triggers on table"),
|
|
892
|
+
contents:
|
|
893
|
+
(triggers.length
|
|
894
|
+
? await getTriggerList(triggers, req)
|
|
895
|
+
: p("Triggers run actions in response to events on this table")) +
|
|
896
|
+
a(
|
|
897
|
+
{
|
|
898
|
+
href: `/actions/new?table=${encodeURIComponent(
|
|
899
|
+
table.name
|
|
900
|
+
)}&on_done_redirect=${encodeURIComponent(`table/${table.name}`)}`,
|
|
901
|
+
class: "btn btn-primary",
|
|
902
|
+
},
|
|
903
|
+
req.__("Create trigger")
|
|
904
|
+
),
|
|
905
|
+
};
|
|
902
906
|
}
|
|
903
907
|
const models = await Model.find({ table_id: table.id });
|
|
904
908
|
const modelCard = div(
|
|
@@ -1093,6 +1097,7 @@ router.get(
|
|
|
1093
1097
|
]
|
|
1094
1098
|
: []),
|
|
1095
1099
|
...(viewCard ? [viewCard] : []),
|
|
1100
|
+
...(triggerCard ? [triggerCard] : []),
|
|
1096
1101
|
{
|
|
1097
1102
|
type: "card",
|
|
1098
1103
|
title: req.__("Edit table properties"),
|
|
@@ -1177,6 +1182,7 @@ router.post(
|
|
|
1177
1182
|
let notify = "";
|
|
1178
1183
|
if (!rest.versioned) rest.versioned = false;
|
|
1179
1184
|
if (!rest.has_sync_info) rest.has_sync_info = false;
|
|
1185
|
+
rest.is_user_group = !!rest.is_user_group;
|
|
1180
1186
|
if (rest.ownership_field_id === "_formula") {
|
|
1181
1187
|
rest.ownership_field_id = null;
|
|
1182
1188
|
const fmlValidRes = expressionValidator(rest.ownership_formula);
|
|
@@ -1937,7 +1943,7 @@ router.post(
|
|
|
1937
1943
|
const table = Table.findOne({ name });
|
|
1938
1944
|
|
|
1939
1945
|
try {
|
|
1940
|
-
await table.deleteRows({}, req.user);
|
|
1946
|
+
await table.deleteRows({}, req.user, true);
|
|
1941
1947
|
req.flash("success", req.__("Deleted all rows"));
|
|
1942
1948
|
} catch (e) {
|
|
1943
1949
|
req.flash("error", e.message);
|