@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/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 { generateCyCode } = require("@saltcorn/data/diagram/cy_generate_utils");
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 { div, script, domReady } = require("@saltcorn/markup/tags");
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 modernCfg = getState().getConfig("home_page_by_role");
19
- let pages = null;
20
- if (modernCfg) {
21
- pages = Object.values(modernCfg)
22
- .filter((val) => val)
23
- .map((val) => Page.findOne({ name: val }));
24
- } else {
25
- pages = new Array();
26
- for (const legacyRole of ["public", "user", "staff", "admin"]) {
27
- const page = await Page.findOne({ name: `${legacyRole}_home` });
28
- if (page) pages.push(page);
29
- }
30
- }
31
- const cyCode = generateCyCode(await buildObjectTrees(pages));
32
- res.sendWrap(
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: [div({ id: "cy" }), script(domReady(cyCode))],
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) return "No rows in table";
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) {