@saltcorn/server 0.7.2-beta.0 → 0.7.2-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +64 -9
- package/auth/routes.js +37 -13
- package/load_plugins.js +39 -27
- package/locales/da.json +1 -1
- package/locales/de.json +155 -155
- package/locales/en.json +25 -4
- package/locales/it.json +3 -2
- package/locales/ru.json +73 -28
- package/locales/zh.json +3 -3
- package/markup/admin.js +25 -9
- package/package.json +11 -8
- package/public/saltcorn-common.js +544 -0
- package/public/saltcorn.css +61 -0
- package/public/saltcorn.js +15 -476
- package/restart_watcher.js +1 -1
- package/routes/admin.js +159 -38
- package/routes/api.js +36 -1
- package/routes/edit.js +2 -1
- package/routes/eventlog.js +30 -30
- package/routes/fields.js +18 -0
- package/routes/files.js +7 -20
- package/routes/homepage.js +35 -7
- package/routes/menu.js +11 -7
- package/routes/tables.js +1 -1
- package/routes/tenant.js +13 -10
- package/routes/viewedit.js +10 -1
- package/serve.js +15 -1
- package/tests/admin.test.js +72 -1
- package/tests/clientjs.test.js +1 -0
- package/tests/viewedit.test.js +94 -0
- package/wrapper.js +56 -12
package/routes/admin.js
CHANGED
|
@@ -23,9 +23,6 @@ const {
|
|
|
23
23
|
div,
|
|
24
24
|
a,
|
|
25
25
|
hr,
|
|
26
|
-
form,
|
|
27
|
-
input,
|
|
28
|
-
label,
|
|
29
26
|
i,
|
|
30
27
|
h4,
|
|
31
28
|
table,
|
|
@@ -33,7 +30,6 @@ const {
|
|
|
33
30
|
td,
|
|
34
31
|
th,
|
|
35
32
|
tr,
|
|
36
|
-
button,
|
|
37
33
|
span,
|
|
38
34
|
p,
|
|
39
35
|
code,
|
|
@@ -45,13 +41,14 @@ const {
|
|
|
45
41
|
getState,
|
|
46
42
|
restart_tenant,
|
|
47
43
|
getTenant,
|
|
48
|
-
get_other_domain_tenant,
|
|
44
|
+
//get_other_domain_tenant,
|
|
49
45
|
get_process_init_time,
|
|
50
46
|
} = require("@saltcorn/data/db/state");
|
|
51
47
|
const { loadAllPlugins } = require("../load_plugins");
|
|
52
48
|
const {
|
|
53
49
|
create_backup,
|
|
54
50
|
restore,
|
|
51
|
+
auto_backup_now,
|
|
55
52
|
} = require("@saltcorn/admin-models/models/backup");
|
|
56
53
|
const {
|
|
57
54
|
runConfigurationCheck,
|
|
@@ -61,7 +58,7 @@ const load_plugins = require("../load_plugins");
|
|
|
61
58
|
const {
|
|
62
59
|
restore_backup,
|
|
63
60
|
send_admin_page,
|
|
64
|
-
send_files_page,
|
|
61
|
+
//send_files_page,
|
|
65
62
|
config_fields_form,
|
|
66
63
|
save_config_from_form,
|
|
67
64
|
flash_restart_if_required,
|
|
@@ -90,8 +87,9 @@ const router = new Router();
|
|
|
90
87
|
module.exports = router;
|
|
91
88
|
|
|
92
89
|
/**
|
|
93
|
-
*
|
|
94
|
-
* @
|
|
90
|
+
* Site identity form
|
|
91
|
+
* @param {object} req -http request
|
|
92
|
+
* @returns {Promise<Form>} form
|
|
95
93
|
*/
|
|
96
94
|
const site_id_form = (req) =>
|
|
97
95
|
config_fields_form({
|
|
@@ -106,13 +104,15 @@ const site_id_form = (req) =>
|
|
|
106
104
|
"page_custom_html",
|
|
107
105
|
"development_mode",
|
|
108
106
|
"log_sql",
|
|
107
|
+
"plugins_store_endpoint",
|
|
108
|
+
"packs_store_endpoint",
|
|
109
109
|
...(getConfigFile() ? ["multitenancy_enabled"] : []),
|
|
110
110
|
],
|
|
111
111
|
action: "/admin",
|
|
112
112
|
submitLabel: req.__("Save"),
|
|
113
113
|
});
|
|
114
114
|
/**
|
|
115
|
-
* Email settings form
|
|
115
|
+
* Email settings form
|
|
116
116
|
* @param {object} req request
|
|
117
117
|
* @returns {Promise<Form>} form
|
|
118
118
|
*/
|
|
@@ -137,6 +137,7 @@ const email_form = async (req) => {
|
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/**
|
|
140
|
+
* Router get /
|
|
140
141
|
* @name get
|
|
141
142
|
* @function
|
|
142
143
|
* @memberof module:routes/admin~routes/adminRouter
|
|
@@ -145,7 +146,6 @@ router.get(
|
|
|
145
146
|
"/",
|
|
146
147
|
isAdmin,
|
|
147
148
|
error_catcher(async (req, res) => {
|
|
148
|
-
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
149
149
|
const form = await site_id_form(req);
|
|
150
150
|
send_admin_page({
|
|
151
151
|
res,
|
|
@@ -294,41 +294,148 @@ router.get(
|
|
|
294
294
|
"/backup",
|
|
295
295
|
isAdmin,
|
|
296
296
|
error_catcher(async (req, res) => {
|
|
297
|
+
const backupForm = autoBackupForm(req);
|
|
298
|
+
backupForm.values.auto_backup_frequency = getState().getConfig(
|
|
299
|
+
"auto_backup_frequency"
|
|
300
|
+
);
|
|
301
|
+
backupForm.values.auto_backup_destination = getState().getConfig(
|
|
302
|
+
"auto_backup_destination"
|
|
303
|
+
);
|
|
304
|
+
backupForm.values.auto_backup_directory = getState().getConfig(
|
|
305
|
+
"auto_backup_directory"
|
|
306
|
+
);
|
|
307
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
308
|
+
|
|
297
309
|
send_admin_page({
|
|
298
310
|
res,
|
|
299
311
|
req,
|
|
300
312
|
active_sub: "Backup",
|
|
301
313
|
contents: {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
314
|
+
above: [
|
|
315
|
+
{
|
|
316
|
+
type: "card",
|
|
317
|
+
title: req.__("Manual backup"),
|
|
318
|
+
contents: {
|
|
319
|
+
besides: [
|
|
308
320
|
div(
|
|
309
|
-
post_btn(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
req.
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
321
|
+
post_btn(
|
|
322
|
+
"/admin/backup",
|
|
323
|
+
i({ class: "fas fa-download me-2" }) +
|
|
324
|
+
req.__("Download a backup"),
|
|
325
|
+
req.csrfToken(),
|
|
326
|
+
{
|
|
327
|
+
btnClass: "btn-outline-primary",
|
|
328
|
+
}
|
|
329
|
+
)
|
|
330
|
+
),
|
|
331
|
+
div(
|
|
332
|
+
restore_backup(req.csrfToken(), [
|
|
333
|
+
i({ class: "fas fa-2x fa-upload me-2" }),
|
|
334
|
+
"",
|
|
335
|
+
req.__("Restore a backup"),
|
|
336
|
+
])
|
|
337
|
+
),
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
isRoot
|
|
342
|
+
? {
|
|
343
|
+
type: "card",
|
|
344
|
+
title: req.__("Automated backup"),
|
|
345
|
+
contents: div(renderForm(backupForm, req.csrfToken())),
|
|
346
|
+
}
|
|
347
|
+
: { type: "blank", contents: "" },
|
|
348
|
+
],
|
|
327
349
|
},
|
|
328
350
|
});
|
|
329
351
|
})
|
|
330
352
|
);
|
|
331
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Auto backup Form
|
|
356
|
+
* @param {object} req
|
|
357
|
+
* @returns {Form} form
|
|
358
|
+
*/
|
|
359
|
+
const autoBackupForm = (req) =>
|
|
360
|
+
new Form({
|
|
361
|
+
action: "/admin/set-auto-backup",
|
|
362
|
+
submitButtonClass: "btn-outline-primary",
|
|
363
|
+
onChange: "remove_outline(this)",
|
|
364
|
+
submitLabel: "Save settings",
|
|
365
|
+
additionalButtons: [
|
|
366
|
+
{
|
|
367
|
+
label: "Backup now",
|
|
368
|
+
id: "btnBackupNow",
|
|
369
|
+
class: "btn btn-outline-secondary",
|
|
370
|
+
onclick: "ajax_post('/admin/auto-backup-now')",
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
fields: [
|
|
374
|
+
{
|
|
375
|
+
type: "String",
|
|
376
|
+
label: req.__("Frequency"),
|
|
377
|
+
name: "auto_backup_frequency",
|
|
378
|
+
required: true,
|
|
379
|
+
attributes: { options: ["Never", "Daily", "Weekly"] },
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
type: "String",
|
|
383
|
+
label: req.__("Destination"),
|
|
384
|
+
name: "auto_backup_destination",
|
|
385
|
+
required: true,
|
|
386
|
+
showIf: { auto_backup_frequency: ["Daily", "Weekly"] },
|
|
387
|
+
attributes: { options: ["Saltcorn files", "Local directory"] },
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
type: "String",
|
|
391
|
+
label: req.__("Directory"),
|
|
392
|
+
name: "auto_backup_directory",
|
|
393
|
+
showIf: {
|
|
394
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
395
|
+
auto_backup_destination: "Local directory",
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
router.post(
|
|
402
|
+
"/set-auto-backup",
|
|
403
|
+
isAdmin,
|
|
404
|
+
error_catcher(async (req, res) => {
|
|
405
|
+
const form = await autoBackupForm(req);
|
|
406
|
+
form.validate(req.body);
|
|
407
|
+
if (form.hasErrors) {
|
|
408
|
+
send_admin_page({
|
|
409
|
+
res,
|
|
410
|
+
req,
|
|
411
|
+
active_sub: "Backup",
|
|
412
|
+
contents: {
|
|
413
|
+
type: "card",
|
|
414
|
+
title: req.__("Backup settings"),
|
|
415
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
await save_config_from_form(form);
|
|
420
|
+
req.flash("success", req.__("Backup settings updated"));
|
|
421
|
+
res.redirect("/admin/backup");
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
);
|
|
425
|
+
router.post(
|
|
426
|
+
"/auto-backup-now",
|
|
427
|
+
isAdmin,
|
|
428
|
+
error_catcher(async (req, res) => {
|
|
429
|
+
try {
|
|
430
|
+
await auto_backup_now();
|
|
431
|
+
req.flash("success", req.__("Backup successful"));
|
|
432
|
+
} catch (e) {
|
|
433
|
+
req.flash("error", e.message);
|
|
434
|
+
}
|
|
435
|
+
res.json({ reload_page: true });
|
|
436
|
+
})
|
|
437
|
+
);
|
|
438
|
+
|
|
332
439
|
/**
|
|
333
440
|
* @name get/system
|
|
334
441
|
* @function
|
|
@@ -527,6 +634,9 @@ router.post(
|
|
|
527
634
|
}
|
|
528
635
|
})
|
|
529
636
|
);
|
|
637
|
+
/**
|
|
638
|
+
* /check-for-updates
|
|
639
|
+
*/
|
|
530
640
|
router.post(
|
|
531
641
|
"/check-for-upgrade",
|
|
532
642
|
isAdmin,
|
|
@@ -548,7 +658,7 @@ router.post(
|
|
|
548
658
|
const fileName = await create_backup();
|
|
549
659
|
res.type("application/zip");
|
|
550
660
|
res.attachment(fileName);
|
|
551
|
-
|
|
661
|
+
const file = fs.createReadStream(fileName);
|
|
552
662
|
file.on("end", function () {
|
|
553
663
|
fs.unlink(fileName, function () {});
|
|
554
664
|
});
|
|
@@ -579,8 +689,9 @@ router.post(
|
|
|
579
689
|
);
|
|
580
690
|
|
|
581
691
|
/**
|
|
692
|
+
* Clear All Form
|
|
582
693
|
* @param {object} req
|
|
583
|
-
* @returns {Form}
|
|
694
|
+
* @returns {Form} form
|
|
584
695
|
*/
|
|
585
696
|
const clearAllForm = (req) =>
|
|
586
697
|
new Form({
|
|
@@ -694,6 +805,7 @@ router.post(
|
|
|
694
805
|
try {
|
|
695
806
|
const file_store = db.connectObj.file_store;
|
|
696
807
|
const admin_users = await User.find({ role_id: 1 }, { orderBy: "id" });
|
|
808
|
+
// greenlock logic
|
|
697
809
|
const Greenlock = require("greenlock");
|
|
698
810
|
const greenlock = Greenlock.create({
|
|
699
811
|
packageRoot: path.resolve(__dirname, ".."),
|
|
@@ -709,6 +821,7 @@ router.post(
|
|
|
709
821
|
subject: domain,
|
|
710
822
|
altnames,
|
|
711
823
|
});
|
|
824
|
+
// letsencrypt
|
|
712
825
|
await getState().setConfig("letsencrypt", true);
|
|
713
826
|
req.flash(
|
|
714
827
|
"success",
|
|
@@ -758,7 +871,9 @@ router.get(
|
|
|
758
871
|
});
|
|
759
872
|
})
|
|
760
873
|
);
|
|
761
|
-
|
|
874
|
+
/**
|
|
875
|
+
* /confiuration-check
|
|
876
|
+
*/
|
|
762
877
|
router.get(
|
|
763
878
|
"/configuration-check",
|
|
764
879
|
isAdmin,
|
|
@@ -787,7 +902,10 @@ router.get(
|
|
|
787
902
|
? div(
|
|
788
903
|
{ class: "alert alert-success", role: "alert" },
|
|
789
904
|
i({ class: "fas fa-check-circle fa-lg me-2" }),
|
|
790
|
-
h5(
|
|
905
|
+
h5(
|
|
906
|
+
{ class: "d-inline" },
|
|
907
|
+
req.__("No errors detected during configuration check")
|
|
908
|
+
)
|
|
791
909
|
)
|
|
792
910
|
: errors.map(mkError)
|
|
793
911
|
),
|
|
@@ -863,6 +981,7 @@ router.post(
|
|
|
863
981
|
if (form.values.plugins) {
|
|
864
982
|
const ps = await Plugin.find();
|
|
865
983
|
for (const p of ps) {
|
|
984
|
+
// todo configurable list of mandatory plugins
|
|
866
985
|
if (!["base", "sbadmin2"].includes(p.name)) await p.delete();
|
|
867
986
|
}
|
|
868
987
|
await getState().refresh_plugins();
|
|
@@ -902,6 +1021,8 @@ router.post(
|
|
|
902
1021
|
req.logout();
|
|
903
1022
|
req.session = null;
|
|
904
1023
|
}
|
|
1024
|
+
// todo make configurable - redirect to create first user
|
|
1025
|
+
// redirect to create first user
|
|
905
1026
|
res.redirect(`/auth/create_first_user`);
|
|
906
1027
|
} else {
|
|
907
1028
|
req.flash(
|
package/routes/api.js
CHANGED
|
@@ -20,6 +20,7 @@ const { error_catcher } = require("./utils.js");
|
|
|
20
20
|
//const { mkTable, renderForm, link, post_btn } = require("@saltcorn/markup");
|
|
21
21
|
const { getState } = require("@saltcorn/data/db/state");
|
|
22
22
|
const Table = require("@saltcorn/data/models/table");
|
|
23
|
+
const View = require("@saltcorn/data/models/view");
|
|
23
24
|
//const Field = require("@saltcorn/data/models/field");
|
|
24
25
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
25
26
|
//const load_plugins = require("../load_plugins");
|
|
@@ -111,6 +112,40 @@ function accessAllowed(req, user, trigger) {
|
|
|
111
112
|
return role <= trigger.min_role;
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
router.post(
|
|
116
|
+
"/viewQuery/:viewName/:queryName",
|
|
117
|
+
error_catcher(async (req, res, next) => {
|
|
118
|
+
let { viewName, queryName } = req.params;
|
|
119
|
+
const view = await View.findOne({ name: viewName });
|
|
120
|
+
if (!view) {
|
|
121
|
+
res.status(404).json({ error: req.__("Not found") });
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
await passport.authenticate(
|
|
125
|
+
"jwt",
|
|
126
|
+
{ session: false },
|
|
127
|
+
async function (err, user, info) {
|
|
128
|
+
const role = user && user.id ? user.role_id : 10;
|
|
129
|
+
if (
|
|
130
|
+
role <= view.min_role ||
|
|
131
|
+
(await view.authorise_get({ req, ...view })) // TODO set query to state
|
|
132
|
+
) {
|
|
133
|
+
const queries = view.queries(false, req);
|
|
134
|
+
if (queries[queryName]) {
|
|
135
|
+
const { args } = req.body;
|
|
136
|
+
const resp = await queries[queryName](...args, true);
|
|
137
|
+
res.json({ success: resp });
|
|
138
|
+
} else {
|
|
139
|
+
res.status(404).json({ error: req.__("Not found") });
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
res.status(401).json({ error: req.__("Not authorized") });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
)(req, res, next);
|
|
146
|
+
})
|
|
147
|
+
);
|
|
148
|
+
|
|
114
149
|
router.get(
|
|
115
150
|
"/:tableName/distinct/:fieldName",
|
|
116
151
|
//passport.authenticate("api-bearer", { session: false }),
|
|
@@ -180,7 +215,7 @@ router.get(
|
|
|
180
215
|
}
|
|
181
216
|
|
|
182
217
|
await passport.authenticate(
|
|
183
|
-
"api-bearer",
|
|
218
|
+
["api-bearer", "jwt"],
|
|
184
219
|
{ session: false },
|
|
185
220
|
async function (err, user, info) {
|
|
186
221
|
if (accessAllowedRead(req, user, table)) {
|
package/routes/edit.js
CHANGED
|
@@ -44,7 +44,8 @@ router.post(
|
|
|
44
44
|
"error",
|
|
45
45
|
req.__("Not allowed to write to table %s", table.name)
|
|
46
46
|
);
|
|
47
|
-
if (req.
|
|
47
|
+
if (req.xhr) res.send("OK");
|
|
48
|
+
else if (req.get("referer")) res.redirect(req.get("referer"));
|
|
48
49
|
else res.redirect(redirect || `/list/${table.name}`);
|
|
49
50
|
})
|
|
50
51
|
);
|
package/routes/eventlog.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @subcategory routes
|
|
6
6
|
*/
|
|
7
7
|
const Router = require("express-promise-router");
|
|
8
|
-
const { isAdmin, error_catcher
|
|
8
|
+
const { isAdmin, error_catcher } = require("./utils.js");
|
|
9
9
|
const { getState } = require("@saltcorn/data/db/state");
|
|
10
10
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
11
11
|
|
|
@@ -21,19 +21,19 @@ module.exports = router;
|
|
|
21
21
|
const {
|
|
22
22
|
mkTable,
|
|
23
23
|
renderForm,
|
|
24
|
-
link,
|
|
25
|
-
post_btn,
|
|
26
|
-
settingsDropdown,
|
|
27
|
-
post_dropdown_item,
|
|
24
|
+
//link,
|
|
25
|
+
//post_btn,
|
|
26
|
+
//settingsDropdown,
|
|
27
|
+
//post_dropdown_item,
|
|
28
28
|
post_delete_btn,
|
|
29
29
|
localeDateTime,
|
|
30
30
|
} = require("@saltcorn/markup");
|
|
31
31
|
const Form = require("@saltcorn/data/models/form");
|
|
32
32
|
const {
|
|
33
33
|
div,
|
|
34
|
-
code,
|
|
34
|
+
//code,
|
|
35
35
|
a,
|
|
36
|
-
span,
|
|
36
|
+
//span,
|
|
37
37
|
tr,
|
|
38
38
|
table,
|
|
39
39
|
tbody,
|
|
@@ -74,7 +74,7 @@ const logSettingsForm = async (req) => {
|
|
|
74
74
|
name: w + "_channel",
|
|
75
75
|
label: w + " channel",
|
|
76
76
|
sublabel:
|
|
77
|
-
"Channels to create events for. Separate by comma; leave blank for all",
|
|
77
|
+
req.__("Channels to create events for. Separate by comma; leave blank for all"),
|
|
78
78
|
type: "String",
|
|
79
79
|
showIf: { [w]: true },
|
|
80
80
|
});
|
|
@@ -168,25 +168,25 @@ router.get(
|
|
|
168
168
|
/**
|
|
169
169
|
* @returns {Form}
|
|
170
170
|
*/
|
|
171
|
-
const customEventForm = () =>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
171
|
+
const customEventForm = async (req) => {
|
|
172
|
+
return new Form({
|
|
173
|
+
action: "/eventlog/custom/new",
|
|
174
|
+
submitButtonClass: "btn-outline-primary",
|
|
175
|
+
onChange: "remove_outline(this)",
|
|
176
|
+
fields: [
|
|
177
|
+
{
|
|
178
|
+
name: "name",
|
|
179
|
+
label: req.__("Event Name"),
|
|
180
|
+
type: "String",
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "hasChannel",
|
|
184
|
+
label: req.__("Has channels?"),
|
|
185
|
+
type: "Bool",
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
};
|
|
190
190
|
/**
|
|
191
191
|
* @name get/custom/new
|
|
192
192
|
* @function
|
|
@@ -197,7 +197,7 @@ router.get(
|
|
|
197
197
|
"/custom/new",
|
|
198
198
|
isAdmin,
|
|
199
199
|
error_catcher(async (req, res) => {
|
|
200
|
-
const form = customEventForm();
|
|
200
|
+
const form = await customEventForm(req);
|
|
201
201
|
send_events_page({
|
|
202
202
|
res,
|
|
203
203
|
req,
|
|
@@ -222,7 +222,7 @@ router.post(
|
|
|
222
222
|
"/custom/new",
|
|
223
223
|
isAdmin,
|
|
224
224
|
error_catcher(async (req, res) => {
|
|
225
|
-
const form = customEventForm();
|
|
225
|
+
const form = await customEventForm(req);
|
|
226
226
|
form.validate(req.body);
|
|
227
227
|
if (form.hasErrors) {
|
|
228
228
|
send_events_page({
|
|
@@ -323,7 +323,7 @@ router.get(
|
|
|
323
323
|
{ orderBy: "occur_at", orderDesc: true, limit: rows_per_page, offset }
|
|
324
324
|
);
|
|
325
325
|
if (evlog.length === rows_per_page || current_page > 1) {
|
|
326
|
-
const nrows = await EventLog.count();
|
|
326
|
+
const nrows = await EventLog.count({});
|
|
327
327
|
if (nrows > rows_per_page || current_page > 1) {
|
|
328
328
|
page_opts.pagination = {
|
|
329
329
|
current_page,
|
package/routes/fields.js
CHANGED
|
@@ -181,6 +181,7 @@ const fieldFlow = (req) =>
|
|
|
181
181
|
var attributes = context.attributes || {};
|
|
182
182
|
attributes.default = context.default;
|
|
183
183
|
attributes.summary_field = context.summary_field;
|
|
184
|
+
attributes.include_fts = context.include_fts;
|
|
184
185
|
attributes.on_delete_cascade = context.on_delete_cascade;
|
|
185
186
|
const {
|
|
186
187
|
table_id,
|
|
@@ -295,6 +296,12 @@ const fieldFlow = (req) =>
|
|
|
295
296
|
input_type: "select",
|
|
296
297
|
options: roles.map((r) => ({ value: r.id, label: r.role })),
|
|
297
298
|
},
|
|
299
|
+
{
|
|
300
|
+
name: "also_delete_file",
|
|
301
|
+
type: "Bool",
|
|
302
|
+
label: req.__("Cascade delete to file"),
|
|
303
|
+
sublabel: req.__("Deleting a row will also delete the file referenced by this field")
|
|
304
|
+
},
|
|
298
305
|
],
|
|
299
306
|
});
|
|
300
307
|
} else {
|
|
@@ -370,6 +377,11 @@ const fieldFlow = (req) =>
|
|
|
370
377
|
value: f.name,
|
|
371
378
|
label: f.label,
|
|
372
379
|
}));
|
|
380
|
+
const textfields = orderedFields
|
|
381
|
+
.filter(
|
|
382
|
+
(f) => (!f.calculated || f.stored) && f.type?.sql_name === "text"
|
|
383
|
+
)
|
|
384
|
+
.map((f) => f.name);
|
|
373
385
|
return new Form({
|
|
374
386
|
fields: [
|
|
375
387
|
new Field({
|
|
@@ -378,6 +390,12 @@ const fieldFlow = (req) =>
|
|
|
378
390
|
input_type: "select",
|
|
379
391
|
options: keyfields,
|
|
380
392
|
}),
|
|
393
|
+
new Field({
|
|
394
|
+
name: "include_fts",
|
|
395
|
+
label: req.__("Include in full-text search"),
|
|
396
|
+
type: "Bool",
|
|
397
|
+
showIf: { summary_field: textfields },
|
|
398
|
+
}),
|
|
381
399
|
new Field({
|
|
382
400
|
name: "on_delete_cascade",
|
|
383
401
|
label: req.__("On delete cascade"),
|
package/routes/files.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Files Route
|
|
2
3
|
* @category server
|
|
3
4
|
* @module routes/files
|
|
4
5
|
* @subcategory routes
|
|
@@ -15,28 +16,12 @@ const {
|
|
|
15
16
|
mkTable,
|
|
16
17
|
renderForm,
|
|
17
18
|
link,
|
|
18
|
-
post_btn,
|
|
19
|
+
//post_btn,
|
|
19
20
|
post_delete_btn,
|
|
20
21
|
} = require("@saltcorn/markup");
|
|
21
22
|
const { isAdmin, error_catcher, setTenant } = require("./utils.js");
|
|
22
|
-
const {
|
|
23
|
-
|
|
24
|
-
h5,
|
|
25
|
-
h1,
|
|
26
|
-
h4,
|
|
27
|
-
nbsp,
|
|
28
|
-
p,
|
|
29
|
-
a,
|
|
30
|
-
div,
|
|
31
|
-
form,
|
|
32
|
-
input,
|
|
33
|
-
select,
|
|
34
|
-
button,
|
|
35
|
-
option,
|
|
36
|
-
text,
|
|
37
|
-
label,
|
|
38
|
-
} = require("@saltcorn/markup/tags");
|
|
39
|
-
const { csrfField } = require("./utils");
|
|
23
|
+
const { h1, div, text } = require("@saltcorn/markup/tags");
|
|
24
|
+
// const { csrfField } = require("./utils");
|
|
40
25
|
const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
|
|
41
26
|
const { strictParseInt } = require("@saltcorn/data/plugin-helper");
|
|
42
27
|
const {
|
|
@@ -44,7 +29,7 @@ const {
|
|
|
44
29
|
config_fields_form,
|
|
45
30
|
save_config_from_form,
|
|
46
31
|
} = require("../markup/admin");
|
|
47
|
-
const fsp = require("fs").promises;
|
|
32
|
+
// const fsp = require("fs").promises;
|
|
48
33
|
const fs = require("fs");
|
|
49
34
|
|
|
50
35
|
/**
|
|
@@ -58,6 +43,7 @@ const router = new Router();
|
|
|
58
43
|
module.exports = router;
|
|
59
44
|
|
|
60
45
|
/**
|
|
46
|
+
* Edit file Role form
|
|
61
47
|
* @param {*} file
|
|
62
48
|
* @param {*} roles
|
|
63
49
|
* @param {*} req
|
|
@@ -81,6 +67,7 @@ router.get(
|
|
|
81
67
|
"/",
|
|
82
68
|
isAdmin,
|
|
83
69
|
error_catcher(async (req, res) => {
|
|
70
|
+
// todo limit select from file by 10 or 20
|
|
84
71
|
const rows = await File.find({}, { orderBy: "filename" });
|
|
85
72
|
const roles = await User.get_roles();
|
|
86
73
|
send_files_page({
|