@saltcorn/server 0.7.4-beta.2 → 0.7.4
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 +26 -12
- package/auth/routes.js +41 -27
- package/locales/en.json +30 -4
- package/locales/ru.json +60 -7
- package/markup/admin.js +3 -4
- package/markup/expression_blurb.js +1 -1
- package/package.json +7 -7
- package/public/blockly.js +11 -5
- package/public/gridedit.js +7 -2
- package/public/saltcorn-common.js +106 -43
- package/public/saltcorn.css +16 -10
- package/public/saltcorn.js +60 -19
- package/routes/actions.js +15 -5
- package/routes/admin.js +35 -25
- package/routes/api.js +7 -6
- package/routes/diagram.js +460 -0
- package/routes/fields.js +20 -7
- package/routes/index.js +2 -0
- package/routes/pageedit.js +11 -9
- package/routes/plugins.js +15 -15
- package/routes/tables.js +6 -2
- package/routes/tag_entries.js +2 -2
- package/routes/tags.js +30 -26
- package/routes/utils.js +60 -20
- package/routes/viewedit.js +40 -20
- package/tests/page.test.js +9 -0
- package/tests/viewedit.test.js +16 -0
package/routes/admin.js
CHANGED
|
@@ -390,7 +390,7 @@ router.get(
|
|
|
390
390
|
renderForm(backupForm, req.csrfToken()),
|
|
391
391
|
a(
|
|
392
392
|
{ href: "/admin/auto-backup-list" },
|
|
393
|
-
"Restore/download automated backups »"
|
|
393
|
+
req.__("Restore/download automated backups »")
|
|
394
394
|
),
|
|
395
395
|
script(
|
|
396
396
|
domReady(
|
|
@@ -406,13 +406,13 @@ router.get(
|
|
|
406
406
|
contents: div(
|
|
407
407
|
p(
|
|
408
408
|
i(
|
|
409
|
-
"Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns)."
|
|
409
|
+
req.__("Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).")
|
|
410
410
|
)
|
|
411
411
|
),
|
|
412
412
|
renderForm(aSnapshotForm, req.csrfToken()),
|
|
413
413
|
a(
|
|
414
414
|
{ href: "/admin/snapshot-list" },
|
|
415
|
-
"List/download snapshots »"
|
|
415
|
+
req.__("List/download snapshots »")
|
|
416
416
|
)
|
|
417
417
|
),
|
|
418
418
|
},
|
|
@@ -472,16 +472,16 @@ router.get(
|
|
|
472
472
|
title: req.__("Restoring automated backup"),
|
|
473
473
|
contents: div(
|
|
474
474
|
ol(
|
|
475
|
-
li("Download one of the backups above"),
|
|
475
|
+
li(req.__("Download one of the backups above")),
|
|
476
476
|
li(
|
|
477
|
-
a({ href: "/admin/clear-all" }, "Clear this application"),
|
|
477
|
+
a({ href: "/admin/clear-all" }, req.__("Clear this application")),
|
|
478
478
|
" ",
|
|
479
|
-
"(tick all boxes)"
|
|
479
|
+
req.__("(tick all boxes)")
|
|
480
480
|
),
|
|
481
481
|
li(
|
|
482
|
-
"When prompted to create the first user, click the link to restore a backup"
|
|
482
|
+
req.__("When prompted to create the first user, click the link to restore a backup")
|
|
483
483
|
),
|
|
484
|
-
li("Select the downloaded backup file")
|
|
484
|
+
li(req.__("Select the downloaded backup file"))
|
|
485
485
|
)
|
|
486
486
|
),
|
|
487
487
|
},
|
|
@@ -551,7 +551,7 @@ router.get(
|
|
|
551
551
|
mkTable(
|
|
552
552
|
[
|
|
553
553
|
{
|
|
554
|
-
label: "When",
|
|
554
|
+
label: req.__("When"),
|
|
555
555
|
key: (r) =>
|
|
556
556
|
`${localeDateTime(r.created)} (${moment(r.created).fromNow()})`,
|
|
557
557
|
},
|
|
@@ -618,7 +618,7 @@ const autoBackupForm = (req) =>
|
|
|
618
618
|
noSubmitButton: true,
|
|
619
619
|
additionalButtons: [
|
|
620
620
|
{
|
|
621
|
-
label: "Backup now",
|
|
621
|
+
label: req.__("Backup now"),
|
|
622
622
|
id: "btnBackupNow",
|
|
623
623
|
class: "btn btn-outline-secondary",
|
|
624
624
|
onclick: "ajax_post('/admin/auto-backup-now')",
|
|
@@ -671,7 +671,7 @@ const snapshotForm = (req) =>
|
|
|
671
671
|
noSubmitButton: true,
|
|
672
672
|
additionalButtons: [
|
|
673
673
|
{
|
|
674
|
-
label: "Snapshot now",
|
|
674
|
+
label: req.__("Snapshot now"),
|
|
675
675
|
id: "btnSnapNow",
|
|
676
676
|
class: "btn btn-outline-secondary",
|
|
677
677
|
onclick: "ajax_post('/admin/snapshot-now')",
|
|
@@ -808,7 +808,6 @@ router.get(
|
|
|
808
808
|
" ",
|
|
809
809
|
req.__("Configuration check")
|
|
810
810
|
),
|
|
811
|
-
|
|
812
811
|
hr(),
|
|
813
812
|
|
|
814
813
|
a(
|
|
@@ -945,7 +944,7 @@ router.post(
|
|
|
945
944
|
});
|
|
946
945
|
child.on("exit", function (code, signal) {
|
|
947
946
|
res.end(
|
|
948
|
-
`Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`
|
|
947
|
+
req.__(`Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`)
|
|
949
948
|
);
|
|
950
949
|
setTimeout(() => {
|
|
951
950
|
if (process.send) process.send("RestartServer");
|
|
@@ -981,7 +980,7 @@ router.post(
|
|
|
981
980
|
res.attachment(fileName);
|
|
982
981
|
const file = fs.createReadStream(fileName);
|
|
983
982
|
file.on("end", function () {
|
|
984
|
-
fs.unlink(fileName, function () {
|
|
983
|
+
fs.unlink(fileName, function () {});
|
|
985
984
|
});
|
|
986
985
|
file.pipe(res);
|
|
987
986
|
})
|
|
@@ -1004,7 +1003,7 @@ router.post(
|
|
|
1004
1003
|
);
|
|
1005
1004
|
if (err) req.flash("error", err);
|
|
1006
1005
|
else req.flash("success", req.__("Successfully restored backup"));
|
|
1007
|
-
fs.unlink(newPath, function () {
|
|
1006
|
+
fs.unlink(newPath, function () {});
|
|
1008
1007
|
res.redirect(`/admin`);
|
|
1009
1008
|
})
|
|
1010
1009
|
);
|
|
@@ -1193,18 +1192,23 @@ router.get(
|
|
|
1193
1192
|
})
|
|
1194
1193
|
);
|
|
1195
1194
|
/**
|
|
1196
|
-
* /
|
|
1195
|
+
* /configuration-check
|
|
1197
1196
|
*/
|
|
1198
1197
|
router.get(
|
|
1199
1198
|
"/configuration-check",
|
|
1200
1199
|
isAdmin,
|
|
1201
1200
|
error_catcher(async (req, res) => {
|
|
1202
|
-
const { passes, errors, pass } = await runConfigurationCheck(req);
|
|
1201
|
+
const { passes, errors, pass, warnings } = await runConfigurationCheck(req);
|
|
1203
1202
|
const mkError = (err) =>
|
|
1204
1203
|
div(
|
|
1205
1204
|
{ class: "alert alert-danger", role: "alert" },
|
|
1206
1205
|
pre({ class: "mb-0" }, code(err))
|
|
1207
1206
|
);
|
|
1207
|
+
const mkWarning = (err) =>
|
|
1208
|
+
div(
|
|
1209
|
+
{ class: "alert alert-warning", role: "alert" },
|
|
1210
|
+
pre({ class: "mb-0" }, code(err))
|
|
1211
|
+
);
|
|
1208
1212
|
res.sendWrap(req.__(`Admin`), {
|
|
1209
1213
|
above: [
|
|
1210
1214
|
{
|
|
@@ -1228,7 +1232,8 @@ router.get(
|
|
|
1228
1232
|
req.__("No errors detected during configuration check")
|
|
1229
1233
|
)
|
|
1230
1234
|
)
|
|
1231
|
-
: errors.map(mkError)
|
|
1235
|
+
: errors.map(mkError),
|
|
1236
|
+
(warnings || []).map(mkWarning)
|
|
1232
1237
|
),
|
|
1233
1238
|
},
|
|
1234
1239
|
{
|
|
@@ -1270,11 +1275,10 @@ const buildDialogScript = () => {
|
|
|
1270
1275
|
|
|
1271
1276
|
function handleMessages() {
|
|
1272
1277
|
notifyAlert("This is still under development and might run longer.")
|
|
1273
|
-
${
|
|
1274
|
-
getState().getConfig("apple_team_id") &&
|
|
1278
|
+
${getState().getConfig("apple_team_id") &&
|
|
1275
1279
|
getState().getConfig("apple_team_id") !== "null"
|
|
1276
|
-
|
|
1277
|
-
|
|
1280
|
+
? ""
|
|
1281
|
+
: `
|
|
1278
1282
|
if ($("#iOSCheckboxId")[0].checked) {
|
|
1279
1283
|
notifyAlert(
|
|
1280
1284
|
"No 'Apple Team ID' is configured, I will try to build a project for the iOS simulator."
|
|
@@ -1548,6 +1552,12 @@ router.post(
|
|
|
1548
1552
|
}
|
|
1549
1553
|
if (appFile) spawnParams.push("-a", appFile);
|
|
1550
1554
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1555
|
+
if (
|
|
1556
|
+
db.is_it_multi_tenant() &&
|
|
1557
|
+
db.getTenantSchema() !== db.connectObj.default_schema
|
|
1558
|
+
) {
|
|
1559
|
+
spawnParams.push("--tenantAppName", db.getTenantSchema());
|
|
1560
|
+
}
|
|
1551
1561
|
const child = spawn("saltcorn", spawnParams, {
|
|
1552
1562
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1553
1563
|
cwd: ".",
|
|
@@ -1576,7 +1586,7 @@ router.post(
|
|
|
1576
1586
|
{
|
|
1577
1587
|
type: "card",
|
|
1578
1588
|
title: req.__("Build Result"),
|
|
1579
|
-
contents: div("The build was successfully"),
|
|
1589
|
+
contents: div(req.__("The build was successfully")),
|
|
1580
1590
|
},
|
|
1581
1591
|
files.length > 0 ? app_files_table(files, req) : "",
|
|
1582
1592
|
],
|
|
@@ -1588,7 +1598,7 @@ router.post(
|
|
|
1588
1598
|
type: "card",
|
|
1589
1599
|
title: req.__("Build Result"),
|
|
1590
1600
|
contents: div(
|
|
1591
|
-
"Unable to build the app:",
|
|
1601
|
+
req.__("Unable to build the app:"),
|
|
1592
1602
|
pre(code(childOutputs.join("<br/>")))
|
|
1593
1603
|
),
|
|
1594
1604
|
},
|
|
@@ -1604,7 +1614,7 @@ router.post(
|
|
|
1604
1614
|
type: "card",
|
|
1605
1615
|
title: req.__("Build Result"),
|
|
1606
1616
|
contents: div(
|
|
1607
|
-
p("Unable to build the app:"),
|
|
1617
|
+
p(req.__("Unable to build the app:")),
|
|
1608
1618
|
pre(code(message)),
|
|
1609
1619
|
pre(code(stack))
|
|
1610
1620
|
),
|
package/routes/api.js
CHANGED
|
@@ -71,8 +71,8 @@ function accessAllowedRead(req, user, table) {
|
|
|
71
71
|
req.user && req.user.id
|
|
72
72
|
? req.user.role_id
|
|
73
73
|
: user && user.role_id
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
? user.role_id
|
|
75
|
+
: 10;
|
|
76
76
|
|
|
77
77
|
return role <= table.min_role_read;
|
|
78
78
|
}
|
|
@@ -89,8 +89,8 @@ function accessAllowedWrite(req, user, table) {
|
|
|
89
89
|
req.user && req.user.id
|
|
90
90
|
? req.user.role_id
|
|
91
91
|
: user && user.role_id
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
? user.role_id
|
|
93
|
+
: 10;
|
|
94
94
|
|
|
95
95
|
return role <= table.min_role_write;
|
|
96
96
|
}
|
|
@@ -106,8 +106,8 @@ function accessAllowed(req, user, trigger) {
|
|
|
106
106
|
req.user && req.user.id
|
|
107
107
|
? req.user.role_id
|
|
108
108
|
: user && user.role_id
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
? user.role_id
|
|
110
|
+
: 10;
|
|
111
111
|
|
|
112
112
|
return role <= trigger.min_role;
|
|
113
113
|
}
|
|
@@ -247,6 +247,7 @@ router.get(
|
|
|
247
247
|
fields: tbl_fields,
|
|
248
248
|
approximate: !!approximate,
|
|
249
249
|
state: req_query,
|
|
250
|
+
table
|
|
250
251
|
});
|
|
251
252
|
rows = await table.getRows(qstate);
|
|
252
253
|
} else {
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
const Page = require("@saltcorn/data/models/page");
|
|
2
|
+
const {
|
|
3
|
+
buildObjectTrees,
|
|
4
|
+
} = require("@saltcorn/data/diagram/node_extract_utils");
|
|
5
|
+
const {
|
|
6
|
+
generateCyCode,
|
|
7
|
+
genereateCyCfg,
|
|
8
|
+
} = require("@saltcorn/data/diagram/cy_generate_utils");
|
|
9
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
10
|
+
const {
|
|
11
|
+
a,
|
|
12
|
+
input,
|
|
13
|
+
label,
|
|
14
|
+
button,
|
|
15
|
+
div,
|
|
16
|
+
script,
|
|
17
|
+
i,
|
|
18
|
+
domReady,
|
|
19
|
+
} = require("@saltcorn/markup/tags");
|
|
20
|
+
const { send_infoarch_page } = require("../markup/admin");
|
|
21
|
+
const { isAdmin, error_catcher } = require("./utils.js");
|
|
22
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
23
|
+
const Router = require("express-promise-router");
|
|
24
|
+
|
|
25
|
+
const router = new Router();
|
|
26
|
+
module.exports = router;
|
|
27
|
+
|
|
28
|
+
function reloadCy() {
|
|
29
|
+
$.ajax("/diagram/data", {
|
|
30
|
+
dataType: "json",
|
|
31
|
+
type: "GET",
|
|
32
|
+
headers: { "CSRF-Token": _sc_globalCsrf },
|
|
33
|
+
data: !tagFilterEnabled ? entityFilter : { ...entityFilter, tagFilterIds },
|
|
34
|
+
}).done((res) => {
|
|
35
|
+
const cfg = {
|
|
36
|
+
container: document.getElementById("cy"),
|
|
37
|
+
...res,
|
|
38
|
+
};
|
|
39
|
+
window.cy = cytoscape(cfg);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function toggleEntityFilter(type) {
|
|
44
|
+
switch (type) {
|
|
45
|
+
case "views": {
|
|
46
|
+
entityFilter.showViews = !entityFilter.showViews;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
case "pages": {
|
|
50
|
+
entityFilter.showPages = !entityFilter.showPages;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
case "tables": {
|
|
54
|
+
entityFilter.showTables = !entityFilter.showTables;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
case "trigger": {
|
|
58
|
+
entityFilter.showTrigger = !entityFilter.showTrigger;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function toggleTagFilter(id) {
|
|
65
|
+
if (!tagFilterEnabled) enableTagFilter();
|
|
66
|
+
const index = tagFilterIds.indexOf(id);
|
|
67
|
+
if (index > -1) {
|
|
68
|
+
tagFilterIds.splice(index, 1);
|
|
69
|
+
} else {
|
|
70
|
+
tagFilterIds.push(id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function enableTagFilter() {
|
|
75
|
+
tagFilterEnabled = true;
|
|
76
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_box_"]')) {
|
|
77
|
+
node.style = "";
|
|
78
|
+
}
|
|
79
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_label_"]')) {
|
|
80
|
+
node.style = "";
|
|
81
|
+
}
|
|
82
|
+
const box = document.getElementById("noTagsId");
|
|
83
|
+
box.checked = false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function toggleTagFilterMode() {
|
|
87
|
+
if (tagFilterEnabled) {
|
|
88
|
+
tagFilterEnabled = false;
|
|
89
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_box_"]')) {
|
|
90
|
+
node.style = "opacity: 0.5;";
|
|
91
|
+
}
|
|
92
|
+
for (const node of document.querySelectorAll('[id^="tagFilter_label_"]')) {
|
|
93
|
+
node.style = "opacity: 0.5;";
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
enableTagFilter();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const buildScript = () => {
|
|
101
|
+
return `const entityFilter = {
|
|
102
|
+
showViews: true,
|
|
103
|
+
showPages: true,
|
|
104
|
+
showTables: true,
|
|
105
|
+
showTrigger: true,
|
|
106
|
+
};
|
|
107
|
+
const tagFilterIds = [];
|
|
108
|
+
let tagFilterEnabled = false;
|
|
109
|
+
${reloadCy.toString()}
|
|
110
|
+
${toggleTagFilterMode.toString()}
|
|
111
|
+
${enableTagFilter.toString()}
|
|
112
|
+
${toggleEntityFilter.toString()}
|
|
113
|
+
${toggleTagFilter.toString()}`;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const findEntryPages = async () => {
|
|
117
|
+
const modernCfg = getState().getConfig("home_page_by_role");
|
|
118
|
+
let pages = null;
|
|
119
|
+
if (modernCfg) {
|
|
120
|
+
pages = Object.values(modernCfg)
|
|
121
|
+
.filter((val) => val)
|
|
122
|
+
.map((val) => Page.findOne({ name: val }));
|
|
123
|
+
} else {
|
|
124
|
+
pages = new Array();
|
|
125
|
+
for (const legacyRole of ["public", "user", "staff", "admin"]) {
|
|
126
|
+
const page = await Page.findOne({ name: `${legacyRole}_home` });
|
|
127
|
+
if (page) pages.push(page);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return pages;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const buildFilterIds = async (tags) => {
|
|
134
|
+
if (!tags || tags.length === 0) return null;
|
|
135
|
+
else {
|
|
136
|
+
const viewFilterIds = new Set();
|
|
137
|
+
const pageFilterIds = new Set();
|
|
138
|
+
const tableFilterIds = new Set();
|
|
139
|
+
const triggerFilterIds = new Set();
|
|
140
|
+
for (const tag of tags) {
|
|
141
|
+
for (const id of await tag.getViewIds()) viewFilterIds.add(id);
|
|
142
|
+
for (const id of await tag.getPageIds()) pageFilterIds.add(id);
|
|
143
|
+
for (const id of await tag.getTableIds()) tableFilterIds.add(id);
|
|
144
|
+
for (const id of await tag.getTriggerIds()) triggerFilterIds.add(id);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
viewFilterIds,
|
|
148
|
+
pageFilterIds,
|
|
149
|
+
tableFilterIds,
|
|
150
|
+
triggerFilterIds,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const parseBool = (str) => {
|
|
156
|
+
return str === "true";
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
router.get(
|
|
160
|
+
"/",
|
|
161
|
+
isAdmin,
|
|
162
|
+
error_catcher(async (req, res) => {
|
|
163
|
+
const extractOpts = {
|
|
164
|
+
entryPages: await findEntryPages(),
|
|
165
|
+
showViews: true,
|
|
166
|
+
showPages: true,
|
|
167
|
+
showTables: true,
|
|
168
|
+
showTrigger: true,
|
|
169
|
+
};
|
|
170
|
+
const initialCyCode = generateCyCode(await buildObjectTrees(extractOpts));
|
|
171
|
+
const tags = await Tag.find();
|
|
172
|
+
send_infoarch_page({
|
|
173
|
+
res,
|
|
174
|
+
req,
|
|
175
|
+
active_sub: "Diagram",
|
|
176
|
+
contents: {
|
|
177
|
+
above: [
|
|
178
|
+
{
|
|
179
|
+
type: "card",
|
|
180
|
+
title: req.__(`Application diagram`),
|
|
181
|
+
contents: [
|
|
182
|
+
div(
|
|
183
|
+
{ class: "btn-group" },
|
|
184
|
+
// New dropdown
|
|
185
|
+
button(
|
|
186
|
+
{
|
|
187
|
+
type: "button",
|
|
188
|
+
class: "btn btn-primary m-2 rounded",
|
|
189
|
+
"data-bs-toggle": "dropdown",
|
|
190
|
+
"aria-expanded": false,
|
|
191
|
+
},
|
|
192
|
+
"New",
|
|
193
|
+
i({ class: "fas fa-plus-square ms-2" })
|
|
194
|
+
),
|
|
195
|
+
|
|
196
|
+
div(
|
|
197
|
+
{
|
|
198
|
+
class: "dropdown-menu",
|
|
199
|
+
},
|
|
200
|
+
input({
|
|
201
|
+
type: "hidden",
|
|
202
|
+
name: "_csrf",
|
|
203
|
+
value: req.csrfToken(),
|
|
204
|
+
}),
|
|
205
|
+
// New View
|
|
206
|
+
div(
|
|
207
|
+
{ class: "m-3" },
|
|
208
|
+
|
|
209
|
+
a(
|
|
210
|
+
{
|
|
211
|
+
href: "/viewedit/new?on_done_redirect=diagram",
|
|
212
|
+
},
|
|
213
|
+
req.__("View")
|
|
214
|
+
)
|
|
215
|
+
),
|
|
216
|
+
// New Page
|
|
217
|
+
div(
|
|
218
|
+
{ class: "m-3" },
|
|
219
|
+
a(
|
|
220
|
+
{
|
|
221
|
+
href: "/pageedit/new?on_done_redirect=diagram",
|
|
222
|
+
},
|
|
223
|
+
req.__("Page")
|
|
224
|
+
)
|
|
225
|
+
),
|
|
226
|
+
// New Table
|
|
227
|
+
div(
|
|
228
|
+
{ class: "m-3" },
|
|
229
|
+
a(
|
|
230
|
+
{
|
|
231
|
+
href: "/table/new",
|
|
232
|
+
},
|
|
233
|
+
req.__("Table")
|
|
234
|
+
)
|
|
235
|
+
),
|
|
236
|
+
// New Trigger
|
|
237
|
+
div(
|
|
238
|
+
{ class: "m-3" },
|
|
239
|
+
a(
|
|
240
|
+
{
|
|
241
|
+
href: "/actions/new?on_done_redirect=diagram",
|
|
242
|
+
},
|
|
243
|
+
req.__("Trigger")
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
),
|
|
247
|
+
// Entity type filter dropdown
|
|
248
|
+
button(
|
|
249
|
+
{
|
|
250
|
+
type: "button",
|
|
251
|
+
class: "btn btn-primary m-2 rounded",
|
|
252
|
+
"data-bs-toggle": "dropdown",
|
|
253
|
+
"aria-expanded": false,
|
|
254
|
+
},
|
|
255
|
+
"All entities"
|
|
256
|
+
),
|
|
257
|
+
div(
|
|
258
|
+
{
|
|
259
|
+
class: "dropdown-menu",
|
|
260
|
+
},
|
|
261
|
+
input({
|
|
262
|
+
type: "hidden",
|
|
263
|
+
name: "_csrf",
|
|
264
|
+
value: req.csrfToken(),
|
|
265
|
+
}),
|
|
266
|
+
// Views checkbox
|
|
267
|
+
div(
|
|
268
|
+
{ class: "m-3 form-check" },
|
|
269
|
+
label(
|
|
270
|
+
{ class: "form-check-label", for: "showViewsId" },
|
|
271
|
+
"Views"
|
|
272
|
+
),
|
|
273
|
+
input({
|
|
274
|
+
type: "checkbox",
|
|
275
|
+
class: "form-check-input",
|
|
276
|
+
id: "showViewsId",
|
|
277
|
+
checked: true,
|
|
278
|
+
name: "show_views",
|
|
279
|
+
value: "true",
|
|
280
|
+
onclick: "toggleEntityFilter('views'); reloadCy();",
|
|
281
|
+
autocomplete: "off",
|
|
282
|
+
})
|
|
283
|
+
),
|
|
284
|
+
// Pages checkbox
|
|
285
|
+
div(
|
|
286
|
+
{ class: "m-3 form-check" },
|
|
287
|
+
label(
|
|
288
|
+
{ class: "form-check-label", for: "showPagesId" },
|
|
289
|
+
"Pages"
|
|
290
|
+
),
|
|
291
|
+
input({
|
|
292
|
+
type: "checkbox",
|
|
293
|
+
class: "form-check-input",
|
|
294
|
+
id: "showPagesId",
|
|
295
|
+
name: "show_pages",
|
|
296
|
+
value: "true",
|
|
297
|
+
checked: true,
|
|
298
|
+
onclick: "toggleEntityFilter('pages'); reloadCy();",
|
|
299
|
+
autocomplete: "off",
|
|
300
|
+
})
|
|
301
|
+
),
|
|
302
|
+
// Tables checkbox
|
|
303
|
+
div(
|
|
304
|
+
{ class: "m-3 form-check" },
|
|
305
|
+
label(
|
|
306
|
+
{ class: "form-check-label", for: "showTablesId" },
|
|
307
|
+
"Tables"
|
|
308
|
+
),
|
|
309
|
+
input({
|
|
310
|
+
type: "checkbox",
|
|
311
|
+
class: "form-check-input",
|
|
312
|
+
id: "showTablesId",
|
|
313
|
+
name: "show_tables",
|
|
314
|
+
value: "true",
|
|
315
|
+
checked: true,
|
|
316
|
+
onclick: "toggleEntityFilter('tables'); reloadCy();",
|
|
317
|
+
autocomplete: "off",
|
|
318
|
+
})
|
|
319
|
+
),
|
|
320
|
+
// Trigger checkbox
|
|
321
|
+
div(
|
|
322
|
+
{ class: "m-3 form-check" },
|
|
323
|
+
label(
|
|
324
|
+
{ class: "form-check-label", for: "showTriggerId" },
|
|
325
|
+
"Trigger"
|
|
326
|
+
),
|
|
327
|
+
input({
|
|
328
|
+
type: "checkbox",
|
|
329
|
+
class: "form-check-input",
|
|
330
|
+
id: "showTriggerId",
|
|
331
|
+
name: "show_trigger",
|
|
332
|
+
value: "true",
|
|
333
|
+
checked: true,
|
|
334
|
+
onclick: "toggleEntityFilter('trigger'); reloadCy();",
|
|
335
|
+
autocomplete: "off",
|
|
336
|
+
})
|
|
337
|
+
)
|
|
338
|
+
),
|
|
339
|
+
// Tags filter dropdown
|
|
340
|
+
button(
|
|
341
|
+
{
|
|
342
|
+
type: "button",
|
|
343
|
+
class: "btn btn-primary m-2 rounded",
|
|
344
|
+
"data-bs-toggle": "dropdown",
|
|
345
|
+
"aria-expanded": false,
|
|
346
|
+
},
|
|
347
|
+
"Tags"
|
|
348
|
+
),
|
|
349
|
+
div(
|
|
350
|
+
{
|
|
351
|
+
class: "dropdown-menu",
|
|
352
|
+
},
|
|
353
|
+
input({
|
|
354
|
+
type: "hidden",
|
|
355
|
+
name: "_csrf",
|
|
356
|
+
value: req.csrfToken(),
|
|
357
|
+
}),
|
|
358
|
+
// no tags checkbox
|
|
359
|
+
div(
|
|
360
|
+
{ class: "m-3 form-check" },
|
|
361
|
+
label(
|
|
362
|
+
{ class: "form-check-label", for: "noTagsId" },
|
|
363
|
+
"no tags"
|
|
364
|
+
),
|
|
365
|
+
input({
|
|
366
|
+
type: "checkbox",
|
|
367
|
+
class: "form-check-input",
|
|
368
|
+
id: "noTagsId",
|
|
369
|
+
name: "no_tags",
|
|
370
|
+
value: "true",
|
|
371
|
+
checked: true,
|
|
372
|
+
onclick: "toggleTagFilterMode(); reloadCy();",
|
|
373
|
+
autocomplete: "off",
|
|
374
|
+
})
|
|
375
|
+
),
|
|
376
|
+
tags.map((tag) => {
|
|
377
|
+
const inputId = `tagFilter_box_${tag.name}_id`;
|
|
378
|
+
return div(
|
|
379
|
+
{ class: "m-3 form-check" },
|
|
380
|
+
label(
|
|
381
|
+
{
|
|
382
|
+
class: "form-check-label",
|
|
383
|
+
id: `tagFilter_label_${tag.name}`,
|
|
384
|
+
style: "opacity: 0.5;",
|
|
385
|
+
for: inputId,
|
|
386
|
+
},
|
|
387
|
+
tag.name
|
|
388
|
+
),
|
|
389
|
+
input({
|
|
390
|
+
type: "checkbox",
|
|
391
|
+
class: "form-check-input",
|
|
392
|
+
id: inputId,
|
|
393
|
+
name: "choice",
|
|
394
|
+
value: tag.id,
|
|
395
|
+
checked: false,
|
|
396
|
+
onclick: `toggleTagFilter(${tag.id}); reloadCy();`,
|
|
397
|
+
autocomplete: "off",
|
|
398
|
+
})
|
|
399
|
+
);
|
|
400
|
+
}),
|
|
401
|
+
div(
|
|
402
|
+
{ class: "m-3" },
|
|
403
|
+
a(
|
|
404
|
+
{
|
|
405
|
+
href: "/tag/new",
|
|
406
|
+
},
|
|
407
|
+
req.__("Add tag"),
|
|
408
|
+
i({ class: "fas fa-plus ms-2" })
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
)
|
|
412
|
+
),
|
|
413
|
+
div({ id: "cy" }),
|
|
414
|
+
script(domReady(initialCyCode)),
|
|
415
|
+
script(buildScript()),
|
|
416
|
+
],
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
headers: [
|
|
421
|
+
{
|
|
422
|
+
script:
|
|
423
|
+
"https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.22.1/cytoscape.min.js",
|
|
424
|
+
style: `
|
|
425
|
+
#cy {
|
|
426
|
+
width: 100%;
|
|
427
|
+
height: 900px;
|
|
428
|
+
display: block;
|
|
429
|
+
}`,
|
|
430
|
+
},
|
|
431
|
+
],
|
|
432
|
+
});
|
|
433
|
+
})
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
router.get(
|
|
437
|
+
"/data",
|
|
438
|
+
isAdmin,
|
|
439
|
+
error_catcher(async (req, res) => {
|
|
440
|
+
const { showViews, showPages, showTables, showTrigger } = req.query;
|
|
441
|
+
const tagFilterIds = req.query.tagFilterIds
|
|
442
|
+
? req.query.tagFilterIds.map((id) => parseInt(id))
|
|
443
|
+
: [];
|
|
444
|
+
const tags = (await Tag.find()).filter(
|
|
445
|
+
(tag) => tagFilterIds.indexOf(tag.id) > -1
|
|
446
|
+
);
|
|
447
|
+
let extractOpts = {
|
|
448
|
+
entryPages: await findEntryPages(),
|
|
449
|
+
showViews: parseBool(showViews),
|
|
450
|
+
showPages: parseBool(showPages),
|
|
451
|
+
showTables: parseBool(showTables),
|
|
452
|
+
showTrigger: parseBool(showTrigger),
|
|
453
|
+
};
|
|
454
|
+
const filterIds = await buildFilterIds(tags);
|
|
455
|
+
if (filterIds) {
|
|
456
|
+
extractOpts = { ...extractOpts, ...filterIds };
|
|
457
|
+
}
|
|
458
|
+
res.json(genereateCyCfg(await buildObjectTrees(extractOpts)));
|
|
459
|
+
})
|
|
460
|
+
);
|