@saltcorn/server 0.7.4-beta.1 → 0.7.4-beta.2

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.
@@ -0,0 +1,419 @@
1
+ const User = require("@saltcorn/data/models/user");
2
+ const Table = require("@saltcorn/data/models/table");
3
+ const { editRoleForm } = require("../markup/forms.js");
4
+ const {
5
+ mkTable,
6
+ link,
7
+ post_delete_btn,
8
+ settingsDropdown,
9
+ post_dropdown_item,
10
+ } = require("@saltcorn/markup");
11
+
12
+ const { h4, p, div, a, input, text } = require("@saltcorn/markup/tags");
13
+
14
+ /**
15
+ * @param {string} col
16
+ * @param {string} lbl
17
+ * @returns {string}
18
+ */
19
+ const badge = (col, lbl) => `<span class="badge bg-${col}">${lbl}</span>&nbsp;`;
20
+
21
+ /**
22
+ * Table badges to show in System Table list views
23
+ * Currently supports:
24
+ * - Owned - if ownership_field_id? What is it?
25
+ * - History - if table has versioning
26
+ * - External - if this is external table
27
+ * @param {object} t table object
28
+ * @param {object} req http request
29
+ * @returns {string} html string with list of badges
30
+ */
31
+ const tableBadges = (t, req) => {
32
+ let s = "";
33
+ if (t.ownership_field_id) s += badge("primary", req.__("Owned"));
34
+ if (t.versioned) s += badge("success", req.__("History"));
35
+ if (t.external) s += badge("info", req.__("External"));
36
+ return s;
37
+ };
38
+
39
+ const valIfSet = (check, value) => (check ? value : "");
40
+
41
+ const listClass = (tagId, showList) =>
42
+ valIfSet(tagId, `collapse ${valIfSet(showList, "show")}`);
43
+
44
+ const tablesList = async (tables, req, { tagId, domId, showList } = {}) => {
45
+ const roles = await User.get_roles();
46
+ const getRole = (rid) => roles.find((r) => r.id === rid).role;
47
+ return tables.length > 0
48
+ ? mkTable(
49
+ [
50
+ {
51
+ label: req.__("Name"),
52
+ key: (r) => link(`/table/${r.id || r.name}`, text(r.name)),
53
+ },
54
+ {
55
+ label: "",
56
+ key: (r) => tableBadges(r, req),
57
+ },
58
+ {
59
+ label: req.__("Access Read/Write"),
60
+ key: (t) =>
61
+ t.external
62
+ ? `${getRole(t.min_role_read)} (read only)`
63
+ : `${getRole(t.min_role_read)}/${getRole(t.min_role_write)}`,
64
+ },
65
+ !tagId
66
+ ? {
67
+ label: req.__("Delete"),
68
+ key: (r) =>
69
+ r.name === "users" || r.external
70
+ ? ""
71
+ : post_delete_btn(`/table/delete/${r.id}`, req, r.name),
72
+ }
73
+ : {
74
+ label: req.__("Remove From Tag"),
75
+ key: (r) =>
76
+ post_delete_btn(
77
+ `/tag-entries/remove/tables/${r.id}/${tagId}`,
78
+ req,
79
+ `${r.name} from this tag`
80
+ ),
81
+ },
82
+ ],
83
+ tables,
84
+ {
85
+ hover: true,
86
+ tableClass: listClass(tagId, showList),
87
+ tableId: domId,
88
+ }
89
+ )
90
+ : div(
91
+ { class: listClass(tagId, showList), id: domId },
92
+ h4(req.__("No tables defined")),
93
+ p(req.__("Tables hold collections of similar data"))
94
+ );
95
+ };
96
+
97
+ /**
98
+ * @param {object} view
99
+ * @param {object[]} roles
100
+ * @param {object} req
101
+ * @returns {Form}
102
+ */
103
+ const editViewRoleForm = (view, roles, req) =>
104
+ editRoleForm({
105
+ url: `/viewedit/setrole/${view.id}`,
106
+ current_role: view.min_role,
107
+ roles,
108
+ req,
109
+ });
110
+
111
+ /**
112
+ * @param {object} view
113
+ * @param {object} req
114
+ * @returns {div}
115
+ */
116
+ const view_dropdown = (view, req) =>
117
+ settingsDropdown(`dropdownMenuButton${view.id}`, [
118
+ a(
119
+ {
120
+ class: "dropdown-item",
121
+ href: `/view/${encodeURIComponent(view.name)}`,
122
+ },
123
+ '<i class="fas fa-running"></i>&nbsp;' + req.__("Run")
124
+ ),
125
+ a(
126
+ {
127
+ class: "dropdown-item",
128
+ href: `/viewedit/edit/${encodeURIComponent(view.name)}`,
129
+ },
130
+ '<i class="fas fa-edit"></i>&nbsp;' + req.__("Edit")
131
+ ),
132
+ post_dropdown_item(
133
+ `/viewedit/add-to-menu/${view.id}`,
134
+ '<i class="fas fa-bars"></i>&nbsp;' + req.__("Add to menu"),
135
+ req
136
+ ),
137
+ post_dropdown_item(
138
+ `/viewedit/clone/${view.id}`,
139
+ '<i class="far fa-copy"></i>&nbsp;' + req.__("Duplicate"),
140
+ req
141
+ ),
142
+ a(
143
+ {
144
+ class: "dropdown-item",
145
+ href: `javascript:ajax_modal('/admin/snapshot-restore/view/${view.name}')`,
146
+ },
147
+ '<i class="fas fa-undo-alt"></i>&nbsp;' + req.__("Restore")
148
+ ),
149
+ div({ class: "dropdown-divider" }),
150
+ post_dropdown_item(
151
+ `/viewedit/delete/${view.id}`,
152
+ '<i class="far fa-trash-alt"></i>&nbsp;' + req.__("Delete"),
153
+ req,
154
+ true,
155
+ view.name
156
+ ),
157
+ ]);
158
+
159
+ const setTableRefs = async (views) => {
160
+ const tables = await Table.find();
161
+ const getTable = (tid) => tables.find((t) => t.id === tid).name;
162
+
163
+ views.forEach((v) => {
164
+ if (v.table_id) v.table = getTable(v.table_id);
165
+ else if (v.exttable_name) v.table = v.exttable_name;
166
+ else v.table = "";
167
+ });
168
+ return views;
169
+ };
170
+
171
+ const viewsList = async (views, req, { tagId, domId, showList } = {}) => {
172
+ const roles = await User.get_roles();
173
+
174
+ return views.length > 0
175
+ ? mkTable(
176
+ [
177
+ {
178
+ label: req.__("Name"),
179
+ key: (r) => link(`/view/${encodeURIComponent(r.name)}`, r.name),
180
+ sortlink: !tagId
181
+ ? `javascript:set_state_field('_sortby', 'name')`
182
+ : undefined,
183
+ },
184
+ // description - currently I dont want to show description in view list
185
+ // because description can be long
186
+ /*
187
+ {
188
+ label: req.__("Description"),
189
+ key: "description",
190
+ // this is sorting by column
191
+ sortlink: `javascript:set_state_field('_sortby', 'description')`,
192
+ },
193
+ */
194
+ // template
195
+ {
196
+ label: req.__("Pattern"),
197
+ key: "viewtemplate",
198
+ sortlink: !tagId
199
+ ? `javascript:set_state_field('_sortby', 'viewtemplate')`
200
+ : undefined,
201
+ },
202
+ {
203
+ label: req.__("Table"),
204
+ key: (r) => link(`/table/${r.table}`, r.table),
205
+ sortlink: !tagId
206
+ ? `javascript:set_state_field('_sortby', 'table')`
207
+ : undefined,
208
+ },
209
+ {
210
+ label: req.__("Role to access"),
211
+ key: (row) => editViewRoleForm(row, roles, req),
212
+ },
213
+ {
214
+ label: "",
215
+ key: (r) =>
216
+ link(
217
+ `/viewedit/config/${encodeURIComponent(r.name)}`,
218
+ req.__("Configure")
219
+ ),
220
+ },
221
+ !tagId
222
+ ? {
223
+ label: "",
224
+ key: (r) => view_dropdown(r, req),
225
+ }
226
+ : {
227
+ label: req.__("Remove From Tag"),
228
+ key: (r) =>
229
+ post_delete_btn(
230
+ `/tag-entries/remove/views/${r.id}/${tagId}`,
231
+ req,
232
+ `${r.name} from this tag`
233
+ ),
234
+ },
235
+ ],
236
+ views,
237
+ {
238
+ hover: true,
239
+ tableClass: listClass(tagId, showList),
240
+ tableId: domId,
241
+ }
242
+ )
243
+ : div(
244
+ { class: listClass(tagId, showList), id: domId },
245
+ h4(req.__("No views defined")),
246
+ p(req.__("Views define how table rows are displayed to the user"))
247
+ );
248
+ };
249
+
250
+ /**
251
+ * @param {object} page
252
+ * @param {object} req
253
+ * @returns {string}
254
+ */
255
+ const page_dropdown = (page, req) =>
256
+ settingsDropdown(`dropdownMenuButton${page.id}`, [
257
+ a(
258
+ {
259
+ class: "dropdown-item",
260
+ href: `/page/${encodeURIComponent(page.name)}`,
261
+ },
262
+ '<i class="fas fa-running"></i>&nbsp;' + req.__("Run")
263
+ ),
264
+ a(
265
+ {
266
+ class: "dropdown-item",
267
+ href: `/pageedit/edit-properties/${encodeURIComponent(page.name)}`,
268
+ },
269
+ '<i class="fas fa-edit"></i>&nbsp;' + req.__("Edit properties")
270
+ ),
271
+ post_dropdown_item(
272
+ `/pageedit/add-to-menu/${page.id}`,
273
+ '<i class="fas fa-bars"></i>&nbsp;' + req.__("Add to menu"),
274
+ req
275
+ ),
276
+ post_dropdown_item(
277
+ `/pageedit/clone/${page.id}`,
278
+ '<i class="far fa-copy"></i>&nbsp;' + req.__("Duplicate"),
279
+ req
280
+ ),
281
+ a(
282
+ {
283
+ class: "dropdown-item",
284
+ href: `javascript:ajax_modal('/admin/snapshot-restore/page/${page.name}')`,
285
+ },
286
+ '<i class="fas fa-undo-alt"></i>&nbsp;' + req.__("Restore")
287
+ ),
288
+ div({ class: "dropdown-divider" }),
289
+ post_dropdown_item(
290
+ `/pageedit/delete/${page.id}`,
291
+ '<i class="far fa-trash-alt"></i>&nbsp;' + req.__("Delete"),
292
+ req,
293
+ true,
294
+ page.name
295
+ ),
296
+ ]);
297
+
298
+ /**
299
+ * @param {object} page
300
+ * @param {*} roles
301
+ * @param {object} req
302
+ * @returns {Form}
303
+ */
304
+ const editPageRoleForm = (page, roles, req) =>
305
+ editRoleForm({
306
+ url: `/pageedit/setrole/${page.id}`,
307
+ current_role: page.min_role,
308
+ roles,
309
+ req,
310
+ });
311
+
312
+ /**
313
+ * @param {*} rows
314
+ * @param {*} roles
315
+ * @param {object} req
316
+ * @returns {div}
317
+ */
318
+ const getPageList = (rows, roles, req, { tagId, domId, showList } = {}) => {
319
+ return mkTable(
320
+ [
321
+ {
322
+ label: req.__("Name"),
323
+ key: (r) => link(`/page/${r.name}`, r.name),
324
+ },
325
+ {
326
+ label: req.__("Role to access"),
327
+ key: (row) => editPageRoleForm(row, roles, req),
328
+ },
329
+ {
330
+ label: req.__("Edit"),
331
+ key: (r) => link(`/pageedit/edit/${r.name}`, req.__("Edit")),
332
+ },
333
+ !tagId
334
+ ? {
335
+ label: "",
336
+ key: (r) => page_dropdown(r, req),
337
+ }
338
+ : {
339
+ label: req.__("Remove From Tag"),
340
+ key: (r) =>
341
+ post_delete_btn(
342
+ `/tag-entries/remove/pages/${r.id}/${tagId}`,
343
+ req,
344
+ `${r.name} from this tag`
345
+ ),
346
+ },
347
+ ,
348
+ ],
349
+ rows,
350
+ {
351
+ hover: true,
352
+ tableClass: tagId ? `collapse ${showList ? "show" : ""}` : "",
353
+ tableId: domId,
354
+ }
355
+ );
356
+ };
357
+
358
+ const getTriggerList = (triggers, req, { tagId, domId, showList } = {}) => {
359
+ return mkTable(
360
+ [
361
+ { label: req.__("Name"), key: "name" },
362
+ { label: req.__("Action"), key: "action" },
363
+ {
364
+ label: req.__("Table or Channel"),
365
+ key: (r) => r.table_name || r.channel,
366
+ },
367
+ {
368
+ label: req.__("When"),
369
+ key: (a) =>
370
+ a.when_trigger === "API call"
371
+ ? `API: ${base_url}api/action/${a.name}`
372
+ : a.when_trigger,
373
+ },
374
+ {
375
+ label: req.__("Test run"),
376
+ key: (r) =>
377
+ r.table_id
378
+ ? ""
379
+ : link(`/actions/testrun/${r.id}`, req.__("Test run")),
380
+ },
381
+ {
382
+ label: req.__("Edit"),
383
+ key: (r) => link(`/actions/edit/${r.id}`, req.__("Edit")),
384
+ },
385
+ {
386
+ label: req.__("Configure"),
387
+ key: (r) => link(`/actions/configure/${r.id}`, req.__("Configure")),
388
+ },
389
+ !tagId
390
+ ? {
391
+ label: req.__("Delete"),
392
+ key: (r) => post_delete_btn(`/actions/delete/${r.id}`, req),
393
+ }
394
+ : {
395
+ label: req.__("Remove From Tag"),
396
+ key: (r) =>
397
+ post_delete_btn(
398
+ `/tag-entries/remove/trigger/${r.id}/${tagId}`,
399
+ req,
400
+ `${r.name} from this tag`
401
+ ),
402
+ },
403
+ ],
404
+ triggers,
405
+ {
406
+ hover: true,
407
+ tableClass: tagId ? `collapse ${showList ? "show" : ""}` : "",
408
+ tableId: domId,
409
+ }
410
+ );
411
+ };
412
+
413
+ module.exports = {
414
+ tablesList,
415
+ setTableRefs,
416
+ viewsList,
417
+ getPageList,
418
+ getTriggerList,
419
+ };
package/routes/fields.js CHANGED
@@ -28,6 +28,7 @@ const { readState } = require("@saltcorn/data/plugin-helper");
28
28
  const { wizardCardTitle } = require("../markup/forms.js");
29
29
  const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
30
30
  const { applyAsync } = require("@saltcorn/data/utils");
31
+ const { text } = require("@saltcorn/markup/tags");
31
32
 
32
33
  /**
33
34
  * @type {object}
@@ -677,14 +678,30 @@ router.post(
677
678
  (f) => f.name === kpath[1]
678
679
  );
679
680
  //console.log({ kpath, fieldview, targetField });
680
- let fv = targetField.type.fieldviews[fieldview];
681
- if (!fv) {
682
- fv =
683
- targetField.type.fieldviews.show ||
684
- targetField.type.fieldviews.as_text;
685
- }
686
681
  const q = { [reftable.pk_name]: row[kpath[0]] };
687
682
  const refRow = await reftable.getRow(q);
683
+ let fv;
684
+ if (targetField.type === "Key") {
685
+ fv = getState().keyFieldviews[fieldview]
686
+ if (!fv) {
687
+ const reftable2 = Table.findOne({ name: targetField.reftable_name })
688
+ const refRow2 = await reftable2.getRow({ [reftable2.pk_name]: refRow[kpath[1]] })
689
+ if (refRow2) {
690
+ res.send(text(`${refRow2[targetField.attributes.summary_field]}`));
691
+ } else {
692
+ res.send("");
693
+ }
694
+ return;
695
+ }
696
+ } else {
697
+ targetField.type.fieldviews[fieldview];
698
+ if (!fv)
699
+ fv =
700
+ targetField.type.fieldviews.show ||
701
+ targetField.type.fieldviews.as_text;
702
+
703
+ }
704
+
688
705
  const configuration = req.query;
689
706
  let configFields = [];
690
707
  if (fv.configFields)
package/routes/index.js CHANGED
@@ -70,6 +70,8 @@ const auth = require("../auth/routes");
70
70
  const useradmin = require("../auth/admin");
71
71
  const roleadmin = require("../auth/roleadmin");
72
72
  const scapi = require("./scapi");
73
+ const tags = require("./tags");
74
+ const tagentries = require("./tag_entries");
73
75
 
74
76
  module.exports =
75
77
  /**
@@ -106,4 +108,6 @@ module.exports =
106
108
  app.use("/useradmin", useradmin);
107
109
  app.use("/roleadmin", roleadmin);
108
110
  app.use("/scapi", scapi);
111
+ app.use("/tag", tags);
112
+ app.use("/tag-entries", tagentries);
109
113
  };
@@ -19,6 +19,7 @@ const Trigger = require("@saltcorn/data/models/trigger");
19
19
  const { getViews, traverseSync } = require("@saltcorn/data/models/layout");
20
20
  const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
21
21
  const db = require("@saltcorn/data/db");
22
+ const { getPageList } = require("./common_lists");
22
23
 
23
24
  const { isAdmin, error_catcher } = require("./utils.js");
24
25
  const {
@@ -32,7 +33,6 @@ const {
32
33
  settingsDropdown,
33
34
  } = require("@saltcorn/markup");
34
35
  const { getActionConfigFields } = require("@saltcorn/data/plugin-helper");
35
- const { editRoleForm, wizardCardTitle } = require("../markup/forms.js");
36
36
  const Library = require("@saltcorn/data/models/library");
37
37
 
38
38
  /**
@@ -45,68 +45,6 @@ const Library = require("@saltcorn/data/models/library");
45
45
  const router = new Router();
46
46
  module.exports = router;
47
47
 
48
- /**
49
- * @param {object} page
50
- * @param {*} roles
51
- * @param {object} req
52
- * @returns {Form}
53
- */
54
- const editPageRoleForm = (page, roles, req) =>
55
- editRoleForm({
56
- url: `/pageedit/setrole/${page.id}`,
57
- current_role: page.min_role,
58
- roles,
59
- req,
60
- });
61
-
62
- /**
63
- * @param {object} page
64
- * @param {object} req
65
- * @returns {string}
66
- */
67
- const page_dropdown = (page, req) =>
68
- settingsDropdown(`dropdownMenuButton${page.id}`, [
69
- a(
70
- {
71
- class: "dropdown-item",
72
- href: `/page/${encodeURIComponent(page.name)}`,
73
- },
74
- '<i class="fas fa-running"></i>&nbsp;' + req.__("Run")
75
- ),
76
- a(
77
- {
78
- class: "dropdown-item",
79
- href: `/pageedit/edit-properties/${encodeURIComponent(page.name)}`,
80
- },
81
- '<i class="fas fa-edit"></i>&nbsp;' + req.__("Edit properties")
82
- ),
83
- post_dropdown_item(
84
- `/pageedit/add-to-menu/${page.id}`,
85
- '<i class="fas fa-bars"></i>&nbsp;' + req.__("Add to menu"),
86
- req
87
- ),
88
- post_dropdown_item(
89
- `/pageedit/clone/${page.id}`,
90
- '<i class="far fa-copy"></i>&nbsp;' + req.__("Duplicate"),
91
- req
92
- ),
93
- a(
94
- {
95
- class: "dropdown-item",
96
- href: `javascript:ajax_modal('/admin/snapshot-restore/page/${page.name}')`,
97
- },
98
- '<i class="fas fa-undo-alt"></i>&nbsp;' + req.__("Restore")
99
- ),
100
- div({ class: "dropdown-divider" }),
101
- post_dropdown_item(
102
- `/pageedit/delete/${page.id}`,
103
- '<i class="far fa-trash-alt"></i>&nbsp;' + req.__("Delete"),
104
- req,
105
- true,
106
- page.name
107
- ),
108
- ]);
109
-
110
48
  /**
111
49
  *
112
50
  * @param {object} req
@@ -188,7 +126,7 @@ const pageBuilderData = async (req, context) => {
188
126
  for (const view of views) {
189
127
  fixed_state_fields[view.name] = [];
190
128
  const table = Table.findOne(view.table_id || view.exttable_name);
191
-
129
+
192
130
  const fs = await view.get_state_fields();
193
131
  for (const frec of fs) {
194
132
  const f = new Field(frec);
@@ -236,46 +174,6 @@ const pageBuilderData = async (req, context) => {
236
174
  };
237
175
  };
238
176
 
239
- /**
240
- * @param {*} rows
241
- * @param {*} roles
242
- * @param {object} req
243
- * @returns {div}
244
- */
245
- const getPageList = (rows, roles, req) => {
246
- return div(
247
- mkTable(
248
- [
249
- {
250
- label: req.__("Name"),
251
- key: (r) => link(`/page/${r.name}`, r.name),
252
- },
253
- {
254
- label: req.__("Role to access"),
255
- key: (row) => editPageRoleForm(row, roles, req),
256
- },
257
- {
258
- label: req.__("Edit"),
259
- key: (r) => link(`/pageedit/edit/${r.name}`, req.__("Edit")),
260
- },
261
- {
262
- label: "",
263
- key: (r) => page_dropdown(r, req),
264
- },
265
- ],
266
- rows,
267
- { hover: true }
268
- ),
269
- a(
270
- {
271
- href: `/pageedit/new`,
272
- class: "btn btn-primary",
273
- },
274
- req.__("Create page")
275
- )
276
- );
277
- };
278
-
279
177
  /**
280
178
  * Root pages configuration Form
281
179
  * Allows to configure root page for each role
@@ -335,7 +233,16 @@ router.get(
335
233
  type: "card",
336
234
  title: req.__("Your pages"),
337
235
  class: "mt-0",
338
- contents: getPageList(pages, roles, req),
236
+ contents: div(
237
+ getPageList(pages, roles, req),
238
+ a(
239
+ {
240
+ href: `/pageedit/new`,
241
+ class: "btn btn-primary",
242
+ },
243
+ req.__("Create page")
244
+ )
245
+ ),
339
246
  },
340
247
  {
341
248
  type: "card",