@saltcorn/server 0.9.4-beta.0 → 0.9.4-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/help/JavaScript action code.tmd +1 -0
- package/locales/en.json +16 -2
- package/package.json +16 -16
- package/public/saltcorn-builder.css +19 -1
- package/public/saltcorn.css +4 -0
- package/public/saltcorn.js +4 -1
- package/restart_watcher.js +1 -0
- package/routes/actions.js +17 -2
- package/routes/common_lists.js +304 -135
- package/routes/fields.js +13 -3
- package/routes/files.js +3 -1
- package/routes/list.js +5 -0
- package/routes/pageedit.js +19 -4
- package/routes/tables.js +29 -6
- package/routes/tag_entries.js +12 -4
- package/routes/tags.js +61 -12
- package/routes/view.js +20 -2
- package/routes/viewedit.js +63 -4
- package/tests/view.test.js +115 -15
- package/tests/viewedit.test.js +0 -21
- package/wrapper.js +2 -2
- package/public/relation_helpers.js +0 -351
package/routes/common_lists.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const User = require("@saltcorn/data/models/user");
|
|
2
2
|
const Table = require("@saltcorn/data/models/table");
|
|
3
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
4
|
+
const TagEntry = require("@saltcorn/data/models/tag_entry");
|
|
3
5
|
const { editRoleForm } = require("../markup/forms.js");
|
|
4
6
|
const {
|
|
5
7
|
mkTable,
|
|
@@ -7,16 +9,16 @@ const {
|
|
|
7
9
|
post_delete_btn,
|
|
8
10
|
settingsDropdown,
|
|
9
11
|
post_dropdown_item,
|
|
12
|
+
badge,
|
|
10
13
|
} = require("@saltcorn/markup");
|
|
11
14
|
const { get_base_url } = require("./utils.js");
|
|
12
|
-
const { h4, p, div, a, i, text } = require("@saltcorn/markup/tags");
|
|
15
|
+
const { h4, p, div, a, i, text, span, nbsp } = require("@saltcorn/markup/tags");
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* @param {string} col
|
|
16
19
|
* @param {string} lbl
|
|
17
20
|
* @returns {string}
|
|
18
21
|
*/
|
|
19
|
-
const badge = (col, lbl) => `<span class="badge bg-${col}">${lbl}</span> `;
|
|
20
22
|
|
|
21
23
|
/**
|
|
22
24
|
* Table badges to show in System Table list views
|
|
@@ -42,57 +44,90 @@ const valIfSet = (check, value) => (check ? value : "");
|
|
|
42
44
|
const listClass = (tagId, showList) =>
|
|
43
45
|
valIfSet(tagId, `collapse ${valIfSet(showList, "show")}`);
|
|
44
46
|
|
|
45
|
-
const tablesList = async (
|
|
47
|
+
const tablesList = async (
|
|
48
|
+
tables,
|
|
49
|
+
req,
|
|
50
|
+
{ tagId, domId, showList, filterOnTag } = {}
|
|
51
|
+
) => {
|
|
46
52
|
const roles = await User.get_roles();
|
|
47
53
|
const getRole = (rid) => roles.find((r) => r.id === rid)?.role || "?";
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
`/tag-entries/remove/tables/${r.id}/${tagId}`,
|
|
79
|
-
req,
|
|
80
|
-
`${r.name} from this tag`
|
|
81
|
-
),
|
|
54
|
+
const tags = await Tag.find();
|
|
55
|
+
const tag_entries = await TagEntry.find({
|
|
56
|
+
not: { table_id: null },
|
|
57
|
+
});
|
|
58
|
+
const tagsById = {};
|
|
59
|
+
tags.forEach((t) => (tagsById[t.id] = t));
|
|
60
|
+
|
|
61
|
+
const tagBadges = (table) => {
|
|
62
|
+
const myTags = tag_entries.filter((te) => te.table_id === table.id);
|
|
63
|
+
return myTags
|
|
64
|
+
.map((te) => tagBadge(tagsById[te.tag_id], "tables"))
|
|
65
|
+
.join(nbsp);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
mkTable(
|
|
70
|
+
[
|
|
71
|
+
{
|
|
72
|
+
label: req.__("Name"),
|
|
73
|
+
key: (r) => link(`/table/${r.id || r.name}`, text(r.name)),
|
|
74
|
+
},
|
|
75
|
+
...(tagId
|
|
76
|
+
? []
|
|
77
|
+
: [
|
|
78
|
+
{
|
|
79
|
+
label: tagsDropdown(
|
|
80
|
+
tags,
|
|
81
|
+
filterOnTag ? `Tag:${filterOnTag.name}` : undefined
|
|
82
|
+
),
|
|
83
|
+
key: (r) => tagBadges(r),
|
|
82
84
|
},
|
|
83
|
-
|
|
84
|
-
tables,
|
|
85
|
+
]),
|
|
85
86
|
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
87
|
+
label: "",
|
|
88
|
+
key: (r) => tableBadges(r, req),
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
{
|
|
92
|
+
label: req.__("Access Read/Write"),
|
|
93
|
+
key: (t) =>
|
|
94
|
+
t.external
|
|
95
|
+
? `${getRole(t.min_role_read)} (read only)`
|
|
96
|
+
: `${getRole(t.min_role_read)}/${getRole(t.min_role_write)}`,
|
|
97
|
+
},
|
|
98
|
+
!tagId
|
|
99
|
+
? {
|
|
100
|
+
label: req.__("Delete"),
|
|
101
|
+
key: (r) =>
|
|
102
|
+
r.name === "users" || r.external
|
|
103
|
+
? ""
|
|
104
|
+
: post_delete_btn(`/table/delete/${r.id}`, req, r.name),
|
|
105
|
+
}
|
|
106
|
+
: {
|
|
107
|
+
label: req.__("Remove From Tag"),
|
|
108
|
+
key: (r) =>
|
|
109
|
+
post_delete_btn(
|
|
110
|
+
`/tag-entries/remove/tables/${r.id}/${tagId}`,
|
|
111
|
+
req,
|
|
112
|
+
`${r.name} from this tag`
|
|
113
|
+
),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
tables,
|
|
117
|
+
{
|
|
118
|
+
hover: true,
|
|
119
|
+
tableClass: listClass(tagId, showList),
|
|
120
|
+
tableId: domId,
|
|
121
|
+
}
|
|
122
|
+
) +
|
|
123
|
+
(tables.length == 0 && !filterOnTag
|
|
124
|
+
? div(
|
|
125
|
+
{ class: listClass(tagId, showList), id: domId },
|
|
126
|
+
h4(req.__("No tables defined")),
|
|
127
|
+
p(req.__("Tables hold collections of similar data"))
|
|
128
|
+
)
|
|
129
|
+
: "")
|
|
130
|
+
);
|
|
96
131
|
};
|
|
97
132
|
|
|
98
133
|
/**
|
|
@@ -177,100 +212,176 @@ const setTableRefs = async (views) => {
|
|
|
177
212
|
return views;
|
|
178
213
|
};
|
|
179
214
|
|
|
215
|
+
const tagBadge = (tag, type) =>
|
|
216
|
+
a(
|
|
217
|
+
{
|
|
218
|
+
href: `/tag/${tag.id}?show_list=${type}`,
|
|
219
|
+
class: "badge bg-secondary",
|
|
220
|
+
},
|
|
221
|
+
tag.name
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const tagsDropdown = (tags, altHeader) =>
|
|
225
|
+
div(
|
|
226
|
+
{ class: "dropdown" },
|
|
227
|
+
div(
|
|
228
|
+
{
|
|
229
|
+
class: "link-style",
|
|
230
|
+
"data-boundary": "viewport",
|
|
231
|
+
type: "button",
|
|
232
|
+
id: "tagsselector",
|
|
233
|
+
"data-bs-toggle": "dropdown",
|
|
234
|
+
"aria-haspopup": "true",
|
|
235
|
+
"aria-expanded": "false",
|
|
236
|
+
},
|
|
237
|
+
altHeader || "Tags",
|
|
238
|
+
i({ class: "ms-1 fas fa-caret-down" })
|
|
239
|
+
),
|
|
240
|
+
div(
|
|
241
|
+
{
|
|
242
|
+
class: "dropdown-menu",
|
|
243
|
+
"aria-labelledby": "tagsselector",
|
|
244
|
+
},
|
|
245
|
+
a(
|
|
246
|
+
{
|
|
247
|
+
class: "dropdown-item",
|
|
248
|
+
// TODO check url why view for page, what do we need for page group
|
|
249
|
+
href: `javascript:unset_state_field('_tag', this)`,
|
|
250
|
+
},
|
|
251
|
+
"All tags"
|
|
252
|
+
),
|
|
253
|
+
tags.map((tag) =>
|
|
254
|
+
a(
|
|
255
|
+
{
|
|
256
|
+
class: "dropdown-item",
|
|
257
|
+
// TODO check url why view for page, what do we need for page group
|
|
258
|
+
href: `javascript:set_state_field('_tag', ${tag.id}, this)`,
|
|
259
|
+
},
|
|
260
|
+
tag.name
|
|
261
|
+
)
|
|
262
|
+
),
|
|
263
|
+
a(
|
|
264
|
+
{
|
|
265
|
+
class: "dropdown-item",
|
|
266
|
+
// TODO check url why view for page, what do we need for page group
|
|
267
|
+
href: `tag`,
|
|
268
|
+
},
|
|
269
|
+
"Manage tags..."
|
|
270
|
+
)
|
|
271
|
+
)
|
|
272
|
+
);
|
|
273
|
+
|
|
180
274
|
const viewsList = async (
|
|
181
275
|
views,
|
|
182
276
|
req,
|
|
183
|
-
{ tagId, domId, showList, on_done_redirect, notable } = {}
|
|
277
|
+
{ tagId, domId, showList, on_done_redirect, notable, filterOnTag } = {}
|
|
184
278
|
) => {
|
|
185
279
|
const roles = await User.get_roles();
|
|
186
280
|
const on_done_redirect_str = on_done_redirect
|
|
187
281
|
? `?on_done_redirect=${on_done_redirect}`
|
|
188
282
|
: "";
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
key: (r) => link(`/table/${r.table}`, r.table),
|
|
223
|
-
sortlink: !tagId
|
|
224
|
-
? `set_state_field('_sortby', 'table', this)`
|
|
225
|
-
: undefined,
|
|
226
|
-
},
|
|
227
|
-
]),
|
|
228
|
-
{
|
|
229
|
-
label: req.__("Role to access"),
|
|
230
|
-
key: (row) =>
|
|
231
|
-
row.id
|
|
232
|
-
? editViewRoleForm(row, roles, req, on_done_redirect_str)
|
|
233
|
-
: "admin",
|
|
234
|
-
},
|
|
235
|
-
{
|
|
236
|
-
label: "",
|
|
237
|
-
key: (r) =>
|
|
238
|
-
r.id && r.viewtemplateObj?.configuration_workflow
|
|
239
|
-
? link(
|
|
240
|
-
`/viewedit/config/${encodeURIComponent(
|
|
241
|
-
r.name
|
|
242
|
-
)}${on_done_redirect_str}`,
|
|
243
|
-
req.__("Configure")
|
|
244
|
-
)
|
|
245
|
-
: "",
|
|
246
|
-
},
|
|
247
|
-
!tagId
|
|
248
|
-
? {
|
|
249
|
-
label: "",
|
|
250
|
-
key: (r) => view_dropdown(r, req, on_done_redirect_str),
|
|
251
|
-
}
|
|
252
|
-
: {
|
|
253
|
-
label: req.__("Remove From Tag"),
|
|
254
|
-
key: (r) =>
|
|
255
|
-
post_delete_btn(
|
|
256
|
-
`/tag-entries/remove/views/${r.id}/${tagId}`,
|
|
257
|
-
req,
|
|
258
|
-
`${r.name} from this tag`
|
|
259
|
-
),
|
|
283
|
+
const tags = await Tag.find();
|
|
284
|
+
const tag_entries = await TagEntry.find({
|
|
285
|
+
not: { view_id: null },
|
|
286
|
+
});
|
|
287
|
+
const tagsById = {};
|
|
288
|
+
tags.forEach((t) => (tagsById[t.id] = t));
|
|
289
|
+
|
|
290
|
+
const tagBadges = (view) => {
|
|
291
|
+
const myTags = tag_entries.filter((te) => te.view_id === view.id);
|
|
292
|
+
return myTags
|
|
293
|
+
.map((te) => tagBadge(tagsById[te.tag_id], "views"))
|
|
294
|
+
.join(nbsp);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
return (
|
|
298
|
+
mkTable(
|
|
299
|
+
[
|
|
300
|
+
{
|
|
301
|
+
label: req.__("Name"),
|
|
302
|
+
key: (r) => link(`/view/${encodeURIComponent(r.name)}`, r.name),
|
|
303
|
+
sortlink: !tagId
|
|
304
|
+
? `set_state_field('_sortby', 'name', this)`
|
|
305
|
+
: undefined,
|
|
306
|
+
},
|
|
307
|
+
...(tagId
|
|
308
|
+
? []
|
|
309
|
+
: [
|
|
310
|
+
{
|
|
311
|
+
label: tagsDropdown(
|
|
312
|
+
tags,
|
|
313
|
+
filterOnTag ? `Tag:${filterOnTag.name}` : undefined
|
|
314
|
+
),
|
|
315
|
+
key: (r) => tagBadges(r),
|
|
260
316
|
},
|
|
261
|
-
|
|
262
|
-
views,
|
|
317
|
+
]),
|
|
263
318
|
{
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
319
|
+
label: req.__("Pattern"),
|
|
320
|
+
key: "viewtemplate",
|
|
321
|
+
sortlink: !tagId
|
|
322
|
+
? `set_state_field('_sortby', 'viewtemplate', this)`
|
|
323
|
+
: undefined,
|
|
324
|
+
},
|
|
325
|
+
...(notable
|
|
326
|
+
? []
|
|
327
|
+
: [
|
|
328
|
+
{
|
|
329
|
+
label: req.__("Table"),
|
|
330
|
+
key: (r) => link(`/table/${r.table}`, r.table),
|
|
331
|
+
sortlink: !tagId
|
|
332
|
+
? `set_state_field('_sortby', 'table', this)`
|
|
333
|
+
: undefined,
|
|
334
|
+
},
|
|
335
|
+
]),
|
|
336
|
+
{
|
|
337
|
+
label: req.__("Role to access"),
|
|
338
|
+
key: (row) =>
|
|
339
|
+
row.id
|
|
340
|
+
? editViewRoleForm(row, roles, req, on_done_redirect_str)
|
|
341
|
+
: "admin",
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
label: "",
|
|
345
|
+
key: (r) =>
|
|
346
|
+
r.id && r.viewtemplateObj?.configuration_workflow
|
|
347
|
+
? link(
|
|
348
|
+
`/viewedit/config/${encodeURIComponent(
|
|
349
|
+
r.name
|
|
350
|
+
)}${on_done_redirect_str}`,
|
|
351
|
+
req.__("Configure")
|
|
352
|
+
)
|
|
353
|
+
: "",
|
|
354
|
+
},
|
|
355
|
+
!tagId
|
|
356
|
+
? {
|
|
357
|
+
label: "",
|
|
358
|
+
key: (r) => view_dropdown(r, req, on_done_redirect_str),
|
|
359
|
+
}
|
|
360
|
+
: {
|
|
361
|
+
label: req.__("Remove From Tag"),
|
|
362
|
+
key: (r) =>
|
|
363
|
+
post_delete_btn(
|
|
364
|
+
`/tag-entries/remove/views/${r.id}/${tagId}`,
|
|
365
|
+
req,
|
|
366
|
+
`${r.name} from this tag`
|
|
367
|
+
),
|
|
368
|
+
},
|
|
369
|
+
],
|
|
370
|
+
views,
|
|
371
|
+
{
|
|
372
|
+
hover: true,
|
|
373
|
+
tableClass: listClass(tagId, showList),
|
|
374
|
+
tableId: domId,
|
|
375
|
+
}
|
|
376
|
+
) +
|
|
377
|
+
(views.length == 0 && !filterOnTag
|
|
378
|
+
? div(
|
|
379
|
+
{ class: listClass(tagId, showList), id: domId },
|
|
380
|
+
h4(req.__("No views defined")),
|
|
381
|
+
p(req.__("Views define how table rows are displayed to the user"))
|
|
382
|
+
)
|
|
383
|
+
: "")
|
|
384
|
+
);
|
|
274
385
|
};
|
|
275
386
|
|
|
276
387
|
const page_group_dropdown = (page_group, req) =>
|
|
@@ -371,13 +482,42 @@ const editPageRoleForm = (page, roles, req, isGroup) =>
|
|
|
371
482
|
* @param {object} req
|
|
372
483
|
* @returns {div}
|
|
373
484
|
*/
|
|
374
|
-
const getPageList = (
|
|
485
|
+
const getPageList = async (
|
|
486
|
+
rows,
|
|
487
|
+
roles,
|
|
488
|
+
req,
|
|
489
|
+
{ tagId, domId, showList, filterOnTag } = {}
|
|
490
|
+
) => {
|
|
491
|
+
const tags = await Tag.find();
|
|
492
|
+
const tag_entries = await TagEntry.find({
|
|
493
|
+
not: { page_id: null },
|
|
494
|
+
});
|
|
495
|
+
const tagsById = {};
|
|
496
|
+
tags.forEach((t) => (tagsById[t.id] = t));
|
|
497
|
+
|
|
498
|
+
const tagBadges = (page) => {
|
|
499
|
+
const myTags = tag_entries.filter((te) => te.page_id === page.id);
|
|
500
|
+
return myTags
|
|
501
|
+
.map((te) => tagBadge(tagsById[te.tag_id], "pages"))
|
|
502
|
+
.join(nbsp);
|
|
503
|
+
};
|
|
375
504
|
return mkTable(
|
|
376
505
|
[
|
|
377
506
|
{
|
|
378
507
|
label: req.__("Name"),
|
|
379
508
|
key: (r) => link(`/page/${r.name}`, r.name),
|
|
380
509
|
},
|
|
510
|
+
...(tagId
|
|
511
|
+
? []
|
|
512
|
+
: [
|
|
513
|
+
{
|
|
514
|
+
label: tagsDropdown(
|
|
515
|
+
tags,
|
|
516
|
+
filterOnTag ? `Tag:${filterOnTag.name}` : undefined
|
|
517
|
+
),
|
|
518
|
+
key: (r) => tagBadges(r),
|
|
519
|
+
},
|
|
520
|
+
]),
|
|
381
521
|
{
|
|
382
522
|
label: req.__("Role to access"),
|
|
383
523
|
key: (row) => editPageRoleForm(row, roles, req),
|
|
@@ -442,11 +582,40 @@ const getPageGroupList = (rows, roles, req) => {
|
|
|
442
582
|
);
|
|
443
583
|
};
|
|
444
584
|
|
|
445
|
-
const getTriggerList = (
|
|
585
|
+
const getTriggerList = async (
|
|
586
|
+
triggers,
|
|
587
|
+
req,
|
|
588
|
+
{ tagId, domId, showList, filterOnTag } = {}
|
|
589
|
+
) => {
|
|
446
590
|
const base_url = get_base_url(req);
|
|
591
|
+
const tags = await Tag.find();
|
|
592
|
+
|
|
593
|
+
const tag_entries = await TagEntry.find({
|
|
594
|
+
not: { trigger_id: null },
|
|
595
|
+
});
|
|
596
|
+
const tagsById = {};
|
|
597
|
+
tags.forEach((t) => (tagsById[t.id] = t));
|
|
598
|
+
|
|
599
|
+
const tagBadges = (trigger) => {
|
|
600
|
+
const myTags = tag_entries.filter((te) => te.trigger_id === trigger.id);
|
|
601
|
+
return myTags
|
|
602
|
+
.map((te) => tagBadge(tagsById[te.tag_id], "triggers"))
|
|
603
|
+
.join(nbsp);
|
|
604
|
+
};
|
|
447
605
|
return mkTable(
|
|
448
606
|
[
|
|
449
607
|
{ label: req.__("Name"), key: "name" },
|
|
608
|
+
...(tagId
|
|
609
|
+
? []
|
|
610
|
+
: [
|
|
611
|
+
{
|
|
612
|
+
label: tagsDropdown(
|
|
613
|
+
tags,
|
|
614
|
+
filterOnTag ? `Tag:${filterOnTag.name}` : undefined
|
|
615
|
+
),
|
|
616
|
+
key: (r) => tagBadges(r),
|
|
617
|
+
},
|
|
618
|
+
]),
|
|
450
619
|
{ label: req.__("Action"), key: "action" },
|
|
451
620
|
{
|
|
452
621
|
label: req.__("Table or Channel"),
|
package/routes/fields.js
CHANGED
|
@@ -479,6 +479,8 @@ const fieldFlow = (req) =>
|
|
|
479
479
|
// todo sublabel
|
|
480
480
|
type: "String",
|
|
481
481
|
class: "validate-expression",
|
|
482
|
+
fieldview: "textarea",
|
|
483
|
+
attributes: { rows: 2 },
|
|
482
484
|
validator: expressionValidator,
|
|
483
485
|
showIf: { expression_type: "JavaScript expression" },
|
|
484
486
|
}),
|
|
@@ -978,7 +980,7 @@ router.post(
|
|
|
978
980
|
const reftable = Table.findOne({ name: field.reftable_name });
|
|
979
981
|
if (!oldRow[ref]) break;
|
|
980
982
|
if (role > reftable.min_role_read) {
|
|
981
|
-
res.
|
|
983
|
+
res.status401.send("");
|
|
982
984
|
return;
|
|
983
985
|
}
|
|
984
986
|
const q = { [reftable.pk_name]: oldRow[ref] };
|
|
@@ -991,7 +993,14 @@ router.post(
|
|
|
991
993
|
}
|
|
992
994
|
if (oldRow) {
|
|
993
995
|
const value = oldRow[kpath[kpath.length - 1]];
|
|
994
|
-
|
|
996
|
+
//TODO run fieldview
|
|
997
|
+
res.send(
|
|
998
|
+
typeof value === "string"
|
|
999
|
+
? value
|
|
1000
|
+
: value?.toString
|
|
1001
|
+
? value.toString()
|
|
1002
|
+
: `${value}`
|
|
1003
|
+
);
|
|
995
1004
|
return;
|
|
996
1005
|
}
|
|
997
1006
|
}
|
|
@@ -1044,9 +1053,10 @@ router.post(
|
|
|
1044
1053
|
const f = get_expression_function(formula, fields);
|
|
1045
1054
|
result = f(row);
|
|
1046
1055
|
}
|
|
1056
|
+
const configuration = req.query;
|
|
1047
1057
|
const fv = field.type.fieldviews[fieldview];
|
|
1048
1058
|
if (!fv) res.send(text(result));
|
|
1049
|
-
else res.send(fv.run(result));
|
|
1059
|
+
else res.send(fv.run(result, req, { row, ...configuration }));
|
|
1050
1060
|
} catch (e) {
|
|
1051
1061
|
return res.status(400).send(`Error: ${e.message}`);
|
|
1052
1062
|
}
|
package/routes/files.js
CHANGED
|
@@ -205,7 +205,8 @@ router.get(
|
|
|
205
205
|
) {
|
|
206
206
|
res.type(file.mimetype);
|
|
207
207
|
const cacheability = file.min_role_read === 100 ? "public" : "private";
|
|
208
|
-
|
|
208
|
+
const maxAge = getState().getConfig("files_cache_maxage", 86400);
|
|
209
|
+
res.set("Cache-Control", `${cacheability}, max-age=${maxAge}`);
|
|
209
210
|
if (file.s3_store) s3storage.serveObject(file, res, false);
|
|
210
211
|
else res.sendFile(file.location);
|
|
211
212
|
} else {
|
|
@@ -565,6 +566,7 @@ const files_settings_form = async (req) => {
|
|
|
565
566
|
field_names: [
|
|
566
567
|
"min_role_upload",
|
|
567
568
|
"file_accept_filter_default",
|
|
569
|
+
"files_cache_maxage",
|
|
568
570
|
"file_upload_debug",
|
|
569
571
|
"file_upload_limit",
|
|
570
572
|
"file_upload_timeout",
|
package/routes/list.js
CHANGED
|
@@ -161,6 +161,11 @@ const typeToGridType = (t, field) => {
|
|
|
161
161
|
jsgField.formatterParams = {
|
|
162
162
|
inputFormat: "iso",
|
|
163
163
|
};
|
|
164
|
+
|
|
165
|
+
if (field.attributes?.day_only) {
|
|
166
|
+
jsgField.editorParams = { dayOnly: true };
|
|
167
|
+
jsgField.formatter = "__isoDateFormatter";
|
|
168
|
+
}
|
|
164
169
|
} else if (t.name === "Color") {
|
|
165
170
|
jsgField.editor = "__colorEditor";
|
|
166
171
|
jsgField.formatter = "__colorFormatter";
|
package/routes/pageedit.js
CHANGED
|
@@ -21,6 +21,8 @@ const { getViews, traverseSync } = require("@saltcorn/data/models/layout");
|
|
|
21
21
|
const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
|
|
22
22
|
const db = require("@saltcorn/data/db");
|
|
23
23
|
const { getPageList, getPageGroupList } = require("./common_lists");
|
|
24
|
+
const TagEntry = require("@saltcorn/data/models/tag_entry");
|
|
25
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
24
26
|
|
|
25
27
|
const {
|
|
26
28
|
isAdmin,
|
|
@@ -293,7 +295,18 @@ router.get(
|
|
|
293
295
|
"/",
|
|
294
296
|
isAdmin,
|
|
295
297
|
error_catcher(async (req, res) => {
|
|
296
|
-
const
|
|
298
|
+
const pageq = {};
|
|
299
|
+
let filterOnTag;
|
|
300
|
+
|
|
301
|
+
if (req.query._tag) {
|
|
302
|
+
const tagEntries = await TagEntry.find({
|
|
303
|
+
tag_id: +req.query._tag,
|
|
304
|
+
not: { page_id: null },
|
|
305
|
+
});
|
|
306
|
+
pageq.id = { in: tagEntries.map((te) => te.page_id).filter(Boolean) };
|
|
307
|
+
filterOnTag = await Tag.findOne({ id: +req.query._tag });
|
|
308
|
+
}
|
|
309
|
+
const pages = await Page.find(pageq, { orderBy: "name", nocase: true });
|
|
297
310
|
const pageGroups = await PageGroup.find(
|
|
298
311
|
{},
|
|
299
312
|
{ orderBy: "name", nocase: true }
|
|
@@ -311,7 +324,7 @@ router.get(
|
|
|
311
324
|
title: req.__("Your pages"),
|
|
312
325
|
class: "mt-0",
|
|
313
326
|
contents: div(
|
|
314
|
-
getPageList(pages, roles, req),
|
|
327
|
+
await getPageList(pages, roles, req, { filterOnTag }),
|
|
315
328
|
a(
|
|
316
329
|
{
|
|
317
330
|
href: `/pageedit/new`,
|
|
@@ -677,9 +690,11 @@ router.post(
|
|
|
677
690
|
|
|
678
691
|
if (id && req.body.layout) {
|
|
679
692
|
await Page.update(+id, { layout: req.body.layout });
|
|
680
|
-
res.json({
|
|
693
|
+
res.json({
|
|
694
|
+
success: "ok",
|
|
695
|
+
});
|
|
681
696
|
} else {
|
|
682
|
-
res.json({ error: "
|
|
697
|
+
res.json({ error: req.__("Unable to save: No page or no layout") });
|
|
683
698
|
}
|
|
684
699
|
})
|
|
685
700
|
);
|