@saltcorn/server 0.7.3-beta.7 → 0.7.4-beta.1
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 +9 -5
- package/auth/routes.js +16 -6
- package/errors.js +51 -48
- package/locales/en.json +29 -1
- package/locales/it.json +2 -1
- package/locales/ru.json +42 -6
- package/locales/zh.json +1 -1
- package/markup/admin.js +4 -3
- 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 +26 -10
- package/public/saltcorn.css +4 -0
- package/public/saltcorn.js +5 -1
- package/routes/admin.js +387 -96
- package/routes/api.js +9 -1
- package/routes/eventlog.js +24 -22
- package/routes/fields.js +11 -13
- package/routes/files.js +5 -5
- package/routes/homepage.js +60 -60
- package/routes/infoarch.js +6 -3
- package/routes/menu.js +65 -4
- package/routes/packs.js +4 -4
- package/routes/page.js +5 -1
- package/routes/pageedit.js +9 -1
- package/routes/plugins.js +187 -123
- package/routes/search.js +4 -2
- package/routes/settings.js +3 -3
- package/routes/tables.js +191 -190
- package/routes/tenant.js +31 -29
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +78 -70
- 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/api.js
CHANGED
|
@@ -112,6 +112,13 @@ function accessAllowed(req, user, trigger) {
|
|
|
112
112
|
return role <= trigger.min_role;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
const getFlashes = (req) =>
|
|
116
|
+
["error", "success", "danger", "warning", "information"]
|
|
117
|
+
.map((type) => {
|
|
118
|
+
return { type, msg: req.flash(type) };
|
|
119
|
+
})
|
|
120
|
+
.filter((a) => a.msg && a.msg.length && a.msg.length > 0);
|
|
121
|
+
|
|
115
122
|
router.post(
|
|
116
123
|
"/viewQuery/:viewName/:queryName",
|
|
117
124
|
error_catcher(async (req, res, next) => {
|
|
@@ -134,7 +141,7 @@ router.post(
|
|
|
134
141
|
if (queries[queryName]) {
|
|
135
142
|
const { args } = req.body;
|
|
136
143
|
const resp = await queries[queryName](...args, true);
|
|
137
|
-
res.json({ success: resp });
|
|
144
|
+
res.json({ success: resp, alerts: getFlashes(req) });
|
|
138
145
|
} else {
|
|
139
146
|
res.status(404).json({ error: req.__("Not found") });
|
|
140
147
|
}
|
|
@@ -235,6 +242,7 @@ router.get(
|
|
|
235
242
|
rows = await table.getJoinedRows(joinOpts);
|
|
236
243
|
} else if (req_query && req_query !== {}) {
|
|
237
244
|
const tbl_fields = await table.getFields();
|
|
245
|
+
readState(req_query, tbl_fields, req);
|
|
238
246
|
const qstate = await stateFieldsToWhere({
|
|
239
247
|
fields: tbl_fields,
|
|
240
248
|
approximate: !!approximate,
|
package/routes/eventlog.js
CHANGED
|
@@ -73,8 +73,9 @@ const logSettingsForm = async (req) => {
|
|
|
73
73
|
fields.push({
|
|
74
74
|
name: w + "_channel",
|
|
75
75
|
label: w + " channel",
|
|
76
|
-
sublabel:
|
|
77
|
-
|
|
76
|
+
sublabel: req.__(
|
|
77
|
+
"Channels to create events for. Separate by comma; leave blank for all"
|
|
78
|
+
),
|
|
78
79
|
type: "String",
|
|
79
80
|
showIf: { [w]: true },
|
|
80
81
|
});
|
|
@@ -82,8 +83,8 @@ const logSettingsForm = async (req) => {
|
|
|
82
83
|
return new Form({
|
|
83
84
|
action: "/eventlog/settings",
|
|
84
85
|
blurb: req.__("Which events should be logged?"),
|
|
85
|
-
|
|
86
|
-
onChange: "
|
|
86
|
+
noSubmitButton: true,
|
|
87
|
+
onChange: "saveAndContinue(this)",
|
|
87
88
|
fields,
|
|
88
89
|
});
|
|
89
90
|
};
|
|
@@ -169,23 +170,23 @@ router.get(
|
|
|
169
170
|
* @returns {Form}
|
|
170
171
|
*/
|
|
171
172
|
const customEventForm = async (req) => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
173
|
+
return new Form({
|
|
174
|
+
action: "/eventlog/custom/new",
|
|
175
|
+
submitButtonClass: "btn-outline-primary",
|
|
176
|
+
onChange: "remove_outline(this)",
|
|
177
|
+
fields: [
|
|
178
|
+
{
|
|
179
|
+
name: "name",
|
|
180
|
+
label: req.__("Event Name"),
|
|
181
|
+
type: "String",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "hasChannel",
|
|
185
|
+
label: req.__("Has channels?"),
|
|
186
|
+
type: "Bool",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
});
|
|
189
190
|
};
|
|
190
191
|
/**
|
|
191
192
|
* @name get/custom/new
|
|
@@ -297,7 +298,8 @@ router.post(
|
|
|
297
298
|
} else {
|
|
298
299
|
await getState().setConfig("event_log_settings", form.values);
|
|
299
300
|
|
|
300
|
-
res.redirect(`/eventlog/settings`);
|
|
301
|
+
if (!req.xhr) res.redirect(`/eventlog/settings`);
|
|
302
|
+
else res.json({ success: "ok" });
|
|
301
303
|
}
|
|
302
304
|
})
|
|
303
305
|
);
|
package/routes/fields.js
CHANGED
|
@@ -348,13 +348,12 @@ const fieldFlow = (req) =>
|
|
|
348
348
|
// todo sublabel
|
|
349
349
|
input_type: "custom_html",
|
|
350
350
|
attributes: {
|
|
351
|
-
html: `<button type="button" id="test_formula_btn" onclick="test_formula('${
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
)}</button>
|
|
351
|
+
html: `<button type="button" id="test_formula_btn" onclick="test_formula('${table.name
|
|
352
|
+
}', ${JSON.stringify(
|
|
353
|
+
context.stored
|
|
354
|
+
)})" class="btn btn-outline-secondary">${req.__(
|
|
355
|
+
"Test"
|
|
356
|
+
)}</button>
|
|
358
357
|
<div id="test_formula_output"></div>`,
|
|
359
358
|
},
|
|
360
359
|
}),
|
|
@@ -633,8 +632,7 @@ router.post(
|
|
|
633
632
|
result = f(rows[0]);
|
|
634
633
|
}
|
|
635
634
|
res.send(
|
|
636
|
-
`Result of running on row with id=${
|
|
637
|
-
rows[0].id
|
|
635
|
+
`Result of running on row with id=${rows[0].id
|
|
638
636
|
} is: <pre>${JSON.stringify(result)}</pre>`
|
|
639
637
|
);
|
|
640
638
|
} catch (e) {
|
|
@@ -788,22 +786,22 @@ router.post(
|
|
|
788
786
|
field.type === "Key"
|
|
789
787
|
? getState().keyFieldviews
|
|
790
788
|
: field.type === "File"
|
|
791
|
-
|
|
792
|
-
|
|
789
|
+
? getState().fileviews
|
|
790
|
+
: field.type.fieldviews;
|
|
793
791
|
if (!field.type || !fieldviews) {
|
|
794
792
|
res.send("");
|
|
795
793
|
return;
|
|
796
794
|
}
|
|
797
795
|
const fv = fieldviews[fieldview];
|
|
798
796
|
if (!fv && field.type === "Key" && fieldview === "select")
|
|
799
|
-
res.send("
|
|
797
|
+
res.send(`<input readonly class="form-control form-select"></input>`);
|
|
800
798
|
else if (!fv) res.send("");
|
|
801
799
|
else if (fv.isEdit || fv.isFilter)
|
|
802
800
|
res.send(
|
|
803
801
|
fv.run(
|
|
804
802
|
field.name,
|
|
805
803
|
undefined,
|
|
806
|
-
{
|
|
804
|
+
{ readonly: true, ...configuration, ...(field.attributes || {}) },
|
|
807
805
|
"",
|
|
808
806
|
false,
|
|
809
807
|
field
|
package/routes/files.js
CHANGED
|
@@ -378,9 +378,6 @@ const storage_form = async (req) => {
|
|
|
378
378
|
],
|
|
379
379
|
action: "/files/storage",
|
|
380
380
|
});
|
|
381
|
-
form.submitButtonClass = "btn-outline-primary";
|
|
382
|
-
form.submitLabel = req.__("Save");
|
|
383
|
-
form.onChange = "remove_outline(this)";
|
|
384
381
|
return form;
|
|
385
382
|
};
|
|
386
383
|
|
|
@@ -431,8 +428,11 @@ router.post(
|
|
|
431
428
|
});
|
|
432
429
|
} else {
|
|
433
430
|
await save_config_from_form(form);
|
|
434
|
-
|
|
435
|
-
|
|
431
|
+
|
|
432
|
+
if (!req.xhr) {
|
|
433
|
+
req.flash("success", req.__("Storage settings updated"));
|
|
434
|
+
res.redirect("/files/storage");
|
|
435
|
+
} else res.json({ success: "ok" });
|
|
436
436
|
}
|
|
437
437
|
})
|
|
438
438
|
);
|
package/routes/homepage.js
CHANGED
|
@@ -54,9 +54,9 @@ const tableCard = (tables, req) => ({
|
|
|
54
54
|
contents:
|
|
55
55
|
(tables.length <= 1
|
|
56
56
|
? p(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
{ class: "mt-2 pe-2" },
|
|
58
|
+
i(req.__("Tables organise data by fields and rows."))
|
|
59
|
+
)
|
|
60
60
|
: "") + tableTable(tables, req),
|
|
61
61
|
bodyClass: "py-0 pe-0",
|
|
62
62
|
footer: div(
|
|
@@ -107,13 +107,13 @@ const viewCard = (views, req) => ({
|
|
|
107
107
|
contents:
|
|
108
108
|
(views.length <= 1
|
|
109
109
|
? p(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
110
|
+
{ class: "mt-2 pe-2" },
|
|
111
|
+
i(
|
|
112
|
+
req.__(
|
|
113
|
+
"Views display data from tables. A view is a view pattern applied to a table, with configuration."
|
|
115
114
|
)
|
|
116
115
|
)
|
|
116
|
+
)
|
|
117
117
|
: "") +
|
|
118
118
|
(views.length > 0 ? viewTable(views, req) : p(req.__("No views"))),
|
|
119
119
|
|
|
@@ -160,13 +160,13 @@ const pageCard = (pages, req) => ({
|
|
|
160
160
|
contents:
|
|
161
161
|
(pages.length <= 1
|
|
162
162
|
? p(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
)
|
|
163
|
+
{ class: "mt-2 pe-2" },
|
|
164
|
+
i(
|
|
165
|
+
req.__(
|
|
166
|
+
"Pages are the web pages of your application built with a drag-and-drop builder. They have static content, and by embedding views, dynamic content."
|
|
168
167
|
)
|
|
169
168
|
)
|
|
169
|
+
)
|
|
170
170
|
: "") +
|
|
171
171
|
(pages.length > 0
|
|
172
172
|
? pageTable(pages, req)
|
|
@@ -191,16 +191,16 @@ const filesTab = async (req) => {
|
|
|
191
191
|
files.length === 0
|
|
192
192
|
? p(req.__("No files"))
|
|
193
193
|
: mkTable(
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
[
|
|
195
|
+
{
|
|
196
|
+
label: req.__("Filename"),
|
|
197
|
+
key: (r) => link(`/files/serve/${r.id}`, r.filename),
|
|
198
|
+
},
|
|
199
|
+
{ label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
|
|
200
|
+
{ label: req.__("Media type"), key: (r) => r.mimetype },
|
|
201
|
+
],
|
|
202
|
+
files
|
|
203
|
+
),
|
|
204
204
|
fileUploadForm(req)
|
|
205
205
|
);
|
|
206
206
|
};
|
|
@@ -244,30 +244,30 @@ const actionsTab = async (req, triggers) => {
|
|
|
244
244
|
return div(
|
|
245
245
|
{ class: "pb-3" },
|
|
246
246
|
triggers.length <= 1 &&
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
p(
|
|
248
|
+
{ class: "mt-2 pe-2" },
|
|
249
|
+
i(req.__("Triggers run actions in response to events."))
|
|
250
|
+
),
|
|
251
251
|
triggers.length === 0
|
|
252
252
|
? p(req.__("No triggers"))
|
|
253
253
|
: mkTable(
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
254
|
+
[
|
|
255
|
+
{ label: req.__("Name"), key: "name" },
|
|
256
|
+
{ label: req.__("Action"), key: "action" },
|
|
257
|
+
{
|
|
258
|
+
label: req.__("Table or Channel"),
|
|
259
|
+
key: (r) => r.table_name || r.channel,
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
label: req.__("When"),
|
|
263
|
+
key: (a) =>
|
|
264
|
+
a.when_trigger === "API call"
|
|
265
|
+
? `API: ${base_url}api/action/${a.name}`
|
|
266
|
+
: a.when_trigger,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
triggers
|
|
270
|
+
),
|
|
271
271
|
a(
|
|
272
272
|
{ href: "/actions/new", class: "btn btn-secondary my-3" },
|
|
273
273
|
req.__("Add trigger")
|
|
@@ -385,15 +385,15 @@ const welcome_page = async (req) => {
|
|
|
385
385
|
tabContents:
|
|
386
386
|
triggers.length > 0
|
|
387
387
|
? {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
388
|
+
Triggers: await actionsTab(req, triggers),
|
|
389
|
+
Files: await filesTab(req),
|
|
390
|
+
Packs: packTab(req, packlist),
|
|
391
|
+
}
|
|
392
392
|
: {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
393
|
+
Packs: packTab(req, packlist),
|
|
394
|
+
Triggers: await actionsTab(req, triggers),
|
|
395
|
+
Files: await filesTab(req),
|
|
396
|
+
},
|
|
397
397
|
},
|
|
398
398
|
{
|
|
399
399
|
type: "card",
|
|
@@ -403,13 +403,13 @@ const welcome_page = async (req) => {
|
|
|
403
403
|
tabContents:
|
|
404
404
|
users.length > 4
|
|
405
405
|
? {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
406
|
+
Users: await usersTab(req, users, roleMap),
|
|
407
|
+
Help: helpCard(req),
|
|
408
|
+
}
|
|
409
409
|
: {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
410
|
+
Help: helpCard(req),
|
|
411
|
+
Users: await usersTab(req, users, roleMap),
|
|
412
|
+
},
|
|
413
413
|
},
|
|
414
414
|
],
|
|
415
415
|
},
|
|
@@ -440,8 +440,8 @@ const no_views_logged_in = async (req, res) => {
|
|
|
440
440
|
packagejson.version,
|
|
441
441
|
latest
|
|
442
442
|
) +
|
|
443
|
-
|
|
444
|
-
|
|
443
|
+
" " +
|
|
444
|
+
a({ href: "/admin/system" }, req.__("Upgrade here"))
|
|
445
445
|
);
|
|
446
446
|
|
|
447
447
|
res.sendWrap(req.__("Hello"), await welcome_page(req));
|
package/routes/infoarch.js
CHANGED
|
@@ -48,8 +48,8 @@ router.get(
|
|
|
48
48
|
const languageForm = (req) =>
|
|
49
49
|
new Form({
|
|
50
50
|
action: "/site-structure/localizer/save-lang",
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
onChange: "saveAndContinue(this)",
|
|
52
|
+
noSubmitButton: true,
|
|
53
53
|
fields: [
|
|
54
54
|
{
|
|
55
55
|
name: "name",
|
|
@@ -270,7 +270,10 @@ router.post(
|
|
|
270
270
|
...cfgLangs,
|
|
271
271
|
[lang.locale]: lang,
|
|
272
272
|
});
|
|
273
|
-
|
|
273
|
+
|
|
274
|
+
if (!req.xhr)
|
|
275
|
+
res.redirect(`/site-structure/localizer/edit/${lang.locale}`);
|
|
276
|
+
else res.json({ success: "ok" });
|
|
274
277
|
}
|
|
275
278
|
})
|
|
276
279
|
);
|
package/routes/menu.js
CHANGED
|
@@ -22,6 +22,9 @@ const { renderForm } = require("@saltcorn/markup");
|
|
|
22
22
|
const { script, domReady, div, ul } = require("@saltcorn/markup/tags");
|
|
23
23
|
const { send_infoarch_page } = require("../markup/admin.js");
|
|
24
24
|
const Table = require("@saltcorn/data/models/table");
|
|
25
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
26
|
+
const { run_action_column } = require("@saltcorn/data/plugin-helper");
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
/**
|
|
27
30
|
* @type {object}
|
|
@@ -61,6 +64,18 @@ const menuForm = async (req) => {
|
|
|
61
64
|
dynSectionFieldOptions[table.name].push(field.name);
|
|
62
65
|
}
|
|
63
66
|
}
|
|
67
|
+
const stateActions = getState().actions;
|
|
68
|
+
const actions = [
|
|
69
|
+
...Object.entries(stateActions)
|
|
70
|
+
.filter(([k, v]) => !v.requireRow && !v.disableInBuilder)
|
|
71
|
+
.map(([k, v]) => k),
|
|
72
|
+
];
|
|
73
|
+
const triggers = await Trigger.find({
|
|
74
|
+
when_trigger: { or: ["API call", "Never"] },
|
|
75
|
+
});
|
|
76
|
+
triggers.forEach((tr) => {
|
|
77
|
+
actions.push(tr.name);
|
|
78
|
+
});
|
|
64
79
|
|
|
65
80
|
return new Form({
|
|
66
81
|
action: "/menu/",
|
|
@@ -92,6 +107,7 @@ const menuForm = async (req) => {
|
|
|
92
107
|
"Dynamic",
|
|
93
108
|
"Search",
|
|
94
109
|
"Separator",
|
|
110
|
+
"Action"
|
|
95
111
|
],
|
|
96
112
|
},
|
|
97
113
|
{
|
|
@@ -101,7 +117,7 @@ const menuForm = async (req) => {
|
|
|
101
117
|
input_type: "text",
|
|
102
118
|
required: true,
|
|
103
119
|
showIf: {
|
|
104
|
-
type: ["View", "Page", "Link", "Header", "Dynamic", "Search"],
|
|
120
|
+
type: ["View", "Page", "Link", "Header", "Dynamic", "Search", "Action"],
|
|
105
121
|
},
|
|
106
122
|
},
|
|
107
123
|
{
|
|
@@ -111,7 +127,7 @@ const menuForm = async (req) => {
|
|
|
111
127
|
attributes: {
|
|
112
128
|
html: `<button type="button" id="myEditor_icon" class="btn btn-outline-secondary"></button>`,
|
|
113
129
|
},
|
|
114
|
-
showIf: { type: ["View", "Page", "Link", "Header"] },
|
|
130
|
+
showIf: { type: ["View", "Page", "Link", "Header", "Action"] },
|
|
115
131
|
},
|
|
116
132
|
{
|
|
117
133
|
name: "icon",
|
|
@@ -149,6 +165,17 @@ const menuForm = async (req) => {
|
|
|
149
165
|
attributes: { options: views.map((r) => r.select_option) },
|
|
150
166
|
showIf: { type: "View" },
|
|
151
167
|
},
|
|
168
|
+
{
|
|
169
|
+
name: "action_name",
|
|
170
|
+
label: req.__("Action"),
|
|
171
|
+
type: "String",
|
|
172
|
+
class: "item-menu",
|
|
173
|
+
required: true,
|
|
174
|
+
attributes: {
|
|
175
|
+
options: actions,
|
|
176
|
+
},
|
|
177
|
+
showIf: { type: "Action" },
|
|
178
|
+
},
|
|
152
179
|
{
|
|
153
180
|
name: "dyn_table",
|
|
154
181
|
label: req.__("Table"),
|
|
@@ -217,7 +244,7 @@ const menuForm = async (req) => {
|
|
|
217
244
|
class: "item-menu",
|
|
218
245
|
type: "String",
|
|
219
246
|
required: true,
|
|
220
|
-
showIf: { type: ["View", "Page", "Link", "Header", "Dynamic"] },
|
|
247
|
+
showIf: { type: ["View", "Page", "Link", "Header", "Dynamic", "Action"] },
|
|
221
248
|
attributes: {
|
|
222
249
|
options: [
|
|
223
250
|
{ name: "", label: "Link" },
|
|
@@ -239,7 +266,7 @@ const menuForm = async (req) => {
|
|
|
239
266
|
{
|
|
240
267
|
name: "location",
|
|
241
268
|
label: req.__("Location"),
|
|
242
|
-
showIf: { type: ["View", "Page", "Link", "Header", "Dynamic"] },
|
|
269
|
+
showIf: { type: ["View", "Page", "Link", "Header", "Dynamic", "Action"] },
|
|
243
270
|
sublabel: req.__("Not all themes support all locations"),
|
|
244
271
|
class: "item-menu",
|
|
245
272
|
type: "String",
|
|
@@ -404,3 +431,37 @@ router.post(
|
|
|
404
431
|
res.json({ success: true });
|
|
405
432
|
})
|
|
406
433
|
);
|
|
434
|
+
|
|
435
|
+
router.post(
|
|
436
|
+
"/runaction/:name",
|
|
437
|
+
error_catcher(async (req, res) => {
|
|
438
|
+
const { name } = req.params;
|
|
439
|
+
const role = (req.user || {}).role_id || 10;
|
|
440
|
+
const state = getState();
|
|
441
|
+
const menu_items = state.getConfig("menu_items");
|
|
442
|
+
let menu_item;
|
|
443
|
+
const search = items =>
|
|
444
|
+
items
|
|
445
|
+
.filter((item) => role <= +item.min_role)
|
|
446
|
+
.forEach(item => {
|
|
447
|
+
if (item.type === "Action" && item.action_name === name)
|
|
448
|
+
menu_item = item;
|
|
449
|
+
else if (item.subitems)
|
|
450
|
+
search(item.subitems);
|
|
451
|
+
})
|
|
452
|
+
search(menu_items);
|
|
453
|
+
if (menu_item)
|
|
454
|
+
try {
|
|
455
|
+
const result = await run_action_column({
|
|
456
|
+
col: menu_item,
|
|
457
|
+
referrer: req.get("Referrer"),
|
|
458
|
+
req,
|
|
459
|
+
});
|
|
460
|
+
res.json({ success: "ok", ...(result || {}) });
|
|
461
|
+
} catch (e) {
|
|
462
|
+
res.status(400).json({ error: e.message || e });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
else res.status(404).json({ error: "Action not found" });
|
|
466
|
+
})
|
|
467
|
+
);
|
package/routes/packs.js
CHANGED
|
@@ -116,7 +116,7 @@ router.get(
|
|
|
116
116
|
type: "breadcrumbs",
|
|
117
117
|
crumbs: [
|
|
118
118
|
{ text: req.__("Settings") },
|
|
119
|
-
{ text: req.__("
|
|
119
|
+
{ text: req.__("Modules"), href: "/plugins" },
|
|
120
120
|
{ text: req.__("Create pack") },
|
|
121
121
|
],
|
|
122
122
|
},
|
|
@@ -184,7 +184,7 @@ router.post(
|
|
|
184
184
|
type: "breadcrumbs",
|
|
185
185
|
crumbs: [
|
|
186
186
|
{ text: req.__("Settings") },
|
|
187
|
-
{ text: req.__("
|
|
187
|
+
{ text: req.__("Modules"), href: "/plugins" },
|
|
188
188
|
{ text: req.__("Create pack") },
|
|
189
189
|
],
|
|
190
190
|
},
|
|
@@ -242,7 +242,7 @@ router.get(
|
|
|
242
242
|
type: "breadcrumbs",
|
|
243
243
|
crumbs: [
|
|
244
244
|
{ text: req.__("Settings") },
|
|
245
|
-
{ text: req.__("
|
|
245
|
+
{ text: req.__("Modules"), href: "/plugins" },
|
|
246
246
|
{ text: req.__("Install pack") },
|
|
247
247
|
],
|
|
248
248
|
},
|
|
@@ -293,7 +293,7 @@ router.post(
|
|
|
293
293
|
type: "breadcrumbs",
|
|
294
294
|
crumbs: [
|
|
295
295
|
{ text: req.__("Settings") },
|
|
296
|
-
{ text: req.__("
|
|
296
|
+
{ text: req.__("Modules"), href: "/plugins" },
|
|
297
297
|
{ text: req.__("Install pack") },
|
|
298
298
|
],
|
|
299
299
|
},
|
package/routes/page.js
CHANGED
|
@@ -36,6 +36,8 @@ router.get(
|
|
|
36
36
|
"/:pagename",
|
|
37
37
|
error_catcher(async (req, res) => {
|
|
38
38
|
const { pagename } = req.params;
|
|
39
|
+
const state = getState();
|
|
40
|
+
state.log(3, `Route /page/${pagename} user=${req.user?.id}`);
|
|
39
41
|
|
|
40
42
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
41
43
|
const db_page = await Page.findOne({ name: pagename });
|
|
@@ -56,10 +58,12 @@ router.get(
|
|
|
56
58
|
contents,
|
|
57
59
|
})
|
|
58
60
|
);
|
|
59
|
-
} else
|
|
61
|
+
} else {
|
|
62
|
+
state.log(2, `Page $pagename} not found or not authorized`);
|
|
60
63
|
res
|
|
61
64
|
.status(404)
|
|
62
65
|
.sendWrap(`${pagename} page`, req.__("Page %s not found", pagename));
|
|
66
|
+
}
|
|
63
67
|
})
|
|
64
68
|
);
|
|
65
69
|
|
package/routes/pageedit.js
CHANGED
|
@@ -90,6 +90,13 @@ const page_dropdown = (page, req) =>
|
|
|
90
90
|
'<i class="far fa-copy"></i> ' + req.__("Duplicate"),
|
|
91
91
|
req
|
|
92
92
|
),
|
|
93
|
+
a(
|
|
94
|
+
{
|
|
95
|
+
class: "dropdown-item",
|
|
96
|
+
href: `javascript:ajax_modal('/admin/snapshot-restore/page/${page.name}')`,
|
|
97
|
+
},
|
|
98
|
+
'<i class="fas fa-undo-alt"></i> ' + req.__("Restore")
|
|
99
|
+
),
|
|
93
100
|
div({ class: "dropdown-divider" }),
|
|
94
101
|
post_dropdown_item(
|
|
95
102
|
`/pageedit/delete/${page.id}`,
|
|
@@ -180,7 +187,8 @@ const pageBuilderData = async (req, context) => {
|
|
|
180
187
|
const fixed_state_fields = {};
|
|
181
188
|
for (const view of views) {
|
|
182
189
|
fixed_state_fields[view.name] = [];
|
|
183
|
-
const table = Table.findOne(
|
|
190
|
+
const table = Table.findOne(view.table_id || view.exttable_name);
|
|
191
|
+
|
|
184
192
|
const fs = await view.get_state_fields();
|
|
185
193
|
for (const frec of fs) {
|
|
186
194
|
const f = new Field(frec);
|