@saltcorn/server 0.9.4-beta.8 → 0.9.4
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 +16 -4
- package/auth/testhelp.js +17 -1
- package/load_plugins.js +8 -2
- package/locales/en.json +29 -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 +43 -2
- package/public/saltcorn-common.js +39 -29
- package/public/saltcorn.js +29 -8
- package/public/tabulator_bootstrap5.min.css +1 -0
- package/restart_watcher.js +1 -0
- package/routes/actions.js +175 -18
- package/routes/admin.js +83 -9
- package/routes/common_lists.js +344 -152
- package/routes/fields.js +18 -3
- package/routes/homepage.js +2 -1
- package/routes/page.js +30 -13
- package/routes/page_groupedit.js +104 -83
- package/routes/pageedit.js +23 -7
- package/routes/tables.js +51 -5
- package/routes/tag_entries.js +18 -5
- package/routes/tags.js +65 -12
- package/routes/utils.js +23 -2
- package/routes/view.js +12 -1
- package/routes/viewedit.js +46 -3
- package/serve.js +177 -10
- package/tests/admin.test.js +17 -11
- package/tests/api.test.js +27 -0
- package/tests/fields.test.js +132 -5
- package/tests/help.test.js +37 -0
- package/tests/page_group.test.js +1 -0
- package/tests/plugins.test.js +0 -12
- package/tests/table.test.js +1 -5
- package/tests/view.test.js +127 -15
- package/tests/viewedit.test.js +52 -8
- package/wrapper.js +9 -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,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
|
@@ -1180,9 +1180,24 @@ router.post(
|
|
|
1180
1180
|
type,
|
|
1181
1181
|
join_field,
|
|
1182
1182
|
join_fieldview,
|
|
1183
|
+
agg_outcome_type,
|
|
1184
|
+
agg_fieldview,
|
|
1185
|
+
agg_field,
|
|
1183
1186
|
_columndef,
|
|
1184
1187
|
} = req.body;
|
|
1185
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
|
+
}
|
|
1186
1201
|
if (typeof type !== "string") {
|
|
1187
1202
|
try {
|
|
1188
1203
|
type = JSON.parse(_columndef).type;
|
|
@@ -1193,19 +1208,19 @@ router.post(
|
|
|
1193
1208
|
const fieldName = type == "Field" ? field_name : join_field;
|
|
1194
1209
|
const fv_name = type == "Field" ? fieldview : join_fieldview;
|
|
1195
1210
|
if (!fieldName) {
|
|
1196
|
-
res.send("");
|
|
1211
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1197
1212
|
return;
|
|
1198
1213
|
}
|
|
1199
1214
|
|
|
1200
1215
|
const field = table.getField(fieldName);
|
|
1201
1216
|
if (!field) {
|
|
1202
|
-
res.send("");
|
|
1217
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1203
1218
|
return;
|
|
1204
1219
|
}
|
|
1205
1220
|
const fieldViewConfigForms = await calcfldViewConfig([field], false, 0);
|
|
1206
1221
|
const formFields = fieldViewConfigForms[field.name][fv_name];
|
|
1207
1222
|
if (!formFields) {
|
|
1208
|
-
res.send("");
|
|
1223
|
+
res.send(req.query?.accept == "json" ? "[]" : "");
|
|
1209
1224
|
return;
|
|
1210
1225
|
}
|
|
1211
1226
|
formFields.forEach((ff) => {
|
package/routes/homepage.js
CHANGED
|
@@ -24,6 +24,7 @@ const packagejson = require("../package.json");
|
|
|
24
24
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
25
25
|
const { fileUploadForm } = require("../markup/forms");
|
|
26
26
|
const { get_base_url, sendHtmlFile, getEligiblePage } = require("./utils.js");
|
|
27
|
+
const semver = require("semver");
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Tables List
|
|
@@ -519,8 +520,8 @@ const no_views_logged_in = async (req, res) => {
|
|
|
519
520
|
const latest =
|
|
520
521
|
isRoot && (await get_latest_npm_version("@saltcorn/cli", 500));
|
|
521
522
|
const can_update =
|
|
522
|
-
packagejson.version !== latest &&
|
|
523
523
|
latest &&
|
|
524
|
+
semver.gt(latest, packagejson.version) &&
|
|
524
525
|
!process.env.SALTCORN_DISABLE_UPGRADE;
|
|
525
526
|
if (latest && can_update && isRoot)
|
|
526
527
|
req.flash(
|