@saltcorn/server 1.1.0-beta.10 → 1.1.0-beta.12
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/help/{Cordova Builder.tmd → Capacitor Builder.tmd } +1 -1
- package/help/Configuration keys.tmd +5 -0
- package/help/index.js +2 -0
- package/locales/en.json +3 -0
- package/markup/admin.js +6 -3
- package/package.json +9 -9
- package/public/flatpickr-dark.css +795 -0
- package/public/saltcorn-common.js +18 -0
- package/public/saltcorn.css +6 -0
- package/public/saltcorn.js +10 -10
- package/routes/admin.js +41 -73
- package/routes/config.js +39 -25
- package/routes/list.js +9 -1
- package/routes/registry.js +45 -3
- package/routes/tenant.js +1 -0
- package/serve.js +5 -1
- package/wrapper.js +1 -1
|
@@ -727,6 +727,22 @@ function doMobileTransforms() {
|
|
|
727
727
|
],
|
|
728
728
|
};
|
|
729
729
|
|
|
730
|
+
// change /plugins or plugins to sc_plugins
|
|
731
|
+
// capacitor reserves the plugins prefix for cordova plugins
|
|
732
|
+
const normalisePluginsPrefix = (path) => {
|
|
733
|
+
if (path.startsWith("/plugins/") || path.startsWith("plugins/"))
|
|
734
|
+
return path.replace(/\/?plugins\//, "sc_plugins/");
|
|
735
|
+
return path;
|
|
736
|
+
};
|
|
737
|
+
$("link").each(function () {
|
|
738
|
+
const path = $(this).attr("href");
|
|
739
|
+
if (path) $(this).attr("href", normalisePluginsPrefix(path));
|
|
740
|
+
});
|
|
741
|
+
$("script").each(function () {
|
|
742
|
+
const path = $(this).attr("src");
|
|
743
|
+
if (path) $(this).attr("src", normalisePluginsPrefix(path));
|
|
744
|
+
});
|
|
745
|
+
|
|
730
746
|
$("a").each(function () {
|
|
731
747
|
let path = $(this).attr("href") || "";
|
|
732
748
|
if (path.startsWith("http")) {
|
|
@@ -1140,11 +1156,13 @@ function initialize_page() {
|
|
|
1140
1156
|
}
|
|
1141
1157
|
}
|
|
1142
1158
|
}
|
|
1159
|
+
|
|
1143
1160
|
setTimeout(() => {
|
|
1144
1161
|
$("#toasts-area")
|
|
1145
1162
|
.find(".show[rendered='server-side'][type='success']")
|
|
1146
1163
|
.removeClass("show");
|
|
1147
1164
|
}, 5000);
|
|
1165
|
+
|
|
1148
1166
|
$(".lazy-accoordion").on("show.bs.collapse", function (e) {
|
|
1149
1167
|
const $e = $(e.target).find("[data-sc-view-source]");
|
|
1150
1168
|
if ($.trim($e.html()) == "") {
|
package/public/saltcorn.css
CHANGED
|
@@ -607,4 +607,10 @@ button.monospace-copy-btn {
|
|
|
607
607
|
|
|
608
608
|
i[class^="unicode-"], i[class*=" unicode-"] {
|
|
609
609
|
font-style: normal;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
.tabulator.table-dark:not(.thead-light) .tabulator-footer, .tabulator.table-dark:not(.thead-light) .tabulator-footer .tabulator-col {
|
|
613
|
+
background-color: #212529;
|
|
614
|
+
border-color: #32383e;
|
|
615
|
+
color: #fff;
|
|
610
616
|
}
|
package/public/saltcorn.js
CHANGED
|
@@ -890,9 +890,9 @@ function build_mobile_app(button) {
|
|
|
890
890
|
|
|
891
891
|
if (
|
|
892
892
|
params.useDocker &&
|
|
893
|
-
!window.
|
|
893
|
+
!window.capacitorBuilderAvailable &&
|
|
894
894
|
!confirm(
|
|
895
|
-
"Docker is selected but the
|
|
895
|
+
"Docker is selected but the Capacitor builder seems not to be installed. " +
|
|
896
896
|
"Do you really want to continue?"
|
|
897
897
|
)
|
|
898
898
|
) {
|
|
@@ -935,11 +935,11 @@ function build_mobile_app(button) {
|
|
|
935
935
|
});
|
|
936
936
|
}
|
|
937
937
|
|
|
938
|
-
function
|
|
939
|
-
ajax_post("/admin/mobile-app/pull-
|
|
938
|
+
function pull_capacitor_builder() {
|
|
939
|
+
ajax_post("/admin/mobile-app/pull-capacitor-builder", {
|
|
940
940
|
success: () => {
|
|
941
941
|
notifyAlert(
|
|
942
|
-
"Pulling the the
|
|
942
|
+
"Pulling the the capacitor-builder. " +
|
|
943
943
|
"To see the progress, open the logs viewer with the System logging verbosity set to 'All'."
|
|
944
944
|
);
|
|
945
945
|
},
|
|
@@ -989,12 +989,12 @@ function check_xcodebuild() {
|
|
|
989
989
|
});
|
|
990
990
|
}
|
|
991
991
|
|
|
992
|
-
function
|
|
993
|
-
$.ajax("/admin/mobile-app/check-
|
|
992
|
+
function check_capacitor_builder() {
|
|
993
|
+
$.ajax("/admin/mobile-app/check-capacitor-builder", {
|
|
994
994
|
type: "GET",
|
|
995
995
|
success: function (res) {
|
|
996
|
-
window.
|
|
997
|
-
if (window.
|
|
996
|
+
window.capacitorBuilderAvailable = !!res.installed;
|
|
997
|
+
if (window.capacitorBuilderAvailable) {
|
|
998
998
|
$("#dockerBuilderStatusId").html(
|
|
999
999
|
`<span>
|
|
1000
1000
|
installed<i class="ps-2 fas fa-check text-success"></i>
|
|
@@ -1088,7 +1088,7 @@ function toggle_tbl_sync() {
|
|
|
1088
1088
|
function toggle_android_platform() {
|
|
1089
1089
|
if ($("#androidCheckboxId")[0].checked === true) {
|
|
1090
1090
|
$("#dockerCheckboxId").attr("hidden", false);
|
|
1091
|
-
$("#dockerCheckboxId").attr("checked", window.
|
|
1091
|
+
$("#dockerCheckboxId").attr("checked", window.capacitorBuilderAvailable);
|
|
1092
1092
|
$("#dockerLabelId").removeClass("d-none");
|
|
1093
1093
|
} else {
|
|
1094
1094
|
$("#dockerCheckboxId").attr("hidden", true);
|
package/routes/admin.js
CHANGED
|
@@ -91,10 +91,7 @@ const {
|
|
|
91
91
|
} = require("../markup/admin.js");
|
|
92
92
|
const packagejson = require("../package.json");
|
|
93
93
|
const Form = require("@saltcorn/data/models/form");
|
|
94
|
-
const {
|
|
95
|
-
get_latest_npm_version,
|
|
96
|
-
isFixedConfig,
|
|
97
|
-
} = require("@saltcorn/data/models/config");
|
|
94
|
+
const { get_latest_npm_version } = require("@saltcorn/data/models/config");
|
|
98
95
|
const { getMailTransport } = require("@saltcorn/data/models/email");
|
|
99
96
|
const {
|
|
100
97
|
getBaseDomain,
|
|
@@ -988,39 +985,6 @@ router.post(
|
|
|
988
985
|
})
|
|
989
986
|
);
|
|
990
987
|
|
|
991
|
-
router.post(
|
|
992
|
-
"/save-config",
|
|
993
|
-
isAdmin,
|
|
994
|
-
error_catcher(async (req, res) => {
|
|
995
|
-
const state = getState();
|
|
996
|
-
|
|
997
|
-
//TODO check this is a config key
|
|
998
|
-
const validKeyName = (k) =>
|
|
999
|
-
k !== "_csrf" && k !== "constructor" && k !== "__proto__";
|
|
1000
|
-
|
|
1001
|
-
for (const [k, v] of Object.entries(req.body)) {
|
|
1002
|
-
if (!isFixedConfig(k) && typeof v !== "undefined" && validKeyName(k)) {
|
|
1003
|
-
//TODO read value from type
|
|
1004
|
-
await state.setConfig(k, v);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
// checkboxes that are false are not sent in post body. Check here
|
|
1009
|
-
const { boolcheck } = req.query;
|
|
1010
|
-
const boolchecks =
|
|
1011
|
-
typeof boolcheck === "undefined"
|
|
1012
|
-
? []
|
|
1013
|
-
: Array.isArray(boolcheck)
|
|
1014
|
-
? boolcheck
|
|
1015
|
-
: [boolcheck];
|
|
1016
|
-
for (const k of boolchecks) {
|
|
1017
|
-
if (typeof req.body[k] === "undefined" && validKeyName(k))
|
|
1018
|
-
await state.setConfig(k, false);
|
|
1019
|
-
}
|
|
1020
|
-
res.json({ success: "ok" });
|
|
1021
|
-
})
|
|
1022
|
-
);
|
|
1023
|
-
|
|
1024
988
|
/**
|
|
1025
989
|
* Do Auto backup now
|
|
1026
990
|
*/
|
|
@@ -1291,10 +1255,14 @@ router.post(
|
|
|
1291
1255
|
})
|
|
1292
1256
|
);
|
|
1293
1257
|
|
|
1294
|
-
const
|
|
1295
|
-
const child = spawn(
|
|
1296
|
-
|
|
1297
|
-
|
|
1258
|
+
const pullCapacitorBuilder = (req, res, version) => {
|
|
1259
|
+
const child = spawn(
|
|
1260
|
+
"docker",
|
|
1261
|
+
["pull", `saltcorn/capacitor-builder:${version}`],
|
|
1262
|
+
{
|
|
1263
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1264
|
+
}
|
|
1265
|
+
);
|
|
1298
1266
|
return new Promise((resolve, reject) => {
|
|
1299
1267
|
child.stdout.on("data", (data) => {
|
|
1300
1268
|
res.write(data);
|
|
@@ -1550,9 +1518,9 @@ const doInstall = async (req, res, version, deepClean, runPull) => {
|
|
|
1550
1518
|
}
|
|
1551
1519
|
if (runPull) {
|
|
1552
1520
|
res.write(
|
|
1553
|
-
req.__("Pulling the
|
|
1521
|
+
req.__("Pulling the capacitor-builder docker image...") + "\n"
|
|
1554
1522
|
);
|
|
1555
|
-
const pullCode = await
|
|
1523
|
+
const pullCode = await pullCapacitorBuilder(req, res, version);
|
|
1556
1524
|
res.write(req.__("Pull done with code %s", pullCode) + "\n");
|
|
1557
1525
|
if (pullCode === 0) {
|
|
1558
1526
|
res.write(req.__("Pruning docker...") + "\n");
|
|
@@ -1997,9 +1965,9 @@ router.get(
|
|
|
1997
1965
|
});
|
|
1998
1966
|
})
|
|
1999
1967
|
);
|
|
2000
|
-
const buildDialogScript = (
|
|
1968
|
+
const buildDialogScript = (capacitorBuilderAvailable, isSbadmin2) =>
|
|
2001
1969
|
`<script>
|
|
2002
|
-
var
|
|
1970
|
+
var capacitorBuilderAvailable = ${capacitorBuilderAvailable};
|
|
2003
1971
|
var isSbadmin2 = ${isSbadmin2};
|
|
2004
1972
|
function showEntrySelect(type) {
|
|
2005
1973
|
for( const currentType of ["view", "page", "pagegroup"]) {
|
|
@@ -2028,7 +1996,7 @@ const buildDialogScript = (cordovaBuilderAvailable, isSbadmin2) =>
|
|
|
2028
1996
|
|
|
2029
1997
|
const imageAvailable = async () => {
|
|
2030
1998
|
try {
|
|
2031
|
-
const image = new Docker().getImage("saltcorn/
|
|
1999
|
+
const image = new Docker().getImage("saltcorn/capacitor-builder");
|
|
2032
2000
|
await image.inspect();
|
|
2033
2001
|
return true;
|
|
2034
2002
|
} catch (e) {
|
|
@@ -2824,10 +2792,10 @@ router.get(
|
|
|
2824
2792
|
div(
|
|
2825
2793
|
label(
|
|
2826
2794
|
{ class: "form-label fw-bold" },
|
|
2827
|
-
req.__("
|
|
2795
|
+
req.__("Capacitor builder") +
|
|
2828
2796
|
a(
|
|
2829
2797
|
{
|
|
2830
|
-
href: "javascript:ajax_modal('/admin/help/
|
|
2798
|
+
href: "javascript:ajax_modal('/admin/help/Capacitor Builder?')",
|
|
2831
2799
|
},
|
|
2832
2800
|
i({ class: "fas fa-question-circle ps-1" })
|
|
2833
2801
|
)
|
|
@@ -2855,9 +2823,8 @@ router.get(
|
|
|
2855
2823
|
{ class: "col-sm-4" },
|
|
2856
2824
|
button(
|
|
2857
2825
|
{
|
|
2858
|
-
id: "pullCordovaBtnId",
|
|
2859
2826
|
type: "button",
|
|
2860
|
-
onClick: `
|
|
2827
|
+
onClick: `pull_capacitor_builder(this);`,
|
|
2861
2828
|
class: "btn btn-warning",
|
|
2862
2829
|
},
|
|
2863
2830
|
req.__("pull")
|
|
@@ -2865,7 +2832,7 @@ router.get(
|
|
|
2865
2832
|
span(
|
|
2866
2833
|
{
|
|
2867
2834
|
role: "button",
|
|
2868
|
-
onClick: "
|
|
2835
|
+
onClick: "check_capacitor_builder()",
|
|
2869
2836
|
},
|
|
2870
2837
|
span({ class: "ps-3" }, req.__("refresh")),
|
|
2871
2838
|
i({ class: "ps-2 fas fa-undo" })
|
|
@@ -3355,36 +3322,37 @@ router.post(
|
|
|
3355
3322
|
});
|
|
3356
3323
|
const childOutputs = [];
|
|
3357
3324
|
child.stdout.on("data", (data) => {
|
|
3358
|
-
|
|
3359
|
-
|
|
3325
|
+
const outMsg = data.toString();
|
|
3326
|
+
getState().log(5, outMsg);
|
|
3327
|
+
if (data) childOutputs.push(outMsg);
|
|
3360
3328
|
});
|
|
3361
3329
|
child.stderr.on("data", (data) => {
|
|
3362
|
-
|
|
3363
|
-
|
|
3330
|
+
const errMsg = data ? data.toString() : req.__("An error occurred");
|
|
3331
|
+
getState().log(5, errMsg);
|
|
3332
|
+
childOutputs.push(errMsg);
|
|
3364
3333
|
});
|
|
3365
3334
|
child.on("exit", (exitCode, signal) => {
|
|
3366
3335
|
const logFile = exitCode === 0 ? "logs.txt" : "error_logs.txt";
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
3376
|
-
await File.set_xattr_of_existing_file(logFile, buildDir, req.user);
|
|
3377
|
-
}
|
|
3336
|
+
const exitMsg = childOutputs.join("\n");
|
|
3337
|
+
fs.writeFile(path.join(buildDir, logFile), exitMsg, async (error) => {
|
|
3338
|
+
if (error) {
|
|
3339
|
+
console.log(`unable to write '${logFile}' to '${buildDir}'`);
|
|
3340
|
+
console.log(error);
|
|
3341
|
+
} else {
|
|
3342
|
+
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
3343
|
+
await File.set_xattr_of_existing_file(logFile, buildDir, req.user);
|
|
3378
3344
|
}
|
|
3379
|
-
);
|
|
3345
|
+
});
|
|
3380
3346
|
});
|
|
3381
3347
|
child.on("error", (msg) => {
|
|
3382
3348
|
const message = msg.message ? msg.message : msg.code;
|
|
3383
3349
|
const stack = msg.stack ? msg.stack : "";
|
|
3384
3350
|
const logFile = "error_logs.txt";
|
|
3351
|
+
const errMsg = [message, stack].join("\n");
|
|
3352
|
+
getState().log(5, msg);
|
|
3385
3353
|
fs.writeFile(
|
|
3386
3354
|
path.join(buildDir, "error_logs.txt"),
|
|
3387
|
-
|
|
3355
|
+
errMsg,
|
|
3388
3356
|
async (error) => {
|
|
3389
3357
|
if (error) {
|
|
3390
3358
|
console.log(`unable to write logFile to '${buildDir}'`);
|
|
@@ -3400,13 +3368,13 @@ router.post(
|
|
|
3400
3368
|
);
|
|
3401
3369
|
|
|
3402
3370
|
router.post(
|
|
3403
|
-
"/mobile-app/pull-
|
|
3371
|
+
"/mobile-app/pull-capacitor-builder",
|
|
3404
3372
|
isAdmin,
|
|
3405
3373
|
error_catcher(async (req, res) => {
|
|
3406
3374
|
const state = getState();
|
|
3407
3375
|
const child = spawn(
|
|
3408
3376
|
`${process.env.DOCKER_BIN ? `${process.env.DOCKER_BIN}/` : ""}docker`,
|
|
3409
|
-
["pull",
|
|
3377
|
+
["pull", `saltcorn/capacitor-builder:${state.scVersion}`],
|
|
3410
3378
|
{
|
|
3411
3379
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3412
3380
|
cwd: ".",
|
|
@@ -3421,11 +3389,11 @@ router.post(
|
|
|
3421
3389
|
child.on("exit", (exitCode, signal) => {
|
|
3422
3390
|
state.log(
|
|
3423
3391
|
2,
|
|
3424
|
-
`"pull
|
|
3392
|
+
`"pull capacitor-builder exit with code: ${exitCode} and signal: ${signal}`
|
|
3425
3393
|
);
|
|
3426
3394
|
});
|
|
3427
3395
|
child.on("error", (msg) => {
|
|
3428
|
-
state.log(1, `pull
|
|
3396
|
+
state.log(1, `pull capacitor-builder error: ${msg}`);
|
|
3429
3397
|
});
|
|
3430
3398
|
|
|
3431
3399
|
res.json({});
|
|
@@ -3433,7 +3401,7 @@ router.post(
|
|
|
3433
3401
|
);
|
|
3434
3402
|
|
|
3435
3403
|
router.get(
|
|
3436
|
-
"/mobile-app/check-
|
|
3404
|
+
"/mobile-app/check-capacitor-builder",
|
|
3437
3405
|
isAdmin,
|
|
3438
3406
|
error_catcher(async (req, res) => {
|
|
3439
3407
|
const installed = await imageAvailable();
|
package/routes/config.js
CHANGED
|
@@ -5,31 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
const Router = require("express-promise-router");
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
const File = require("@saltcorn/data/models/file");
|
|
10
|
-
const Table = require("@saltcorn/data/models/table");
|
|
11
|
-
const View = require("@saltcorn/data/models/view");
|
|
12
|
-
const Form = require("@saltcorn/data/models/form");
|
|
13
|
-
const { isAdmin, setTenant, error_catcher } = require("./utils.js");
|
|
8
|
+
const { isAdmin, error_catcher } = require("./utils.js");
|
|
14
9
|
const { getState } = require("@saltcorn/data/db/state");
|
|
15
10
|
|
|
16
|
-
const {
|
|
17
|
-
mkTable,
|
|
18
|
-
renderForm,
|
|
19
|
-
link,
|
|
20
|
-
post_btn,
|
|
21
|
-
post_delete_btn,
|
|
22
|
-
} = require("@saltcorn/markup");
|
|
23
|
-
const {
|
|
24
|
-
getConfig,
|
|
25
|
-
setConfig,
|
|
26
|
-
getAllConfigOrDefaults,
|
|
27
|
-
deleteConfig,
|
|
28
|
-
configTypes,
|
|
29
|
-
isFixedConfig,
|
|
30
|
-
} = require("@saltcorn/data/models/config");
|
|
31
|
-
const { table, tbody, tr, th, td, div } = require("@saltcorn/markup/tags");
|
|
32
|
-
|
|
33
11
|
/**
|
|
34
12
|
* @type {object}
|
|
35
13
|
* @const
|
|
@@ -52,7 +30,43 @@ router.post(
|
|
|
52
30
|
error_catcher(async (req, res) => {
|
|
53
31
|
const { key } = req.params;
|
|
54
32
|
await getState().deleteConfig(key);
|
|
55
|
-
req.
|
|
56
|
-
|
|
33
|
+
if (req.xhr) res.json({ success: "ok" });
|
|
34
|
+
else {
|
|
35
|
+
req.flash("success", req.__(`Configuration key %s deleted`, key));
|
|
36
|
+
res.redirect(`/admin`);
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
router.post(
|
|
42
|
+
"/save",
|
|
43
|
+
isAdmin,
|
|
44
|
+
error_catcher(async (req, res) => {
|
|
45
|
+
const state = getState();
|
|
46
|
+
|
|
47
|
+
//TODO check this is a config key
|
|
48
|
+
const validKeyName = (k) =>
|
|
49
|
+
k !== "_csrf" && k !== "constructor" && k !== "__proto__";
|
|
50
|
+
|
|
51
|
+
for (const [k, v] of Object.entries(req.body)) {
|
|
52
|
+
if (!state.isFixedConfig(k) && typeof v !== "undefined" && validKeyName(k)) {
|
|
53
|
+
//TODO read value from type
|
|
54
|
+
await state.setConfig(k, v);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// checkboxes that are false are not sent in post body. Check here
|
|
59
|
+
const { boolcheck } = req.query;
|
|
60
|
+
const boolchecks =
|
|
61
|
+
typeof boolcheck === "undefined"
|
|
62
|
+
? []
|
|
63
|
+
: Array.isArray(boolcheck)
|
|
64
|
+
? boolcheck
|
|
65
|
+
: [boolcheck];
|
|
66
|
+
for (const k of boolchecks) {
|
|
67
|
+
if (typeof req.body[k] === "undefined" && validKeyName(k))
|
|
68
|
+
await state.setConfig(k, false);
|
|
69
|
+
}
|
|
70
|
+
res.json({ success: "ok" });
|
|
57
71
|
})
|
|
58
72
|
);
|
package/routes/list.js
CHANGED
|
@@ -268,6 +268,7 @@ router.get(
|
|
|
268
268
|
clipboard: false,
|
|
269
269
|
cellClick: "__delete_tabulator_row",
|
|
270
270
|
});
|
|
271
|
+
const isDark = getState().getLightDarkMode(req.user) === "dark";
|
|
271
272
|
res.sendWrap(
|
|
272
273
|
{
|
|
273
274
|
title: req.__(`%s data table`, table.name),
|
|
@@ -297,6 +298,13 @@ router.get(
|
|
|
297
298
|
{
|
|
298
299
|
css: `/static_assets/${db.connectObj.version_tag}/flatpickr.min.css`,
|
|
299
300
|
},
|
|
301
|
+
...(isDark
|
|
302
|
+
? [
|
|
303
|
+
{
|
|
304
|
+
css: `/static_assets/${db.connectObj.version_tag}/flatpickr-dark.css`,
|
|
305
|
+
},
|
|
306
|
+
]
|
|
307
|
+
: []),
|
|
300
308
|
],
|
|
301
309
|
},
|
|
302
310
|
{
|
|
@@ -431,7 +439,7 @@ router.get(
|
|
|
431
439
|
div({
|
|
432
440
|
id: "jsGrid",
|
|
433
441
|
class:
|
|
434
|
-
getState().getLightDarkMode() === "dark"
|
|
442
|
+
getState().getLightDarkMode(req.user) === "dark"
|
|
435
443
|
? "table-dark"
|
|
436
444
|
: undefined,
|
|
437
445
|
})
|
package/routes/registry.js
CHANGED
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
domReady,
|
|
8
8
|
a,
|
|
9
9
|
div,
|
|
10
|
+
h4,
|
|
10
11
|
i,
|
|
11
12
|
text,
|
|
12
13
|
button,
|
|
@@ -40,6 +41,7 @@ const {
|
|
|
40
41
|
install_pack,
|
|
41
42
|
} = require("@saltcorn/admin-models/models/pack");
|
|
42
43
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
44
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
43
45
|
/**
|
|
44
46
|
* @type {object}
|
|
45
47
|
* @const
|
|
@@ -79,7 +81,17 @@ router.get(
|
|
|
79
81
|
{},
|
|
80
82
|
{ orderBy: "name", nocase: true }
|
|
81
83
|
);
|
|
82
|
-
|
|
84
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
85
|
+
|
|
86
|
+
const all_configs_obj = await getState().getAllConfigOrDefaults();
|
|
87
|
+
const all_configs = Object.entries(all_configs_obj)
|
|
88
|
+
.map(([name, v]) => ({
|
|
89
|
+
...v,
|
|
90
|
+
name,
|
|
91
|
+
}))
|
|
92
|
+
.filter((c) => isRoot || !c.root_only);
|
|
93
|
+
|
|
94
|
+
let tables, views, pages, triggers, configs;
|
|
83
95
|
if (q) {
|
|
84
96
|
const qlower = q.toLowerCase();
|
|
85
97
|
const includesQ = (s) => s.toLowerCase().includes(qlower);
|
|
@@ -100,11 +112,13 @@ router.get(
|
|
|
100
112
|
const pack = await trigger_pack(t);
|
|
101
113
|
return includesQ(JSON.stringify(pack));
|
|
102
114
|
});
|
|
115
|
+
configs = all_configs.filter((c) => includesQ(JSON.stringify(c)));
|
|
103
116
|
} else {
|
|
104
117
|
tables = all_tables;
|
|
105
118
|
views = all_views;
|
|
106
119
|
pages = all_pages;
|
|
107
120
|
triggers = all_triggers;
|
|
121
|
+
configs = all_configs;
|
|
108
122
|
}
|
|
109
123
|
const li_link = (etype1, ename1) =>
|
|
110
124
|
li(
|
|
@@ -124,7 +138,7 @@ router.get(
|
|
|
124
138
|
action: `/registry-editor?etype=${etype}&ename=${encodeURIComponent(
|
|
125
139
|
ename
|
|
126
140
|
)}${qlink}`,
|
|
127
|
-
|
|
141
|
+
formStyle: "vert",
|
|
128
142
|
values: { regval: JSON.stringify(jsonVal, null, 2) },
|
|
129
143
|
fields: [
|
|
130
144
|
{
|
|
@@ -177,6 +191,14 @@ router.get(
|
|
|
177
191
|
const ppack = await page_pack(all_pages.find((v) => v.name === ename));
|
|
178
192
|
edContents = renderForm(mkForm(ppack), req.csrfToken());
|
|
179
193
|
break;
|
|
194
|
+
case "config":
|
|
195
|
+
const config = all_configs.find((t) => t.name === ename);
|
|
196
|
+
edContents =
|
|
197
|
+
h4(config.label) +
|
|
198
|
+
(config.blurb || "") +
|
|
199
|
+
(config.sublabel || "") +
|
|
200
|
+
renderForm(mkForm(config.value), req.csrfToken());
|
|
201
|
+
break;
|
|
180
202
|
case "trigger":
|
|
181
203
|
const trigger = all_triggers.find((t) => t.name === ename);
|
|
182
204
|
const trpack = await trigger_pack(trigger);
|
|
@@ -282,6 +304,16 @@ router.get(
|
|
|
282
304
|
triggers.map((t) => li_link("trigger", t.name))
|
|
283
305
|
)
|
|
284
306
|
)
|
|
307
|
+
),
|
|
308
|
+
li(
|
|
309
|
+
details(
|
|
310
|
+
{ open: q || etype === "CONFIG" }, //
|
|
311
|
+
summary("Configuration"),
|
|
312
|
+
ul(
|
|
313
|
+
{ class: "ps-3" },
|
|
314
|
+
configs.map((t) => li_link("config", t.name))
|
|
315
|
+
)
|
|
316
|
+
)
|
|
285
317
|
)
|
|
286
318
|
)
|
|
287
319
|
),
|
|
@@ -309,7 +341,14 @@ router.post(
|
|
|
309
341
|
const qlink = q ? `&q=${encodeURIComponent(q)}` : "";
|
|
310
342
|
|
|
311
343
|
const entVal = JSON.parse(req.body.regval);
|
|
312
|
-
let pack = {
|
|
344
|
+
let pack = {
|
|
345
|
+
plugins: [],
|
|
346
|
+
tables: [],
|
|
347
|
+
views: [],
|
|
348
|
+
pages: [],
|
|
349
|
+
triggers: [],
|
|
350
|
+
config: {},
|
|
351
|
+
};
|
|
313
352
|
|
|
314
353
|
switch (etype) {
|
|
315
354
|
case "table":
|
|
@@ -324,6 +363,9 @@ router.post(
|
|
|
324
363
|
case "trigger":
|
|
325
364
|
pack.triggers = [entVal];
|
|
326
365
|
break;
|
|
366
|
+
case "config":
|
|
367
|
+
pack.config[ename] = entVal;
|
|
368
|
+
break;
|
|
327
369
|
}
|
|
328
370
|
await install_pack(pack);
|
|
329
371
|
res.redirect(
|
package/routes/tenant.js
CHANGED
|
@@ -493,6 +493,7 @@ const tenant_settings_form = (req) =>
|
|
|
493
493
|
"tenant_template",
|
|
494
494
|
"tenant_baseurl",
|
|
495
495
|
"tenant_create_unauth_redirect",
|
|
496
|
+
"tenant_inherit_cfgs",
|
|
496
497
|
{ section_header: req.__("Tenant application capabilities") },
|
|
497
498
|
"tenants_install_git",
|
|
498
499
|
"tenants_set_npm_modules",
|
package/serve.js
CHANGED
|
@@ -121,7 +121,11 @@ const initMaster = async ({ disableMigrate }, useClusterAdaptor = true) => {
|
|
|
121
121
|
if (getState().getConfig("log_sql", false)) db.set_sql_logging();
|
|
122
122
|
if (db.is_it_multi_tenant()) {
|
|
123
123
|
const tenants = await getAllTenants();
|
|
124
|
-
await init_multi_tenant(
|
|
124
|
+
await init_multi_tenant(
|
|
125
|
+
async () => await loadAllPlugins(true),
|
|
126
|
+
disableMigrate,
|
|
127
|
+
tenants
|
|
128
|
+
);
|
|
125
129
|
}
|
|
126
130
|
eachTenant(async () => {
|
|
127
131
|
const state = getState();
|
package/wrapper.js
CHANGED
|
@@ -194,7 +194,7 @@ const get_headers = (req, version_tag, description, extras = []) => {
|
|
|
194
194
|
}, _sc_globalCsrf = "${req.csrfToken()}", _sc_version_tag = "${version_tag}"${
|
|
195
195
|
locale ? `, _sc_locale = "${locale}"` : ""
|
|
196
196
|
}, _sc_lightmode = ${JSON.stringify(
|
|
197
|
-
state.getLightDarkMode(req.user)
|
|
197
|
+
state.getLightDarkMode?.(req.user) || "light"
|
|
198
198
|
)};</script>`,
|
|
199
199
|
},
|
|
200
200
|
{ css: `/static_assets/${version_tag}/saltcorn.css` },
|