@saltcorn/server 0.7.4 → 0.8.0-beta.1
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 +18 -11
- package/auth/admin.js +370 -120
- package/auth/roleadmin.js +5 -23
- package/auth/routes.js +40 -15
- package/locales/de.json +1049 -273
- package/locales/en.json +58 -3
- package/locales/es.json +134 -134
- package/locales/it.json +6 -1
- package/locales/ru.json +44 -7
- package/markup/admin.js +46 -42
- package/markup/forms.js +4 -3
- package/package.json +8 -7
- package/public/blockly.js +19 -31
- package/public/diagram_utils.js +530 -0
- package/public/gridedit.js +4 -1
- package/public/jquery-menu-editor.min.js +112 -112
- package/public/saltcorn-common.js +31 -8
- package/public/saltcorn.css +11 -0
- package/public/saltcorn.js +211 -70
- package/restart_watcher.js +1 -0
- package/routes/actions.js +6 -14
- package/routes/admin.js +229 -79
- package/routes/api.js +19 -2
- package/routes/common_lists.js +137 -134
- package/routes/delete.js +6 -5
- package/routes/diagram.js +43 -117
- package/routes/edit.js +5 -10
- package/routes/fields.js +63 -29
- package/routes/files.js +137 -101
- package/routes/homepage.js +2 -2
- package/routes/infoarch.js +2 -2
- package/routes/list.js +12 -13
- package/routes/page.js +16 -3
- package/routes/pageedit.js +13 -8
- package/routes/scapi.js +1 -1
- package/routes/search.js +1 -1
- package/routes/tables.js +9 -14
- package/routes/tag_entries.js +31 -10
- package/routes/tags.js +10 -10
- package/routes/tenant.js +114 -50
- package/routes/utils.js +12 -0
- package/routes/view.js +3 -4
- package/routes/viewedit.js +57 -55
- package/serve.js +5 -0
- package/tests/admin.test.js +6 -2
- package/tests/auth.test.js +20 -0
- package/tests/fields.test.js +1 -0
- package/tests/files.test.js +11 -20
- package/tests/tenant.test.js +12 -2
- package/tests/viewedit.test.js +15 -1
package/public/saltcorn.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
function sortby(k, desc) {
|
|
2
|
-
set_state_fields({
|
|
1
|
+
function sortby(k, desc, viewIdentifier) {
|
|
2
|
+
set_state_fields({
|
|
3
|
+
[viewIdentifier ? `_${viewIdentifier}_sortby` : "_sortby"]: k,
|
|
4
|
+
[viewIdentifier ? `_${viewIdentifier}_sortdesc` : "_sortdesc"]: desc
|
|
5
|
+
? "on"
|
|
6
|
+
: { unset: true },
|
|
7
|
+
});
|
|
3
8
|
}
|
|
4
|
-
function gopage(n, pagesize, extra = {}) {
|
|
5
|
-
|
|
9
|
+
function gopage(n, pagesize, viewIdentifier, extra = {}) {
|
|
10
|
+
const cfg = {
|
|
11
|
+
...extra,
|
|
12
|
+
[viewIdentifier ? `_${viewIdentifier}_page` : "_page"]: n,
|
|
13
|
+
[viewIdentifier ? `_${viewIdentifier}_pagesize` : "_pagesize"]: pagesize,
|
|
14
|
+
};
|
|
15
|
+
set_state_fields(cfg);
|
|
6
16
|
}
|
|
7
17
|
|
|
8
18
|
if (localStorage.getItem("reload_on_init")) {
|
|
@@ -77,8 +87,21 @@ function check_state_field(that) {
|
|
|
77
87
|
pjax_to(dest.replace("&&", "&").replace("?&", "?"));
|
|
78
88
|
}
|
|
79
89
|
|
|
90
|
+
function invalidate_pagings(href) {
|
|
91
|
+
let newhref = href;
|
|
92
|
+
const queryObj = Object.fromEntries(new URL(newhref).searchParams.entries());
|
|
93
|
+
const toRemove = Object.keys(queryObj).filter((val) => is_paging_param(val));
|
|
94
|
+
for (const k of toRemove) {
|
|
95
|
+
newhref = removeQueryStringParameter(newhref, k);
|
|
96
|
+
}
|
|
97
|
+
return newhref;
|
|
98
|
+
}
|
|
99
|
+
|
|
80
100
|
function set_state_fields(kvs) {
|
|
81
|
-
|
|
101
|
+
let newhref = get_current_state_url();
|
|
102
|
+
if (Object.keys(kvs).some((k) => !is_paging_param(k))) {
|
|
103
|
+
newhref = invalidate_pagings(newhref);
|
|
104
|
+
}
|
|
82
105
|
Object.entries(kvs).forEach((kv) => {
|
|
83
106
|
if (kv[1].unset && kv[1].unset === true)
|
|
84
107
|
newhref = removeQueryStringParameter(newhref, kv[0]);
|
|
@@ -94,14 +117,15 @@ let loadPage = true;
|
|
|
94
117
|
$(function () {
|
|
95
118
|
$(window).bind("popstate", function (event) {
|
|
96
119
|
const ensure_no_final_hash = (s) => (s.endsWith("#") ? s.slice(0, -1) : s);
|
|
97
|
-
|
|
98
|
-
|
|
120
|
+
const newUrl = ensure_no_final_hash(window.location.href);
|
|
121
|
+
if (loadPage && newUrl !== window.location.href)
|
|
122
|
+
window.location.assign(newUrl);
|
|
99
123
|
});
|
|
100
124
|
});
|
|
101
125
|
|
|
102
126
|
function pjax_to(href) {
|
|
103
127
|
let $modal = $("#scmodal");
|
|
104
|
-
const inModal = $modal.length && $modal.hasClass("show")
|
|
128
|
+
const inModal = $modal.length && $modal.hasClass("show");
|
|
105
129
|
let $dest = inModal ? $("#scmodal .modal-body") : $("#page-inner-content");
|
|
106
130
|
|
|
107
131
|
if (!$dest.length) window.location.href = href;
|
|
@@ -126,7 +150,7 @@ function pjax_to(href) {
|
|
|
126
150
|
},
|
|
127
151
|
error: function (res) {
|
|
128
152
|
notifyAlert({ type: "danger", text: res.responseText });
|
|
129
|
-
}
|
|
153
|
+
},
|
|
130
154
|
});
|
|
131
155
|
}
|
|
132
156
|
}
|
|
@@ -135,21 +159,19 @@ function href_to(href) {
|
|
|
135
159
|
window.location.href = href;
|
|
136
160
|
}
|
|
137
161
|
function clear_state(omit_fields_str) {
|
|
138
|
-
let newUrl = get_current_state_url().split("?")[0]
|
|
139
|
-
const hash = get_current_state_url().split("#")[1]
|
|
162
|
+
let newUrl = get_current_state_url().split("?")[0];
|
|
163
|
+
const hash = get_current_state_url().split("#")[1];
|
|
140
164
|
if (omit_fields_str) {
|
|
141
|
-
const omit_fields = omit_fields_str.split(
|
|
142
|
-
let qs = (get_current_state_url().split("?")[1] || "").split("#")[0]
|
|
165
|
+
const omit_fields = omit_fields_str.split(",").map((s) => s.trim());
|
|
166
|
+
let qs = (get_current_state_url().split("?")[1] || "").split("#")[0];
|
|
143
167
|
let params = new URLSearchParams(qs);
|
|
144
|
-
newUrl = newUrl +
|
|
145
|
-
omit_fields.forEach(f => {
|
|
168
|
+
newUrl = newUrl + "?";
|
|
169
|
+
omit_fields.forEach((f) => {
|
|
146
170
|
if (params.get(f))
|
|
147
171
|
newUrl = updateQueryStringParameter(newUrl, f, params.get(f));
|
|
148
|
-
})
|
|
149
|
-
|
|
172
|
+
});
|
|
150
173
|
}
|
|
151
|
-
if (hash)
|
|
152
|
-
newUrl += '#' + hash;
|
|
174
|
+
if (hash) newUrl += "#" + hash;
|
|
153
175
|
|
|
154
176
|
pjax_to(newUrl);
|
|
155
177
|
}
|
|
@@ -170,12 +192,14 @@ function view_post(viewname, route, data, onDone) {
|
|
|
170
192
|
? "application/x-www-form-urlencoded"
|
|
171
193
|
: "application/json",
|
|
172
194
|
data: typeof data === "string" ? data : JSON.stringify(data),
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
195
|
+
})
|
|
196
|
+
.done(function (res) {
|
|
197
|
+
if (onDone) onDone(res);
|
|
198
|
+
ajax_done(res);
|
|
199
|
+
})
|
|
200
|
+
.fail(function (res) {
|
|
201
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
202
|
+
});
|
|
179
203
|
}
|
|
180
204
|
var logged_errors = [];
|
|
181
205
|
function globalErrorCatcher(message, source, lineno, colno, error) {
|
|
@@ -254,6 +278,8 @@ function ajax_modal(url, opts = {}) {
|
|
|
254
278
|
|
|
255
279
|
function saveAndContinue(e, k) {
|
|
256
280
|
var form = $(e).closest("form");
|
|
281
|
+
const valres = form[0].reportValidity()
|
|
282
|
+
if (!valres) return;
|
|
257
283
|
submitWithEmptyAction(form[0]);
|
|
258
284
|
var url = form.attr("action");
|
|
259
285
|
var form_data = form.serialize();
|
|
@@ -300,12 +326,47 @@ function applyViewConfig(e, url, k) {
|
|
|
300
326
|
error: function (request) { },
|
|
301
327
|
success: function (res) {
|
|
302
328
|
k && k(res);
|
|
329
|
+
!k && updateViewPreview();
|
|
303
330
|
},
|
|
304
331
|
});
|
|
305
332
|
|
|
306
333
|
return false;
|
|
307
334
|
}
|
|
308
335
|
|
|
336
|
+
function updateViewPreview() {
|
|
337
|
+
const $preview = $("#viewcfg-preview[data-preview-url]");
|
|
338
|
+
if ($preview.length > 0) {
|
|
339
|
+
const url = $preview.attr("data-preview-url");
|
|
340
|
+
$preview.css({ opacity: 0.5 });
|
|
341
|
+
$.ajax(url, {
|
|
342
|
+
type: "POST",
|
|
343
|
+
headers: {
|
|
344
|
+
"CSRF-Token": _sc_globalCsrf,
|
|
345
|
+
},
|
|
346
|
+
|
|
347
|
+
error: function (request) { },
|
|
348
|
+
success: function (res) {
|
|
349
|
+
$preview.css({ opacity: 1.0 });
|
|
350
|
+
|
|
351
|
+
//disable elements in preview
|
|
352
|
+
$preview.html(res);
|
|
353
|
+
$preview.find("a").attr("href", "#");
|
|
354
|
+
$preview
|
|
355
|
+
.find("[onclick], button, a, input, select")
|
|
356
|
+
.attr("onclick", "return false");
|
|
357
|
+
|
|
358
|
+
$preview.find("textarea").attr("disabled", true);
|
|
359
|
+
$preview.find("input").attr("readonly", true);
|
|
360
|
+
|
|
361
|
+
//disable functions preview migght try to call
|
|
362
|
+
set_state_field = () => { }
|
|
363
|
+
set_state_fields = () => { }
|
|
364
|
+
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
309
370
|
function ajaxSubmitForm(e) {
|
|
310
371
|
var form = $(e).closest("form");
|
|
311
372
|
var url = form.attr("action");
|
|
@@ -421,6 +482,17 @@ function test_formula(tablename, stored) {
|
|
|
421
482
|
});
|
|
422
483
|
}
|
|
423
484
|
|
|
485
|
+
function create_new_folder(folder) {
|
|
486
|
+
const name = window.prompt("Name of the new folder");
|
|
487
|
+
if (name)
|
|
488
|
+
ajax_post(`/files/new-folder`, {
|
|
489
|
+
data: { name, folder },
|
|
490
|
+
success: (data) => {
|
|
491
|
+
location.reload();
|
|
492
|
+
},
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
424
496
|
async function fill_formula_btn_click(btn, k) {
|
|
425
497
|
const formula = decodeURIComponent($(btn).attr("data-formula"));
|
|
426
498
|
const free_vars = JSON.parse(
|
|
@@ -453,58 +525,127 @@ async function fill_formula_btn_click(btn, k) {
|
|
|
453
525
|
$(btn).closest(".input-group").find("input").val(val);
|
|
454
526
|
if (k) k();
|
|
455
527
|
} catch (e) {
|
|
456
|
-
notifyAlert({
|
|
457
|
-
|
|
528
|
+
notifyAlert({
|
|
529
|
+
type: "danger",
|
|
530
|
+
text: `Error evaluating fill formula: ${e.message}`,
|
|
531
|
+
});
|
|
532
|
+
console.error(e);
|
|
458
533
|
}
|
|
459
534
|
}
|
|
460
535
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
return this.each(function (index, element) {
|
|
478
|
-
$(element).on("show.bs.tab", function () {
|
|
479
|
-
var stateObject = { url: $(this).attr("href") };
|
|
480
|
-
|
|
481
|
-
if (window.location.hash && stateObject.url !== window.location.hash) {
|
|
482
|
-
window.history.pushState(
|
|
483
|
-
stateObject,
|
|
484
|
-
document.title,
|
|
485
|
-
window.location.pathname +
|
|
486
|
-
window.location.search +
|
|
487
|
-
$(this).attr("href")
|
|
488
|
-
);
|
|
536
|
+
function removeSpinner(elementId, orginalHtml) {
|
|
537
|
+
$(`#${elementId}`).html(orginalHtml);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function poll_mobile_build_finished(outDirName, pollCount, orginalBtnHtml) {
|
|
541
|
+
$.ajax("/admin/build-mobile-app/finished", {
|
|
542
|
+
type: "GET",
|
|
543
|
+
data: { build_dir: outDirName },
|
|
544
|
+
success: function (res) {
|
|
545
|
+
if (!res.finished) {
|
|
546
|
+
if (pollCount >= 50) {
|
|
547
|
+
removeSpinner("buildMobileAppBtnId", orginalBtnHtml);
|
|
548
|
+
notifyAlert({
|
|
549
|
+
type: "danger",
|
|
550
|
+
text: "unable to get the build results",
|
|
551
|
+
});
|
|
489
552
|
} else {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
window.location.pathname +
|
|
494
|
-
window.location.search +
|
|
495
|
-
$(this).attr("href")
|
|
496
|
-
);
|
|
553
|
+
setTimeout(() => {
|
|
554
|
+
poll_mobile_build_finished(outDirName, ++pollCount, orginalBtnHtml);
|
|
555
|
+
}, 5000);
|
|
497
556
|
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
557
|
+
} else {
|
|
558
|
+
href_to(
|
|
559
|
+
`build-mobile-app/result?build_dir_name=${encodeURIComponent(
|
|
560
|
+
outDirName
|
|
561
|
+
)}`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function build_mobile_app(button) {
|
|
569
|
+
const form = $(button).closest("form");
|
|
570
|
+
const params = {};
|
|
571
|
+
form.serializeArray().forEach((item) => {
|
|
572
|
+
params[item.name] = item.value;
|
|
573
|
+
});
|
|
574
|
+
ajax_post("/admin/build-mobile-app", {
|
|
575
|
+
data: params,
|
|
576
|
+
success: (data) => {
|
|
577
|
+
if (data.build_dir_name) {
|
|
578
|
+
handleMessages();
|
|
579
|
+
const orginalBtnHtml = $("#buildMobileAppBtnId").html();
|
|
580
|
+
press_store_button(button);
|
|
581
|
+
poll_mobile_build_finished(data.build_dir_name, 0, orginalBtnHtml);
|
|
504
582
|
}
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
(() => {
|
|
588
|
+
const e = document.querySelector("[data-sidebar-toggler]");
|
|
589
|
+
let closed = localStorage.getItem("sidebarClosed") === "true";
|
|
590
|
+
if (e) {
|
|
591
|
+
if (closed) {
|
|
592
|
+
e.dispatchEvent(new Event("click"));
|
|
593
|
+
}
|
|
594
|
+
e.addEventListener("click", () => {
|
|
595
|
+
closed = !closed;
|
|
596
|
+
localStorage.setItem("sidebarClosed", `${closed}`);
|
|
505
597
|
});
|
|
506
|
-
}
|
|
507
|
-
})(
|
|
598
|
+
}
|
|
599
|
+
})()
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
/*
|
|
603
|
+
https://github.com/jeffdavidgreen/bootstrap-html5-history-tabs/blob/master/bootstrap-history-tabs.js
|
|
604
|
+
Copyright (c) 2015 Jeff Green
|
|
605
|
+
*/
|
|
606
|
+
|
|
607
|
+
+ (function ($) {
|
|
608
|
+
"use strict";
|
|
609
|
+
$.fn.historyTabs = function () {
|
|
610
|
+
var that = this;
|
|
611
|
+
window.addEventListener("popstate", function (event) {
|
|
612
|
+
if (event.state) {
|
|
613
|
+
$(that)
|
|
614
|
+
.filter('[href="' + event.state.url + '"]')
|
|
615
|
+
.tab("show");
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
return this.each(function (index, element) {
|
|
619
|
+
$(element).on("show.bs.tab", function () {
|
|
620
|
+
var stateObject = { url: $(this).attr("href") };
|
|
621
|
+
|
|
622
|
+
if (window.location.hash && stateObject.url !== window.location.hash) {
|
|
623
|
+
window.history.pushState(
|
|
624
|
+
stateObject,
|
|
625
|
+
document.title,
|
|
626
|
+
window.location.pathname +
|
|
627
|
+
window.location.search +
|
|
628
|
+
$(this).attr("href")
|
|
629
|
+
);
|
|
630
|
+
} else {
|
|
631
|
+
window.history.replaceState(
|
|
632
|
+
stateObject,
|
|
633
|
+
document.title,
|
|
634
|
+
window.location.pathname +
|
|
635
|
+
window.location.search +
|
|
636
|
+
$(this).attr("href")
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
});
|
|
640
|
+
if (!window.location.hash && $(element).is(".active")) {
|
|
641
|
+
// Shows the first element if there are no query parameters.
|
|
642
|
+
$(element).tab("show");
|
|
643
|
+
} else if ($(this).attr("href") === window.location.hash) {
|
|
644
|
+
$(element).tab("show");
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
};
|
|
648
|
+
})(jQuery);
|
|
508
649
|
|
|
509
650
|
// Copyright (c) 2011 Marcus Ekwall, http://writeless.se/
|
|
510
651
|
// https://github.com/mekwall/jquery-throttle
|
package/restart_watcher.js
CHANGED
package/routes/actions.js
CHANGED
|
@@ -8,7 +8,6 @@ const Router = require("express-promise-router");
|
|
|
8
8
|
const {
|
|
9
9
|
isAdmin,
|
|
10
10
|
error_catcher,
|
|
11
|
-
get_base_url,
|
|
12
11
|
addOnDoneRedirect,
|
|
13
12
|
} = require("./utils.js");
|
|
14
13
|
const { getState } = require("@saltcorn/data/db/state");
|
|
@@ -25,17 +24,9 @@ const { getTriggerList } = require("./common_lists");
|
|
|
25
24
|
const router = new Router();
|
|
26
25
|
module.exports = router;
|
|
27
26
|
const {
|
|
28
|
-
mkTable,
|
|
29
27
|
renderForm,
|
|
30
28
|
link,
|
|
31
|
-
// post_btn,
|
|
32
|
-
// settingsDropdown,
|
|
33
|
-
// post_dropdown_item,
|
|
34
|
-
post_delete_btn,
|
|
35
|
-
localeDateTime,
|
|
36
|
-
// localeDateTime,
|
|
37
29
|
} = require("@saltcorn/markup");
|
|
38
|
-
const actions = require("@saltcorn/data/base-plugin/actions");
|
|
39
30
|
const Form = require("@saltcorn/data/models/form");
|
|
40
31
|
const {
|
|
41
32
|
div,
|
|
@@ -52,14 +43,11 @@ const {
|
|
|
52
43
|
h6,
|
|
53
44
|
pre,
|
|
54
45
|
text,
|
|
55
|
-
hr,
|
|
56
46
|
} = require("@saltcorn/markup/tags");
|
|
57
47
|
const Table = require("@saltcorn/data/models/table");
|
|
58
48
|
const { getActionConfigFields } = require("@saltcorn/data/plugin-helper");
|
|
59
49
|
const { send_events_page } = require("../markup/admin.js");
|
|
60
|
-
const EventLog = require("@saltcorn/data/models/eventlog");
|
|
61
50
|
const User = require("@saltcorn/data/models/user");
|
|
62
|
-
const form = require("@saltcorn/markup/form");
|
|
63
51
|
const {
|
|
64
52
|
blocklyImportScripts,
|
|
65
53
|
blocklyToolbox,
|
|
@@ -92,7 +80,6 @@ router.get(
|
|
|
92
80
|
error_catcher(async (req, res) => {
|
|
93
81
|
const triggers = await Trigger.findAllWithTableName();
|
|
94
82
|
const actions = await getActions();
|
|
95
|
-
const base_url = get_base_url(req);
|
|
96
83
|
send_events_page({
|
|
97
84
|
res,
|
|
98
85
|
req,
|
|
@@ -388,6 +375,11 @@ router.get(
|
|
|
388
375
|
error_catcher(async (req, res) => {
|
|
389
376
|
const { id } = req.params;
|
|
390
377
|
const trigger = await Trigger.findOne({ id });
|
|
378
|
+
if (!trigger) {
|
|
379
|
+
req.flash("warning", req.__("Action not found"));
|
|
380
|
+
res.redirect(`/actions/`);
|
|
381
|
+
return
|
|
382
|
+
}
|
|
391
383
|
const action = getState().actions[trigger.action];
|
|
392
384
|
if (!action) {
|
|
393
385
|
req.flash("warning", req.__("Action not found"));
|
|
@@ -583,7 +575,7 @@ router.get(
|
|
|
583
575
|
};
|
|
584
576
|
let table, row;
|
|
585
577
|
if (trigger.table_id) {
|
|
586
|
-
table = await Table.findOne(trigger.table_id);
|
|
578
|
+
table = await Table.findOne( { id: trigger.table_id } );
|
|
587
579
|
row = await table.getRow({});
|
|
588
580
|
}
|
|
589
581
|
try {
|