@saltcorn/server 0.9.6-beta.2 → 0.9.6-beta.20
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 +6 -1
- package/auth/admin.js +55 -53
- package/auth/routes.js +28 -10
- package/auth/testhelp.js +86 -0
- package/help/Field label.tmd +11 -0
- package/help/Field types.tmd +39 -0
- package/help/Ownership field.tmd +76 -0
- package/help/Ownership formula.tmd +75 -0
- package/help/Table roles.tmd +20 -0
- package/help/User groups.tmd +35 -0
- package/load_plugins.js +33 -5
- package/locales/en.json +29 -1
- package/locales/it.json +3 -2
- package/markup/admin.js +1 -0
- package/markup/forms.js +5 -1
- package/package.json +9 -9
- package/public/log_viewer_utils.js +32 -0
- package/public/mermaid.min.js +705 -306
- package/public/saltcorn-builder.css +23 -0
- package/public/saltcorn-common.js +248 -80
- package/public/saltcorn.css +80 -0
- package/public/saltcorn.js +86 -2
- package/restart_watcher.js +1 -0
- package/routes/actions.js +27 -0
- package/routes/admin.js +175 -64
- package/routes/api.js +6 -0
- package/routes/common_lists.js +42 -32
- package/routes/fields.js +70 -42
- package/routes/homepage.js +2 -0
- package/routes/index.js +2 -0
- package/routes/menu.js +69 -4
- package/routes/notifications.js +90 -10
- package/routes/pageedit.js +18 -13
- package/routes/plugins.js +11 -2
- package/routes/registry.js +289 -0
- package/routes/search.js +10 -4
- package/routes/tables.js +51 -27
- package/routes/tenant.js +4 -15
- package/routes/utils.js +25 -8
- package/routes/view.js +1 -1
- package/routes/viewedit.js +11 -7
- package/serve.js +27 -5
- package/tests/edit.test.js +426 -0
- package/tests/fields.test.js +21 -0
- package/tests/filter.test.js +68 -0
- package/tests/page.test.js +2 -2
- package/tests/plugins.test.js +2 -0
- package/tests/sync.test.js +59 -0
- package/wrapper.js +4 -1
|
@@ -509,3 +509,26 @@ div.builder-config-field {
|
|
|
509
509
|
border: 1px solid black;
|
|
510
510
|
margin-top: 2px;
|
|
511
511
|
}
|
|
512
|
+
|
|
513
|
+
div.componets-and-library-accordion {
|
|
514
|
+
padding-right: 0 !important;
|
|
515
|
+
}
|
|
516
|
+
.builder-left-shrunk .componets-and-library-accordion {
|
|
517
|
+
min-height: 0 !important;
|
|
518
|
+
padding-right: 0;
|
|
519
|
+
}
|
|
520
|
+
.toolbox-card .builder-layers {
|
|
521
|
+
min-height: 200px;
|
|
522
|
+
overflow-y: auto;
|
|
523
|
+
max-height: max-content !important;
|
|
524
|
+
}
|
|
525
|
+
.toolbox-card:nth-child(2) {
|
|
526
|
+
margin-bottom: 0px;
|
|
527
|
+
}
|
|
528
|
+
#builder-main-canvas {
|
|
529
|
+
height: calc(100dvh - 50px) !important;
|
|
530
|
+
min-height: 600px;
|
|
531
|
+
}
|
|
532
|
+
#builder-main-canvas.h-100 {
|
|
533
|
+
height: 100% !important;
|
|
534
|
+
}
|
|
@@ -8,6 +8,23 @@ jQuery.fn.swapWith = function (to) {
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
+
function monospace_block_click(e) {
|
|
12
|
+
let e1 = $(e).next("pre");
|
|
13
|
+
let mine = $(e).html();
|
|
14
|
+
$(e).html($(e1).html());
|
|
15
|
+
$(e1).html(mine);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function copy_monospace_block(e) {
|
|
19
|
+
let e1 = $(e).next("pre");
|
|
20
|
+
let e2 = $(e1).next("pre");
|
|
21
|
+
if (!e2.length) return navigator.clipboard.writeText($(el).text());
|
|
22
|
+
const e1t = e1.text();
|
|
23
|
+
const e2t = e2.text();
|
|
24
|
+
if (e1t.length > e2t.length) return navigator.clipboard.writeText(e1t);
|
|
25
|
+
else return navigator.clipboard.writeText(e2t);
|
|
26
|
+
}
|
|
27
|
+
|
|
11
28
|
function setScreenInfoCookie() {
|
|
12
29
|
document.cookie = `_sc_screen_info_=${JSON.stringify({
|
|
13
30
|
width: window.screen.width,
|
|
@@ -179,14 +196,19 @@ function apply_showif() {
|
|
|
179
196
|
is_or ? "&_or_field=" + k : ""
|
|
180
197
|
}`;
|
|
181
198
|
};
|
|
182
|
-
const qss = Object.entries(dynwhere.whereParsed).map(kvToQs);
|
|
199
|
+
const qss = Object.entries(dynwhere.whereParsed).map((kv) => kvToQs(kv));
|
|
200
|
+
if (dynwhere.existingValue) {
|
|
201
|
+
qss.push(`id=${dynwhere.existingValue}`);
|
|
202
|
+
qss.push(`_or_field=id`);
|
|
203
|
+
}
|
|
183
204
|
if (dynwhere.dereference) {
|
|
184
205
|
if (Array.isArray(dynwhere.dereference))
|
|
185
206
|
qss.push(...dynwhere.dereference.map((d) => `dereference=${d}`));
|
|
186
207
|
else qss.push(`dereference=${dynwhere.dereference}`);
|
|
187
208
|
}
|
|
188
209
|
const qs = qss.join("&");
|
|
189
|
-
|
|
210
|
+
let current = e.attr("data-selected");
|
|
211
|
+
if (current === "null") current = null;
|
|
190
212
|
e.change(function (ec) {
|
|
191
213
|
e.attr("data-selected", ec.target.value);
|
|
192
214
|
});
|
|
@@ -195,12 +217,14 @@ function apply_showif() {
|
|
|
195
217
|
if (currentOptionsSet === qs) return;
|
|
196
218
|
|
|
197
219
|
const activate = (success, qs) => {
|
|
220
|
+
//re-fetch current, because it may have changed
|
|
221
|
+
let current = e.attr("data-selected");
|
|
222
|
+
if (current === "null") current = null;
|
|
198
223
|
if (e.prop("data-fetch-options-current-set") === qs) return;
|
|
199
224
|
e.empty();
|
|
200
225
|
e.prop("data-fetch-options-current-set", qs);
|
|
201
226
|
const toAppend = [];
|
|
202
|
-
|
|
203
|
-
toAppend.push({ label: dynwhere.neutral_label || "", value: "" });
|
|
227
|
+
|
|
204
228
|
let currentDataOption = undefined;
|
|
205
229
|
const dataOptions = [];
|
|
206
230
|
//console.log(success);
|
|
@@ -231,13 +255,24 @@ function apply_showif() {
|
|
|
231
255
|
? 1
|
|
232
256
|
: -1
|
|
233
257
|
);
|
|
258
|
+
if (!dynwhere.required)
|
|
259
|
+
toAppend.unshift({ label: dynwhere.neutral_label || "", value: "" });
|
|
260
|
+
if (dynwhere.required && dynwhere.placeholder)
|
|
261
|
+
toAppend.unshift({
|
|
262
|
+
disabled: true,
|
|
263
|
+
label: dynwhere.placeholder,
|
|
264
|
+
value: "",
|
|
265
|
+
selected: !current,
|
|
266
|
+
});
|
|
234
267
|
e.html(
|
|
235
268
|
toAppend
|
|
236
269
|
.map(
|
|
237
|
-
({ label, value, selected }) =>
|
|
270
|
+
({ label, value, selected, disabled }) =>
|
|
238
271
|
`<option${selected ? ` selected` : ""}${
|
|
239
|
-
|
|
240
|
-
}
|
|
272
|
+
disabled ? ` disabled` : ""
|
|
273
|
+
}${typeof value !== "undefined" ? ` value="${value}"` : ""}>${
|
|
274
|
+
label || ""
|
|
275
|
+
}</option>`
|
|
241
276
|
)
|
|
242
277
|
.join("")
|
|
243
278
|
);
|
|
@@ -428,7 +463,7 @@ function get_form_record(e_in, select_labels) {
|
|
|
428
463
|
|
|
429
464
|
const e = e_in.viewname
|
|
430
465
|
? $(`form[data-viewname="${e_in.viewname}"]`)
|
|
431
|
-
: e_in.closest(".form-namespace");
|
|
466
|
+
: $(e_in).closest(".form-namespace");
|
|
432
467
|
|
|
433
468
|
const form = $(e).closest("form");
|
|
434
469
|
|
|
@@ -462,6 +497,38 @@ function get_form_record(e_in, select_labels) {
|
|
|
462
497
|
rec[name] = f(rec[name], $this);
|
|
463
498
|
}
|
|
464
499
|
});
|
|
500
|
+
|
|
501
|
+
const joinFieldsStr =
|
|
502
|
+
typeof e_in !== "string" && $(e_in).attr("data-show-if-joinfields");
|
|
503
|
+
if (joinFieldsStr) {
|
|
504
|
+
const joinFields = JSON.parse(decodeURIComponent(joinFieldsStr));
|
|
505
|
+
|
|
506
|
+
const joinVals = $(e_in).prop("data-join-values");
|
|
507
|
+
const kvals = $(e_in).prop("data-join-key-values") || {};
|
|
508
|
+
let differentKeys = false;
|
|
509
|
+
for (const { ref } of joinFields) {
|
|
510
|
+
if (rec[ref] != kvals[ref]) differentKeys = true;
|
|
511
|
+
}
|
|
512
|
+
if (!joinVals || differentKeys) {
|
|
513
|
+
$(e_in).prop("data-join-values", {});
|
|
514
|
+
const keyVals = {};
|
|
515
|
+
for (const { ref, target, refTable } of joinFields) {
|
|
516
|
+
keyVals[ref] = rec[ref];
|
|
517
|
+
$.ajax(`/api/${refTable}?id=${rec[ref]}`, {
|
|
518
|
+
success: (val) => {
|
|
519
|
+
const jvs = $(e_in).prop("data-join-values") || {};
|
|
520
|
+
|
|
521
|
+
jvs[ref] = val.success[0];
|
|
522
|
+
$(e_in).prop("data-join-values", jvs);
|
|
523
|
+
apply_showif();
|
|
524
|
+
},
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
$(e_in).prop("data-join-key-values", keyVals);
|
|
528
|
+
} else if (joinFieldsStr) {
|
|
529
|
+
Object.assign(rec, joinVals);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
465
532
|
return rec;
|
|
466
533
|
}
|
|
467
534
|
function showIfFormulaInputs(e, fml) {
|
|
@@ -588,6 +655,138 @@ function escapeHtml(text) {
|
|
|
588
655
|
function reload_on_init() {
|
|
589
656
|
localStorage.setItem("reload_on_init", true);
|
|
590
657
|
}
|
|
658
|
+
|
|
659
|
+
function doMobileTransforms() {
|
|
660
|
+
const replaceAttr = (el, attr, web, mobile) => {
|
|
661
|
+
const jThis = $(el);
|
|
662
|
+
const skip = jThis.attr("skip-mobile-adjust");
|
|
663
|
+
if (!skip) {
|
|
664
|
+
const attrVal = jThis.attr(attr);
|
|
665
|
+
if (attrVal?.includes(web)) {
|
|
666
|
+
jThis.attr(attr, attrVal.replace(web, mobile));
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
const replacers = {
|
|
672
|
+
href: [
|
|
673
|
+
{
|
|
674
|
+
web: "javascript:history.back()",
|
|
675
|
+
mobile: "javascript:parent.goBack()",
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
web: "javascript:ajax_modal",
|
|
679
|
+
mobile: "javascript:mobile_modal",
|
|
680
|
+
},
|
|
681
|
+
],
|
|
682
|
+
onclick: [
|
|
683
|
+
{
|
|
684
|
+
web: "history.back()",
|
|
685
|
+
mobile: "parent.goBack()",
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
web: "ajax_modal",
|
|
689
|
+
mobile: "mobile_modal",
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
web: "ajax_post_",
|
|
693
|
+
mobile: "local_post_",
|
|
694
|
+
},
|
|
695
|
+
],
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
$("a").each(function () {
|
|
699
|
+
let path = $(this).attr("href") || "";
|
|
700
|
+
if (path.startsWith("http")) {
|
|
701
|
+
const url = new URL(path);
|
|
702
|
+
path = `${url.pathname}${url.search}`;
|
|
703
|
+
}
|
|
704
|
+
if (path.startsWith("/view/") || path.startsWith("/page/")) {
|
|
705
|
+
const jThis = $(this);
|
|
706
|
+
const skip = jThis.attr("skip-mobile-adjust");
|
|
707
|
+
if (!skip) {
|
|
708
|
+
jThis.removeAttr("href");
|
|
709
|
+
jThis.attr("onclick", `execLink('${path}')`);
|
|
710
|
+
if (jThis.find("i,img").length === 0 && !jThis.css("color")) {
|
|
711
|
+
jThis.css(
|
|
712
|
+
"color",
|
|
713
|
+
"rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1))"
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
} else if (path.includes("/files/serve/")) {
|
|
718
|
+
const tokens = path.split("/files/serve/");
|
|
719
|
+
if (tokens.length > 1)
|
|
720
|
+
$(this).attr("href", `javascript:openFile('${tokens[1]}')`);
|
|
721
|
+
} else if (path.includes("/files/download/")) {
|
|
722
|
+
const tokens = path.split("/files/download/");
|
|
723
|
+
if (tokens.length > 1)
|
|
724
|
+
$(this).attr(
|
|
725
|
+
"href",
|
|
726
|
+
`javascript:notifyAlert('File donwloads are not supported.')`
|
|
727
|
+
);
|
|
728
|
+
} else {
|
|
729
|
+
for (const [k, v] of Object.entries(replacers)) {
|
|
730
|
+
for ({ web, mobile } of v) replaceAttr(this, k, web, mobile);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
$("button").each(function () {
|
|
736
|
+
for (const [k, v] of Object.entries({ onclick: replacers.onclick })) {
|
|
737
|
+
for ({ web, mobile } of v) replaceAttr(this, k, v.web, v.mobile);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
$("[mobile-img-path]").each(async function () {
|
|
742
|
+
if (parent.loadEncodedFile) {
|
|
743
|
+
const fileId = $(this).attr("mobile-img-path");
|
|
744
|
+
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
745
|
+
this.src = base64Encoded;
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
$("[mobile-bg-img-path]").each(async function () {
|
|
750
|
+
if (parent.loadEncodedFile) {
|
|
751
|
+
const fileId = $(this).attr("mobile-bg-img-path");
|
|
752
|
+
if (fileId) {
|
|
753
|
+
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
754
|
+
this.style.backgroundImage = `url("${base64Encoded}")`;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
$("img:not([mobile-img-path]):not([mobile-bg-img-path])").each(
|
|
760
|
+
async function () {
|
|
761
|
+
if (parent.loadEncodedFile) {
|
|
762
|
+
const jThis = $(this);
|
|
763
|
+
const src = jThis.attr("src");
|
|
764
|
+
if (src?.includes("/files/serve/")) {
|
|
765
|
+
const tokens = src.split("/files/serve/");
|
|
766
|
+
if (tokens.length > 1) {
|
|
767
|
+
const fileId = tokens[1];
|
|
768
|
+
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
769
|
+
this.src = base64Encoded;
|
|
770
|
+
}
|
|
771
|
+
} else if (src?.includes("/files/resize/")) {
|
|
772
|
+
const tokens = src.split("/files/resize/");
|
|
773
|
+
if (tokens.length > 1) {
|
|
774
|
+
const idAndDims = tokens[1].split("/");
|
|
775
|
+
const width = idAndDims[0];
|
|
776
|
+
const height = idAndDims.length > 2 ? idAndDims[1] : undefined;
|
|
777
|
+
const fileId = idAndDims[idAndDims.length - 1];
|
|
778
|
+
const style = { width: `${width || 50}px` };
|
|
779
|
+
if (height > 0) style.height = `${height}px`;
|
|
780
|
+
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
781
|
+
this.src = base64Encoded;
|
|
782
|
+
jThis.css(style);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
591
790
|
function initialize_page() {
|
|
592
791
|
if (window._sc_locale && window.dayjs) dayjs.locale(window._sc_locale);
|
|
593
792
|
const isNode = getIsNode();
|
|
@@ -762,58 +961,7 @@ function initialize_page() {
|
|
|
762
961
|
</form>`
|
|
763
962
|
);
|
|
764
963
|
});
|
|
765
|
-
if (!isNode)
|
|
766
|
-
$("[mobile-img-path]").each(async function () {
|
|
767
|
-
if (parent.loadEncodedFile) {
|
|
768
|
-
const fileId = $(this).attr("mobile-img-path");
|
|
769
|
-
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
770
|
-
this.src = base64Encoded;
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
|
|
774
|
-
$("[mobile-bg-img-path]").each(async function () {
|
|
775
|
-
if (parent.loadEncodedFile) {
|
|
776
|
-
const fileId = $(this).attr("mobile-bg-img-path");
|
|
777
|
-
if (fileId) {
|
|
778
|
-
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
779
|
-
this.style.backgroundImage = `url("${base64Encoded}")`;
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
$("a").each(function () {
|
|
785
|
-
let path = $(this).attr("href") || "";
|
|
786
|
-
if (path.startsWith("http")) {
|
|
787
|
-
const url = new URL(path);
|
|
788
|
-
path = `${url.pathname}${url.search}`;
|
|
789
|
-
}
|
|
790
|
-
if (path.startsWith("/view/")) {
|
|
791
|
-
const jThis = $(this);
|
|
792
|
-
const skip = jThis.attr("skip-mobile-adjust");
|
|
793
|
-
if (!skip) {
|
|
794
|
-
jThis.attr("href", `javascript:execLink('${path}')`);
|
|
795
|
-
if (jThis.find("i,img").length === 0 && !jThis.css("color")) {
|
|
796
|
-
jThis.css(
|
|
797
|
-
"color",
|
|
798
|
-
"rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1))"
|
|
799
|
-
);
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
$("img").each(async function () {
|
|
806
|
-
if (parent.loadEncodedFile) {
|
|
807
|
-
const jThis = $(this);
|
|
808
|
-
const src = jThis.attr("src");
|
|
809
|
-
if (src?.startsWith("/files/serve/")) {
|
|
810
|
-
const fileId = src.replace("/files/serve/", "");
|
|
811
|
-
const base64Encoded = await parent.loadEncodedFile(fileId);
|
|
812
|
-
this.src = base64Encoded;
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
});
|
|
816
|
-
}
|
|
964
|
+
if (!isNode) doMobileTransforms();
|
|
817
965
|
function setExplainer(that) {
|
|
818
966
|
var id = $(that).attr("id") + "_explainer";
|
|
819
967
|
|
|
@@ -1219,7 +1367,13 @@ function restore_old_button(btnId) {
|
|
|
1219
1367
|
btn.removeData("old-text");
|
|
1220
1368
|
}
|
|
1221
1369
|
|
|
1222
|
-
async function common_done(res,
|
|
1370
|
+
async function common_done(res, viewnameOrElem, isWeb = true) {
|
|
1371
|
+
const viewname =
|
|
1372
|
+
typeof viewnameOrElem === "string"
|
|
1373
|
+
? viewnameOrElem
|
|
1374
|
+
: $(viewnameOrElem)
|
|
1375
|
+
.closest("[data-sc-embed-viewname]")
|
|
1376
|
+
.attr("data-sc-embed-viewname");
|
|
1223
1377
|
if (window._sc_loglevel > 4)
|
|
1224
1378
|
console.log("ajax result directives", viewname, res);
|
|
1225
1379
|
const handle = async (element, fn) => {
|
|
@@ -1231,30 +1385,33 @@ async function common_done(res, viewname, isWeb = true) {
|
|
|
1231
1385
|
if (res.row && res.field_names) {
|
|
1232
1386
|
const f = new Function(`viewname, row, {${res.field_names}}`, s);
|
|
1233
1387
|
const evalres = await f(viewname, res.row, res.row);
|
|
1234
|
-
if (evalres) await common_done(evalres,
|
|
1388
|
+
if (evalres) await common_done(evalres, viewnameOrElem, isWeb);
|
|
1235
1389
|
} else if (res.row) {
|
|
1236
1390
|
const f = new Function(`viewname, row`, s);
|
|
1237
1391
|
const evalres = await f(viewname, res.row);
|
|
1238
|
-
if (evalres) await common_done(evalres,
|
|
1392
|
+
if (evalres) await common_done(evalres, viewnameOrElem, isWeb);
|
|
1239
1393
|
} else {
|
|
1240
1394
|
const f = new Function(`viewname`, s);
|
|
1241
1395
|
const evalres = await f(viewname);
|
|
1242
|
-
if (evalres) await common_done(evalres,
|
|
1396
|
+
if (evalres) await common_done(evalres, viewnameOrElem, isWeb);
|
|
1243
1397
|
}
|
|
1244
1398
|
};
|
|
1245
1399
|
if (res.notify) await handle(res.notify, notifyAlert);
|
|
1246
|
-
if (res.error)
|
|
1400
|
+
if (res.error) {
|
|
1401
|
+
if (window._sc_loglevel > 4) console.trace("error response", res.error);
|
|
1247
1402
|
await handle(res.error, (text) =>
|
|
1248
1403
|
notifyAlert({ type: "danger", text: text })
|
|
1249
1404
|
);
|
|
1405
|
+
}
|
|
1250
1406
|
if (res.notify_success)
|
|
1251
1407
|
await handle(res.notify_success, (text) =>
|
|
1252
1408
|
notifyAlert({ type: "success", text: text })
|
|
1253
1409
|
);
|
|
1254
1410
|
if (res.set_fields && (viewname || res.set_fields._viewname)) {
|
|
1255
|
-
const form =
|
|
1256
|
-
|
|
1257
|
-
|
|
1411
|
+
const form =
|
|
1412
|
+
typeof viewnameOrElem === "string" || res.set_fields._viewname
|
|
1413
|
+
? $(`form[data-viewname="${res.set_fields._viewname || viewname}"]`)
|
|
1414
|
+
: $(viewnameOrElem).closest("form[data-viewname]");
|
|
1258
1415
|
if (form.length === 0 && set_state_fields) {
|
|
1259
1416
|
// assume this is a filter
|
|
1260
1417
|
set_state_fields(
|
|
@@ -1279,9 +1436,14 @@ async function common_done(res, viewname, isWeb = true) {
|
|
|
1279
1436
|
if (input.attr("type") === "checkbox")
|
|
1280
1437
|
input.prop("checked", res.set_fields[k]);
|
|
1281
1438
|
else input.val(res.set_fields[k]);
|
|
1439
|
+
if (input.attr("data-selected")) {
|
|
1440
|
+
input.attr("data-selected", res.set_fields[k]);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1282
1443
|
input.trigger("set_form_field");
|
|
1283
1444
|
});
|
|
1284
1445
|
}
|
|
1446
|
+
form.trigger("change");
|
|
1285
1447
|
}
|
|
1286
1448
|
|
|
1287
1449
|
if (res.download) {
|
|
@@ -1311,15 +1473,15 @@ async function common_done(res, viewname, isWeb = true) {
|
|
|
1311
1473
|
});
|
|
1312
1474
|
}
|
|
1313
1475
|
if (res.eval_js) await handle(res.eval_js, eval_it);
|
|
1314
|
-
|
|
1315
|
-
if (res.goto && !isWeb)
|
|
1316
|
-
// TODO ch
|
|
1317
|
-
notifyAlert({
|
|
1318
|
-
type: "danger",
|
|
1319
|
-
text: "Goto is not supported in a mobile deployment.",
|
|
1320
|
-
});
|
|
1321
1476
|
else if (res.goto) {
|
|
1322
|
-
if (
|
|
1477
|
+
if (!isWeb) {
|
|
1478
|
+
const next = new URL(res.goto, "http://localhost");
|
|
1479
|
+
const pathname = next.pathname;
|
|
1480
|
+
if (pathname.startsWith("/view/") || pathname.startsWith("/page/")) {
|
|
1481
|
+
const route = `get${pathname}${next.search ? "?" + next.search : ""}`;
|
|
1482
|
+
await parent.handleRoute(route);
|
|
1483
|
+
} else parent.cordova.InAppBrowser.open(res.goto, "_system");
|
|
1484
|
+
} else if (res.target === "_blank") window.open(res.goto, "_blank").focus();
|
|
1323
1485
|
else {
|
|
1324
1486
|
const prev = new URL(window.location.href);
|
|
1325
1487
|
const next = new URL(res.goto, prev.origin);
|
|
@@ -1440,10 +1602,10 @@ const columnSummary = (col) => {
|
|
|
1440
1602
|
};
|
|
1441
1603
|
|
|
1442
1604
|
function submitWithEmptyAction(form) {
|
|
1443
|
-
var formAction = form.action;
|
|
1444
|
-
form.action
|
|
1605
|
+
var formAction = form.getAttribute("action");
|
|
1606
|
+
form.setAttribute("action", "javascript:void(0)");
|
|
1445
1607
|
form.submit();
|
|
1446
|
-
form.action
|
|
1608
|
+
form.setAttribute("action", formAction);
|
|
1447
1609
|
}
|
|
1448
1610
|
|
|
1449
1611
|
function unique_field_from_rows(
|
|
@@ -1667,7 +1829,13 @@ function close_saltcorn_modal() {
|
|
|
1667
1829
|
function reload_embedded_view(viewname, new_query_string) {
|
|
1668
1830
|
const isNode = getIsNode();
|
|
1669
1831
|
const updater = ($e, res) => {
|
|
1670
|
-
$e.
|
|
1832
|
+
const localState = $e.attr("data-sc-local-state");
|
|
1833
|
+
const parent = $e.parent();
|
|
1834
|
+
$e.replaceWith(res);
|
|
1835
|
+
if (localState && !new_query_string) {
|
|
1836
|
+
const newE = parent.find(`[data-sc-embed-viewname="${viewname}"]`);
|
|
1837
|
+
newE.attr("data-sc-local-state", localState);
|
|
1838
|
+
}
|
|
1671
1839
|
initialize_page();
|
|
1672
1840
|
};
|
|
1673
1841
|
if (window._sc_loglevel > 4)
|
package/public/saltcorn.css
CHANGED
|
@@ -506,3 +506,83 @@ tr[onclick] {
|
|
|
506
506
|
.modal-header {
|
|
507
507
|
justify-content: space-between;
|
|
508
508
|
}
|
|
509
|
+
|
|
510
|
+
ul.katetree {
|
|
511
|
+
list-style-type: none;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
ul.katetree details ul {
|
|
515
|
+
list-style-type: none;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
pre.monospace-block {
|
|
519
|
+
font-family: monospace;
|
|
520
|
+
color: unset;
|
|
521
|
+
background: unset;
|
|
522
|
+
padding: unset;
|
|
523
|
+
border-radius: unset;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.d-none-prefer {
|
|
527
|
+
display: none;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
button.monospace-copy-btn:has(+ pre.monospace-block:hover) {
|
|
531
|
+
display: block !important ;
|
|
532
|
+
}
|
|
533
|
+
button.monospace-copy-btn:hover {
|
|
534
|
+
display: block !important ;
|
|
535
|
+
}
|
|
536
|
+
button.monospace-copy-btn {
|
|
537
|
+
position: absolute;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows {
|
|
541
|
+
border-spacing: 0px 8px;
|
|
542
|
+
border-collapse: separate;
|
|
543
|
+
}
|
|
544
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows tbody {
|
|
545
|
+
transform: translateY(8px);
|
|
546
|
+
}
|
|
547
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows tr {
|
|
548
|
+
border-radius: 6px;
|
|
549
|
+
}
|
|
550
|
+
#page-inner-content.sbadmin2-theme
|
|
551
|
+
.table-hover.table-card-rows
|
|
552
|
+
> tbody
|
|
553
|
+
> tr:hover
|
|
554
|
+
> * {
|
|
555
|
+
--tblr-table-bg-state: transparent !important;
|
|
556
|
+
--bs-table-accent-bg: transparent !important;
|
|
557
|
+
color: var(--bs-table-hover-color);
|
|
558
|
+
background-color: var(--bs-table-hover-bg);
|
|
559
|
+
}
|
|
560
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows thead th {
|
|
561
|
+
background-color: #eaecf4;
|
|
562
|
+
border-block: 1px solid #d3d3d3 !important;
|
|
563
|
+
padding: 18px 15px;
|
|
564
|
+
font-weight: 600;
|
|
565
|
+
}
|
|
566
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows thead th:first-child {
|
|
567
|
+
border-left: 1px solid #d3d3d3 !important;
|
|
568
|
+
border-radius: 6px 0 0 6px;
|
|
569
|
+
}
|
|
570
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows thead th:last-child {
|
|
571
|
+
border-right: 1px #d3d3d3 !important;
|
|
572
|
+
border-radius: 0 6px 6px 0;
|
|
573
|
+
}
|
|
574
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows tbody td {
|
|
575
|
+
border-block: 1px solid #e3e6f0 !important;
|
|
576
|
+
position: relative;
|
|
577
|
+
z-index: 11;
|
|
578
|
+
padding: 15px 15px;
|
|
579
|
+
background-color: #fff;
|
|
580
|
+
}
|
|
581
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows tbody td:first-child {
|
|
582
|
+
border-left: 1px solid #e3e6f0 !important;
|
|
583
|
+
border-radius: 6px 0 0 6px;
|
|
584
|
+
}
|
|
585
|
+
#page-inner-content.sbadmin2-theme .table.table-card-rows tbody td:last-child {
|
|
586
|
+
border-right: 1px #e3e6f0 !important;
|
|
587
|
+
border-radius: 0 6px 6px 0;
|
|
588
|
+
}
|
package/public/saltcorn.js
CHANGED
|
@@ -236,7 +236,13 @@ function reset_spinners() {
|
|
|
236
236
|
});
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
function view_post(
|
|
239
|
+
function view_post(viewnameOrElem, route, data, onDone, sendState) {
|
|
240
|
+
const viewname =
|
|
241
|
+
typeof viewnameOrElem === "string"
|
|
242
|
+
? viewnameOrElem
|
|
243
|
+
: $(viewnameOrElem)
|
|
244
|
+
.closest("[data-sc-embed-viewname]")
|
|
245
|
+
.attr("data-sc-embed-viewname");
|
|
240
246
|
const query = sendState
|
|
241
247
|
? `?${new URL(get_current_state_url()).searchParams.toString()}`
|
|
242
248
|
: "";
|
|
@@ -254,7 +260,7 @@ function view_post(viewname, route, data, onDone, sendState) {
|
|
|
254
260
|
})
|
|
255
261
|
.done(function (res) {
|
|
256
262
|
if (onDone) onDone(res);
|
|
257
|
-
ajax_done(res,
|
|
263
|
+
ajax_done(res, viewnameOrElem);
|
|
258
264
|
reset_spinners();
|
|
259
265
|
})
|
|
260
266
|
.fail(function (res) {
|
|
@@ -1028,6 +1034,84 @@ function execLink(path) {
|
|
|
1028
1034
|
window.location.href = `${location.origin}${path}`;
|
|
1029
1035
|
}
|
|
1030
1036
|
|
|
1037
|
+
let defferedPrompt;
|
|
1038
|
+
window.addEventListener("beforeinstallprompt", (e) => {
|
|
1039
|
+
e.preventDefault();
|
|
1040
|
+
defferedPrompt = e;
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
function isAndroidMobile() {
|
|
1044
|
+
const ua = navigator.userAgent || navigator.vendor || window.opera;
|
|
1045
|
+
return /android/i.test(ua) && /mobile/i.test(ua);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function validatePWAManifest(manifest) {
|
|
1049
|
+
const errors = [];
|
|
1050
|
+
if (!manifest) errors.push("The manifest.json file is missing. ");
|
|
1051
|
+
else {
|
|
1052
|
+
if (!manifest.icons || manifest.icons.length === 0)
|
|
1053
|
+
errors.push("At least one icon is required");
|
|
1054
|
+
else if (
|
|
1055
|
+
manifest.icons.length > 0 &&
|
|
1056
|
+
!manifest.icons.some((icon) => {
|
|
1057
|
+
const sizes = icon.sizes.split("x");
|
|
1058
|
+
const x = parseInt(sizes[0]);
|
|
1059
|
+
const y = parseInt(sizes[1]);
|
|
1060
|
+
return x === y && x >= 144;
|
|
1061
|
+
})
|
|
1062
|
+
) {
|
|
1063
|
+
errors.push(
|
|
1064
|
+
"At least one square icon of size 144x144 or larger is required"
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
if (isAndroidMobile() && manifest.display === "browser") {
|
|
1068
|
+
errors.push(
|
|
1069
|
+
"The display property 'browser' may not work on mobile devices"
|
|
1070
|
+
);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
return errors;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
function supportsBeforeInstallPrompt() {
|
|
1077
|
+
return "onbeforeinstallprompt" in window;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function installPWA() {
|
|
1081
|
+
if (defferedPrompt) defferedPrompt.prompt();
|
|
1082
|
+
else if (!supportsBeforeInstallPrompt()) {
|
|
1083
|
+
notifyAlert({
|
|
1084
|
+
type: "danger",
|
|
1085
|
+
text:
|
|
1086
|
+
"It looks like your browser doesn’t support this feature. " +
|
|
1087
|
+
"Please try the standard installation method provided by your browser, or switch to a different browser.",
|
|
1088
|
+
});
|
|
1089
|
+
} else {
|
|
1090
|
+
const manifestUrl = `${window.location.origin}/notifications/manifest.json`;
|
|
1091
|
+
notifyAlert({
|
|
1092
|
+
type: "danger",
|
|
1093
|
+
text:
|
|
1094
|
+
"Unable to install the app. " +
|
|
1095
|
+
"Please check if the app is already installed or " +
|
|
1096
|
+
`inspect your manifest.json <a href="${manifestUrl}?pretty=true" target="_blank">here</a>`,
|
|
1097
|
+
});
|
|
1098
|
+
$.ajax(manifestUrl, {
|
|
1099
|
+
success: (res) => {
|
|
1100
|
+
const errors = validatePWAManifest(res);
|
|
1101
|
+
if (errors.length > 0)
|
|
1102
|
+
notifyAlert({
|
|
1103
|
+
type: "warning",
|
|
1104
|
+
text: `${errors.join(", ")}`,
|
|
1105
|
+
});
|
|
1106
|
+
},
|
|
1107
|
+
error: (res) => {
|
|
1108
|
+
console.log("Error fetching manifest.json");
|
|
1109
|
+
console.log(res);
|
|
1110
|
+
},
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1031
1115
|
(() => {
|
|
1032
1116
|
const e = document.querySelector("[data-sidebar-toggler]");
|
|
1033
1117
|
let closed = localStorage.getItem("sidebarClosed") === "true";
|