@saltcorn/server 1.1.0-beta.2 → 1.1.0-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/auth/admin.js +1 -1
- package/auth/roleadmin.js +10 -2
- package/help/{Cordova Builder.tmd → Capacitor Builder.tmd } +1 -1
- package/help/Configuration keys.tmd +5 -0
- package/help/index.js +2 -0
- package/load_plugins.js +9 -3
- package/locales/en.json +21 -1
- package/locales/pl.json +19 -2
- package/markup/admin.js +7 -3
- package/package.json +9 -9
- package/public/codemirror.css +33 -0
- package/public/flatpickr-dark.css +795 -0
- package/public/gridedit.js +1 -1
- package/public/mermaid.min.js +1077 -792
- package/public/saltcorn-common.js +76 -42
- package/public/saltcorn.css +7 -4
- package/public/saltcorn.js +56 -21
- package/routes/actions.js +937 -4
- package/routes/admin.js +59 -85
- package/routes/config.js +39 -25
- package/routes/eventlog.js +41 -1
- package/routes/fields.js +8 -2
- package/routes/homepage.js +13 -3
- package/routes/list.js +17 -1
- package/routes/registry.js +45 -3
- package/routes/tables.js +58 -20
- package/routes/tenant.js +53 -3
- package/tests/plugin_install.test.js +10 -10
- package/tests/plugins.test.js +6 -6
- package/wrapper.js +3 -1
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,
|
|
@@ -155,6 +152,7 @@ admin_config_route({
|
|
|
155
152
|
field_names: [
|
|
156
153
|
"site_name",
|
|
157
154
|
"timezone",
|
|
155
|
+
"default_locale",
|
|
158
156
|
"base_url",
|
|
159
157
|
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
160
158
|
{ section_header: "Logo image" },
|
|
@@ -534,6 +532,7 @@ router.get(
|
|
|
534
532
|
{},
|
|
535
533
|
{ orderBy: "created", orderDesc: true, fields: ["id", "created", "hash"] }
|
|
536
534
|
);
|
|
535
|
+
const locale = getState().getConfig("default_locale", "en");
|
|
537
536
|
send_admin_page({
|
|
538
537
|
res,
|
|
539
538
|
req,
|
|
@@ -555,9 +554,11 @@ router.get(
|
|
|
555
554
|
)}`,
|
|
556
555
|
target: "_blank",
|
|
557
556
|
},
|
|
558
|
-
`${localeDateTime(
|
|
559
|
-
snap.created
|
|
560
|
-
|
|
557
|
+
`${localeDateTime(
|
|
558
|
+
snap.created,
|
|
559
|
+
{},
|
|
560
|
+
locale
|
|
561
|
+
)} (${moment(snap.created).fromNow()})`
|
|
561
562
|
)
|
|
562
563
|
)
|
|
563
564
|
)
|
|
@@ -595,6 +596,7 @@ router.get(
|
|
|
595
596
|
error_catcher(async (req, res) => {
|
|
596
597
|
const { type, name } = req.params;
|
|
597
598
|
const snaps = await Snapshot.entity_history(type, name);
|
|
599
|
+
const locale = getState().getConfig("default_locale", "en");
|
|
598
600
|
res.set("Page-Title", `Restore ${text(name)}`);
|
|
599
601
|
res.send(
|
|
600
602
|
mkTable(
|
|
@@ -602,7 +604,9 @@ router.get(
|
|
|
602
604
|
{
|
|
603
605
|
label: req.__("When"),
|
|
604
606
|
key: (r) =>
|
|
605
|
-
`${localeDateTime(r.created)} (${moment(
|
|
607
|
+
`${localeDateTime(r.created, {}, locale)} (${moment(
|
|
608
|
+
r.created
|
|
609
|
+
).fromNow()})`,
|
|
606
610
|
},
|
|
607
611
|
|
|
608
612
|
{
|
|
@@ -981,39 +985,6 @@ router.post(
|
|
|
981
985
|
})
|
|
982
986
|
);
|
|
983
987
|
|
|
984
|
-
router.post(
|
|
985
|
-
"/save-config",
|
|
986
|
-
isAdmin,
|
|
987
|
-
error_catcher(async (req, res) => {
|
|
988
|
-
const state = getState();
|
|
989
|
-
|
|
990
|
-
//TODO check this is a config key
|
|
991
|
-
const validKeyName = (k) =>
|
|
992
|
-
k !== "_csrf" && k !== "constructor" && k !== "__proto__";
|
|
993
|
-
|
|
994
|
-
for (const [k, v] of Object.entries(req.body)) {
|
|
995
|
-
if (!isFixedConfig(k) && typeof v !== "undefined" && validKeyName(k)) {
|
|
996
|
-
//TODO read value from type
|
|
997
|
-
await state.setConfig(k, v);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
// checkboxes that are false are not sent in post body. Check here
|
|
1002
|
-
const { boolcheck } = req.query;
|
|
1003
|
-
const boolchecks =
|
|
1004
|
-
typeof boolcheck === "undefined"
|
|
1005
|
-
? []
|
|
1006
|
-
: Array.isArray(boolcheck)
|
|
1007
|
-
? boolcheck
|
|
1008
|
-
: [boolcheck];
|
|
1009
|
-
for (const k of boolchecks) {
|
|
1010
|
-
if (typeof req.body[k] === "undefined" && validKeyName(k))
|
|
1011
|
-
await state.setConfig(k, false);
|
|
1012
|
-
}
|
|
1013
|
-
res.json({ success: "ok" });
|
|
1014
|
-
})
|
|
1015
|
-
);
|
|
1016
|
-
|
|
1017
988
|
/**
|
|
1018
989
|
* Do Auto backup now
|
|
1019
990
|
*/
|
|
@@ -1284,10 +1255,14 @@ router.post(
|
|
|
1284
1255
|
})
|
|
1285
1256
|
);
|
|
1286
1257
|
|
|
1287
|
-
const
|
|
1288
|
-
const child = spawn(
|
|
1289
|
-
|
|
1290
|
-
|
|
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
|
+
);
|
|
1291
1266
|
return new Promise((resolve, reject) => {
|
|
1292
1267
|
child.stdout.on("data", (data) => {
|
|
1293
1268
|
res.write(data);
|
|
@@ -1543,9 +1518,9 @@ const doInstall = async (req, res, version, deepClean, runPull) => {
|
|
|
1543
1518
|
}
|
|
1544
1519
|
if (runPull) {
|
|
1545
1520
|
res.write(
|
|
1546
|
-
req.__("Pulling the
|
|
1521
|
+
req.__("Pulling the capacitor-builder docker image...") + "\n"
|
|
1547
1522
|
);
|
|
1548
|
-
const pullCode = await
|
|
1523
|
+
const pullCode = await pullCapacitorBuilder(req, res, version);
|
|
1549
1524
|
res.write(req.__("Pull done with code %s", pullCode) + "\n");
|
|
1550
1525
|
if (pullCode === 0) {
|
|
1551
1526
|
res.write(req.__("Pruning docker...") + "\n");
|
|
@@ -1741,8 +1716,8 @@ router.post(
|
|
|
1741
1716
|
|
|
1742
1717
|
let altname = await tenant_letsencrypt_name(subdomain);
|
|
1743
1718
|
|
|
1744
|
-
if (!altname || domain) {
|
|
1745
|
-
|
|
1719
|
+
if (!altname || !domain) {
|
|
1720
|
+
res.json({ error: "Set Base URL for both tenant and root first." });
|
|
1746
1721
|
return;
|
|
1747
1722
|
}
|
|
1748
1723
|
|
|
@@ -1759,13 +1734,14 @@ router.post(
|
|
|
1759
1734
|
|
|
1760
1735
|
await greenlock.sites.add({
|
|
1761
1736
|
subject: altname,
|
|
1737
|
+
altnames: [altname],
|
|
1762
1738
|
});
|
|
1763
1739
|
// letsencrypt
|
|
1764
1740
|
const tenant_letsencrypt_sites = getState().getConfig(
|
|
1765
1741
|
"tenant_letsencrypt_sites",
|
|
1766
1742
|
[]
|
|
1767
1743
|
);
|
|
1768
|
-
await getState().setConfig(tenant_letsencrypt_sites, [
|
|
1744
|
+
await getState().setConfig("tenant_letsencrypt_sites", [
|
|
1769
1745
|
altname,
|
|
1770
1746
|
...tenant_letsencrypt_sites,
|
|
1771
1747
|
]);
|
|
@@ -1775,12 +1751,10 @@ router.post(
|
|
|
1775
1751
|
notify: "Certificate added, please restart server",
|
|
1776
1752
|
});
|
|
1777
1753
|
} catch (e) {
|
|
1778
|
-
|
|
1779
|
-
res.redirect("/useradmin/ssl");
|
|
1754
|
+
res.json({ error: e.message });
|
|
1780
1755
|
}
|
|
1781
1756
|
} else {
|
|
1782
|
-
|
|
1783
|
-
res.redirect("/useradmin/ssl");
|
|
1757
|
+
res.json({ error: req.__("Not possible for tenant") });
|
|
1784
1758
|
}
|
|
1785
1759
|
})
|
|
1786
1760
|
);
|
|
@@ -1849,7 +1823,7 @@ router.post(
|
|
|
1849
1823
|
"tenant_letsencrypt_sites",
|
|
1850
1824
|
[]
|
|
1851
1825
|
);
|
|
1852
|
-
await getState().setConfig(tenant_letsencrypt_sites, [
|
|
1826
|
+
await getState().setConfig("tenant_letsencrypt_sites", [
|
|
1853
1827
|
...altnames,
|
|
1854
1828
|
...tenant_letsencrypt_sites,
|
|
1855
1829
|
]);
|
|
@@ -1991,9 +1965,9 @@ router.get(
|
|
|
1991
1965
|
});
|
|
1992
1966
|
})
|
|
1993
1967
|
);
|
|
1994
|
-
const buildDialogScript = (
|
|
1968
|
+
const buildDialogScript = (capacitorBuilderAvailable, isSbadmin2) =>
|
|
1995
1969
|
`<script>
|
|
1996
|
-
var
|
|
1970
|
+
var capacitorBuilderAvailable = ${capacitorBuilderAvailable};
|
|
1997
1971
|
var isSbadmin2 = ${isSbadmin2};
|
|
1998
1972
|
function showEntrySelect(type) {
|
|
1999
1973
|
for( const currentType of ["view", "page", "pagegroup"]) {
|
|
@@ -2022,7 +1996,7 @@ const buildDialogScript = (cordovaBuilderAvailable, isSbadmin2) =>
|
|
|
2022
1996
|
|
|
2023
1997
|
const imageAvailable = async () => {
|
|
2024
1998
|
try {
|
|
2025
|
-
const image = new Docker().getImage("saltcorn/
|
|
1999
|
+
const image = new Docker().getImage("saltcorn/capacitor-builder");
|
|
2026
2000
|
await image.inspect();
|
|
2027
2001
|
return true;
|
|
2028
2002
|
} catch (e) {
|
|
@@ -2818,10 +2792,10 @@ router.get(
|
|
|
2818
2792
|
div(
|
|
2819
2793
|
label(
|
|
2820
2794
|
{ class: "form-label fw-bold" },
|
|
2821
|
-
req.__("
|
|
2795
|
+
req.__("Capacitor builder") +
|
|
2822
2796
|
a(
|
|
2823
2797
|
{
|
|
2824
|
-
href: "javascript:ajax_modal('/admin/help/
|
|
2798
|
+
href: "javascript:ajax_modal('/admin/help/Capacitor Builder?')",
|
|
2825
2799
|
},
|
|
2826
2800
|
i({ class: "fas fa-question-circle ps-1" })
|
|
2827
2801
|
)
|
|
@@ -2849,9 +2823,8 @@ router.get(
|
|
|
2849
2823
|
{ class: "col-sm-4" },
|
|
2850
2824
|
button(
|
|
2851
2825
|
{
|
|
2852
|
-
id: "pullCordovaBtnId",
|
|
2853
2826
|
type: "button",
|
|
2854
|
-
onClick: `
|
|
2827
|
+
onClick: `pull_capacitor_builder(this);`,
|
|
2855
2828
|
class: "btn btn-warning",
|
|
2856
2829
|
},
|
|
2857
2830
|
req.__("pull")
|
|
@@ -2859,7 +2832,7 @@ router.get(
|
|
|
2859
2832
|
span(
|
|
2860
2833
|
{
|
|
2861
2834
|
role: "button",
|
|
2862
|
-
onClick: "
|
|
2835
|
+
onClick: "check_capacitor_builder()",
|
|
2863
2836
|
},
|
|
2864
2837
|
span({ class: "ps-3" }, req.__("refresh")),
|
|
2865
2838
|
i({ class: "ps-2 fas fa-undo" })
|
|
@@ -3349,36 +3322,37 @@ router.post(
|
|
|
3349
3322
|
});
|
|
3350
3323
|
const childOutputs = [];
|
|
3351
3324
|
child.stdout.on("data", (data) => {
|
|
3352
|
-
|
|
3353
|
-
|
|
3325
|
+
const outMsg = data.toString();
|
|
3326
|
+
getState().log(5, outMsg);
|
|
3327
|
+
if (data) childOutputs.push(outMsg);
|
|
3354
3328
|
});
|
|
3355
3329
|
child.stderr.on("data", (data) => {
|
|
3356
|
-
|
|
3357
|
-
|
|
3330
|
+
const errMsg = data ? data.toString() : req.__("An error occurred");
|
|
3331
|
+
getState().log(5, errMsg);
|
|
3332
|
+
childOutputs.push(errMsg);
|
|
3358
3333
|
});
|
|
3359
3334
|
child.on("exit", (exitCode, signal) => {
|
|
3360
3335
|
const logFile = exitCode === 0 ? "logs.txt" : "error_logs.txt";
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
3370
|
-
await File.set_xattr_of_existing_file(logFile, buildDir, req.user);
|
|
3371
|
-
}
|
|
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);
|
|
3372
3344
|
}
|
|
3373
|
-
);
|
|
3345
|
+
});
|
|
3374
3346
|
});
|
|
3375
3347
|
child.on("error", (msg) => {
|
|
3376
3348
|
const message = msg.message ? msg.message : msg.code;
|
|
3377
3349
|
const stack = msg.stack ? msg.stack : "";
|
|
3378
3350
|
const logFile = "error_logs.txt";
|
|
3351
|
+
const errMsg = [message, stack].join("\n");
|
|
3352
|
+
getState().log(5, msg);
|
|
3379
3353
|
fs.writeFile(
|
|
3380
3354
|
path.join(buildDir, "error_logs.txt"),
|
|
3381
|
-
|
|
3355
|
+
errMsg,
|
|
3382
3356
|
async (error) => {
|
|
3383
3357
|
if (error) {
|
|
3384
3358
|
console.log(`unable to write logFile to '${buildDir}'`);
|
|
@@ -3394,13 +3368,13 @@ router.post(
|
|
|
3394
3368
|
);
|
|
3395
3369
|
|
|
3396
3370
|
router.post(
|
|
3397
|
-
"/mobile-app/pull-
|
|
3371
|
+
"/mobile-app/pull-capacitor-builder",
|
|
3398
3372
|
isAdmin,
|
|
3399
3373
|
error_catcher(async (req, res) => {
|
|
3400
3374
|
const state = getState();
|
|
3401
3375
|
const child = spawn(
|
|
3402
3376
|
`${process.env.DOCKER_BIN ? `${process.env.DOCKER_BIN}/` : ""}docker`,
|
|
3403
|
-
["pull",
|
|
3377
|
+
["pull", `saltcorn/capacitor-builder:${state.scVersion}`],
|
|
3404
3378
|
{
|
|
3405
3379
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3406
3380
|
cwd: ".",
|
|
@@ -3415,11 +3389,11 @@ router.post(
|
|
|
3415
3389
|
child.on("exit", (exitCode, signal) => {
|
|
3416
3390
|
state.log(
|
|
3417
3391
|
2,
|
|
3418
|
-
`"pull
|
|
3392
|
+
`"pull capacitor-builder exit with code: ${exitCode} and signal: ${signal}`
|
|
3419
3393
|
);
|
|
3420
3394
|
});
|
|
3421
3395
|
child.on("error", (msg) => {
|
|
3422
|
-
state.log(1, `pull
|
|
3396
|
+
state.log(1, `pull capacitor-builder error: ${msg}`);
|
|
3423
3397
|
});
|
|
3424
3398
|
|
|
3425
3399
|
res.json({});
|
|
@@ -3427,7 +3401,7 @@ router.post(
|
|
|
3427
3401
|
);
|
|
3428
3402
|
|
|
3429
3403
|
router.get(
|
|
3430
|
-
"/mobile-app/check-
|
|
3404
|
+
"/mobile-app/check-capacitor-builder",
|
|
3431
3405
|
isAdmin,
|
|
3432
3406
|
error_catcher(async (req, res) => {
|
|
3433
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/eventlog.js
CHANGED
|
@@ -81,6 +81,31 @@ const logSettingsForm = async (req) => {
|
|
|
81
81
|
input_type: "date",
|
|
82
82
|
attributes: { minDate: new Date(), maxDate: hoursFuture(24 * 7 * 2) },
|
|
83
83
|
},
|
|
84
|
+
{
|
|
85
|
+
input_type: "section_header",
|
|
86
|
+
label: req.__("Delete old workflow runs with status after days"),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "delete_finished_workflows_days",
|
|
90
|
+
label: req.__("Finished"),
|
|
91
|
+
type: "Integer",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "delete_error_workflows_days",
|
|
95
|
+
label: req.__("Error"),
|
|
96
|
+
type: "Integer",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "delete_waiting_workflows_days",
|
|
100
|
+
label: req.__("Waiting"),
|
|
101
|
+
type: "Integer",
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
{
|
|
105
|
+
name: "delete_running_workflows_days",
|
|
106
|
+
label: req.__("Running"),
|
|
107
|
+
type: "Integer",
|
|
108
|
+
},
|
|
84
109
|
{
|
|
85
110
|
input_type: "section_header",
|
|
86
111
|
label: req.__("Which events should be logged?"),
|
|
@@ -143,6 +168,10 @@ router.get(
|
|
|
143
168
|
"next_weekly_event",
|
|
144
169
|
{}
|
|
145
170
|
);
|
|
171
|
+
["error", "finished", "running", "waiting"].forEach((k) => {
|
|
172
|
+
let cfgk = `delete_${k}_workflows_days`;
|
|
173
|
+
form.values[cfgk] = getState().getConfig(cfgk);
|
|
174
|
+
});
|
|
146
175
|
|
|
147
176
|
send_events_page({
|
|
148
177
|
res,
|
|
@@ -348,6 +377,13 @@ router.post(
|
|
|
348
377
|
delete form.values[k];
|
|
349
378
|
}
|
|
350
379
|
}
|
|
380
|
+
for (const status of ["error", "finished", "running", "waiting"]) {
|
|
381
|
+
let k = `delete_${status}_workflows_days`;
|
|
382
|
+
if (form.values[k]) {
|
|
383
|
+
await getState().setConfig(k, form.values[k]);
|
|
384
|
+
delete form.values[k];
|
|
385
|
+
}
|
|
386
|
+
}
|
|
351
387
|
|
|
352
388
|
await getState().setConfig("event_log_settings", form.values);
|
|
353
389
|
|
|
@@ -424,6 +460,7 @@ router.get(
|
|
|
424
460
|
error_catcher(async (req, res) => {
|
|
425
461
|
const { id } = req.params;
|
|
426
462
|
const ev = await EventLog.findOneWithUser(id);
|
|
463
|
+
const locale = getState().getConfig("default_locale", "en");
|
|
427
464
|
send_events_page({
|
|
428
465
|
res,
|
|
429
466
|
req,
|
|
@@ -435,7 +472,10 @@ router.get(
|
|
|
435
472
|
table(
|
|
436
473
|
{ class: "table eventlog" },
|
|
437
474
|
tbody(
|
|
438
|
-
tr(
|
|
475
|
+
tr(
|
|
476
|
+
th(req.__("When")),
|
|
477
|
+
td(localeDateTime(ev.occur_at, {}, locale))
|
|
478
|
+
),
|
|
439
479
|
tr(th(req.__("Type")), td(ev.event_type)),
|
|
440
480
|
tr(th(req.__("Channel")), td(ev.channel)),
|
|
441
481
|
tr(th(req.__("User")), td(ev.email))
|
package/routes/fields.js
CHANGED
|
@@ -301,6 +301,10 @@ const fieldFlow = (req) =>
|
|
|
301
301
|
if (context.id) {
|
|
302
302
|
const field = await Field.findOne({ id: context.id });
|
|
303
303
|
try {
|
|
304
|
+
if (fldRow.label && field.label != fldRow.label) {
|
|
305
|
+
fldRow.name = Field.labelToName(fldRow.label);
|
|
306
|
+
}
|
|
307
|
+
|
|
304
308
|
await field.update(fldRow);
|
|
305
309
|
} catch (e) {
|
|
306
310
|
return {
|
|
@@ -362,10 +366,12 @@ const fieldFlow = (req) =>
|
|
|
362
366
|
name: req.__("Attributes"),
|
|
363
367
|
contextField: "attributes",
|
|
364
368
|
onlyWhen: (context) => {
|
|
365
|
-
|
|
369
|
+
const type = getState().types[context.type];
|
|
370
|
+
if (context.calculated && !type?.setTypeAttributesForCalculatedFields)
|
|
371
|
+
return false;
|
|
372
|
+
|
|
366
373
|
if (context.type === "File") return true;
|
|
367
374
|
if (new Field(context).is_fkey) return false;
|
|
368
|
-
const type = getState().types[context.type];
|
|
369
375
|
if (!type) return false;
|
|
370
376
|
const attrs = Field.getTypeAttributes(
|
|
371
377
|
type.attributes,
|
package/routes/homepage.js
CHANGED
|
@@ -549,7 +549,14 @@ const no_views_logged_in = async (req, res) => {
|
|
|
549
549
|
* @returns {Promise<boolean>}
|
|
550
550
|
*/
|
|
551
551
|
const get_config_response = async (role_id, res, req) => {
|
|
552
|
-
const wrap = async (
|
|
552
|
+
const wrap = async (
|
|
553
|
+
contents,
|
|
554
|
+
homeCfg,
|
|
555
|
+
title,
|
|
556
|
+
description,
|
|
557
|
+
no_menu,
|
|
558
|
+
requestFluidLayout
|
|
559
|
+
) => {
|
|
553
560
|
if (contents.html_file) await sendHtmlFile(req, res, contents.html_file);
|
|
554
561
|
else
|
|
555
562
|
res.sendWrap(
|
|
@@ -558,6 +565,7 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
558
565
|
description: description || "",
|
|
559
566
|
bodyClass: "page_" + db.sqlsanitize(homeCfg),
|
|
560
567
|
no_menu,
|
|
568
|
+
requestFluidLayout,
|
|
561
569
|
},
|
|
562
570
|
contents
|
|
563
571
|
);
|
|
@@ -578,7 +586,8 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
578
586
|
homeCfg,
|
|
579
587
|
db_page.title,
|
|
580
588
|
db_page.description,
|
|
581
|
-
db_page.attributes?.no_menu
|
|
589
|
+
db_page.attributes?.no_menu,
|
|
590
|
+
db_page.attributes?.request_fluid_layout
|
|
582
591
|
);
|
|
583
592
|
else {
|
|
584
593
|
const group = PageGroup.findOne({ name: homeCfg });
|
|
@@ -592,7 +601,8 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
592
601
|
homeCfg,
|
|
593
602
|
eligible.title,
|
|
594
603
|
eligible.description,
|
|
595
|
-
eligible.attributes?.no_menu
|
|
604
|
+
eligible.attributes?.no_menu,
|
|
605
|
+
eligible.attributes?.request_fluid_layout
|
|
596
606
|
);
|
|
597
607
|
} else wrap(req.__("%s has no eligible page", group.name), homeCfg);
|
|
598
608
|
} else res.redirect(homeCfg);
|
package/routes/list.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
const Table = require("@saltcorn/data/models/table");
|
|
27
27
|
const { isAdmin, error_catcher } = require("./utils");
|
|
28
28
|
const moment = require("moment");
|
|
29
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* @type {object}
|
|
@@ -267,9 +268,11 @@ router.get(
|
|
|
267
268
|
clipboard: false,
|
|
268
269
|
cellClick: "__delete_tabulator_row",
|
|
269
270
|
});
|
|
271
|
+
const isDark = getState().getLightDarkMode(req.user) === "dark";
|
|
270
272
|
res.sendWrap(
|
|
271
273
|
{
|
|
272
274
|
title: req.__(`%s data table`, table.name),
|
|
275
|
+
requestFluidLayout: true,
|
|
273
276
|
headers: [
|
|
274
277
|
//jsgrid - grid editor external component
|
|
275
278
|
{
|
|
@@ -295,6 +298,13 @@ router.get(
|
|
|
295
298
|
{
|
|
296
299
|
css: `/static_assets/${db.connectObj.version_tag}/flatpickr.min.css`,
|
|
297
300
|
},
|
|
301
|
+
...(isDark
|
|
302
|
+
? [
|
|
303
|
+
{
|
|
304
|
+
css: `/static_assets/${db.connectObj.version_tag}/flatpickr-dark.css`,
|
|
305
|
+
},
|
|
306
|
+
]
|
|
307
|
+
: []),
|
|
298
308
|
],
|
|
299
309
|
},
|
|
300
310
|
{
|
|
@@ -426,7 +436,13 @@ router.get(
|
|
|
426
436
|
),
|
|
427
437
|
div({ id: "jsGridNotify" }),
|
|
428
438
|
|
|
429
|
-
div({
|
|
439
|
+
div({
|
|
440
|
+
id: "jsGrid",
|
|
441
|
+
class:
|
|
442
|
+
getState().getLightDarkMode(req.user) === "dark"
|
|
443
|
+
? "table-dark"
|
|
444
|
+
: undefined,
|
|
445
|
+
})
|
|
430
446
|
),
|
|
431
447
|
},
|
|
432
448
|
],
|