@saltcorn/server 0.7.3 → 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.
- package/auth/admin.js +2 -1
- package/auth/routes.js +80 -92
- package/errors.js +51 -48
- package/locales/en.json +47 -2
- package/locales/it.json +2 -1
- package/locales/ru.json +42 -6
- package/locales/zh.json +1 -1
- package/markup/admin.js +15 -1
- package/markup/plugin-store.js +5 -5
- package/package.json +7 -7
- package/public/jquery-menu-editor.min.js +1 -1
- package/public/saltcorn-builder.css +75 -0
- package/public/saltcorn-common.js +24 -9
- package/public/saltcorn.css +28 -1
- package/public/saltcorn.js +9 -7
- package/routes/actions.js +2 -39
- package/routes/admin.js +375 -90
- package/routes/api.js +9 -1
- package/routes/common_lists.js +419 -0
- package/routes/fields.js +34 -19
- package/routes/homepage.js +60 -60
- package/routes/index.js +4 -0
- package/routes/menu.js +65 -4
- package/routes/packs.js +4 -4
- package/routes/page.js +5 -1
- package/routes/pageedit.js +13 -98
- package/routes/plugins.js +116 -118
- package/routes/settings.js +3 -3
- package/routes/tables.js +158 -193
- package/routes/tag_entries.js +173 -0
- package/routes/tags.js +266 -0
- package/routes/tenant.js +27 -27
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +22 -132
- package/serve.js +54 -38
- package/tests/admin.test.js +1 -1
- package/tests/api.test.js +17 -0
- package/tests/clientjs.test.js +11 -1
- package/tests/plugins.test.js +1 -1
- package/tests/viewedit.test.js +1 -1
- package/wrapper.js +57 -55
package/routes/tags.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
const { a, text } = require("@saltcorn/markup/tags");
|
|
2
|
+
|
|
3
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
4
|
+
const Router = require("express-promise-router");
|
|
5
|
+
const Form = require("@saltcorn/data/models/form");
|
|
6
|
+
const User = require("@saltcorn/data/models/user");
|
|
7
|
+
|
|
8
|
+
const { isAdmin, error_catcher, csrfField } = require("./utils");
|
|
9
|
+
const { send_admin_page } = require("../markup/admin");
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
mkTable,
|
|
13
|
+
post_delete_btn,
|
|
14
|
+
link,
|
|
15
|
+
renderForm,
|
|
16
|
+
} = require("@saltcorn/markup");
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
tablesList,
|
|
20
|
+
setTableRefs,
|
|
21
|
+
viewsList,
|
|
22
|
+
getPageList,
|
|
23
|
+
getTriggerList,
|
|
24
|
+
} = require("./common_lists");
|
|
25
|
+
|
|
26
|
+
const router = new Router();
|
|
27
|
+
module.exports = router;
|
|
28
|
+
|
|
29
|
+
router.get(
|
|
30
|
+
"/",
|
|
31
|
+
isAdmin,
|
|
32
|
+
error_catcher(async (req, res) => {
|
|
33
|
+
const rows = await Tag.find();
|
|
34
|
+
send_admin_page({
|
|
35
|
+
res,
|
|
36
|
+
req,
|
|
37
|
+
active_sub: "Tags",
|
|
38
|
+
contents: [
|
|
39
|
+
mkTable(
|
|
40
|
+
[
|
|
41
|
+
{
|
|
42
|
+
label: req.__("Tagname"),
|
|
43
|
+
key: (r) =>
|
|
44
|
+
link(`/tag/${r.id || r.name}?show_list=tables`, text(r.name)),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: req.__("Delete"),
|
|
48
|
+
key: (r) => post_delete_btn(`/tag/delete/${r.id}`, req, r.name),
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
rows,
|
|
52
|
+
{}
|
|
53
|
+
),
|
|
54
|
+
a(
|
|
55
|
+
{
|
|
56
|
+
href: `/tag/new`,
|
|
57
|
+
class: "btn btn-primary",
|
|
58
|
+
},
|
|
59
|
+
req.__("Create tag")
|
|
60
|
+
),
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
})
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
router.get(
|
|
67
|
+
"/new",
|
|
68
|
+
isAdmin,
|
|
69
|
+
error_catcher(async (req, res) => {
|
|
70
|
+
res.sendWrap(req.__(`New tag`), {
|
|
71
|
+
above: [
|
|
72
|
+
{
|
|
73
|
+
type: "card",
|
|
74
|
+
title: req.__(`New tag`),
|
|
75
|
+
contents: renderForm(
|
|
76
|
+
new Form({
|
|
77
|
+
action: "/tag",
|
|
78
|
+
submitLabel: req.__("Create"),
|
|
79
|
+
fields: [
|
|
80
|
+
{
|
|
81
|
+
label: req.__("Tag name"),
|
|
82
|
+
name: "name",
|
|
83
|
+
input_type: "text",
|
|
84
|
+
required: true,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
}),
|
|
88
|
+
req.csrfToken()
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
});
|
|
93
|
+
})
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const headerWithCollapser = (title, cardId, showList) =>
|
|
97
|
+
a(
|
|
98
|
+
{
|
|
99
|
+
class: `card-header-left-collapse ${!showList ? "collapsed" : ""} ps-3`,
|
|
100
|
+
"data-bs-toggle": "collapse",
|
|
101
|
+
href: `#${cardId}`,
|
|
102
|
+
"aria-expanded": "false",
|
|
103
|
+
"aria-controls": cardId,
|
|
104
|
+
role: "button",
|
|
105
|
+
},
|
|
106
|
+
title
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const isShowList = (showList, listType) => showList === listType;
|
|
110
|
+
|
|
111
|
+
router.get(
|
|
112
|
+
"/:idorname",
|
|
113
|
+
isAdmin,
|
|
114
|
+
error_catcher(async (req, res) => {
|
|
115
|
+
const { idorname } = req.params;
|
|
116
|
+
const { show_list } = req.query;
|
|
117
|
+
const id = parseInt(idorname);
|
|
118
|
+
const tag = await Tag.findOne(id ? { id } : { name: idorname });
|
|
119
|
+
if (!tag) {
|
|
120
|
+
req.flash("error", req.__("Tag not found"));
|
|
121
|
+
return res.redirect(`/tag`);
|
|
122
|
+
}
|
|
123
|
+
const tables = await tag.getTables();
|
|
124
|
+
const views = await tag.getViews();
|
|
125
|
+
await setTableRefs(views);
|
|
126
|
+
const pages = await tag.getPages();
|
|
127
|
+
const trigger = await tag.getTrigger();
|
|
128
|
+
const roles = await User.get_roles();
|
|
129
|
+
|
|
130
|
+
const tablesDomId = "tablesListId";
|
|
131
|
+
const viewsDomId = "viewsListId";
|
|
132
|
+
const pagesDomId = "pagesDomId";
|
|
133
|
+
const triggerDomId = "triggerDomId";
|
|
134
|
+
res.sendWrap(req.__("%s Tag", tag.name), {
|
|
135
|
+
above: [
|
|
136
|
+
{
|
|
137
|
+
type: "breadcrumbs",
|
|
138
|
+
crumbs: [{ text: `Tag: ${tag.name}` }],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
type: "card",
|
|
142
|
+
title: headerWithCollapser(
|
|
143
|
+
req.__("Tables"),
|
|
144
|
+
tablesDomId,
|
|
145
|
+
isShowList(show_list, "tables")
|
|
146
|
+
),
|
|
147
|
+
contents: [
|
|
148
|
+
await tablesList(tables, req, {
|
|
149
|
+
tagId: tag.id,
|
|
150
|
+
domId: tablesDomId,
|
|
151
|
+
showList: isShowList(show_list, "tables"),
|
|
152
|
+
}),
|
|
153
|
+
a(
|
|
154
|
+
{
|
|
155
|
+
href: `/tag-entries/add/tables/${tag.id}`,
|
|
156
|
+
class: "btn btn-primary",
|
|
157
|
+
},
|
|
158
|
+
req.__("Add tables")
|
|
159
|
+
),
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
type: "card",
|
|
164
|
+
title: headerWithCollapser(
|
|
165
|
+
req.__("Views"),
|
|
166
|
+
viewsDomId,
|
|
167
|
+
isShowList(show_list, "views")
|
|
168
|
+
),
|
|
169
|
+
contents: [
|
|
170
|
+
await viewsList(views, req, {
|
|
171
|
+
tagId: tag.id,
|
|
172
|
+
domId: viewsDomId,
|
|
173
|
+
showList: isShowList(show_list, "views"),
|
|
174
|
+
}),
|
|
175
|
+
a(
|
|
176
|
+
{
|
|
177
|
+
href: `/tag-entries/add/views/${tag.id}`,
|
|
178
|
+
class: "btn btn-primary",
|
|
179
|
+
},
|
|
180
|
+
req.__("Add views")
|
|
181
|
+
),
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: "card",
|
|
186
|
+
title: headerWithCollapser(
|
|
187
|
+
req.__("Pages"),
|
|
188
|
+
pagesDomId,
|
|
189
|
+
isShowList(show_list, "pages")
|
|
190
|
+
),
|
|
191
|
+
contents: [
|
|
192
|
+
getPageList(pages, roles, req, {
|
|
193
|
+
tagId: tag.id,
|
|
194
|
+
domId: pagesDomId,
|
|
195
|
+
showList: isShowList(show_list, "pages"),
|
|
196
|
+
}),
|
|
197
|
+
a(
|
|
198
|
+
{
|
|
199
|
+
href: `/tag-entries/add/pages/${tag.id}`,
|
|
200
|
+
class: "btn btn-primary",
|
|
201
|
+
},
|
|
202
|
+
req.__("Add tages")
|
|
203
|
+
),
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: "card",
|
|
208
|
+
bodyId: "collapseTriggerCard",
|
|
209
|
+
title: headerWithCollapser(
|
|
210
|
+
req.__("Trigger"),
|
|
211
|
+
triggerDomId,
|
|
212
|
+
isShowList(show_list, "trigger")
|
|
213
|
+
),
|
|
214
|
+
contents: [
|
|
215
|
+
getTriggerList(trigger, req, {
|
|
216
|
+
tagId: tag.id,
|
|
217
|
+
domId: triggerDomId,
|
|
218
|
+
showList: isShowList(show_list, "trigger"),
|
|
219
|
+
}),
|
|
220
|
+
a(
|
|
221
|
+
{
|
|
222
|
+
href: `/tag-entries/add/trigger/${tag.id}`,
|
|
223
|
+
class: "btn btn-primary",
|
|
224
|
+
},
|
|
225
|
+
req.__("Add trigger")
|
|
226
|
+
),
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// create
|
|
235
|
+
router.post(
|
|
236
|
+
"/",
|
|
237
|
+
isAdmin,
|
|
238
|
+
error_catcher(async (req, res) => {
|
|
239
|
+
const { name } = req.body;
|
|
240
|
+
const tag = await Tag.create({ name });
|
|
241
|
+
req.flash("success", req.__(`Tag %s created`, name));
|
|
242
|
+
res.redirect(`/tag/${tag.id}?show_list=tables`);
|
|
243
|
+
})
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
// delete
|
|
247
|
+
router.post(
|
|
248
|
+
"/delete/:id",
|
|
249
|
+
isAdmin,
|
|
250
|
+
error_catcher(async (req, res) => {
|
|
251
|
+
const { id } = req.params;
|
|
252
|
+
const tag = await Tag.findOne({ id });
|
|
253
|
+
if (!tag) {
|
|
254
|
+
req.flash("error", req.__("Tag not found"));
|
|
255
|
+
return res.redirect("/tag");
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
await tag.delete();
|
|
259
|
+
req.flash("success", req.__("Tag %s deleted", tag.name));
|
|
260
|
+
res.redirect(`/tag`);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
req.flash("error", error.message);
|
|
263
|
+
res.redirect(`/tag`);
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
);
|
package/routes/tenant.js
CHANGED
|
@@ -161,32 +161,32 @@ router.get(
|
|
|
161
161
|
req.__(
|
|
162
162
|
"Hosting on this site is provided for free and with no guarantee of availability or security of your application. "
|
|
163
163
|
) +
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
164
|
+
" " +
|
|
165
|
+
req.__(
|
|
166
|
+
"This facility is intended solely for you to evaluate the suitability of Saltcorn. "
|
|
167
|
+
) +
|
|
168
|
+
" " +
|
|
169
|
+
req.__(
|
|
170
|
+
"If you would like to store private information that needs to be secure, please use self-hosted Saltcorn. "
|
|
171
|
+
) +
|
|
172
|
+
" " +
|
|
173
|
+
req.__(
|
|
174
|
+
'See <a href="https://github.com/saltcorn/saltcorn">GitHub repository</a> for instructions<p>'
|
|
175
|
+
)
|
|
176
176
|
)
|
|
177
177
|
);
|
|
178
178
|
|
|
179
179
|
res.sendWrap(
|
|
180
180
|
req.__("Create application"),
|
|
181
181
|
create_tenant_warning +
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
182
|
+
renderForm(tenant_form(req), req.csrfToken()) +
|
|
183
|
+
p(
|
|
184
|
+
{ class: "mt-2" },
|
|
185
|
+
req.__("To login to a previously created application, go to: "),
|
|
186
|
+
code(`${req.protocol}://`) +
|
|
187
|
+
i(req.__("Application name")) +
|
|
188
|
+
code("." + req.hostname)
|
|
189
|
+
)
|
|
190
190
|
);
|
|
191
191
|
})
|
|
192
192
|
);
|
|
@@ -294,13 +294,13 @@ router.post(
|
|
|
294
294
|
" " +
|
|
295
295
|
hasTemplate
|
|
296
296
|
? req.__(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
297
|
+
'Use this link: <a href="%s">%s</a> to revisit your application at any time.',
|
|
298
|
+
newurl,
|
|
299
|
+
newurl
|
|
300
|
+
)
|
|
301
301
|
: req.__(
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
"Use this link to revisit your application at any time."
|
|
303
|
+
)
|
|
304
304
|
)
|
|
305
305
|
)
|
|
306
306
|
);
|
|
@@ -586,7 +586,7 @@ router.get(
|
|
|
586
586
|
td(a({ href: info.base_url + "actions" }, info.nactions))
|
|
587
587
|
),
|
|
588
588
|
tr(
|
|
589
|
-
th(req.__("
|
|
589
|
+
th(req.__("Modules")),
|
|
590
590
|
td(a({ href: info.base_url + "plugins" }, info.nplugins))
|
|
591
591
|
),
|
|
592
592
|
tr(
|
package/routes/utils.js
CHANGED
|
@@ -128,6 +128,7 @@ const setTenant = (req, res, next) => {
|
|
|
128
128
|
} else {
|
|
129
129
|
db.runWithTenant(other_domain, () => {
|
|
130
130
|
setLanguage(req, res, state);
|
|
131
|
+
state.log(5, `${req.method} ${req.originalUrl}`);
|
|
131
132
|
next();
|
|
132
133
|
});
|
|
133
134
|
}
|
|
@@ -140,12 +141,14 @@ const setTenant = (req, res, next) => {
|
|
|
140
141
|
} else {
|
|
141
142
|
db.runWithTenant(ten, () => {
|
|
142
143
|
setLanguage(req, res, state);
|
|
144
|
+
state.log(5, `${req.method} ${req.originalUrl}`);
|
|
143
145
|
next();
|
|
144
146
|
});
|
|
145
147
|
}
|
|
146
148
|
}
|
|
147
149
|
} else {
|
|
148
150
|
setLanguage(req, res);
|
|
151
|
+
getState().log(5, `${req.method} ${req.originalUrl}`);
|
|
149
152
|
next();
|
|
150
153
|
}
|
|
151
154
|
};
|
|
@@ -240,4 +243,5 @@ module.exports = {
|
|
|
240
243
|
getGitRevision,
|
|
241
244
|
getSessionStore,
|
|
242
245
|
setTenant,
|
|
246
|
+
get_tenant_from_req
|
|
243
247
|
};
|
package/routes/view.js
CHANGED
|
@@ -20,6 +20,7 @@ const {
|
|
|
20
20
|
} = require("../routes/utils.js");
|
|
21
21
|
const { add_edit_bar } = require("../markup/admin.js");
|
|
22
22
|
const { InvalidConfiguration } = require("@saltcorn/data/utils");
|
|
23
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* @type {object}
|
|
@@ -44,8 +45,11 @@ router.get(
|
|
|
44
45
|
const query = { ...req.query };
|
|
45
46
|
const view = await View.findOne({ name: viewname });
|
|
46
47
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
48
|
+
const state = getState();
|
|
49
|
+
state.log(3, `Route /view/${viewname} user=${req.user?.id}`);
|
|
47
50
|
if (!view) {
|
|
48
51
|
req.flash("danger", req.__(`No such view: %s`, text(viewname)));
|
|
52
|
+
state.log(2, `View ${viewname} not found`);
|
|
49
53
|
res.redirect("/");
|
|
50
54
|
return;
|
|
51
55
|
}
|
|
@@ -56,6 +60,7 @@ router.get(
|
|
|
56
60
|
!(await view.authorise_get({ query, req, ...view }))
|
|
57
61
|
) {
|
|
58
62
|
req.flash("danger", req.__("Not authorized"));
|
|
63
|
+
state.log(2, `View ${viewname} not authorized`);
|
|
59
64
|
res.redirect("/");
|
|
60
65
|
return;
|
|
61
66
|
}
|
|
@@ -123,13 +128,21 @@ router.post(
|
|
|
123
128
|
error_catcher(async (req, res) => {
|
|
124
129
|
const { viewname, route } = req.params;
|
|
125
130
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
131
|
+
const state = getState();
|
|
132
|
+
state.log(
|
|
133
|
+
3,
|
|
134
|
+
`Route /view/${viewname} viewroute ${route} user=${req.user?.id}`
|
|
135
|
+
);
|
|
126
136
|
|
|
127
137
|
const view = await View.findOne({ name: viewname });
|
|
128
138
|
if (!view) {
|
|
129
139
|
req.flash("danger", req.__(`No such view: %s`, text(viewname)));
|
|
140
|
+
state.log(2, `View ${viewname} not found`);
|
|
130
141
|
res.redirect("/");
|
|
131
142
|
} else if (role > view.min_role) {
|
|
132
143
|
req.flash("danger", req.__("Not authorized"));
|
|
144
|
+
state.log(2, `View ${viewname} viewroute ${route} not authorized`);
|
|
145
|
+
|
|
133
146
|
res.redirect("/");
|
|
134
147
|
} else {
|
|
135
148
|
await view.runRoute(route, req.body, res, { res, req });
|
|
@@ -150,10 +163,12 @@ router.post(
|
|
|
150
163
|
const { viewname } = req.params;
|
|
151
164
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
152
165
|
const query = { ...req.query };
|
|
153
|
-
|
|
166
|
+
const state = getState();
|
|
167
|
+
state.log(3, `Route /view/${viewname} POST user=${req.user?.id}`);
|
|
154
168
|
const view = await View.findOne({ name: viewname });
|
|
155
169
|
if (!view) {
|
|
156
170
|
req.flash("danger", req.__(`No such view: %s`, text(viewname)));
|
|
171
|
+
state.log(2, `View ${viewname} not found`);
|
|
157
172
|
res.redirect("/");
|
|
158
173
|
return;
|
|
159
174
|
}
|
|
@@ -164,6 +179,8 @@ router.post(
|
|
|
164
179
|
!(await view.authorise_post({ body: req.body, req, ...view }))
|
|
165
180
|
) {
|
|
166
181
|
req.flash("danger", req.__("Not authorized"));
|
|
182
|
+
state.log(2, `View ${viewname} POST not authorized`);
|
|
183
|
+
|
|
167
184
|
res.redirect("/");
|
|
168
185
|
} else if (!view.runPost) {
|
|
169
186
|
throw new InvalidConfiguration(
|
package/routes/viewedit.js
CHANGED
|
@@ -31,6 +31,7 @@ const {
|
|
|
31
31
|
|
|
32
32
|
const { getState } = require("@saltcorn/data/db/state");
|
|
33
33
|
const { isAdmin, error_catcher } = require("./utils.js");
|
|
34
|
+
const { setTableRefs, viewsList } = require("./common_lists");
|
|
34
35
|
const Form = require("@saltcorn/data/models/form");
|
|
35
36
|
const Field = require("@saltcorn/data/models/field");
|
|
36
37
|
const Table = require("@saltcorn/data/models/table");
|
|
@@ -38,10 +39,10 @@ const View = require("@saltcorn/data/models/view");
|
|
|
38
39
|
const Workflow = require("@saltcorn/data/models/workflow");
|
|
39
40
|
const User = require("@saltcorn/data/models/user");
|
|
40
41
|
const Page = require("@saltcorn/data/models/page");
|
|
42
|
+
const Tag = require("@saltcorn/data/models/tag");
|
|
41
43
|
const db = require("@saltcorn/data/db");
|
|
42
44
|
|
|
43
45
|
const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
|
|
44
|
-
const { editRoleForm } = require("../markup/forms.js");
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
48
|
* @type {object}
|
|
@@ -53,61 +54,6 @@ const { editRoleForm } = require("../markup/forms.js");
|
|
|
53
54
|
const router = new Router();
|
|
54
55
|
module.exports = router;
|
|
55
56
|
|
|
56
|
-
/**
|
|
57
|
-
* @param {object} view
|
|
58
|
-
* @param {object[]} roles
|
|
59
|
-
* @param {object} req
|
|
60
|
-
* @returns {Form}
|
|
61
|
-
*/
|
|
62
|
-
const editViewRoleForm = (view, roles, req) =>
|
|
63
|
-
editRoleForm({
|
|
64
|
-
url: `/viewedit/setrole/${view.id}`,
|
|
65
|
-
current_role: view.min_role,
|
|
66
|
-
roles,
|
|
67
|
-
req,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param {object} view
|
|
72
|
-
* @param {object} req
|
|
73
|
-
* @returns {div}
|
|
74
|
-
*/
|
|
75
|
-
const view_dropdown = (view, req) =>
|
|
76
|
-
settingsDropdown(`dropdownMenuButton${view.id}`, [
|
|
77
|
-
a(
|
|
78
|
-
{
|
|
79
|
-
class: "dropdown-item",
|
|
80
|
-
href: `/view/${encodeURIComponent(view.name)}`,
|
|
81
|
-
},
|
|
82
|
-
'<i class="fas fa-running"></i> ' + req.__("Run")
|
|
83
|
-
),
|
|
84
|
-
a(
|
|
85
|
-
{
|
|
86
|
-
class: "dropdown-item",
|
|
87
|
-
href: `/viewedit/edit/${encodeURIComponent(view.name)}`,
|
|
88
|
-
},
|
|
89
|
-
'<i class="fas fa-edit"></i> ' + req.__("Edit")
|
|
90
|
-
),
|
|
91
|
-
post_dropdown_item(
|
|
92
|
-
`/viewedit/add-to-menu/${view.id}`,
|
|
93
|
-
'<i class="fas fa-bars"></i> ' + req.__("Add to menu"),
|
|
94
|
-
req
|
|
95
|
-
),
|
|
96
|
-
post_dropdown_item(
|
|
97
|
-
`/viewedit/clone/${view.id}`,
|
|
98
|
-
'<i class="far fa-copy"></i> ' + req.__("Duplicate"),
|
|
99
|
-
req
|
|
100
|
-
),
|
|
101
|
-
div({ class: "dropdown-divider" }),
|
|
102
|
-
post_dropdown_item(
|
|
103
|
-
`/viewedit/delete/${view.id}`,
|
|
104
|
-
'<i class="far fa-trash-alt"></i> ' + req.__("Delete"),
|
|
105
|
-
req,
|
|
106
|
-
true,
|
|
107
|
-
view.name
|
|
108
|
-
),
|
|
109
|
-
]);
|
|
110
|
-
|
|
111
57
|
/**
|
|
112
58
|
* @name get
|
|
113
59
|
* @function
|
|
@@ -118,77 +64,18 @@ router.get(
|
|
|
118
64
|
"/",
|
|
119
65
|
isAdmin,
|
|
120
66
|
error_catcher(async (req, res) => {
|
|
121
|
-
|
|
67
|
+
let orderBy = "name";
|
|
122
68
|
if (req.query._sortby === "viewtemplate") orderBy = "viewtemplate";
|
|
69
|
+
const views = await View.find({}, { orderBy, nocase: true });
|
|
70
|
+
await setTableRefs(views);
|
|
123
71
|
|
|
124
|
-
var views = await View.find({}, { orderBy, nocase: true });
|
|
125
|
-
const tables = await Table.find();
|
|
126
|
-
const getTable = (tid) => tables.find((t) => t.id === tid).name;
|
|
127
|
-
views.forEach((v) => {
|
|
128
|
-
if (v.table_id) v.table = getTable(v.table_id);
|
|
129
|
-
else if (v.exttable_name) v.table = v.exttable_name;
|
|
130
|
-
else v.table = "";
|
|
131
|
-
});
|
|
132
72
|
if (req.query._sortby === "table")
|
|
133
73
|
views.sort((a, b) =>
|
|
134
74
|
a.table.toLowerCase() > b.table.toLowerCase() ? 1 : -1
|
|
135
75
|
);
|
|
136
|
-
const roles = await User.get_roles();
|
|
137
76
|
|
|
138
|
-
const viewMarkup =
|
|
139
|
-
|
|
140
|
-
? mkTable(
|
|
141
|
-
[
|
|
142
|
-
{
|
|
143
|
-
label: req.__("Name"),
|
|
144
|
-
key: (r) => link(`/view/${encodeURIComponent(r.name)}`, r.name),
|
|
145
|
-
sortlink: `javascript:set_state_field('_sortby', 'name')`,
|
|
146
|
-
},
|
|
147
|
-
// description - currently I dont want to show description in view list
|
|
148
|
-
// because description can be long
|
|
149
|
-
/*
|
|
150
|
-
{
|
|
151
|
-
label: req.__("Description"),
|
|
152
|
-
key: "description",
|
|
153
|
-
// this is sorting by column
|
|
154
|
-
sortlink: `javascript:set_state_field('_sortby', 'description')`,
|
|
155
|
-
},
|
|
156
|
-
*/
|
|
157
|
-
// template
|
|
158
|
-
{
|
|
159
|
-
label: req.__("Template"),
|
|
160
|
-
key: "viewtemplate",
|
|
161
|
-
sortlink: `javascript:set_state_field('_sortby', 'viewtemplate')`,
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
label: req.__("Table"),
|
|
165
|
-
key: (r) => link(`/table/${r.table}`, r.table),
|
|
166
|
-
sortlink: `javascript:set_state_field('_sortby', 'table')`,
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
label: req.__("Role to access"),
|
|
170
|
-
key: (row) => editViewRoleForm(row, roles, req),
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
label: "",
|
|
174
|
-
key: (r) =>
|
|
175
|
-
link(
|
|
176
|
-
`/viewedit/config/${encodeURIComponent(r.name)}`,
|
|
177
|
-
req.__("Configure")
|
|
178
|
-
),
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
label: "",
|
|
182
|
-
key: (r) => view_dropdown(r, req),
|
|
183
|
-
},
|
|
184
|
-
],
|
|
185
|
-
views,
|
|
186
|
-
{ hover: true }
|
|
187
|
-
)
|
|
188
|
-
: div(
|
|
189
|
-
h4(req.__("No views defined")),
|
|
190
|
-
p(req.__("Views define how table rows are displayed to the user"))
|
|
191
|
-
);
|
|
77
|
+
const viewMarkup = await viewsList(views, req);
|
|
78
|
+
const tables = await Table.find();
|
|
192
79
|
res.sendWrap(req.__(`Views`), {
|
|
193
80
|
above: [
|
|
194
81
|
{
|
|
@@ -263,10 +150,12 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
263
150
|
),
|
|
264
151
|
}),
|
|
265
152
|
new Field({
|
|
266
|
-
label: req.__("
|
|
153
|
+
label: req.__("View pattern"),
|
|
267
154
|
name: "viewtemplate",
|
|
268
155
|
input_type: "select",
|
|
269
|
-
sublabel: req.__(
|
|
156
|
+
sublabel: req.__(
|
|
157
|
+
"The view pattern sets the foundation of how the view relates to the table and the behaviour of the view"
|
|
158
|
+
),
|
|
270
159
|
options: Object.keys(getState().viewtemplates),
|
|
271
160
|
attributes: {
|
|
272
161
|
explainers: mapObjectValues(
|
|
@@ -320,15 +209,15 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
320
209
|
}),
|
|
321
210
|
...(isEdit
|
|
322
211
|
? [
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
212
|
+
new Field({
|
|
213
|
+
name: "viewtemplate",
|
|
214
|
+
input_type: "hidden",
|
|
215
|
+
}),
|
|
216
|
+
new Field({
|
|
217
|
+
name: "table_name",
|
|
218
|
+
input_type: "hidden",
|
|
219
|
+
}),
|
|
220
|
+
]
|
|
332
221
|
: []),
|
|
333
222
|
],
|
|
334
223
|
values,
|
|
@@ -355,7 +244,8 @@ router.get(
|
|
|
355
244
|
}
|
|
356
245
|
const tables = await Table.find_with_external();
|
|
357
246
|
const currentTable = tables.find(
|
|
358
|
-
(t) =>
|
|
247
|
+
(t) =>
|
|
248
|
+
(t.id && t.id === viewrow.table_id) || t.name === viewrow.exttable_name
|
|
359
249
|
);
|
|
360
250
|
viewrow.table_name = currentTable && currentTable.name;
|
|
361
251
|
if (viewrow.slug && currentTable) {
|