@saltcorn/server 0.9.4-beta.2 → 0.9.4-beta.20
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 +16 -1
- package/auth/admin.js +19 -3
- package/auth/routes.js +8 -2
- package/help/JavaScript action code.tmd +1 -0
- package/load_plugins.js +8 -2
- package/locales/en.json +34 -1
- package/markup/admin.js +22 -18
- package/package.json +10 -9
- package/public/dayjslocales/af.js +1 -0
- package/public/dayjslocales/am.js +1 -0
- package/public/dayjslocales/ar-dz.js +1 -0
- package/public/dayjslocales/ar-iq.js +1 -0
- package/public/dayjslocales/ar-kw.js +1 -0
- package/public/dayjslocales/ar-ly.js +1 -0
- package/public/dayjslocales/ar-ma.js +1 -0
- package/public/dayjslocales/ar-sa.js +1 -0
- package/public/dayjslocales/ar-tn.js +1 -0
- package/public/dayjslocales/ar.js +1 -0
- package/public/dayjslocales/az.js +1 -0
- package/public/dayjslocales/be.js +1 -0
- package/public/dayjslocales/bg.js +1 -0
- package/public/dayjslocales/bi.js +1 -0
- package/public/dayjslocales/bm.js +1 -0
- package/public/dayjslocales/bn-bd.js +1 -0
- package/public/dayjslocales/bn.js +1 -0
- package/public/dayjslocales/bo.js +1 -0
- package/public/dayjslocales/br.js +1 -0
- package/public/dayjslocales/bs.js +1 -0
- package/public/dayjslocales/ca.js +1 -0
- package/public/dayjslocales/cs.js +1 -0
- package/public/dayjslocales/cv.js +1 -0
- package/public/dayjslocales/cy.js +1 -0
- package/public/dayjslocales/da.js +1 -0
- package/public/dayjslocales/de-at.js +1 -0
- package/public/dayjslocales/de-ch.js +1 -0
- package/public/dayjslocales/de.js +1 -0
- package/public/dayjslocales/dv.js +1 -0
- package/public/dayjslocales/el.js +1 -0
- package/public/dayjslocales/en-au.js +1 -0
- package/public/dayjslocales/en-ca.js +1 -0
- package/public/dayjslocales/en-gb.js +1 -0
- package/public/dayjslocales/en-ie.js +1 -0
- package/public/dayjslocales/en-il.js +1 -0
- package/public/dayjslocales/en-in.js +1 -0
- package/public/dayjslocales/en-nz.js +1 -0
- package/public/dayjslocales/en-sg.js +1 -0
- package/public/dayjslocales/en-tt.js +1 -0
- package/public/dayjslocales/en.js +1 -0
- package/public/dayjslocales/eo.js +1 -0
- package/public/dayjslocales/es-do.js +1 -0
- package/public/dayjslocales/es-mx.js +1 -0
- package/public/dayjslocales/es-pr.js +1 -0
- package/public/dayjslocales/es-us.js +1 -0
- package/public/dayjslocales/es.js +1 -0
- package/public/dayjslocales/et.js +1 -0
- package/public/dayjslocales/eu.js +1 -0
- package/public/dayjslocales/fa.js +1 -0
- package/public/dayjslocales/fi.js +1 -0
- package/public/dayjslocales/fo.js +1 -0
- package/public/dayjslocales/fr-ca.js +1 -0
- package/public/dayjslocales/fr-ch.js +1 -0
- package/public/dayjslocales/fr.js +1 -0
- package/public/dayjslocales/fy.js +1 -0
- package/public/dayjslocales/ga.js +1 -0
- package/public/dayjslocales/gd.js +1 -0
- package/public/dayjslocales/gl.js +1 -0
- package/public/dayjslocales/gom-latn.js +1 -0
- package/public/dayjslocales/gu.js +1 -0
- package/public/dayjslocales/he.js +1 -0
- package/public/dayjslocales/hi.js +1 -0
- package/public/dayjslocales/hr.js +1 -0
- package/public/dayjslocales/ht.js +1 -0
- package/public/dayjslocales/hu.js +1 -0
- package/public/dayjslocales/hy-am.js +1 -0
- package/public/dayjslocales/id.js +1 -0
- package/public/dayjslocales/is.js +1 -0
- package/public/dayjslocales/it-ch.js +1 -0
- package/public/dayjslocales/it.js +1 -0
- package/public/dayjslocales/ja.js +1 -0
- package/public/dayjslocales/jv.js +1 -0
- package/public/dayjslocales/ka.js +1 -0
- package/public/dayjslocales/kk.js +1 -0
- package/public/dayjslocales/km.js +1 -0
- package/public/dayjslocales/kn.js +1 -0
- package/public/dayjslocales/ko.js +1 -0
- package/public/dayjslocales/ku.js +1 -0
- package/public/dayjslocales/ky.js +1 -0
- package/public/dayjslocales/lb.js +1 -0
- package/public/dayjslocales/lo.js +1 -0
- package/public/dayjslocales/lt.js +1 -0
- package/public/dayjslocales/lv.js +1 -0
- package/public/dayjslocales/me.js +1 -0
- package/public/dayjslocales/mi.js +1 -0
- package/public/dayjslocales/mk.js +1 -0
- package/public/dayjslocales/ml.js +1 -0
- package/public/dayjslocales/mn.js +1 -0
- package/public/dayjslocales/mr.js +1 -0
- package/public/dayjslocales/ms-my.js +1 -0
- package/public/dayjslocales/ms.js +1 -0
- package/public/dayjslocales/mt.js +1 -0
- package/public/dayjslocales/my.js +1 -0
- package/public/dayjslocales/nb.js +1 -0
- package/public/dayjslocales/ne.js +1 -0
- package/public/dayjslocales/nl-be.js +1 -0
- package/public/dayjslocales/nl.js +1 -0
- package/public/dayjslocales/nn.js +1 -0
- package/public/dayjslocales/oc-lnc.js +1 -0
- package/public/dayjslocales/pa-in.js +1 -0
- package/public/dayjslocales/pl.js +1 -0
- package/public/dayjslocales/pt-br.js +1 -0
- package/public/dayjslocales/pt.js +1 -0
- package/public/dayjslocales/rn.js +1 -0
- package/public/dayjslocales/ro.js +1 -0
- package/public/dayjslocales/ru.js +1 -0
- package/public/dayjslocales/rw.js +1 -0
- package/public/dayjslocales/sd.js +1 -0
- package/public/dayjslocales/se.js +1 -0
- package/public/dayjslocales/si.js +1 -0
- package/public/dayjslocales/sk.js +1 -0
- package/public/dayjslocales/sl.js +1 -0
- package/public/dayjslocales/sq.js +1 -0
- package/public/dayjslocales/sr-cyrl.js +1 -0
- package/public/dayjslocales/sr.js +1 -0
- package/public/dayjslocales/ss.js +1 -0
- package/public/dayjslocales/sv-fi.js +1 -0
- package/public/dayjslocales/sv.js +1 -0
- package/public/dayjslocales/sw.js +1 -0
- package/public/dayjslocales/ta.js +1 -0
- package/public/dayjslocales/te.js +1 -0
- package/public/dayjslocales/tet.js +1 -0
- package/public/dayjslocales/tg.js +1 -0
- package/public/dayjslocales/th.js +1 -0
- package/public/dayjslocales/tk.js +1 -0
- package/public/dayjslocales/tl-ph.js +1 -0
- package/public/dayjslocales/tlh.js +1 -0
- package/public/dayjslocales/tr.js +1 -0
- package/public/dayjslocales/tzl.js +1 -0
- package/public/dayjslocales/tzm-latn.js +1 -0
- package/public/dayjslocales/tzm.js +1 -0
- package/public/dayjslocales/ug-cn.js +1 -0
- package/public/dayjslocales/uk.js +1 -0
- package/public/dayjslocales/ur.js +1 -0
- package/public/dayjslocales/uz-latn.js +1 -0
- package/public/dayjslocales/uz.js +1 -0
- package/public/dayjslocales/vi.js +1 -0
- package/public/dayjslocales/x-pseudo.js +1 -0
- package/public/dayjslocales/yo.js +1 -0
- package/public/dayjslocales/zh-cn.js +1 -0
- package/public/dayjslocales/zh-hk.js +1 -0
- package/public/dayjslocales/zh-tw.js +1 -0
- package/public/dayjslocales/zh.js +1 -0
- package/public/gridedit.js +2 -2
- package/public/log_viewer_utils.js +156 -0
- package/public/saltcorn-builder.css +62 -3
- package/public/saltcorn-common.js +8 -0
- package/public/saltcorn.js +30 -9
- package/public/tabulator_bootstrap5.min.css +1 -0
- package/restart_watcher.js +1 -0
- package/routes/actions.js +175 -18
- package/routes/admin.js +77 -5
- package/routes/common_lists.js +344 -152
- package/routes/fields.js +29 -5
- package/routes/files.js +3 -1
- package/routes/homepage.js +2 -1
- package/routes/list.js +5 -0
- package/routes/page.js +30 -13
- package/routes/page_groupedit.js +104 -83
- package/routes/pageedit.js +23 -7
- package/routes/tables.js +56 -6
- package/routes/tag_entries.js +18 -5
- package/routes/tags.js +65 -12
- package/routes/utils.js +23 -2
- package/routes/view.js +21 -2
- package/routes/viewedit.js +70 -4
- package/serve.js +177 -10
- package/tests/admin.test.js +17 -11
- package/tests/page_group.test.js +1 -0
- package/tests/table.test.js +1 -5
- package/tests/view.test.js +115 -15
- package/tests/viewedit.test.js +52 -29
- package/wrapper.js +11 -3
- 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,20 +482,50 @@ 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
|
-
key: (r) => link(`/page/${r.name}`, r.name),
|
|
508
|
+
key: (r) => link(`/page/${encodeURIComponent(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),
|
|
384
524
|
},
|
|
385
525
|
{
|
|
386
526
|
label: req.__("Edit"),
|
|
387
|
-
key: (r) =>
|
|
527
|
+
key: (r) =>
|
|
528
|
+
link(`/pageedit/edit/${encodeURIComponent(r.name)}`, req.__("Edit")),
|
|
388
529
|
},
|
|
389
530
|
!tagId
|
|
390
531
|
? {
|
|
@@ -442,19 +583,70 @@ const getPageGroupList = (rows, roles, req) => {
|
|
|
442
583
|
);
|
|
443
584
|
};
|
|
444
585
|
|
|
445
|
-
const
|
|
586
|
+
const trigger_dropdown = (trigger, req, on_done_redirect_str = "") =>
|
|
587
|
+
settingsDropdown(`dropdownMenuButton${trigger.id}`, [
|
|
588
|
+
a(
|
|
589
|
+
{
|
|
590
|
+
class: "dropdown-item",
|
|
591
|
+
href: `/actions/edit/${trigger.id}${on_done_redirect_str}`,
|
|
592
|
+
},
|
|
593
|
+
'<i class="fas fa-edit"></i> ' + req.__("Edit")
|
|
594
|
+
),
|
|
595
|
+
|
|
596
|
+
a(
|
|
597
|
+
{
|
|
598
|
+
class: "dropdown-item",
|
|
599
|
+
href: `javascript:ajax_modal('/admin/snapshot-restore/trigger/${trigger.name}')`,
|
|
600
|
+
},
|
|
601
|
+
'<i class="fas fa-undo-alt"></i> ' + req.__("Restore")
|
|
602
|
+
),
|
|
603
|
+
div({ class: "dropdown-divider" }),
|
|
604
|
+
|
|
605
|
+
post_dropdown_item(
|
|
606
|
+
`/actions/delete/${trigger.id}${on_done_redirect_str}`,
|
|
607
|
+
'<i class="far fa-trash-alt"></i> ' + req.__("Delete"),
|
|
608
|
+
req,
|
|
609
|
+
true,
|
|
610
|
+
trigger.name
|
|
611
|
+
),
|
|
612
|
+
]);
|
|
613
|
+
|
|
614
|
+
const getTriggerList = async (
|
|
615
|
+
triggers,
|
|
616
|
+
req,
|
|
617
|
+
{ tagId, domId, showList, filterOnTag } = {}
|
|
618
|
+
) => {
|
|
446
619
|
const base_url = get_base_url(req);
|
|
620
|
+
const tags = await Tag.find();
|
|
621
|
+
|
|
622
|
+
const tag_entries = await TagEntry.find({
|
|
623
|
+
not: { trigger_id: null },
|
|
624
|
+
});
|
|
625
|
+
const tagsById = {};
|
|
626
|
+
tags.forEach((t) => (tagsById[t.id] = t));
|
|
627
|
+
|
|
628
|
+
const tagBadges = (trigger) => {
|
|
629
|
+
const myTags = tag_entries.filter((te) => te.trigger_id === trigger.id);
|
|
630
|
+
return myTags
|
|
631
|
+
.map((te) => tagBadge(tagsById[te.tag_id], "triggers"))
|
|
632
|
+
.join(nbsp);
|
|
633
|
+
};
|
|
447
634
|
return mkTable(
|
|
448
635
|
[
|
|
449
636
|
{ label: req.__("Name"), key: "name" },
|
|
637
|
+
...(tagId
|
|
638
|
+
? []
|
|
639
|
+
: [
|
|
640
|
+
{
|
|
641
|
+
label: tagsDropdown(
|
|
642
|
+
tags,
|
|
643
|
+
filterOnTag ? `Tag:${filterOnTag.name}` : undefined
|
|
644
|
+
),
|
|
645
|
+
key: (r) => tagBadges(r),
|
|
646
|
+
},
|
|
647
|
+
]),
|
|
450
648
|
{ label: req.__("Action"), key: "action" },
|
|
451
|
-
|
|
452
|
-
label: req.__("Table or Channel"),
|
|
453
|
-
key: (r) =>
|
|
454
|
-
r.table_name
|
|
455
|
-
? a({ href: `/table/${r.table_name}` }, r.table_name)
|
|
456
|
-
: r.channel,
|
|
457
|
-
},
|
|
649
|
+
|
|
458
650
|
{
|
|
459
651
|
label: req.__("When"),
|
|
460
652
|
key: (act) =>
|
|
@@ -469,15 +661,15 @@ const getTriggerList = (triggers, req, { tagId, domId, showList } = {}) => {
|
|
|
469
661
|
: ""),
|
|
470
662
|
},
|
|
471
663
|
{
|
|
472
|
-
label: req.__("
|
|
664
|
+
label: req.__("Table or Channel"),
|
|
473
665
|
key: (r) =>
|
|
474
|
-
r.
|
|
475
|
-
?
|
|
476
|
-
:
|
|
666
|
+
r.table_name
|
|
667
|
+
? a({ href: `/table/${r.table_name}` }, r.table_name)
|
|
668
|
+
: r.channel,
|
|
477
669
|
},
|
|
478
670
|
{
|
|
479
|
-
label: req.__("
|
|
480
|
-
key: (r) => link(`/actions/
|
|
671
|
+
label: req.__("Test run"),
|
|
672
|
+
key: (r) => link(`/actions/testrun/${r.id}`, req.__("Test run")),
|
|
481
673
|
},
|
|
482
674
|
{
|
|
483
675
|
label: req.__("Configure"),
|
|
@@ -485,8 +677,8 @@ const getTriggerList = (triggers, req, { tagId, domId, showList } = {}) => {
|
|
|
485
677
|
},
|
|
486
678
|
!tagId
|
|
487
679
|
? {
|
|
488
|
-
label:
|
|
489
|
-
key: (r) =>
|
|
680
|
+
label: "",
|
|
681
|
+
key: (r) => trigger_dropdown(r, req),
|
|
490
682
|
}
|
|
491
683
|
: {
|
|
492
684
|
label: req.__("Remove From Tag"),
|
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
|
}
|
|
@@ -1171,9 +1180,24 @@ router.post(
|
|
|
1171
1180
|
type,
|
|
1172
1181
|
join_field,
|
|
1173
1182
|
join_fieldview,
|
|
1183
|
+
agg_outcome_type,
|
|
1184
|
+
agg_fieldview,
|
|
1185
|
+
agg_field,
|
|
1174
1186
|
_columndef,
|
|
1175
1187
|
} = req.body;
|
|
1176
1188
|
const table = Table.findOne({ name: tableName });
|
|
1189
|
+
if (agg_outcome_type && agg_fieldview) {
|
|
1190
|
+
const type = getState().types[agg_outcome_type];
|
|
1191
|
+
const fv = type?.fieldviews?.[agg_fieldview];
|
|
1192
|
+
if (!fv?.configFields) {
|
|
1193
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
const field = table.getField(agg_field);
|
|
1197
|
+
const cfgfields = await applyAsync(fv.configFields, field || { table });
|
|
1198
|
+
res.json(cfgfields);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1177
1201
|
if (typeof type !== "string") {
|
|
1178
1202
|
try {
|
|
1179
1203
|
type = JSON.parse(_columndef).type;
|
|
@@ -1184,19 +1208,19 @@ router.post(
|
|
|
1184
1208
|
const fieldName = type == "Field" ? field_name : join_field;
|
|
1185
1209
|
const fv_name = type == "Field" ? fieldview : join_fieldview;
|
|
1186
1210
|
if (!fieldName) {
|
|
1187
|
-
res.send("");
|
|
1211
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1188
1212
|
return;
|
|
1189
1213
|
}
|
|
1190
1214
|
|
|
1191
1215
|
const field = table.getField(fieldName);
|
|
1192
1216
|
if (!field) {
|
|
1193
|
-
res.send("");
|
|
1217
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1194
1218
|
return;
|
|
1195
1219
|
}
|
|
1196
1220
|
const fieldViewConfigForms = await calcfldViewConfig([field], false, 0);
|
|
1197
1221
|
const formFields = fieldViewConfigForms[field.name][fv_name];
|
|
1198
1222
|
if (!formFields) {
|
|
1199
|
-
res.send("");
|
|
1223
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1200
1224
|
return;
|
|
1201
1225
|
}
|
|
1202
1226
|
formFields.forEach((ff) => {
|
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",
|