@saltcorn/server 0.8.1-beta.5 → 0.8.1-rc.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/auth/admin.js +20 -10
- package/locales/en.json +5 -1
- package/markup/admin.js +4 -2
- package/package.json +8 -8
- package/public/saltcorn-common.js +27 -0
- package/public/saltcorn.js +17 -1
- package/routes/actions.js +4 -0
- package/routes/admin.js +23 -13
- package/routes/eventlog.js +1 -0
- package/routes/fields.js +51 -8
- package/routes/files.js +2 -0
- package/routes/list.js +11 -0
- package/routes/menu.js +9 -2
- package/routes/pageedit.js +2 -1
- package/routes/plugins.js +17 -4
- package/routes/search.js +1 -0
- package/routes/tables.js +15 -13
- package/routes/tenant.js +1 -0
- package/routes/view.js +2 -0
- package/routes/viewedit.js +10 -2
- package/wrapper.js +4 -38
package/auth/admin.js
CHANGED
|
@@ -376,6 +376,7 @@ router.get(
|
|
|
376
376
|
active_sub: "Login and Signup",
|
|
377
377
|
contents: {
|
|
378
378
|
type: "card",
|
|
379
|
+
titleAjaxIndicator: true,
|
|
379
380
|
title: req.__("Authentication settings"),
|
|
380
381
|
contents: [renderForm(form, req.csrfToken())],
|
|
381
382
|
},
|
|
@@ -408,9 +409,10 @@ router.post(
|
|
|
408
409
|
});
|
|
409
410
|
} else {
|
|
410
411
|
await save_config_from_form(form);
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
412
|
+
if (!req.xhr) {
|
|
413
|
+
req.flash("success", req.__("Authentication settings updated"));
|
|
414
|
+
res.redirect("/useradmin/settings");
|
|
415
|
+
} else res.json({ success: "ok" });
|
|
414
416
|
}
|
|
415
417
|
})
|
|
416
418
|
);
|
|
@@ -432,6 +434,7 @@ router.get(
|
|
|
432
434
|
active_sub: "HTTP",
|
|
433
435
|
contents: {
|
|
434
436
|
type: "card",
|
|
437
|
+
titleAjaxIndicator: true,
|
|
435
438
|
title: req.__("HTTP settings"),
|
|
436
439
|
contents: [renderForm(form, req.csrfToken())],
|
|
437
440
|
},
|
|
@@ -464,9 +467,11 @@ router.post(
|
|
|
464
467
|
});
|
|
465
468
|
} else {
|
|
466
469
|
await save_config_from_form(form);
|
|
467
|
-
|
|
468
|
-
if (!req.xhr)
|
|
469
|
-
|
|
470
|
+
|
|
471
|
+
if (!req.xhr) {
|
|
472
|
+
req.flash("success", req.__("HTTP settings updated"));
|
|
473
|
+
res.redirect("/useradmin/http");
|
|
474
|
+
} else res.json({ success: "ok" });
|
|
470
475
|
}
|
|
471
476
|
})
|
|
472
477
|
);
|
|
@@ -488,6 +493,7 @@ router.get(
|
|
|
488
493
|
active_sub: "Permissions",
|
|
489
494
|
contents: {
|
|
490
495
|
type: "card",
|
|
496
|
+
titleAjaxIndicator: true,
|
|
491
497
|
title: req.__("Permissions settings"),
|
|
492
498
|
contents: [renderForm(form, req.csrfToken())],
|
|
493
499
|
},
|
|
@@ -514,15 +520,17 @@ router.post(
|
|
|
514
520
|
active_sub: "Permissions",
|
|
515
521
|
contents: {
|
|
516
522
|
type: "card",
|
|
523
|
+
titleAjaxIndicator: true,
|
|
517
524
|
title: req.__("Permissions settings"),
|
|
518
525
|
contents: [renderForm(form, req.csrfToken())],
|
|
519
526
|
},
|
|
520
527
|
});
|
|
521
528
|
} else {
|
|
522
529
|
await save_config_from_form(form);
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
530
|
+
if (!req.xhr) {
|
|
531
|
+
req.flash("success", req.__("Permissions settings updated"));
|
|
532
|
+
res.redirect("/useradmin/permissions");
|
|
533
|
+
} else res.json({ success: "ok" });
|
|
526
534
|
}
|
|
527
535
|
})
|
|
528
536
|
);
|
|
@@ -677,8 +685,9 @@ router.get(
|
|
|
677
685
|
active_sub: "SSL",
|
|
678
686
|
contents: {
|
|
679
687
|
type: "card",
|
|
680
|
-
title: req.__("
|
|
688
|
+
title: req.__("Custom SSL certificates"),
|
|
681
689
|
sub2_page: req.__("Custom SSL certificates"),
|
|
690
|
+
titleAjaxIndicator: true,
|
|
682
691
|
contents: [renderForm(form, req.csrfToken())],
|
|
683
692
|
},
|
|
684
693
|
});
|
|
@@ -817,6 +826,7 @@ router.get(
|
|
|
817
826
|
contents: {
|
|
818
827
|
type: "card",
|
|
819
828
|
title: req.__("Table access"),
|
|
829
|
+
titleAjaxIndicator: true,
|
|
820
830
|
contents,
|
|
821
831
|
},
|
|
822
832
|
});
|
package/locales/en.json
CHANGED
|
@@ -1073,5 +1073,9 @@
|
|
|
1073
1073
|
"Become user": "Become user",
|
|
1074
1074
|
"Your are now logged in as %s. Logout and login again to assume your usual identity": "Your are now logged in as %s. Logout and login again to assume your usual identity",
|
|
1075
1075
|
"Done": "Done",
|
|
1076
|
-
"Configure trigger %s": "Configure trigger %s"
|
|
1076
|
+
"Configure trigger %s": "Configure trigger %s",
|
|
1077
|
+
"Saved 2FA policy for role": "Saved 2FA policy for role",
|
|
1078
|
+
"HTTP settings updated": "HTTP settings updated",
|
|
1079
|
+
"%s configuration": "%s configuration",
|
|
1080
|
+
"Save indicator": "Save indicator"
|
|
1077
1081
|
}
|
package/markup/admin.js
CHANGED
|
@@ -135,6 +135,7 @@ const send_settings_page = ({
|
|
|
135
135
|
headers,
|
|
136
136
|
no_nav_pills,
|
|
137
137
|
sub2_page,
|
|
138
|
+
page_title,
|
|
138
139
|
}) => {
|
|
139
140
|
const pillCard = no_nav_pills
|
|
140
141
|
? []
|
|
@@ -163,12 +164,13 @@ const send_settings_page = ({
|
|
|
163
164
|
},
|
|
164
165
|
];
|
|
165
166
|
// headers
|
|
167
|
+
const pg_title = page_title || req.__(active_sub);
|
|
166
168
|
const title = headers
|
|
167
169
|
? {
|
|
168
|
-
title:
|
|
170
|
+
title: pg_title,
|
|
169
171
|
headers,
|
|
170
172
|
}
|
|
171
|
-
:
|
|
173
|
+
: pg_title;
|
|
172
174
|
res.sendWrap(title, {
|
|
173
175
|
above: [
|
|
174
176
|
{
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.8.1-
|
|
3
|
+
"version": "0.8.1-rc.2",
|
|
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.8.1-
|
|
10
|
-
"@saltcorn/builder": "0.8.1-
|
|
11
|
-
"@saltcorn/data": "0.8.1-
|
|
12
|
-
"@saltcorn/admin-models": "0.8.1-
|
|
13
|
-
"@saltcorn/filemanager": "0.8.1-
|
|
14
|
-
"@saltcorn/markup": "0.8.1-
|
|
15
|
-
"@saltcorn/sbadmin2": "0.8.1-
|
|
9
|
+
"@saltcorn/base-plugin": "0.8.1-rc.2",
|
|
10
|
+
"@saltcorn/builder": "0.8.1-rc.2",
|
|
11
|
+
"@saltcorn/data": "0.8.1-rc.2",
|
|
12
|
+
"@saltcorn/admin-models": "0.8.1-rc.2",
|
|
13
|
+
"@saltcorn/filemanager": "0.8.1-rc.2",
|
|
14
|
+
"@saltcorn/markup": "0.8.1-rc.2",
|
|
15
|
+
"@saltcorn/sbadmin2": "0.8.1-rc.2",
|
|
16
16
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
17
17
|
"@socket.io/sticky": "^1.0.1",
|
|
18
18
|
"aws-sdk": "^2.1037.0",
|
|
@@ -523,6 +523,33 @@ function initialize_page() {
|
|
|
523
523
|
|
|
524
524
|
$(initialize_page);
|
|
525
525
|
|
|
526
|
+
function ajax_indicator(show, e) {
|
|
527
|
+
const $ind = e
|
|
528
|
+
? $(e).closest(".card,.modal").find(".sc-ajax-indicator")
|
|
529
|
+
: $(".sc-ajax-indicator");
|
|
530
|
+
$ind.find("svg").attr("data-icon", "save");
|
|
531
|
+
$ind.find("i").removeClass("fa-exclamation-triangle").addClass("fa-save");
|
|
532
|
+
$ind.css("color", "");
|
|
533
|
+
$ind.removeAttr("title");
|
|
534
|
+
if (show) $ind.show();
|
|
535
|
+
else $ind.fadeOut();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function ajax_indicate_error(e, resp) {
|
|
539
|
+
//console.error("ind error", resp);
|
|
540
|
+
const $ind = e
|
|
541
|
+
? $(e).closest(".card,.modal").find(".sc-ajax-indicator")
|
|
542
|
+
: $(".sc-ajax-indicator");
|
|
543
|
+
$ind.css("color", "#e74a3b");
|
|
544
|
+
$ind.find("svg").attr("data-icon", "exclamation-triangle");
|
|
545
|
+
$ind.find("i").removeClass("fa-save").addClass("fa-exclamation-triangle");
|
|
546
|
+
$ind.attr(
|
|
547
|
+
"title",
|
|
548
|
+
"Save error: " + (resp ? resp.responseText || resp.statusText : "unknown")
|
|
549
|
+
);
|
|
550
|
+
$ind.show();
|
|
551
|
+
}
|
|
552
|
+
|
|
526
553
|
function enable_codemirror(f) {
|
|
527
554
|
$("<link/>", {
|
|
528
555
|
rel: "stylesheet",
|
package/public/saltcorn.js
CHANGED
|
@@ -246,6 +246,9 @@ function ensure_modal_exists_and_closed() {
|
|
|
246
246
|
<div class="modal-content">
|
|
247
247
|
<div class="modal-header">
|
|
248
248
|
<h5 class="modal-title">Modal title</h5>
|
|
249
|
+
<span class="sc-ajax-indicator-wrapper">
|
|
250
|
+
<span class="sc-ajax-indicator ms-2" style="display: none;"><i class="fas fa-save"></i></span>
|
|
251
|
+
</span>
|
|
249
252
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
|
|
250
253
|
</button>
|
|
251
254
|
</div>
|
|
@@ -280,6 +283,11 @@ function ajax_modal(url, opts = {}) {
|
|
|
280
283
|
success: function (res, textStatus, request) {
|
|
281
284
|
var title = request.getResponseHeader("Page-Title");
|
|
282
285
|
var width = request.getResponseHeader("SaltcornModalWidth");
|
|
286
|
+
var saveIndicate = !!request.getResponseHeader(
|
|
287
|
+
"SaltcornModalSaveIndicator"
|
|
288
|
+
);
|
|
289
|
+
if (saveIndicate) $(".sc-ajax-indicator-wrapper").show();
|
|
290
|
+
else $(".sc-ajax-indicator-wrapper").hide();
|
|
283
291
|
if (width) $(".modal-dialog").css("max-width", width);
|
|
284
292
|
else $(".modal-dialog").css("max-width", "");
|
|
285
293
|
if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
|
|
@@ -303,6 +311,7 @@ function saveAndContinue(e, k) {
|
|
|
303
311
|
submitWithEmptyAction(form[0]);
|
|
304
312
|
var url = form.attr("action");
|
|
305
313
|
var form_data = form.serialize();
|
|
314
|
+
ajax_indicator(true, e);
|
|
306
315
|
$.ajax(url, {
|
|
307
316
|
type: "POST",
|
|
308
317
|
headers: {
|
|
@@ -310,6 +319,7 @@ function saveAndContinue(e, k) {
|
|
|
310
319
|
},
|
|
311
320
|
data: form_data,
|
|
312
321
|
success: function (res) {
|
|
322
|
+
ajax_indicator(false);
|
|
313
323
|
if (res.id && form.find("input[name=id")) {
|
|
314
324
|
form.append(
|
|
315
325
|
`<input type="hidden" class="form-control " name="id" value="${res.id}">`
|
|
@@ -318,6 +328,7 @@ function saveAndContinue(e, k) {
|
|
|
318
328
|
},
|
|
319
329
|
error: function (request) {
|
|
320
330
|
$("#page-inner-content").html(request.responseText);
|
|
331
|
+
ajax_indicate_error(e, request);
|
|
321
332
|
initialize_page();
|
|
322
333
|
},
|
|
323
334
|
complete: function () {
|
|
@@ -335,6 +346,7 @@ function applyViewConfig(e, url, k) {
|
|
|
335
346
|
form_data.forEach((item) => {
|
|
336
347
|
cfg[item.name] = item.value;
|
|
337
348
|
});
|
|
349
|
+
ajax_indicator(true, e);
|
|
338
350
|
$.ajax(url, {
|
|
339
351
|
type: "POST",
|
|
340
352
|
dataType: "json",
|
|
@@ -343,11 +355,15 @@ function applyViewConfig(e, url, k) {
|
|
|
343
355
|
"CSRF-Token": _sc_globalCsrf,
|
|
344
356
|
},
|
|
345
357
|
data: JSON.stringify(cfg),
|
|
346
|
-
error: function (request) {
|
|
358
|
+
error: function (request) {
|
|
359
|
+
ajax_indicate_error(e, request);
|
|
360
|
+
},
|
|
347
361
|
success: function (res) {
|
|
362
|
+
ajax_indicator(false);
|
|
348
363
|
k && k(res);
|
|
349
364
|
!k && updateViewPreview();
|
|
350
365
|
},
|
|
366
|
+
complete: () => {},
|
|
351
367
|
});
|
|
352
368
|
|
|
353
369
|
return false;
|
package/routes/actions.js
CHANGED
|
@@ -399,8 +399,10 @@ router.get(
|
|
|
399
399
|
req,
|
|
400
400
|
active_sub: "Triggers",
|
|
401
401
|
sub2_page: "Configure",
|
|
402
|
+
page_title: trigger.name,
|
|
402
403
|
contents: {
|
|
403
404
|
type: "card",
|
|
405
|
+
titleAjaxIndicator: true,
|
|
404
406
|
title: req.__("Configure trigger %s", trigger.name),
|
|
405
407
|
contents: {
|
|
406
408
|
widths: [8, 4],
|
|
@@ -468,8 +470,10 @@ router.get(
|
|
|
468
470
|
req,
|
|
469
471
|
active_sub: "Triggers",
|
|
470
472
|
sub2_page: "Configure",
|
|
473
|
+
page_title: req.__(`%s configuration`, trigger.name),
|
|
471
474
|
contents: {
|
|
472
475
|
type: "card",
|
|
476
|
+
titleAjaxIndicator: true,
|
|
473
477
|
title: req.__("Configure trigger %s", trigger.name),
|
|
474
478
|
contents: renderForm(form, req.csrfToken()),
|
|
475
479
|
},
|
package/routes/admin.js
CHANGED
|
@@ -194,6 +194,7 @@ router.get(
|
|
|
194
194
|
contents: {
|
|
195
195
|
type: "card",
|
|
196
196
|
title: req.__("Site identity settings"),
|
|
197
|
+
titleAjaxIndicator: true,
|
|
197
198
|
contents: [renderForm(form, req.csrfToken())],
|
|
198
199
|
},
|
|
199
200
|
});
|
|
@@ -251,6 +252,7 @@ router.get(
|
|
|
251
252
|
contents: {
|
|
252
253
|
type: "card",
|
|
253
254
|
title: req.__("Email settings"),
|
|
255
|
+
titleAjaxIndicator: true,
|
|
254
256
|
contents: [
|
|
255
257
|
renderForm(form, req.csrfToken()),
|
|
256
258
|
a(
|
|
@@ -321,9 +323,10 @@ router.post(
|
|
|
321
323
|
});
|
|
322
324
|
} else {
|
|
323
325
|
await save_config_from_form(form);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
326
|
+
if (!req.xhr) {
|
|
327
|
+
req.flash("success", req.__("Email settings updated"));
|
|
328
|
+
res.redirect("/admin/email");
|
|
329
|
+
} else res.json({ success: "ok" });
|
|
327
330
|
}
|
|
328
331
|
})
|
|
329
332
|
);
|
|
@@ -391,6 +394,7 @@ router.get(
|
|
|
391
394
|
? {
|
|
392
395
|
type: "card",
|
|
393
396
|
title: req.__("Automated backup"),
|
|
397
|
+
titleAjaxIndicator: true,
|
|
394
398
|
contents: div(
|
|
395
399
|
renderForm(backupForm, req.csrfToken()),
|
|
396
400
|
a(
|
|
@@ -408,6 +412,7 @@ router.get(
|
|
|
408
412
|
{
|
|
409
413
|
type: "card",
|
|
410
414
|
title: req.__("Snapshots"),
|
|
415
|
+
titleAjaxIndicator: true,
|
|
411
416
|
contents: div(
|
|
412
417
|
p(
|
|
413
418
|
i(
|
|
@@ -708,9 +713,11 @@ router.post(
|
|
|
708
713
|
form.validate(req.body);
|
|
709
714
|
|
|
710
715
|
await save_config_from_form(form);
|
|
711
|
-
|
|
712
|
-
if (!req.xhr)
|
|
713
|
-
|
|
716
|
+
|
|
717
|
+
if (!req.xhr) {
|
|
718
|
+
req.flash("success", req.__("Snapshot settings updated"));
|
|
719
|
+
res.redirect("/admin/backup");
|
|
720
|
+
} else res.json({ success: "ok" });
|
|
714
721
|
})
|
|
715
722
|
);
|
|
716
723
|
router.post(
|
|
@@ -732,9 +739,10 @@ router.post(
|
|
|
732
739
|
});
|
|
733
740
|
} else {
|
|
734
741
|
await save_config_from_form(form);
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
742
|
+
if (!req.xhr) {
|
|
743
|
+
req.flash("success", req.__("Backup settings updated"));
|
|
744
|
+
res.redirect("/admin/backup");
|
|
745
|
+
} else res.json({ success: "ok" });
|
|
738
746
|
}
|
|
739
747
|
})
|
|
740
748
|
);
|
|
@@ -1306,7 +1314,7 @@ const buildDialogScript = () => {
|
|
|
1306
1314
|
}
|
|
1307
1315
|
|
|
1308
1316
|
function handleMessages() {
|
|
1309
|
-
notifyAlert("
|
|
1317
|
+
notifyAlert("Building the app, please wait.")
|
|
1310
1318
|
${
|
|
1311
1319
|
getState().getConfig("apple_team_id") &&
|
|
1312
1320
|
getState().getConfig("apple_team_id") !== "null"
|
|
@@ -1858,6 +1866,7 @@ router.get(
|
|
|
1858
1866
|
contents: {
|
|
1859
1867
|
type: "card",
|
|
1860
1868
|
title: req.__("Development settings"),
|
|
1869
|
+
titleAjaxIndicator: true,
|
|
1861
1870
|
contents: [
|
|
1862
1871
|
renderForm(form, req.csrfToken()) /*,
|
|
1863
1872
|
a(
|
|
@@ -1899,9 +1908,10 @@ router.post(
|
|
|
1899
1908
|
});
|
|
1900
1909
|
} else {
|
|
1901
1910
|
await save_config_from_form(form);
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1911
|
+
if (!req.xhr) {
|
|
1912
|
+
req.flash("success", req.__("Development mode settings updated"));
|
|
1913
|
+
res.redirect("/admin/dev");
|
|
1914
|
+
} else res.json({ success: "ok" });
|
|
1905
1915
|
}
|
|
1906
1916
|
})
|
|
1907
1917
|
);
|
package/routes/eventlog.js
CHANGED
package/routes/fields.js
CHANGED
|
@@ -711,20 +711,54 @@ router.post(
|
|
|
711
711
|
const fields = await table.getFields();
|
|
712
712
|
let row = { ...req.body };
|
|
713
713
|
if (row && Object.keys(row).length > 0) readState(row, fields);
|
|
714
|
+
|
|
715
|
+
//need to get join fields from ownership into row
|
|
716
|
+
const joinFields = {};
|
|
717
|
+
if (table.ownership_formula && role > table.min_role_read) {
|
|
718
|
+
const freeVars = freeVariables(table.ownership_formula);
|
|
719
|
+
add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
720
|
+
}
|
|
721
|
+
//console.log(joinFields, row);
|
|
714
722
|
const id = req.query.id || row.id;
|
|
715
723
|
if (id) {
|
|
716
|
-
let dbrow = await table.
|
|
724
|
+
let [dbrow] = await table.getJoinedRows({ where: { id }, joinFields });
|
|
717
725
|
row = { ...dbrow, ...row };
|
|
718
726
|
//prevent overwriting ownership field
|
|
719
727
|
if (table.ownership_field_id) {
|
|
720
728
|
const ofield = fields.find((f) => f.id === table.ownership_field_id);
|
|
721
729
|
row[ofield.name] = dbrow[ofield.name];
|
|
722
730
|
}
|
|
731
|
+
} else {
|
|
732
|
+
//may need to add joinfields
|
|
733
|
+
for (const { ref } of Object.values(joinFields)) {
|
|
734
|
+
if (row[ref]) {
|
|
735
|
+
const field = fields.find((f) => f.name === ref);
|
|
736
|
+
const reftable = await Table.findOne({ name: field.reftable_name });
|
|
737
|
+
const refFields = await reftable.getFields();
|
|
738
|
+
|
|
739
|
+
const joinFields = {};
|
|
740
|
+
if (reftable.ownership_formula && role > reftable.min_role_read) {
|
|
741
|
+
const freeVars = freeVariables(reftable.ownership_formula);
|
|
742
|
+
add_free_variables_to_joinfields(freeVars, joinFields, refFields);
|
|
743
|
+
}
|
|
744
|
+
const [refRow] = await reftable.getJoinedRows({
|
|
745
|
+
where: { id: row[ref] },
|
|
746
|
+
joinFields,
|
|
747
|
+
});
|
|
748
|
+
if (
|
|
749
|
+
role <= reftable.min_role_read ||
|
|
750
|
+
(req.user && reftable.is_owner(req.user, refRow))
|
|
751
|
+
) {
|
|
752
|
+
row[ref] = refRow;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
723
756
|
}
|
|
724
757
|
if (
|
|
725
758
|
role > table.min_role_read &&
|
|
726
759
|
!(req.user && table.is_owner(req.user, row))
|
|
727
760
|
) {
|
|
761
|
+
//console.log("not owner", row, table.is_owner(req.user, row));
|
|
728
762
|
res.status(401).send("");
|
|
729
763
|
return;
|
|
730
764
|
}
|
|
@@ -734,16 +768,25 @@ router.post(
|
|
|
734
768
|
if (kpath.length === 2 && row[kpath[0]]) {
|
|
735
769
|
const field = fields.find((f) => f.name === kpath[0]);
|
|
736
770
|
const reftable = await Table.findOne({ name: field.reftable_name });
|
|
737
|
-
|
|
771
|
+
const refFields = await reftable.getFields();
|
|
772
|
+
const targetField = refFields.find((f) => f.name === kpath[1]);
|
|
773
|
+
//console.log({ kpath, fieldview, targetField });
|
|
774
|
+
const q = { [reftable.pk_name]: row[kpath[0]] };
|
|
775
|
+
const joinFields = {};
|
|
776
|
+
if (reftable.ownership_formula && role > reftable.min_role_read) {
|
|
777
|
+
const freeVars = freeVariables(reftable.ownership_formula);
|
|
778
|
+
add_free_variables_to_joinfields(freeVars, joinFields, refFields);
|
|
779
|
+
}
|
|
780
|
+
const [refRow] = await reftable.getJoinedRows({ where: q, joinFields });
|
|
781
|
+
if (
|
|
782
|
+
role > reftable.min_role_read &&
|
|
783
|
+
!(req.user && reftable.is_owner(req.user, refRow))
|
|
784
|
+
) {
|
|
785
|
+
//console.log("not jointable owner", refRow);
|
|
786
|
+
|
|
738
787
|
res.status(401).send("");
|
|
739
788
|
return;
|
|
740
789
|
}
|
|
741
|
-
const targetField = (await reftable.getFields()).find(
|
|
742
|
-
(f) => f.name === kpath[1]
|
|
743
|
-
);
|
|
744
|
-
//console.log({ kpath, fieldview, targetField });
|
|
745
|
-
const q = { [reftable.pk_name]: row[kpath[0]] };
|
|
746
|
-
const refRow = await reftable.getRow(q);
|
|
747
790
|
let fv;
|
|
748
791
|
if (targetField.type === "Key") {
|
|
749
792
|
fv = getState().keyFieldviews[fieldview];
|
package/routes/files.js
CHANGED
|
@@ -438,6 +438,7 @@ router.get(
|
|
|
438
438
|
contents: {
|
|
439
439
|
type: "card",
|
|
440
440
|
title: req.__("Storage settings"),
|
|
441
|
+
titleAjaxIndicator: true,
|
|
441
442
|
contents: [renderForm(form, req.csrfToken())],
|
|
442
443
|
},
|
|
443
444
|
});
|
|
@@ -512,6 +513,7 @@ router.get(
|
|
|
512
513
|
active_sub: "Settings",
|
|
513
514
|
contents: {
|
|
514
515
|
type: "card",
|
|
516
|
+
titleAjaxIndicator: true,
|
|
515
517
|
title: req.__("Files settings"),
|
|
516
518
|
contents: [renderForm(form, req.csrfToken())],
|
|
517
519
|
},
|
package/routes/list.js
CHANGED
|
@@ -308,6 +308,13 @@ router.get(
|
|
|
308
308
|
],
|
|
309
309
|
right: div(
|
|
310
310
|
{ class: "d-flex" },
|
|
311
|
+
div(
|
|
312
|
+
{
|
|
313
|
+
class: "sc-ajax-indicator me-2",
|
|
314
|
+
style: { display: "none" },
|
|
315
|
+
},
|
|
316
|
+
i({ class: "fas fa-save" })
|
|
317
|
+
),
|
|
311
318
|
button(
|
|
312
319
|
{
|
|
313
320
|
class: "btn btn-sm btn-primary me-2",
|
|
@@ -391,6 +398,7 @@ router.get(
|
|
|
391
398
|
});
|
|
392
399
|
window.tabulator_table.on("cellEdited", function(cell){
|
|
393
400
|
const row = cell.getRow().getData()
|
|
401
|
+
ajax_indicator(true);
|
|
394
402
|
$.ajax({
|
|
395
403
|
type: "POST",
|
|
396
404
|
url: "/api/${table.name}/" + (row.id||""),
|
|
@@ -400,12 +408,15 @@ router.get(
|
|
|
400
408
|
},
|
|
401
409
|
error: tabulator_error_handler,
|
|
402
410
|
}).done(function (resp) {
|
|
411
|
+
ajax_indicator(false);
|
|
403
412
|
//if (item._versions) item._versions = +item._versions + 1;
|
|
404
413
|
//data.resolve(fixKeys(item));
|
|
405
414
|
if(resp.success &&typeof resp.success ==="number" && !row.id) {
|
|
406
415
|
window.tabulator_table.updateRow(cell.getRow(), {id: resp.success});
|
|
407
416
|
}
|
|
408
417
|
|
|
418
|
+
}).fail(function (resp) {
|
|
419
|
+
ajax_indicate_error(undefined, resp);
|
|
409
420
|
});
|
|
410
421
|
});
|
|
411
422
|
window.tabulator_table_name="${table.name}";`)
|
package/routes/menu.js
CHANGED
|
@@ -309,8 +309,14 @@ const menuEditorScript = (menu_items) => `
|
|
|
309
309
|
const s = editor.getString()
|
|
310
310
|
if(s===lastState && !skip_check) return;
|
|
311
311
|
lastState=s;
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
ajax_indicator(true);
|
|
313
|
+
ajax_post('/menu', {
|
|
314
|
+
data: s,
|
|
315
|
+
success: ()=>{ ajax_indicator(false)},
|
|
316
|
+
dataType : 'json',
|
|
317
|
+
contentType: 'application/json;charset=UTF-8',
|
|
318
|
+
error: (r) => {ajax_indicate_error(undefined, r); }
|
|
319
|
+
})
|
|
314
320
|
}
|
|
315
321
|
var sortableListOptions = {
|
|
316
322
|
placeholderCss: {'background-color': "#cccccc"},
|
|
@@ -395,6 +401,7 @@ router.get(
|
|
|
395
401
|
contents: {
|
|
396
402
|
type: "card",
|
|
397
403
|
title: req.__(`Menu editor`),
|
|
404
|
+
titleAjaxIndicator: true,
|
|
398
405
|
contents: {
|
|
399
406
|
above: [
|
|
400
407
|
{
|
package/routes/pageedit.js
CHANGED
|
@@ -251,6 +251,7 @@ router.get(
|
|
|
251
251
|
{
|
|
252
252
|
type: "card",
|
|
253
253
|
title: req.__("Root pages"),
|
|
254
|
+
titleAjaxIndicator: true,
|
|
254
255
|
contents: renderForm(
|
|
255
256
|
getRootPageForm(pages, roles, req),
|
|
256
257
|
req.csrfToken()
|
|
@@ -402,7 +403,7 @@ router.get(
|
|
|
402
403
|
version_tag: db.connectObj.version_tag,
|
|
403
404
|
};
|
|
404
405
|
res.sendWrap(
|
|
405
|
-
req.__(
|
|
406
|
+
req.__(`%s configuration`, page.name),
|
|
406
407
|
wrap(renderBuilder(builderData, req.csrfToken()), true, req, page)
|
|
407
408
|
);
|
|
408
409
|
}
|
package/routes/plugins.js
CHANGED
|
@@ -583,10 +583,23 @@ router.get(
|
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
586
|
+
above: [
|
|
587
|
+
{
|
|
588
|
+
type: "breadcrumbs",
|
|
589
|
+
crumbs: [
|
|
590
|
+
{ text: req.__("Settings"), href: "/settings" },
|
|
591
|
+
{ text: req.__("Module store"), href: "/plugins" },
|
|
592
|
+
{ text: plugin.name },
|
|
593
|
+
],
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
type: "card",
|
|
597
|
+
class: "mt-0",
|
|
598
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
599
|
+
titleAjaxIndicator: true,
|
|
600
|
+
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
601
|
+
},
|
|
602
|
+
],
|
|
590
603
|
});
|
|
591
604
|
})
|
|
592
605
|
);
|
package/routes/search.js
CHANGED
package/routes/tables.js
CHANGED
|
@@ -828,6 +828,7 @@ router.get(
|
|
|
828
828
|
{
|
|
829
829
|
type: "card",
|
|
830
830
|
title: req.__("Edit table properties"),
|
|
831
|
+
titleAjaxIndicator: true,
|
|
831
832
|
contents: renderForm(tblForm, req.csrfToken()),
|
|
832
833
|
},
|
|
833
834
|
],
|
|
@@ -899,20 +900,21 @@ router.post(
|
|
|
899
900
|
}
|
|
900
901
|
} else rest.ownership_formula = null;
|
|
901
902
|
await table.update(rest);
|
|
902
|
-
if (!old_versioned && rest.versioned)
|
|
903
|
-
req.flash(
|
|
904
|
-
"success",
|
|
905
|
-
req.__("Table saved with version history enabled")
|
|
906
|
-
);
|
|
907
|
-
else if (old_versioned && !rest.versioned)
|
|
908
|
-
req.flash(
|
|
909
|
-
"success",
|
|
910
|
-
req.__("Table saved with version history disabled")
|
|
911
|
-
);
|
|
912
|
-
else if (!hasError) req.flash("success", req.__("Table saved"));
|
|
913
903
|
|
|
914
|
-
if (!req.xhr)
|
|
915
|
-
|
|
904
|
+
if (!req.xhr) {
|
|
905
|
+
if (!old_versioned && rest.versioned)
|
|
906
|
+
req.flash(
|
|
907
|
+
"success",
|
|
908
|
+
req.__("Table saved with version history enabled")
|
|
909
|
+
);
|
|
910
|
+
else if (old_versioned && !rest.versioned)
|
|
911
|
+
req.flash(
|
|
912
|
+
"success",
|
|
913
|
+
req.__("Table saved with version history disabled")
|
|
914
|
+
);
|
|
915
|
+
else if (!hasError) req.flash("success", req.__("Table saved"));
|
|
916
|
+
res.redirect(`/table/${id}`);
|
|
917
|
+
} else res.json({ success: "ok", notify });
|
|
916
918
|
}
|
|
917
919
|
})
|
|
918
920
|
);
|
package/routes/tenant.js
CHANGED
package/routes/view.js
CHANGED
package/routes/viewedit.js
CHANGED
|
@@ -239,6 +239,13 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
239
239
|
options: ["px", "%", "vw", "em", "rem"],
|
|
240
240
|
},
|
|
241
241
|
},
|
|
242
|
+
{
|
|
243
|
+
name: "popup_save_indicator",
|
|
244
|
+
label: req.__("Save indicator"),
|
|
245
|
+
type: "Bool",
|
|
246
|
+
parent_field: "attributes",
|
|
247
|
+
tab: "Popup settings",
|
|
248
|
+
},
|
|
242
249
|
...(isEdit
|
|
243
250
|
? [
|
|
244
251
|
new Field({
|
|
@@ -465,6 +472,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
465
472
|
type: noCard ? "container" : "card",
|
|
466
473
|
class: !noCard && "mt-0",
|
|
467
474
|
title: wfres.title,
|
|
475
|
+
titleAjaxIndicator: true,
|
|
468
476
|
contents,
|
|
469
477
|
},
|
|
470
478
|
...(previewURL
|
|
@@ -485,7 +493,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
485
493
|
if (wfres.renderForm)
|
|
486
494
|
res.sendWrap(
|
|
487
495
|
{
|
|
488
|
-
title: req.__(
|
|
496
|
+
title: req.__(`%s configuration`, view.name),
|
|
489
497
|
headers: [
|
|
490
498
|
{
|
|
491
499
|
script: `/static_assets/${db.connectObj.version_tag}/jquery-menu-editor.min.js`,
|
|
@@ -510,7 +518,7 @@ const respondWorkflow = (view, wf, wfres, req, res) => {
|
|
|
510
518
|
else if (wfres.renderBuilder) {
|
|
511
519
|
wfres.renderBuilder.options.view_id = view.id;
|
|
512
520
|
res.sendWrap(
|
|
513
|
-
req.__(
|
|
521
|
+
req.__(`%s configuration`, view.name),
|
|
514
522
|
wrap(renderBuilder(wfres.renderBuilder, req.csrfToken()), true)
|
|
515
523
|
);
|
|
516
524
|
} else res.redirect(wfres.redirect);
|
package/wrapper.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @module wrapper
|
|
4
4
|
*/
|
|
5
5
|
const { getState } = require("@saltcorn/data/db/state");
|
|
6
|
+
const { get_extra_menu } = require("@saltcorn/data/web-mobile-commons");
|
|
6
7
|
//const db = require("@saltcorn/data/db");
|
|
7
8
|
const { h3, div, small } = require("@saltcorn/markup/tags");
|
|
8
9
|
const { renderForm, link } = require("@saltcorn/markup");
|
|
@@ -18,43 +19,6 @@ const getFlashes = (req) =>
|
|
|
18
19
|
return { type, msg: req.flash(type) };
|
|
19
20
|
})
|
|
20
21
|
.filter((a) => a.msg && a.msg.length && a.msg.length > 0);
|
|
21
|
-
/**
|
|
22
|
-
* Get extra menu
|
|
23
|
-
* @param role
|
|
24
|
-
* @param state
|
|
25
|
-
* @param req
|
|
26
|
-
* @returns {*}
|
|
27
|
-
*/
|
|
28
|
-
const get_extra_menu = (role, state, req) => {
|
|
29
|
-
let cfg = getState().getConfig("unrolled_menu_items", []);
|
|
30
|
-
if (!cfg || cfg.length === 0) {
|
|
31
|
-
cfg = getState().getConfig("menu_items", []);
|
|
32
|
-
}
|
|
33
|
-
const locale = req.getLocale();
|
|
34
|
-
const __ = (s) => state.i18n.__({ phrase: s, locale }) || s;
|
|
35
|
-
const transform = (items) =>
|
|
36
|
-
items
|
|
37
|
-
.filter((item) => role <= +item.min_role)
|
|
38
|
-
.map((item) => ({
|
|
39
|
-
label: __(item.label),
|
|
40
|
-
icon: item.icon,
|
|
41
|
-
location: item.location,
|
|
42
|
-
style: item.style || "",
|
|
43
|
-
type: item.type,
|
|
44
|
-
link:
|
|
45
|
-
item.type === "Link"
|
|
46
|
-
? item.url
|
|
47
|
-
: item.type === "Action"
|
|
48
|
-
? `javascript:ajax_post_json('/menu/runaction/${item.action_name}')`
|
|
49
|
-
: item.type === "View"
|
|
50
|
-
? `/view/${encodeURIComponent(item.viewname)}`
|
|
51
|
-
: item.type === "Page"
|
|
52
|
-
? `/page/${encodeURIComponent(item.pagename)}`
|
|
53
|
-
: undefined,
|
|
54
|
-
...(item.subitems ? { subitems: transform(item.subitems) } : {}),
|
|
55
|
-
}));
|
|
56
|
-
return transform(cfg);
|
|
57
|
-
};
|
|
58
22
|
/**
|
|
59
23
|
* Get menu
|
|
60
24
|
* @param req
|
|
@@ -67,7 +31,9 @@ const get_menu = (req) => {
|
|
|
67
31
|
|
|
68
32
|
const allow_signup = state.getConfig("allow_signup");
|
|
69
33
|
const login_menu = state.getConfig("login_menu");
|
|
70
|
-
const
|
|
34
|
+
const locale = req.getLocale();
|
|
35
|
+
const __ = (s) => state.i18n.__({ phrase: s, locale }) || s;
|
|
36
|
+
const extra_menu = get_extra_menu(role, __);
|
|
71
37
|
const authItems = isAuth
|
|
72
38
|
? [
|
|
73
39
|
{
|