@saltcorn/server 0.9.6-beta.0 → 0.9.6-beta.10
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 +54 -53
- package/load_plugins.js +5 -1
- package/locales/en.json +15 -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/saltcorn-common.js +80 -18
- package/public/saltcorn.css +8 -0
- package/public/saltcorn.js +64 -14
- package/routes/actions.js +8 -0
- package/routes/admin.js +51 -51
- package/routes/fields.js +62 -41
- package/routes/index.js +2 -0
- package/routes/pageedit.js +18 -13
- package/routes/plugins.js +6 -1
- package/routes/registry.js +289 -0
- package/routes/tables.js +4 -0
- package/routes/utils.js +5 -2
- package/routes/view.js +1 -1
- package/routes/viewedit.js +11 -7
- package/tests/page.test.js +2 -2
- package/tests/plugins.test.js +2 -0
package/routes/admin.js
CHANGED
|
@@ -1710,11 +1710,14 @@ router.get(
|
|
|
1710
1710
|
above: [
|
|
1711
1711
|
{
|
|
1712
1712
|
type: "card",
|
|
1713
|
+
titleAjaxIndicator: true,
|
|
1713
1714
|
title: req.__("Build mobile app"),
|
|
1714
1715
|
contents: form(
|
|
1715
1716
|
{
|
|
1716
1717
|
action: "/admin/build-mobile-app",
|
|
1717
1718
|
method: "post",
|
|
1719
|
+
onchange: "builderMenuChanged(this)",
|
|
1720
|
+
id: "buildMobileAppForm",
|
|
1718
1721
|
},
|
|
1719
1722
|
|
|
1720
1723
|
fieldset(
|
|
@@ -2178,19 +2181,21 @@ router.get(
|
|
|
2178
2181
|
class: "form-control form-select",
|
|
2179
2182
|
multiple: true,
|
|
2180
2183
|
},
|
|
2181
|
-
withSyncInfo
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
hidden:
|
|
2187
|
-
builderSettings.synchedTables?.indexOf(
|
|
2184
|
+
withSyncInfo
|
|
2185
|
+
.filter(
|
|
2186
|
+
(table) =>
|
|
2187
|
+
!builderSettings.synchedTables ||
|
|
2188
|
+
builderSettings.synchedTables.indexOf(
|
|
2188
2189
|
table.name
|
|
2189
|
-
)
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2190
|
+
) < 0
|
|
2191
|
+
)
|
|
2192
|
+
.map((table) =>
|
|
2193
|
+
option({
|
|
2194
|
+
id: `${table.name}_unsynched_opt`,
|
|
2195
|
+
value: table.name,
|
|
2196
|
+
label: table.name,
|
|
2197
|
+
})
|
|
2198
|
+
)
|
|
2194
2199
|
)
|
|
2195
2200
|
),
|
|
2196
2201
|
div(
|
|
@@ -2228,19 +2233,20 @@ router.get(
|
|
|
2228
2233
|
class: "form-control form-select",
|
|
2229
2234
|
multiple: true,
|
|
2230
2235
|
},
|
|
2231
|
-
withSyncInfo
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
value: table.name,
|
|
2235
|
-
label: table.name,
|
|
2236
|
-
hidden:
|
|
2236
|
+
withSyncInfo
|
|
2237
|
+
.filter(
|
|
2238
|
+
(table) =>
|
|
2237
2239
|
builderSettings.synchedTables?.indexOf(
|
|
2238
2240
|
table.name
|
|
2239
2241
|
) >= 0
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2242
|
+
)
|
|
2243
|
+
.map((table) =>
|
|
2244
|
+
option({
|
|
2245
|
+
id: `${table.name}_synched_opt`,
|
|
2246
|
+
value: table.name,
|
|
2247
|
+
label: table.name,
|
|
2248
|
+
})
|
|
2249
|
+
)
|
|
2244
2250
|
)
|
|
2245
2251
|
)
|
|
2246
2252
|
)
|
|
@@ -2875,13 +2881,6 @@ router.post(
|
|
|
2875
2881
|
) {
|
|
2876
2882
|
spawnParams.push("--tenantAppName", db.getTenantSchema());
|
|
2877
2883
|
}
|
|
2878
|
-
const excludedPlugins = (await Plugin.find())
|
|
2879
|
-
.filter(
|
|
2880
|
-
(plugin) =>
|
|
2881
|
-
["base", "sbadmin2"].indexOf(plugin.name) < 0 &&
|
|
2882
|
-
includedPlugins.indexOf(plugin.name) < 0
|
|
2883
|
-
)
|
|
2884
|
-
.map((plugin) => plugin.name);
|
|
2885
2884
|
|
|
2886
2885
|
if (buildType) spawnParams.push("--buildType", buildType);
|
|
2887
2886
|
if (keystoreFile) spawnParams.push("--androidKeystore", keystoreFile);
|
|
@@ -2889,28 +2888,6 @@ router.post(
|
|
|
2889
2888
|
spawnParams.push("--androidKeyStoreAlias", keystoreAlias);
|
|
2890
2889
|
if (keystorePassword)
|
|
2891
2890
|
spawnParams.push("--androidKeystorePassword", keystorePassword);
|
|
2892
|
-
await getState().setConfig("mobile_builder_settings", {
|
|
2893
|
-
entryPoint,
|
|
2894
|
-
entryPointType,
|
|
2895
|
-
androidPlatform,
|
|
2896
|
-
iOSPlatform,
|
|
2897
|
-
useDocker,
|
|
2898
|
-
appName,
|
|
2899
|
-
appId,
|
|
2900
|
-
appVersion,
|
|
2901
|
-
appIcon,
|
|
2902
|
-
serverURL,
|
|
2903
|
-
splashPage,
|
|
2904
|
-
autoPublicLogin,
|
|
2905
|
-
allowOfflineMode,
|
|
2906
|
-
synchedTables: synchedTables,
|
|
2907
|
-
includedPlugins: includedPlugins,
|
|
2908
|
-
excludedPlugins,
|
|
2909
|
-
provisioningProfile,
|
|
2910
|
-
keystoreFile,
|
|
2911
|
-
keystoreAlias,
|
|
2912
|
-
buildType,
|
|
2913
|
-
});
|
|
2914
2891
|
// end http call, return the out directory name
|
|
2915
2892
|
// the gui polls for results
|
|
2916
2893
|
res.json({ build_dir_name: outDirName });
|
|
@@ -3014,6 +2991,29 @@ router.get(
|
|
|
3014
2991
|
})
|
|
3015
2992
|
);
|
|
3016
2993
|
|
|
2994
|
+
router.post(
|
|
2995
|
+
"/mobile-app/save-config",
|
|
2996
|
+
isAdmin,
|
|
2997
|
+
error_catcher(async (req, res) => {
|
|
2998
|
+
try {
|
|
2999
|
+
const newCfg = { ...req.body };
|
|
3000
|
+
const excludedPlugins = (await Plugin.find())
|
|
3001
|
+
.filter(
|
|
3002
|
+
(plugin) =>
|
|
3003
|
+
["base", "sbadmin2"].indexOf(plugin.name) < 0 &&
|
|
3004
|
+
newCfg.includedPlugins.indexOf(plugin.name) < 0
|
|
3005
|
+
)
|
|
3006
|
+
.map((plugin) => plugin.name);
|
|
3007
|
+
newCfg.excludedPlugins = excludedPlugins;
|
|
3008
|
+
await getState().setConfig("mobile_builder_settings", newCfg);
|
|
3009
|
+
res.json({ success: true });
|
|
3010
|
+
} catch (e) {
|
|
3011
|
+
getState().log(1, `Unable to save mobile builder config: ${e.message}`);
|
|
3012
|
+
res.json({ error: e.message });
|
|
3013
|
+
}
|
|
3014
|
+
})
|
|
3015
|
+
);
|
|
3016
|
+
|
|
3017
3017
|
/**
|
|
3018
3018
|
* Do Clear All
|
|
3019
3019
|
* @function
|
package/routes/fields.js
CHANGED
|
@@ -255,6 +255,7 @@ const fieldFlow = (req) =>
|
|
|
255
255
|
expression = "__aggregation";
|
|
256
256
|
attributes.agg_relation = context.agg_relation;
|
|
257
257
|
attributes.agg_field = context.agg_field;
|
|
258
|
+
attributes.agg_order_by = context.agg_order_by;
|
|
258
259
|
attributes.aggwhere = context.aggwhere;
|
|
259
260
|
attributes.aggregate = context.aggregate;
|
|
260
261
|
const [table, ref] = context.agg_relation.split(".");
|
|
@@ -435,46 +436,64 @@ const fieldFlow = (req) =>
|
|
|
435
436
|
|
|
436
437
|
const { child_field_list, child_relations } =
|
|
437
438
|
await table.get_child_relations(true);
|
|
438
|
-
const agg_field_opts =
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
439
|
+
const agg_field_opts = [];
|
|
440
|
+
const agg_order_opts = [];
|
|
441
|
+
child_relations.forEach(({ table, key_field, through }) => {
|
|
442
|
+
const aggKey =
|
|
443
|
+
(through ? `${through.name}->` : "") +
|
|
444
|
+
`${table.name}.${key_field.name}`;
|
|
445
|
+
aggStatOptions[aggKey] = [
|
|
446
|
+
"Count",
|
|
447
|
+
"CountUnique",
|
|
448
|
+
"Avg",
|
|
449
|
+
"Sum",
|
|
450
|
+
"Max",
|
|
451
|
+
"Min",
|
|
452
|
+
"Array_Agg",
|
|
453
|
+
];
|
|
454
|
+
table.fields.forEach((f) => {
|
|
455
|
+
if (f.type && f.type.name === "Date") {
|
|
456
|
+
aggStatOptions[aggKey].push(`Latest ${f.name}`);
|
|
457
|
+
aggStatOptions[aggKey].push(`Earliest ${f.name}`);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
agg_field_opts.push({
|
|
461
|
+
name: `agg_field`,
|
|
462
|
+
label: req.__("On Field"),
|
|
463
|
+
type: "String",
|
|
464
|
+
required: true,
|
|
465
|
+
attributes: {
|
|
466
|
+
options: table.fields
|
|
467
|
+
.filter((f) => !f.calculated || f.stored)
|
|
468
|
+
.map((f) => ({
|
|
469
|
+
label: f.name,
|
|
470
|
+
name: `${f.name}@${f.type_name}`,
|
|
471
|
+
})),
|
|
472
|
+
},
|
|
473
|
+
showIf: {
|
|
474
|
+
agg_relation: aggKey,
|
|
475
|
+
expression_type: "Aggregation",
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
agg_order_opts.push({
|
|
479
|
+
name: `agg_order_by`,
|
|
480
|
+
label: req.__("Order by"),
|
|
481
|
+
type: "String",
|
|
482
|
+
attributes: {
|
|
483
|
+
options: table.fields
|
|
484
|
+
.filter((f) => !f.calculated || f.stored)
|
|
485
|
+
.map((f) => ({
|
|
486
|
+
label: f.name,
|
|
487
|
+
name: f.name,
|
|
488
|
+
})),
|
|
489
|
+
},
|
|
490
|
+
showIf: {
|
|
491
|
+
agg_relation: aggKey,
|
|
492
|
+
expression_type: "Aggregation",
|
|
493
|
+
aggregate: "Array_Agg",
|
|
494
|
+
},
|
|
495
|
+
});
|
|
496
|
+
});
|
|
478
497
|
return new Form({
|
|
479
498
|
fields: [
|
|
480
499
|
{
|
|
@@ -520,6 +539,7 @@ const fieldFlow = (req) =>
|
|
|
520
539
|
required: false,
|
|
521
540
|
showIf: { expression_type: "Aggregation" },
|
|
522
541
|
},
|
|
542
|
+
...agg_order_opts,
|
|
523
543
|
{
|
|
524
544
|
name: "model",
|
|
525
545
|
label: req.__("Model"),
|
|
@@ -1156,7 +1176,8 @@ router.post(
|
|
|
1156
1176
|
if (!fv) res.send(text(result));
|
|
1157
1177
|
else res.send(fv.run(result, req, { row, ...configuration }));
|
|
1158
1178
|
} catch (e) {
|
|
1159
|
-
|
|
1179
|
+
console.error("show-calculated error", e);
|
|
1180
|
+
return res.status(200).send(``);
|
|
1160
1181
|
}
|
|
1161
1182
|
})
|
|
1162
1183
|
);
|
package/routes/index.js
CHANGED
|
@@ -36,6 +36,7 @@ const roleadmin = require("../auth/roleadmin");
|
|
|
36
36
|
const tags = require("./tags");
|
|
37
37
|
const tagentries = require("./tag_entries");
|
|
38
38
|
const diagram = require("./diagram");
|
|
39
|
+
const registry = require("./registry");
|
|
39
40
|
const sync = require("./sync");
|
|
40
41
|
|
|
41
42
|
module.exports =
|
|
@@ -78,5 +79,6 @@ module.exports =
|
|
|
78
79
|
app.use("/tag", tags);
|
|
79
80
|
app.use("/tag-entries", tagentries);
|
|
80
81
|
app.use("/diagram", diagram);
|
|
82
|
+
app.use("/registry-editor", registry);
|
|
81
83
|
app.use("/sync", sync);
|
|
82
84
|
};
|
package/routes/pageedit.js
CHANGED
|
@@ -94,7 +94,7 @@ const pagePropertiesForm = async (req, isNew) => {
|
|
|
94
94
|
if (groups.includes(s) && isNew)
|
|
95
95
|
return req.__("A page group with this name already exists");
|
|
96
96
|
},
|
|
97
|
-
sublabel: req.__("A short name that will be in
|
|
97
|
+
sublabel: req.__("A short name that will be in the page URL"),
|
|
98
98
|
type: "String",
|
|
99
99
|
attributes: { autofocus: true },
|
|
100
100
|
}),
|
|
@@ -107,13 +107,15 @@ const pagePropertiesForm = async (req, isNew) => {
|
|
|
107
107
|
new Field({
|
|
108
108
|
label: req.__("Description"),
|
|
109
109
|
name: "description",
|
|
110
|
-
sublabel: req.__(
|
|
110
|
+
sublabel: req.__(
|
|
111
|
+
"A longer description that is not visible but appears in the page header and is indexed by search engines"
|
|
112
|
+
),
|
|
111
113
|
input_type: "text",
|
|
112
114
|
}),
|
|
113
115
|
{
|
|
114
116
|
name: "min_role",
|
|
115
117
|
label: req.__("Minimum role"),
|
|
116
|
-
sublabel: req.__("
|
|
118
|
+
sublabel: req.__("User role required to access page"),
|
|
117
119
|
input_type: "select",
|
|
118
120
|
options: roles.map((r) => ({ value: r.id, label: r.role })),
|
|
119
121
|
},
|
|
@@ -336,6 +338,15 @@ router.get(
|
|
|
336
338
|
)
|
|
337
339
|
),
|
|
338
340
|
},
|
|
341
|
+
{
|
|
342
|
+
type: "card",
|
|
343
|
+
title: req.__("Root pages"),
|
|
344
|
+
titleAjaxIndicator: true,
|
|
345
|
+
contents: renderForm(
|
|
346
|
+
getRootPageForm(pages, pageGroups, roles, req),
|
|
347
|
+
req.csrfToken()
|
|
348
|
+
),
|
|
349
|
+
},
|
|
339
350
|
{
|
|
340
351
|
type: "card",
|
|
341
352
|
title: req.__("Your page groups"),
|
|
@@ -357,15 +368,6 @@ router.get(
|
|
|
357
368
|
)
|
|
358
369
|
),
|
|
359
370
|
},
|
|
360
|
-
{
|
|
361
|
-
type: "card",
|
|
362
|
-
title: req.__("Root pages"),
|
|
363
|
-
titleAjaxIndicator: true,
|
|
364
|
-
contents: renderForm(
|
|
365
|
-
getRootPageForm(pages, pageGroups, roles, req),
|
|
366
|
-
req.csrfToken()
|
|
367
|
-
),
|
|
368
|
-
},
|
|
369
371
|
],
|
|
370
372
|
});
|
|
371
373
|
})
|
|
@@ -392,6 +394,7 @@ const wrap = (contents, noCard, req, page) => ({
|
|
|
392
394
|
{
|
|
393
395
|
type: noCard ? "container" : "card",
|
|
394
396
|
title: page ? page.name : req.__("New"),
|
|
397
|
+
titleAjaxIndicator: true,
|
|
395
398
|
contents,
|
|
396
399
|
},
|
|
397
400
|
],
|
|
@@ -418,6 +421,7 @@ router.get(
|
|
|
418
421
|
form.hidden("id");
|
|
419
422
|
form.values = page;
|
|
420
423
|
form.values.no_menu = page.attributes?.no_menu;
|
|
424
|
+
form.onChange = `saveAndContinue(this)`;
|
|
421
425
|
res.sendWrap(
|
|
422
426
|
req.__(`Page attributes`),
|
|
423
427
|
wrap(renderForm(form, req.csrfToken()), false, req, page)
|
|
@@ -477,7 +481,8 @@ router.post(
|
|
|
477
481
|
pageRow.layout = {};
|
|
478
482
|
}
|
|
479
483
|
await Page.update(+id, pageRow);
|
|
480
|
-
res.
|
|
484
|
+
if (req.xhr) res.json({ success: "ok" });
|
|
485
|
+
else res.redirect(`/pageedit/`);
|
|
481
486
|
} else {
|
|
482
487
|
if (!pageRow.layout) pageRow.layout = {};
|
|
483
488
|
if (!pageRow.fixed_states) pageRow.fixed_states = {};
|
package/routes/plugins.js
CHANGED
|
@@ -1315,7 +1315,12 @@ router.get(
|
|
|
1315
1315
|
await upgrade_all_tenants_plugins((p, f) =>
|
|
1316
1316
|
load_plugins.loadPlugin(p, f)
|
|
1317
1317
|
);
|
|
1318
|
-
req.flash(
|
|
1318
|
+
req.flash(
|
|
1319
|
+
"success",
|
|
1320
|
+
req.__(`Modules up-to-date. Please restart server`) +
|
|
1321
|
+
". " +
|
|
1322
|
+
a({ href: "/admin/system" }, req.__("Restart here"))
|
|
1323
|
+
);
|
|
1319
1324
|
} else {
|
|
1320
1325
|
const installed_plugins = await Plugin.find({});
|
|
1321
1326
|
for (const plugin of installed_plugins) {
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
const Router = require("express-promise-router");
|
|
2
|
+
|
|
3
|
+
const db = require("@saltcorn/data/db");
|
|
4
|
+
const { mkTable, link, post_btn, renderForm } = require("@saltcorn/markup");
|
|
5
|
+
const {
|
|
6
|
+
script,
|
|
7
|
+
domReady,
|
|
8
|
+
a,
|
|
9
|
+
div,
|
|
10
|
+
i,
|
|
11
|
+
text,
|
|
12
|
+
button,
|
|
13
|
+
input,
|
|
14
|
+
label,
|
|
15
|
+
form,
|
|
16
|
+
ul,
|
|
17
|
+
li,
|
|
18
|
+
details,
|
|
19
|
+
summary,
|
|
20
|
+
} = require("@saltcorn/markup/tags");
|
|
21
|
+
const Table = require("@saltcorn/data/models/table");
|
|
22
|
+
const { isAdmin, error_catcher } = require("./utils");
|
|
23
|
+
const { send_infoarch_page } = require("../markup/admin.js");
|
|
24
|
+
const View = require("@saltcorn/data/models/view");
|
|
25
|
+
const Page = require("@saltcorn/data/models/page");
|
|
26
|
+
const Form = require("@saltcorn/data/models/form");
|
|
27
|
+
const {
|
|
28
|
+
table_pack,
|
|
29
|
+
view_pack,
|
|
30
|
+
plugin_pack,
|
|
31
|
+
page_pack,
|
|
32
|
+
page_group_pack,
|
|
33
|
+
role_pack,
|
|
34
|
+
library_pack,
|
|
35
|
+
trigger_pack,
|
|
36
|
+
tag_pack,
|
|
37
|
+
model_pack,
|
|
38
|
+
model_instance_pack,
|
|
39
|
+
event_log_pack,
|
|
40
|
+
install_pack,
|
|
41
|
+
} = require("@saltcorn/admin-models/models/pack");
|
|
42
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
43
|
+
/**
|
|
44
|
+
* @type {object}
|
|
45
|
+
* @const
|
|
46
|
+
* @namespace listRouter
|
|
47
|
+
* @category server
|
|
48
|
+
* @subcategory routes
|
|
49
|
+
*/
|
|
50
|
+
const router = new Router();
|
|
51
|
+
|
|
52
|
+
// export our router to be mounted by the parent application
|
|
53
|
+
module.exports = router;
|
|
54
|
+
|
|
55
|
+
async function asyncFilter(arr, cb) {
|
|
56
|
+
const filtered = [];
|
|
57
|
+
|
|
58
|
+
for (const element of arr) {
|
|
59
|
+
const needAdd = await cb(element);
|
|
60
|
+
|
|
61
|
+
if (needAdd) {
|
|
62
|
+
filtered.push(element);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return filtered;
|
|
67
|
+
}
|
|
68
|
+
router.get(
|
|
69
|
+
"/",
|
|
70
|
+
isAdmin,
|
|
71
|
+
error_catcher(async (req, res) => {
|
|
72
|
+
const { etype, ename, q } = req.query;
|
|
73
|
+
const qlink = q ? `&q=${encodeURIComponent(q)}` : "";
|
|
74
|
+
let edContents = "Choose an entity to edit";
|
|
75
|
+
const all_tables = await Table.find({}, { orderBy: "name", nocase: true });
|
|
76
|
+
const all_views = await View.find({}, { orderBy: "name", nocase: true });
|
|
77
|
+
const all_pages = await Page.find({}, { orderBy: "name", nocase: true });
|
|
78
|
+
const all_triggers = await Trigger.find(
|
|
79
|
+
{},
|
|
80
|
+
{ orderBy: "name", nocase: true }
|
|
81
|
+
);
|
|
82
|
+
let tables, views, pages, triggers;
|
|
83
|
+
if (q) {
|
|
84
|
+
const qlower = q.toLowerCase();
|
|
85
|
+
const includesQ = (s) => s.toLowerCase().includes(qlower);
|
|
86
|
+
|
|
87
|
+
tables = await asyncFilter(all_tables, async (t) => {
|
|
88
|
+
const pack = await table_pack(t);
|
|
89
|
+
return includesQ(JSON.stringify(pack));
|
|
90
|
+
});
|
|
91
|
+
views = await asyncFilter(all_views, async (t) => {
|
|
92
|
+
const pack = await view_pack(t);
|
|
93
|
+
return includesQ(JSON.stringify(pack));
|
|
94
|
+
});
|
|
95
|
+
pages = await asyncFilter(all_pages, async (t) => {
|
|
96
|
+
const pack = await page_pack(t);
|
|
97
|
+
return includesQ(JSON.stringify(pack));
|
|
98
|
+
});
|
|
99
|
+
triggers = await asyncFilter(all_triggers, async (t) => {
|
|
100
|
+
const pack = await trigger_pack(t);
|
|
101
|
+
return includesQ(JSON.stringify(pack));
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
tables = all_tables;
|
|
105
|
+
views = all_views;
|
|
106
|
+
pages = all_pages;
|
|
107
|
+
triggers = all_triggers;
|
|
108
|
+
}
|
|
109
|
+
const li_link = (etype1, ename1) =>
|
|
110
|
+
li(
|
|
111
|
+
a(
|
|
112
|
+
{
|
|
113
|
+
href: `/registry-editor?etype=${etype1}&ename=${encodeURIComponent(
|
|
114
|
+
ename1
|
|
115
|
+
)}${qlink}`,
|
|
116
|
+
class: etype1 === etype && ename1 === ename ? "fw-bold" : undefined,
|
|
117
|
+
},
|
|
118
|
+
ename1
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
const mkForm = (jsonVal) =>
|
|
122
|
+
new Form({
|
|
123
|
+
labelCols: 0,
|
|
124
|
+
action: `/registry-editor?etype=${etype}&ename=${encodeURIComponent(
|
|
125
|
+
ename
|
|
126
|
+
)}${qlink}`,
|
|
127
|
+
|
|
128
|
+
values: { regval: JSON.stringify(jsonVal, null, 2) },
|
|
129
|
+
fields: [
|
|
130
|
+
{
|
|
131
|
+
name: "regval",
|
|
132
|
+
label: "",
|
|
133
|
+
input_type: "code",
|
|
134
|
+
attributes: { mode: "application/json" },
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
switch (etype) {
|
|
139
|
+
case "table":
|
|
140
|
+
const tpack = await table_pack(
|
|
141
|
+
all_tables.find((t) => t.name === ename)
|
|
142
|
+
);
|
|
143
|
+
edContents = renderForm(mkForm(tpack), req.csrfToken());
|
|
144
|
+
break;
|
|
145
|
+
case "view":
|
|
146
|
+
const vpack = await view_pack(all_views.find((v) => v.name === ename));
|
|
147
|
+
edContents = renderForm(mkForm(vpack), req.csrfToken());
|
|
148
|
+
break;
|
|
149
|
+
case "page":
|
|
150
|
+
const ppack = await page_pack(all_pages.find((v) => v.name === ename));
|
|
151
|
+
edContents = renderForm(mkForm(ppack), req.csrfToken());
|
|
152
|
+
break;
|
|
153
|
+
case "trigger":
|
|
154
|
+
const trpack = await trigger_pack(
|
|
155
|
+
all_triggers.find((t) => t.name === ename)
|
|
156
|
+
);
|
|
157
|
+
edContents = renderForm(mkForm(trpack), req.csrfToken());
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
send_infoarch_page({
|
|
161
|
+
res,
|
|
162
|
+
req,
|
|
163
|
+
active_sub: "Registry editor",
|
|
164
|
+
contents: {
|
|
165
|
+
widths: [3, 9],
|
|
166
|
+
besides: [
|
|
167
|
+
{
|
|
168
|
+
type: "card",
|
|
169
|
+
bodyClass: "p-1",
|
|
170
|
+
title: "Entities",
|
|
171
|
+
contents: div(
|
|
172
|
+
form(
|
|
173
|
+
{ method: "GET", action: `/registry-editor` },
|
|
174
|
+
div(
|
|
175
|
+
{ class: "input-group search-bar mb-2" },
|
|
176
|
+
etype &&
|
|
177
|
+
input({ type: "hidden", name: "etype", value: etype }),
|
|
178
|
+
ename &&
|
|
179
|
+
input({ type: "hidden", name: "ename", value: ename }),
|
|
180
|
+
input({
|
|
181
|
+
type: "search",
|
|
182
|
+
class: "form-control search-bar ps-2 hasbl",
|
|
183
|
+
placeholder: "Search",
|
|
184
|
+
name: "q",
|
|
185
|
+
value: q,
|
|
186
|
+
"aria-label": "Search",
|
|
187
|
+
"aria-describedby": "button-search-submit",
|
|
188
|
+
}),
|
|
189
|
+
button(
|
|
190
|
+
{
|
|
191
|
+
class: "btn btn-outline-secondary search-bar",
|
|
192
|
+
type: "submit",
|
|
193
|
+
},
|
|
194
|
+
i({ class: "fas fa-search" })
|
|
195
|
+
)
|
|
196
|
+
)
|
|
197
|
+
),
|
|
198
|
+
// following https://iamkate.com/code/tree-views/
|
|
199
|
+
ul(
|
|
200
|
+
{ class: "katetree ps-2" },
|
|
201
|
+
li(
|
|
202
|
+
details(
|
|
203
|
+
{ open: q || etype === "table" },
|
|
204
|
+
summary("Tables"),
|
|
205
|
+
ul(
|
|
206
|
+
{ class: "ps-3" },
|
|
207
|
+
tables.map((t) => li_link("table", t.name))
|
|
208
|
+
)
|
|
209
|
+
)
|
|
210
|
+
),
|
|
211
|
+
li(
|
|
212
|
+
details(
|
|
213
|
+
{ open: q || etype === "view" },
|
|
214
|
+
summary("Views"),
|
|
215
|
+
ul(
|
|
216
|
+
{ class: "ps-3" },
|
|
217
|
+
views.map((v) => li_link("view", v.name))
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
),
|
|
221
|
+
li(
|
|
222
|
+
details(
|
|
223
|
+
{ open: q || etype === "page" }, //
|
|
224
|
+
summary("Pages"),
|
|
225
|
+
ul(
|
|
226
|
+
{ class: "ps-3" },
|
|
227
|
+
pages.map((p) => li_link("page", p.name))
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
),
|
|
231
|
+
li(
|
|
232
|
+
details(
|
|
233
|
+
{ open: q || etype === "trigger" }, //
|
|
234
|
+
summary("Triggers"),
|
|
235
|
+
ul(
|
|
236
|
+
{ class: "ps-3" },
|
|
237
|
+
triggers.map((t) => li_link("trigger", t.name))
|
|
238
|
+
)
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
)
|
|
242
|
+
),
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
type: "card",
|
|
246
|
+
title:
|
|
247
|
+
ename && etype
|
|
248
|
+
? `Registry editor: ${ename} ${etype}`
|
|
249
|
+
: "Registry editor",
|
|
250
|
+
contents: edContents,
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
});
|
|
255
|
+
})
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
router.post(
|
|
259
|
+
"/",
|
|
260
|
+
isAdmin,
|
|
261
|
+
error_catcher(async (req, res) => {
|
|
262
|
+
const { etype, ename, q } = req.query;
|
|
263
|
+
const qlink = q ? `&q=${encodeURIComponent(q)}` : "";
|
|
264
|
+
|
|
265
|
+
const entVal = JSON.parse(req.body.regval);
|
|
266
|
+
let pack = { plugins: [], tables: [], views: [], pages: [], triggers: [] };
|
|
267
|
+
|
|
268
|
+
switch (etype) {
|
|
269
|
+
case "table":
|
|
270
|
+
pack.tables = [entVal];
|
|
271
|
+
break;
|
|
272
|
+
case "view":
|
|
273
|
+
pack.views = [entVal];
|
|
274
|
+
break;
|
|
275
|
+
case "page":
|
|
276
|
+
pack.pages = [entVal];
|
|
277
|
+
break;
|
|
278
|
+
case "trigger":
|
|
279
|
+
pack.triggers = [entVal];
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
await install_pack(pack);
|
|
283
|
+
res.redirect(
|
|
284
|
+
`/registry-editor?etype=${etype}&ename=${encodeURIComponent(
|
|
285
|
+
ename
|
|
286
|
+
)}${qlink}`
|
|
287
|
+
);
|
|
288
|
+
})
|
|
289
|
+
);
|