@saltcorn/server 0.7.4-beta.3 → 0.8.0-beta.0
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 +43 -22
- package/auth/admin.js +173 -74
- package/auth/routes.js +67 -28
- package/locales/en.json +54 -2
- package/locales/es.json +134 -134
- package/locales/ru.json +32 -5
- package/markup/admin.js +40 -38
- package/markup/forms.js +4 -3
- package/package.json +8 -7
- package/public/diagram_utils.js +530 -0
- package/public/gridedit.js +4 -1
- package/public/jquery-menu-editor.min.js +112 -112
- package/public/saltcorn-common.js +114 -26
- package/public/saltcorn.css +27 -10
- package/public/saltcorn.js +223 -76
- package/restart_watcher.js +1 -0
- package/routes/actions.js +20 -6
- package/routes/admin.js +243 -82
- package/routes/api.js +19 -2
- package/routes/common_lists.js +137 -134
- package/routes/diagram.js +362 -35
- package/routes/fields.js +4 -1
- package/routes/files.js +137 -101
- package/routes/homepage.js +2 -2
- package/routes/infoarch.js +2 -2
- package/routes/list.js +4 -4
- package/routes/page.js +16 -3
- package/routes/pageedit.js +22 -14
- package/routes/scapi.js +1 -1
- package/routes/search.js +1 -1
- package/routes/tables.js +4 -5
- package/routes/tag_entries.js +31 -10
- package/routes/tags.js +36 -32
- package/routes/tenant.js +98 -36
- package/routes/utils.js +72 -20
- package/routes/view.js +0 -1
- package/routes/viewedit.js +55 -22
- package/serve.js +5 -0
- package/tests/admin.test.js +2 -0
- package/tests/auth.test.js +20 -0
- package/tests/files.test.js +11 -20
- package/tests/tenant.test.js +4 -2
package/routes/diagram.js
CHANGED
|
@@ -2,58 +2,385 @@ const Page = require("@saltcorn/data/models/page");
|
|
|
2
2
|
const {
|
|
3
3
|
buildObjectTrees,
|
|
4
4
|
} = require("@saltcorn/data/diagram/node_extract_utils");
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
generateCyCode,
|
|
7
|
+
genereateCyCfg,
|
|
8
|
+
} = require("@saltcorn/data/diagram/cy_generate_utils");
|
|
6
9
|
const { getState } = require("@saltcorn/data/db/state");
|
|
7
|
-
const {
|
|
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");
|
|
8
21
|
const { isAdmin, error_catcher } = require("./utils.js");
|
|
22
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
9
23
|
const Router = require("express-promise-router");
|
|
24
|
+
const User = require("@saltcorn/data/models/user");
|
|
10
25
|
|
|
11
26
|
const router = new Router();
|
|
12
27
|
module.exports = router;
|
|
13
28
|
|
|
29
|
+
const buildGlobalVars = (tags, roles) => {
|
|
30
|
+
return `
|
|
31
|
+
const allTags = ${JSON.stringify(tags)};
|
|
32
|
+
const roles = ${JSON.stringify(roles)};
|
|
33
|
+
`;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const findEntryPages = async () => {
|
|
37
|
+
const modernCfg = getState().getConfig("home_page_by_role");
|
|
38
|
+
let pages;
|
|
39
|
+
if (modernCfg) {
|
|
40
|
+
pages = Object.values(modernCfg)
|
|
41
|
+
.filter((val) => val)
|
|
42
|
+
.map((val) => Page.findOne({ name: val }));
|
|
43
|
+
} else {
|
|
44
|
+
pages = [];
|
|
45
|
+
for (const legacyRole of ["public", "user", "staff", "admin"]) {
|
|
46
|
+
const page = await Page.findOne({ name: `${legacyRole}_home` });
|
|
47
|
+
if (page) pages.push(page);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return pages;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const buildFilterIds = async (tags) => {
|
|
54
|
+
if (!tags || tags.length === 0) return null;
|
|
55
|
+
else {
|
|
56
|
+
const viewFilterIds = new Set();
|
|
57
|
+
const pageFilterIds = new Set();
|
|
58
|
+
const tableFilterIds = new Set();
|
|
59
|
+
const triggerFilterIds = new Set();
|
|
60
|
+
for (const tag of tags) {
|
|
61
|
+
for (const id of await tag.getViewIds()) viewFilterIds.add(id);
|
|
62
|
+
for (const id of await tag.getPageIds()) pageFilterIds.add(id);
|
|
63
|
+
for (const id of await tag.getTableIds()) tableFilterIds.add(id);
|
|
64
|
+
for (const id of await tag.getTriggerIds()) triggerFilterIds.add(id);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
viewFilterIds,
|
|
68
|
+
pageFilterIds,
|
|
69
|
+
tableFilterIds,
|
|
70
|
+
triggerFilterIds,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const parseBool = (str) => {
|
|
76
|
+
return str === "true";
|
|
77
|
+
};
|
|
78
|
+
|
|
14
79
|
router.get(
|
|
15
80
|
"/",
|
|
16
81
|
isAdmin,
|
|
17
82
|
error_catcher(async (req, res) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
{
|
|
34
|
-
title: req.__(`Application diagram`),
|
|
35
|
-
headers: [
|
|
36
|
-
{
|
|
37
|
-
script:
|
|
38
|
-
"https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.22.1/cytoscape.min.js",
|
|
39
|
-
style: `
|
|
40
|
-
#cy {
|
|
41
|
-
width: 100%;
|
|
42
|
-
height: 900px;
|
|
43
|
-
display: block;
|
|
44
|
-
}`,
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
{
|
|
83
|
+
const extractOpts = {
|
|
84
|
+
entryPages: await findEntryPages(),
|
|
85
|
+
showViews: true,
|
|
86
|
+
showPages: true,
|
|
87
|
+
showTables: true,
|
|
88
|
+
showTrigger: true,
|
|
89
|
+
};
|
|
90
|
+
const initialCyCode = generateCyCode(await buildObjectTrees(extractOpts));
|
|
91
|
+
const tags = await Tag.find();
|
|
92
|
+
const roles = await User.get_roles();
|
|
93
|
+
send_infoarch_page({
|
|
94
|
+
res,
|
|
95
|
+
req,
|
|
96
|
+
active_sub: "Diagram",
|
|
97
|
+
contents: {
|
|
49
98
|
above: [
|
|
50
99
|
{
|
|
51
100
|
type: "card",
|
|
52
101
|
title: req.__(`Application diagram`),
|
|
53
|
-
contents: [
|
|
102
|
+
contents: [
|
|
103
|
+
div(
|
|
104
|
+
{ class: "btn-group" },
|
|
105
|
+
// New dropdown
|
|
106
|
+
button(
|
|
107
|
+
{
|
|
108
|
+
type: "button",
|
|
109
|
+
class: "btn btn-primary m-2 rounded",
|
|
110
|
+
"data-bs-toggle": "dropdown",
|
|
111
|
+
"aria-expanded": false,
|
|
112
|
+
},
|
|
113
|
+
"New",
|
|
114
|
+
i({ class: "fas fa-plus-square ms-2" })
|
|
115
|
+
),
|
|
116
|
+
|
|
117
|
+
div(
|
|
118
|
+
{
|
|
119
|
+
class: "dropdown-menu",
|
|
120
|
+
},
|
|
121
|
+
// New View
|
|
122
|
+
div(
|
|
123
|
+
{ class: "m-3" },
|
|
124
|
+
|
|
125
|
+
a(
|
|
126
|
+
{
|
|
127
|
+
href: "/viewedit/new?on_done_redirect=diagram",
|
|
128
|
+
},
|
|
129
|
+
req.__("View")
|
|
130
|
+
)
|
|
131
|
+
),
|
|
132
|
+
// New Page
|
|
133
|
+
div(
|
|
134
|
+
{ class: "m-3" },
|
|
135
|
+
a(
|
|
136
|
+
{
|
|
137
|
+
href: "/pageedit/new?on_done_redirect=diagram",
|
|
138
|
+
},
|
|
139
|
+
req.__("Page")
|
|
140
|
+
)
|
|
141
|
+
),
|
|
142
|
+
// New Table
|
|
143
|
+
div(
|
|
144
|
+
{ class: "m-3" },
|
|
145
|
+
a(
|
|
146
|
+
{
|
|
147
|
+
href: "/table/new",
|
|
148
|
+
},
|
|
149
|
+
req.__("Table")
|
|
150
|
+
)
|
|
151
|
+
),
|
|
152
|
+
// New Trigger
|
|
153
|
+
div(
|
|
154
|
+
{ class: "m-3" },
|
|
155
|
+
a(
|
|
156
|
+
{
|
|
157
|
+
href: "/actions/new?on_done_redirect=diagram",
|
|
158
|
+
},
|
|
159
|
+
req.__("Trigger")
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
),
|
|
163
|
+
// Entity type filter dropdown
|
|
164
|
+
button(
|
|
165
|
+
{
|
|
166
|
+
type: "button",
|
|
167
|
+
class: "btn btn-primary m-2 rounded",
|
|
168
|
+
"data-bs-toggle": "dropdown",
|
|
169
|
+
"aria-expanded": false,
|
|
170
|
+
},
|
|
171
|
+
req.__("All entities")
|
|
172
|
+
),
|
|
173
|
+
div(
|
|
174
|
+
{
|
|
175
|
+
class: "dropdown-menu",
|
|
176
|
+
},
|
|
177
|
+
// Views checkbox
|
|
178
|
+
div(
|
|
179
|
+
{ class: "m-3 form-check" },
|
|
180
|
+
label(
|
|
181
|
+
{ class: "form-check-label", for: "showViewsId" },
|
|
182
|
+
req.__("Views")
|
|
183
|
+
),
|
|
184
|
+
input({
|
|
185
|
+
type: "checkbox",
|
|
186
|
+
class: "form-check-input",
|
|
187
|
+
id: "showViewsId",
|
|
188
|
+
checked: true,
|
|
189
|
+
name: "show_views",
|
|
190
|
+
value: "true",
|
|
191
|
+
onclick: "toggleEntityFilter('views'); reloadCy();",
|
|
192
|
+
autocomplete: "off",
|
|
193
|
+
})
|
|
194
|
+
),
|
|
195
|
+
// Pages checkbox
|
|
196
|
+
div(
|
|
197
|
+
{ class: "m-3 form-check" },
|
|
198
|
+
label(
|
|
199
|
+
{ class: "form-check-label", for: "showPagesId" },
|
|
200
|
+
req.__("Pages")
|
|
201
|
+
),
|
|
202
|
+
input({
|
|
203
|
+
type: "checkbox",
|
|
204
|
+
class: "form-check-input",
|
|
205
|
+
id: "showPagesId",
|
|
206
|
+
name: "show_pages",
|
|
207
|
+
value: "true",
|
|
208
|
+
checked: true,
|
|
209
|
+
onclick: "toggleEntityFilter('pages'); reloadCy();",
|
|
210
|
+
autocomplete: "off",
|
|
211
|
+
})
|
|
212
|
+
),
|
|
213
|
+
// Tables checkbox
|
|
214
|
+
div(
|
|
215
|
+
{ class: "m-3 form-check" },
|
|
216
|
+
label(
|
|
217
|
+
{ class: "form-check-label", for: "showTablesId" },
|
|
218
|
+
req.__("Tables")
|
|
219
|
+
),
|
|
220
|
+
input({
|
|
221
|
+
type: "checkbox",
|
|
222
|
+
class: "form-check-input",
|
|
223
|
+
id: "showTablesId",
|
|
224
|
+
name: "show_tables",
|
|
225
|
+
value: "true",
|
|
226
|
+
checked: true,
|
|
227
|
+
onclick: "toggleEntityFilter('tables'); reloadCy();",
|
|
228
|
+
autocomplete: "off",
|
|
229
|
+
})
|
|
230
|
+
),
|
|
231
|
+
// Trigger checkbox
|
|
232
|
+
div(
|
|
233
|
+
{ class: "m-3 form-check" },
|
|
234
|
+
label(
|
|
235
|
+
{ class: "form-check-label", for: "showTriggerId" },
|
|
236
|
+
req.__("Triggers")
|
|
237
|
+
),
|
|
238
|
+
input({
|
|
239
|
+
type: "checkbox",
|
|
240
|
+
class: "form-check-input",
|
|
241
|
+
id: "showTriggerId",
|
|
242
|
+
name: "show_trigger",
|
|
243
|
+
value: "true",
|
|
244
|
+
checked: true,
|
|
245
|
+
onclick: "toggleEntityFilter('trigger'); reloadCy();",
|
|
246
|
+
autocomplete: "off",
|
|
247
|
+
})
|
|
248
|
+
)
|
|
249
|
+
),
|
|
250
|
+
// Tags filter dropdown
|
|
251
|
+
button(
|
|
252
|
+
{
|
|
253
|
+
type: "button",
|
|
254
|
+
class: "btn btn-primary m-2 rounded",
|
|
255
|
+
"data-bs-toggle": "dropdown",
|
|
256
|
+
"aria-expanded": false,
|
|
257
|
+
},
|
|
258
|
+
req.__("Tags")
|
|
259
|
+
),
|
|
260
|
+
div(
|
|
261
|
+
{
|
|
262
|
+
class: "dropdown-menu",
|
|
263
|
+
},
|
|
264
|
+
// no tags checkbox
|
|
265
|
+
div(
|
|
266
|
+
{ class: "m-3 form-check" },
|
|
267
|
+
label(
|
|
268
|
+
{ class: "form-check-label", for: "noTagsId" },
|
|
269
|
+
req.__("no tags")
|
|
270
|
+
),
|
|
271
|
+
input({
|
|
272
|
+
type: "checkbox",
|
|
273
|
+
class: "form-check-input",
|
|
274
|
+
id: "noTagsId",
|
|
275
|
+
name: "no_tags",
|
|
276
|
+
value: "true",
|
|
277
|
+
checked: true,
|
|
278
|
+
onclick: "toggleTagFilterMode(); reloadCy();",
|
|
279
|
+
autocomplete: "off",
|
|
280
|
+
})
|
|
281
|
+
),
|
|
282
|
+
tags.map((tag) => {
|
|
283
|
+
const inputId = `tagFilter_box_${tag.name}_id`;
|
|
284
|
+
return div(
|
|
285
|
+
{ class: "m-3 form-check" },
|
|
286
|
+
label(
|
|
287
|
+
{
|
|
288
|
+
class: "form-check-label",
|
|
289
|
+
id: `tagFilter_label_${tag.name}`,
|
|
290
|
+
style: "opacity: 0.5;",
|
|
291
|
+
for: inputId,
|
|
292
|
+
},
|
|
293
|
+
tag.name
|
|
294
|
+
),
|
|
295
|
+
input({
|
|
296
|
+
type: "checkbox",
|
|
297
|
+
class: "form-check-input",
|
|
298
|
+
id: inputId,
|
|
299
|
+
name: "choice",
|
|
300
|
+
value: tag.id,
|
|
301
|
+
checked: false,
|
|
302
|
+
onclick: `toggleTagFilter(${tag.id}); reloadCy();`,
|
|
303
|
+
autocomplete: "off",
|
|
304
|
+
})
|
|
305
|
+
);
|
|
306
|
+
}),
|
|
307
|
+
div(
|
|
308
|
+
{ class: "m-3" },
|
|
309
|
+
a(
|
|
310
|
+
{
|
|
311
|
+
href: "/tag/new",
|
|
312
|
+
},
|
|
313
|
+
req.__("Add tag"),
|
|
314
|
+
i({ class: "fas fa-plus ms-2" })
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
),
|
|
318
|
+
// refresh button
|
|
319
|
+
button(
|
|
320
|
+
{
|
|
321
|
+
type: "button",
|
|
322
|
+
class: "btn btn-primary m-2 rounded",
|
|
323
|
+
onclick: "reloadCy(true);",
|
|
324
|
+
},
|
|
325
|
+
i({ class: "fas fa-sync-alt" })
|
|
326
|
+
)
|
|
327
|
+
),
|
|
328
|
+
div({ id: "cy" }),
|
|
329
|
+
script(buildGlobalVars(tags, roles)),
|
|
330
|
+
script({ src: "/diagram_utils.js" }),
|
|
331
|
+
script(domReady(initialCyCode)),
|
|
332
|
+
],
|
|
54
333
|
},
|
|
55
334
|
],
|
|
56
|
-
}
|
|
335
|
+
},
|
|
336
|
+
headers: [
|
|
337
|
+
{
|
|
338
|
+
style: `
|
|
339
|
+
#cy {
|
|
340
|
+
width: 100%;
|
|
341
|
+
height: 900px;
|
|
342
|
+
display: block;
|
|
343
|
+
}`,
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
script:
|
|
347
|
+
"https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.9.2/umd/popper.min.js",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
script:
|
|
351
|
+
"https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.22.1/cytoscape.min.js",
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
script:
|
|
355
|
+
"https://cdnjs.cloudflare.com/ajax/libs/cytoscape-popper/2.0.0/cytoscape-popper.min.js",
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
});
|
|
359
|
+
})
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
router.get(
|
|
363
|
+
"/data",
|
|
364
|
+
isAdmin,
|
|
365
|
+
error_catcher(async (req, res) => {
|
|
366
|
+
const { showViews, showPages, showTables, showTrigger } = req.query;
|
|
367
|
+
const tagFilterIds = req.query.tagFilterIds
|
|
368
|
+
? req.query.tagFilterIds.map((id) => parseInt(id))
|
|
369
|
+
: [];
|
|
370
|
+
const tags = (await Tag.find()).filter(
|
|
371
|
+
(tag) => tagFilterIds.indexOf(tag.id) > -1
|
|
57
372
|
);
|
|
373
|
+
let extractOpts = {
|
|
374
|
+
entryPages: await findEntryPages(),
|
|
375
|
+
showViews: parseBool(showViews),
|
|
376
|
+
showPages: parseBool(showPages),
|
|
377
|
+
showTables: parseBool(showTables),
|
|
378
|
+
showTrigger: parseBool(showTrigger),
|
|
379
|
+
};
|
|
380
|
+
const filterIds = await buildFilterIds(tags);
|
|
381
|
+
if (filterIds) {
|
|
382
|
+
extractOpts = { ...extractOpts, ...filterIds };
|
|
383
|
+
}
|
|
384
|
+
res.json(genereateCyCfg(await buildObjectTrees(extractOpts)));
|
|
58
385
|
})
|
|
59
386
|
);
|
package/routes/fields.js
CHANGED
|
@@ -627,7 +627,10 @@ router.post(
|
|
|
627
627
|
if (stored)
|
|
628
628
|
add_free_variables_to_joinfields(freeVars, joinFields, fields)
|
|
629
629
|
const rows = await table.getJoinedRows({ joinFields, orderBy: "RANDOM()", limit: 1 });
|
|
630
|
-
if (rows.length < 1)
|
|
630
|
+
if (rows.length < 1) {
|
|
631
|
+
res.send("No rows in table");
|
|
632
|
+
return
|
|
633
|
+
}
|
|
631
634
|
let result;
|
|
632
635
|
try {
|
|
633
636
|
if (stored) {
|