@saltcorn/server 0.9.0-beta.0 → 0.9.0-beta.10
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/app.js +58 -6
- package/auth/routes.js +16 -20
- package/errors.js +15 -4
- package/help/Actions.tmd +9 -0
- package/help/Extra state formula.tmd +62 -0
- package/help/Field views.tmd +22 -0
- package/help/JavaScript action code.tmd +161 -0
- package/help/Table formula constraint.tmd +6 -4
- package/help/View patterns.tmd +35 -0
- package/help/Where formula.tmd +30 -0
- package/help/index.js +19 -10
- package/load_plugins.js +4 -2
- package/locales/da.json +709 -709
- package/locales/de.json +1049 -1049
- package/locales/en.json +18 -2
- package/locales/pl.json +1155 -1155
- package/locales/ru.json +1101 -1101
- package/locales/si.json +1196 -1196
- package/locales/uk.json +1168 -1168
- package/locales/zh.json +886 -886
- package/package.json +10 -9
- package/public/saltcorn-builder.css +4 -0
- package/public/saltcorn-common.js +85 -17
- package/public/saltcorn.css +14 -0
- package/public/saltcorn.js +33 -7
- package/routes/actions.js +5 -3
- package/routes/admin.js +146 -20
- package/routes/fields.js +15 -3
- package/routes/menu.js +1 -1
- package/routes/packs.js +134 -9
- package/routes/plugins.js +186 -36
- package/routes/sync.js +4 -1
- package/routes/tables.js +4 -3
- package/routes/viewedit.js +21 -1
- package/tests/admin.test.js +2 -2
- package/tests/sync.test.js +140 -6
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.0-beta.
|
|
3
|
+
"version": "0.9.0-beta.10",
|
|
4
4
|
"description": "Server app for Saltcorn, open-source no-code platform",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@saltcorn/base-plugin": "0.9.0-beta.
|
|
10
|
-
"@saltcorn/builder": "0.9.0-beta.
|
|
11
|
-
"@saltcorn/data": "0.9.0-beta.
|
|
12
|
-
"@saltcorn/admin-models": "0.9.0-beta.
|
|
13
|
-
"@saltcorn/filemanager": "0.9.0-beta.
|
|
14
|
-
"@saltcorn/markup": "0.9.0-beta.
|
|
15
|
-
"@saltcorn/sbadmin2": "0.9.0-beta.
|
|
9
|
+
"@saltcorn/base-plugin": "0.9.0-beta.10",
|
|
10
|
+
"@saltcorn/builder": "0.9.0-beta.10",
|
|
11
|
+
"@saltcorn/data": "0.9.0-beta.10",
|
|
12
|
+
"@saltcorn/admin-models": "0.9.0-beta.10",
|
|
13
|
+
"@saltcorn/filemanager": "0.9.0-beta.10",
|
|
14
|
+
"@saltcorn/markup": "0.9.0-beta.10",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.9.0-beta.10",
|
|
16
16
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
17
17
|
"@socket.io/sticky": "^1.0.1",
|
|
18
18
|
"adm-zip": "0.5.10",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"node-fetch": "2.6.9",
|
|
46
46
|
"node-watch": "^0.7.2",
|
|
47
47
|
"notp": "2.0.3",
|
|
48
|
+
"npm-registry-fetch": "16.0.0",
|
|
48
49
|
"passport": "^0.6.0",
|
|
49
50
|
"passport-custom": "^1.1.1",
|
|
50
51
|
"passport-http-bearer": "^1.0.1",
|
|
@@ -55,7 +56,7 @@
|
|
|
55
56
|
"qrcode": "1.5.1",
|
|
56
57
|
"resize-with-sharp-or-jimp": "0.1.6",
|
|
57
58
|
"socket.io": "4.6.0",
|
|
58
|
-
"systeminformation": "^5.
|
|
59
|
+
"systeminformation": "^5.21.7",
|
|
59
60
|
"thirty-two": "1.0.2",
|
|
60
61
|
"tmp-promise": "^3.0.2",
|
|
61
62
|
"uuid": "^8.2.0",
|
|
@@ -51,6 +51,7 @@ const nubBy = (prop, xs) => {
|
|
|
51
51
|
});
|
|
52
52
|
};
|
|
53
53
|
function apply_showif() {
|
|
54
|
+
const isNode = typeof parent?.saltcorn?.data?.state === "undefined";
|
|
54
55
|
$("[data-show-if]").each(function (ix, element) {
|
|
55
56
|
var e = $(element);
|
|
56
57
|
try {
|
|
@@ -78,6 +79,15 @@ function apply_showif() {
|
|
|
78
79
|
console.error(e);
|
|
79
80
|
}
|
|
80
81
|
});
|
|
82
|
+
$("[data-dyn-href]").each(function (ix, element) {
|
|
83
|
+
const e = $(element);
|
|
84
|
+
const rec = get_form_record(e);
|
|
85
|
+
const href = new Function(
|
|
86
|
+
`{${Object.keys(rec).join(",")}}`,
|
|
87
|
+
"return " + e.attr("data-dyn-href")
|
|
88
|
+
)(rec);
|
|
89
|
+
e.attr("href", href);
|
|
90
|
+
});
|
|
81
91
|
$("[data-calc-options]").each(function (ix, element) {
|
|
82
92
|
var e = $(element);
|
|
83
93
|
var data = JSON.parse(decodeURIComponent(e.attr("data-calc-options")));
|
|
@@ -263,10 +273,12 @@ function apply_showif() {
|
|
|
263
273
|
|
|
264
274
|
if (typeof cache[recS] !== "undefined") {
|
|
265
275
|
e.html(cache[recS]);
|
|
276
|
+
e.prop("data-source-url-current", recS);
|
|
266
277
|
activate_onchange_coldef();
|
|
267
278
|
return;
|
|
268
279
|
}
|
|
269
|
-
|
|
280
|
+
|
|
281
|
+
const cb = {
|
|
270
282
|
success: (data) => {
|
|
271
283
|
e.html(data);
|
|
272
284
|
const cacheNow = e.prop("data-source-url-cache") || {};
|
|
@@ -286,7 +298,11 @@ function apply_showif() {
|
|
|
286
298
|
});
|
|
287
299
|
e.html("");
|
|
288
300
|
},
|
|
289
|
-
}
|
|
301
|
+
};
|
|
302
|
+
if (isNode) ajax_post_json(e.attr("data-source-url"), rec, cb);
|
|
303
|
+
else {
|
|
304
|
+
local_post_json(e.attr("data-source-url"), rec, cb);
|
|
305
|
+
}
|
|
290
306
|
});
|
|
291
307
|
const locale =
|
|
292
308
|
navigator.userLanguage ||
|
|
@@ -335,18 +351,21 @@ function splitTargetMatch(elemValue, target, keySpec) {
|
|
|
335
351
|
return elemValueShort === target;
|
|
336
352
|
}
|
|
337
353
|
|
|
338
|
-
function get_form_record(
|
|
354
|
+
function get_form_record(e_in, select_labels) {
|
|
339
355
|
const rec = {};
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
356
|
+
|
|
357
|
+
const e = e_in.viewname
|
|
358
|
+
? $(`form[data-viewname=${e_in.viewname}]`)
|
|
359
|
+
: e_in.closest(".form-namespace");
|
|
360
|
+
|
|
361
|
+
e.find("input[name],select[name],textarea[name]").each(function () {
|
|
362
|
+
const name = $(this).attr("data-fieldname") || $(this).attr("name");
|
|
363
|
+
if (select_labels && $(this).prop("tagName").toLowerCase() === "select")
|
|
364
|
+
rec[name] = $(this).find("option:selected").text();
|
|
365
|
+
else if ($(this).prop("type") === "checkbox")
|
|
366
|
+
rec[name] = $(this).prop("checked");
|
|
367
|
+
else rec[name] = $(this).val();
|
|
368
|
+
});
|
|
350
369
|
return rec;
|
|
351
370
|
}
|
|
352
371
|
function showIfFormulaInputs(e, fml) {
|
|
@@ -506,6 +525,11 @@ function initialize_page() {
|
|
|
506
525
|
});
|
|
507
526
|
|
|
508
527
|
$("form").change(apply_showif);
|
|
528
|
+
// also change if we select same
|
|
529
|
+
$("form select").on("blur", (e) => {
|
|
530
|
+
if (!e || !e.target) return;
|
|
531
|
+
$(e.target).closest("form").trigger("change");
|
|
532
|
+
});
|
|
509
533
|
apply_showif();
|
|
510
534
|
apply_showif();
|
|
511
535
|
$("[data-inline-edit-dest-url]").each(function () {
|
|
@@ -527,6 +551,7 @@ function initialize_page() {
|
|
|
527
551
|
var ajax = !!$(this).attr("data-inline-edit-ajax");
|
|
528
552
|
var type = $(this).attr("data-inline-edit-type");
|
|
529
553
|
var schema = $(this).attr("data-inline-edit-schema");
|
|
554
|
+
var decimalPlaces = $(this).attr("data-inline-edit-decimal-places");
|
|
530
555
|
if (schema) {
|
|
531
556
|
schema = JSON.parse(decodeURIComponent(schema));
|
|
532
557
|
}
|
|
@@ -549,6 +574,7 @@ function initialize_page() {
|
|
|
549
574
|
type,
|
|
550
575
|
is_key,
|
|
551
576
|
schema,
|
|
577
|
+
...(decimalPlaces ? { decimalPlaces } : {}),
|
|
552
578
|
})
|
|
553
579
|
);
|
|
554
580
|
const doAjaxOptionsFetch = (tblName, target) => {
|
|
@@ -601,7 +627,17 @@ function initialize_page() {
|
|
|
601
627
|
}
|
|
602
628
|
<input type="${
|
|
603
629
|
type === "Integer" || type === "Float" ? "number" : "text"
|
|
604
|
-
}"
|
|
630
|
+
}" ${
|
|
631
|
+
type === "Float"
|
|
632
|
+
? `step="${
|
|
633
|
+
decimalPlaces
|
|
634
|
+
? Math.round(
|
|
635
|
+
Math.pow(10, -decimalPlaces) * Math.pow(10, decimalPlaces)
|
|
636
|
+
) / Math.pow(10, decimalPlaces)
|
|
637
|
+
: "any"
|
|
638
|
+
}"`
|
|
639
|
+
: ""
|
|
640
|
+
} name="${key}" value="${escapeHtml(current)}">
|
|
605
641
|
<button type="submit" class="btn btn-sm btn-primary">OK</button>
|
|
606
642
|
<button onclick="cancel_inline_edit(event, '${opts}')" type="button" class="btn btn-sm btn-danger"><i class="fas fa-times"></i></button>
|
|
607
643
|
</form>`
|
|
@@ -654,10 +690,13 @@ function initialize_page() {
|
|
|
654
690
|
setTimeout(() => {
|
|
655
691
|
codes.forEach((el) => {
|
|
656
692
|
//console.log($(el).attr("mode"), el);
|
|
693
|
+
if ($(el).hasClass("codemirror-enabled")) return;
|
|
694
|
+
|
|
657
695
|
const cm = CodeMirror.fromTextArea(el, {
|
|
658
696
|
lineNumbers: true,
|
|
659
697
|
mode: $(el).attr("mode"),
|
|
660
698
|
});
|
|
699
|
+
$(el).addClass("codemirror-enabled");
|
|
661
700
|
cm.on(
|
|
662
701
|
"change",
|
|
663
702
|
$.debounce(() => {
|
|
@@ -768,6 +807,11 @@ function inline_submit_success(e, form, opts) {
|
|
|
768
807
|
: ""
|
|
769
808
|
}
|
|
770
809
|
${opts.current_label ? `data-inline-edit-current-label="${val}"` : ""}
|
|
810
|
+
${
|
|
811
|
+
opts.decimalPlaces
|
|
812
|
+
? `data-inline-edit-decimal-places="${opts.decimalPlaces}"`
|
|
813
|
+
: ""
|
|
814
|
+
}
|
|
771
815
|
data-inline-edit-dest-url="${opts.url}">
|
|
772
816
|
<span class="current">${val}</span>
|
|
773
817
|
<i class="editicon ${!isNode ? "visible" : ""} fas fa-edit ms-1"></i>
|
|
@@ -945,15 +989,26 @@ function emptyAlerts() {
|
|
|
945
989
|
$("#toasts-area").html("");
|
|
946
990
|
}
|
|
947
991
|
|
|
948
|
-
function press_store_button(clicked) {
|
|
992
|
+
function press_store_button(clicked, keepOld) {
|
|
949
993
|
let btn = clicked;
|
|
950
994
|
if ($(clicked).is("form")) btn = $(clicked).find("button[type=submit]");
|
|
951
|
-
|
|
995
|
+
if (keepOld) {
|
|
996
|
+
const oldText = $(btn).html();
|
|
997
|
+
$(btn).data("old-text", oldText);
|
|
998
|
+
}
|
|
952
999
|
const width = $(btn).width();
|
|
953
1000
|
$(btn).html('<i class="fas fa-spinner fa-spin"></i>').width(width);
|
|
954
1001
|
}
|
|
955
1002
|
|
|
956
|
-
function
|
|
1003
|
+
function restore_old_button(btnId) {
|
|
1004
|
+
const btn = $(`#${btnId}`);
|
|
1005
|
+
const oldText = $(btn).data("old-text");
|
|
1006
|
+
btn.html(oldText);
|
|
1007
|
+
btn.css({ width: "" });
|
|
1008
|
+
btn.removeData("old-text");
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function common_done(res, viewname, isWeb = true) {
|
|
957
1012
|
const handle = (element, fn) => {
|
|
958
1013
|
if (Array.isArray(element)) for (const current of element) fn(current);
|
|
959
1014
|
else fn(element);
|
|
@@ -982,6 +1037,18 @@ function common_done(res, isWeb = true) {
|
|
|
982
1037
|
});
|
|
983
1038
|
});
|
|
984
1039
|
}
|
|
1040
|
+
if (res.set_fields && viewname) {
|
|
1041
|
+
Object.keys(res.set_fields).forEach((k) => {
|
|
1042
|
+
const form = $(`form[data-viewname=${viewname}]`);
|
|
1043
|
+
const input = form.find(
|
|
1044
|
+
`input[name=${k}], textarea[name=${k}], select[name=${k}]`
|
|
1045
|
+
);
|
|
1046
|
+
if (input.attr("type") === "checkbox")
|
|
1047
|
+
input.prop("checked", res.set_fields[k]);
|
|
1048
|
+
else input.val(res.set_fields[k]);
|
|
1049
|
+
input.trigger("set_form_field");
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
985
1052
|
if (res.goto && !isWeb)
|
|
986
1053
|
// TODO ch
|
|
987
1054
|
notifyAlert({
|
|
@@ -997,6 +1064,7 @@ function common_done(res, isWeb = true) {
|
|
|
997
1064
|
if (
|
|
998
1065
|
prev.origin === next.origin &&
|
|
999
1066
|
prev.pathname === next.pathname &&
|
|
1067
|
+
prev.searchParams.toString() === next.searchParams.toString() &&
|
|
1000
1068
|
next.hash !== prev.hash
|
|
1001
1069
|
)
|
|
1002
1070
|
location.reload();
|
package/public/saltcorn.css
CHANGED
|
@@ -472,3 +472,17 @@ div.unread-notify {
|
|
|
472
472
|
cursor: pointer;
|
|
473
473
|
text-decoration: underline;
|
|
474
474
|
}
|
|
475
|
+
|
|
476
|
+
table.help-md thead {
|
|
477
|
+
border-bottom: 1px solid black;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
table.help-md {
|
|
481
|
+
margin-bottom: 1em;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
table.help-md td:nth-child(2),
|
|
485
|
+
table.help-md th:nth-child(2) {
|
|
486
|
+
padding-left: 10px;
|
|
487
|
+
padding-right: 10px;
|
|
488
|
+
}
|
package/public/saltcorn.js
CHANGED
|
@@ -198,8 +198,8 @@ function clear_state(omit_fields_str, e) {
|
|
|
198
198
|
pjax_to(newUrl, e);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
function ajax_done(res) {
|
|
202
|
-
common_done(res);
|
|
201
|
+
function ajax_done(res, viewname) {
|
|
202
|
+
common_done(res, viewname);
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
function view_post(viewname, route, data, onDone, sendState) {
|
|
@@ -220,7 +220,7 @@ function view_post(viewname, route, data, onDone, sendState) {
|
|
|
220
220
|
})
|
|
221
221
|
.done(function (res) {
|
|
222
222
|
if (onDone) onDone(res);
|
|
223
|
-
ajax_done(res);
|
|
223
|
+
ajax_done(res, viewname);
|
|
224
224
|
})
|
|
225
225
|
.fail(function (res) {
|
|
226
226
|
notifyAlert({ type: "danger", text: res.responseText });
|
|
@@ -340,6 +340,28 @@ function ajax_modal(url, opts = {}) {
|
|
|
340
340
|
$("body").css("overflow", "");
|
|
341
341
|
});
|
|
342
342
|
},
|
|
343
|
+
...(opts.onError
|
|
344
|
+
? {
|
|
345
|
+
error: opts.onError,
|
|
346
|
+
}
|
|
347
|
+
: {}),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function selectVersionError(res, btnId) {
|
|
352
|
+
notifyAlert({
|
|
353
|
+
type: "danger",
|
|
354
|
+
text: res.responseJSON?.error || "unknown error",
|
|
355
|
+
});
|
|
356
|
+
restore_old_button(btnId);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function submitWithAjax(e) {
|
|
360
|
+
saveAndContinue(e, (res) => {
|
|
361
|
+
if (res && res.responseJSON && res.responseJSON.url_when_done)
|
|
362
|
+
window.location.href = res.responseJSON.url_when_done;
|
|
363
|
+
if (res && res.responseJSON && res.responseJSON.error)
|
|
364
|
+
notifyAlert({ type: "danger", text: res.responseJSON.error });
|
|
343
365
|
});
|
|
344
366
|
}
|
|
345
367
|
|
|
@@ -368,6 +390,9 @@ function saveAndContinue(e, k) {
|
|
|
368
390
|
if (res.notify) {
|
|
369
391
|
notifyAlert(res.notify);
|
|
370
392
|
}
|
|
393
|
+
if (res.reload_page) {
|
|
394
|
+
location.reload(); //TODO notify to cookie if reload or goto
|
|
395
|
+
}
|
|
371
396
|
},
|
|
372
397
|
error: function (request) {
|
|
373
398
|
var ct = request.getResponseHeader("content-type") || "";
|
|
@@ -388,8 +413,8 @@ function saveAndContinue(e, k) {
|
|
|
388
413
|
}
|
|
389
414
|
ajax_indicate_error(e, request);
|
|
390
415
|
},
|
|
391
|
-
complete: function () {
|
|
392
|
-
if (k) k();
|
|
416
|
+
complete: function (res) {
|
|
417
|
+
if (k) k(res);
|
|
393
418
|
},
|
|
394
419
|
});
|
|
395
420
|
|
|
@@ -410,7 +435,8 @@ function updateMatchingRows(e, viewname) {
|
|
|
410
435
|
}
|
|
411
436
|
}
|
|
412
437
|
|
|
413
|
-
function applyViewConfig(e, url, k) {
|
|
438
|
+
function applyViewConfig(e, url, k, event) {
|
|
439
|
+
if (event && event.target && event.target.id === "myEditor_icon") return;
|
|
414
440
|
var form = $(e).closest("form");
|
|
415
441
|
var form_data = form.serializeArray();
|
|
416
442
|
const cfg = {};
|
|
@@ -491,7 +517,7 @@ function ajaxSubmitForm(e) {
|
|
|
491
517
|
var no_reload = $("#scmodal").hasClass("no-submit-reload");
|
|
492
518
|
$("#scmodal").modal("hide");
|
|
493
519
|
if (!no_reload) location.reload();
|
|
494
|
-
else common_done(res);
|
|
520
|
+
else common_done(res, form.attr("data-viewname"));
|
|
495
521
|
},
|
|
496
522
|
error: function (request) {
|
|
497
523
|
var title = request.getResponseHeader("Page-Title");
|
package/routes/actions.js
CHANGED
|
@@ -174,8 +174,9 @@ const triggerForm = async (req, trigger) => {
|
|
|
174
174
|
attributes: {
|
|
175
175
|
explainers: {
|
|
176
176
|
Often: req.__("Every 5 minutes"),
|
|
177
|
-
Never:
|
|
178
|
-
|
|
177
|
+
Never: req.__(
|
|
178
|
+
"Not scheduled but can be run as an action from a button click"
|
|
179
|
+
),
|
|
179
180
|
},
|
|
180
181
|
},
|
|
181
182
|
},
|
|
@@ -201,6 +202,7 @@ const triggerForm = async (req, trigger) => {
|
|
|
201
202
|
label: req.__("Action"),
|
|
202
203
|
type: "String",
|
|
203
204
|
required: true,
|
|
205
|
+
help: { topic: "Actions" },
|
|
204
206
|
attributes: {
|
|
205
207
|
calcOptions: ["when_trigger", action_options],
|
|
206
208
|
},
|
|
@@ -402,7 +404,7 @@ router.get(
|
|
|
402
404
|
form.values = trigger.configuration;
|
|
403
405
|
const events = Trigger.when_options;
|
|
404
406
|
const actions = Trigger.find({
|
|
405
|
-
when_trigger: {or: ["API call", "Never"]},
|
|
407
|
+
when_trigger: { or: ["API call", "Never"] },
|
|
406
408
|
});
|
|
407
409
|
const tables = (await Table.find({})).map((t) => ({
|
|
408
410
|
name: t.name,
|