@saltcorn/server 0.7.4 → 0.8.0-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/app.js +18 -11
- package/auth/admin.js +370 -120
- package/auth/roleadmin.js +5 -23
- package/auth/routes.js +40 -15
- package/locales/de.json +1049 -273
- package/locales/en.json +58 -3
- package/locales/es.json +134 -134
- package/locales/it.json +6 -1
- package/locales/ru.json +44 -7
- package/markup/admin.js +46 -42
- package/markup/forms.js +4 -3
- package/package.json +8 -7
- package/public/blockly.js +19 -31
- package/public/diagram_utils.js +530 -0
- package/public/gridedit.js +4 -1
- package/public/jquery-menu-editor.min.js +112 -112
- package/public/saltcorn-common.js +31 -8
- package/public/saltcorn.css +11 -0
- package/public/saltcorn.js +211 -70
- package/restart_watcher.js +1 -0
- package/routes/actions.js +6 -14
- package/routes/admin.js +229 -79
- package/routes/api.js +19 -2
- package/routes/common_lists.js +137 -134
- package/routes/delete.js +6 -5
- package/routes/diagram.js +43 -117
- package/routes/edit.js +5 -10
- package/routes/fields.js +63 -29
- package/routes/files.js +137 -101
- package/routes/homepage.js +2 -2
- package/routes/infoarch.js +2 -2
- package/routes/list.js +12 -13
- package/routes/page.js +16 -3
- package/routes/pageedit.js +13 -8
- package/routes/scapi.js +1 -1
- package/routes/search.js +1 -1
- package/routes/tables.js +9 -14
- package/routes/tag_entries.js +31 -10
- package/routes/tags.js +10 -10
- package/routes/tenant.js +114 -50
- package/routes/utils.js +12 -0
- package/routes/view.js +3 -4
- package/routes/viewedit.js +57 -55
- package/serve.js +5 -0
- package/tests/admin.test.js +6 -2
- package/tests/auth.test.js +20 -0
- package/tests/fields.test.js +1 -0
- package/tests/files.test.js +11 -20
- package/tests/tenant.test.js +12 -2
- package/tests/viewedit.test.js +15 -1
package/routes/fields.js
CHANGED
|
@@ -25,7 +25,10 @@ const db = require("@saltcorn/data/db");
|
|
|
25
25
|
|
|
26
26
|
const { isAdmin, error_catcher } = require("./utils.js");
|
|
27
27
|
const expressionBlurb = require("../markup/expression_blurb");
|
|
28
|
-
const {
|
|
28
|
+
const {
|
|
29
|
+
readState,
|
|
30
|
+
add_free_variables_to_joinfields,
|
|
31
|
+
} = require("@saltcorn/data/plugin-helper");
|
|
29
32
|
const { wizardCardTitle } = require("../markup/forms.js");
|
|
30
33
|
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
31
34
|
const { applyAsync } = require("@saltcorn/data/utils");
|
|
@@ -185,6 +188,7 @@ const fieldFlow = (req) =>
|
|
|
185
188
|
attributes.summary_field = context.summary_field;
|
|
186
189
|
attributes.include_fts = context.include_fts;
|
|
187
190
|
attributes.on_delete_cascade = context.on_delete_cascade;
|
|
191
|
+
attributes.on_delete = context.on_delete;
|
|
188
192
|
const {
|
|
189
193
|
table_id,
|
|
190
194
|
name,
|
|
@@ -350,12 +354,13 @@ const fieldFlow = (req) =>
|
|
|
350
354
|
// todo sublabel
|
|
351
355
|
input_type: "custom_html",
|
|
352
356
|
attributes: {
|
|
353
|
-
html: `<button type="button" id="test_formula_btn" onclick="test_formula('${
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
html: `<button type="button" id="test_formula_btn" onclick="test_formula('${
|
|
358
|
+
table.name
|
|
359
|
+
}', ${JSON.stringify(
|
|
360
|
+
context.stored
|
|
361
|
+
)})" class="btn btn-outline-secondary">${req.__(
|
|
362
|
+
"Test"
|
|
363
|
+
)}</button>
|
|
359
364
|
<div id="test_formula_output"></div>`,
|
|
360
365
|
},
|
|
361
366
|
}),
|
|
@@ -400,13 +405,32 @@ const fieldFlow = (req) =>
|
|
|
400
405
|
type: "Bool",
|
|
401
406
|
showIf: { summary_field: textfields },
|
|
402
407
|
}),
|
|
403
|
-
new Field({
|
|
408
|
+
/*new Field({
|
|
404
409
|
name: "on_delete_cascade",
|
|
405
410
|
label: req.__("On delete cascade"),
|
|
406
411
|
type: "Bool",
|
|
407
412
|
sublabel: req.__(
|
|
408
413
|
"If the parent row is deleted, automatically delete the child rows."
|
|
409
414
|
),
|
|
415
|
+
}),*/
|
|
416
|
+
new Field({
|
|
417
|
+
name: "on_delete",
|
|
418
|
+
label: req.__("On delete"),
|
|
419
|
+
input_type: "select",
|
|
420
|
+
options: ["Fail", "Cascade", "Set null"],
|
|
421
|
+
required: true,
|
|
422
|
+
attributes: {
|
|
423
|
+
explainers: {
|
|
424
|
+
Fail: "Prevent any deletion of parent rows",
|
|
425
|
+
Cascade:
|
|
426
|
+
"If the parent row is deleted, automatically delete the child rows.",
|
|
427
|
+
"Set null":
|
|
428
|
+
"If the parent row is deleted, set key fields on child rows to null",
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
sublabel: req.__(
|
|
432
|
+
"If the parent row is deleted, do this to the child rows."
|
|
433
|
+
),
|
|
410
434
|
}),
|
|
411
435
|
],
|
|
412
436
|
});
|
|
@@ -622,12 +646,18 @@ router.post(
|
|
|
622
646
|
const { formula, tablename, stored } = req.body;
|
|
623
647
|
const table = await Table.findOne({ name: tablename });
|
|
624
648
|
const fields = await table.getFields();
|
|
625
|
-
const freeVars = freeVariables(formula)
|
|
626
|
-
const joinFields = {}
|
|
627
|
-
if (stored)
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
649
|
+
const freeVars = freeVariables(formula);
|
|
650
|
+
const joinFields = {};
|
|
651
|
+
if (stored) add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
652
|
+
const rows = await table.getJoinedRows({
|
|
653
|
+
joinFields,
|
|
654
|
+
orderBy: "RANDOM()",
|
|
655
|
+
limit: 1,
|
|
656
|
+
});
|
|
657
|
+
if (rows.length < 1) {
|
|
658
|
+
res.send("No rows in table");
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
631
661
|
let result;
|
|
632
662
|
try {
|
|
633
663
|
if (stored) {
|
|
@@ -638,7 +668,8 @@ router.post(
|
|
|
638
668
|
result = f(rows[0]);
|
|
639
669
|
}
|
|
640
670
|
res.send(
|
|
641
|
-
`Result of running on row with id=${
|
|
671
|
+
`Result of running on row with id=${
|
|
672
|
+
rows[0].id
|
|
642
673
|
} is: <pre>${JSON.stringify(result)}</pre>`
|
|
643
674
|
);
|
|
644
675
|
} catch (e) {
|
|
@@ -668,10 +699,9 @@ router.post(
|
|
|
668
699
|
const fields = await table.getFields();
|
|
669
700
|
let row = { ...req.body };
|
|
670
701
|
if (!row || Object.keys(row).length === 0) {
|
|
671
|
-
const { id } = req.query
|
|
672
|
-
if (id) row = await table.getRow({ id })
|
|
673
|
-
} else
|
|
674
|
-
readState(row, fields);
|
|
702
|
+
const { id } = req.query;
|
|
703
|
+
if (id) row = await table.getRow({ id });
|
|
704
|
+
} else readState(row, fields);
|
|
675
705
|
|
|
676
706
|
if (fieldName.includes(".")) {
|
|
677
707
|
//join field
|
|
@@ -691,12 +721,18 @@ router.post(
|
|
|
691
721
|
const refRow = await reftable.getRow(q);
|
|
692
722
|
let fv;
|
|
693
723
|
if (targetField.type === "Key") {
|
|
694
|
-
fv = getState().keyFieldviews[fieldview]
|
|
724
|
+
fv = getState().keyFieldviews[fieldview];
|
|
695
725
|
if (!fv) {
|
|
696
|
-
const reftable2 = Table.findOne({
|
|
697
|
-
|
|
726
|
+
const reftable2 = Table.findOne({
|
|
727
|
+
name: targetField.reftable_name,
|
|
728
|
+
});
|
|
729
|
+
const refRow2 = await reftable2.getRow({
|
|
730
|
+
[reftable2.pk_name]: refRow[kpath[1]],
|
|
731
|
+
});
|
|
698
732
|
if (refRow2) {
|
|
699
|
-
res.send(
|
|
733
|
+
res.send(
|
|
734
|
+
text(`${refRow2[targetField.attributes.summary_field]}`)
|
|
735
|
+
);
|
|
700
736
|
} else {
|
|
701
737
|
res.send("");
|
|
702
738
|
}
|
|
@@ -708,7 +744,6 @@ router.post(
|
|
|
708
744
|
fv =
|
|
709
745
|
targetField.type.fieldviews.show ||
|
|
710
746
|
targetField.type.fieldviews.as_text;
|
|
711
|
-
|
|
712
747
|
}
|
|
713
748
|
|
|
714
749
|
const configuration = req.query;
|
|
@@ -753,7 +788,7 @@ router.post(
|
|
|
753
788
|
let result;
|
|
754
789
|
try {
|
|
755
790
|
if (!field.calculated) {
|
|
756
|
-
result = row[field.name]
|
|
791
|
+
result = row[field.name];
|
|
757
792
|
} else if (field.stored) {
|
|
758
793
|
const f = get_async_expression_function(formula, fields);
|
|
759
794
|
result = await f(row);
|
|
@@ -762,8 +797,7 @@ router.post(
|
|
|
762
797
|
result = f(row);
|
|
763
798
|
}
|
|
764
799
|
const fv = field.type.fieldviews[fieldview];
|
|
765
|
-
if (!fv)
|
|
766
|
-
res.send(text(result));
|
|
800
|
+
if (!fv) res.send(text(result));
|
|
767
801
|
else res.send(fv.run(result));
|
|
768
802
|
} catch (e) {
|
|
769
803
|
return res.status(400).send(`Error: ${e.message}`);
|
|
@@ -816,8 +850,8 @@ router.post(
|
|
|
816
850
|
field.type === "Key"
|
|
817
851
|
? getState().keyFieldviews
|
|
818
852
|
: field.type === "File"
|
|
819
|
-
|
|
820
|
-
|
|
853
|
+
? getState().fileviews
|
|
854
|
+
: field.type.fieldviews;
|
|
821
855
|
if (!field.type || !fieldviews) {
|
|
822
856
|
res.send("");
|
|
823
857
|
return;
|
package/routes/files.js
CHANGED
|
@@ -11,6 +11,7 @@ const User = require("@saltcorn/data/models/user");
|
|
|
11
11
|
const { getState } = require("@saltcorn/data/db/state");
|
|
12
12
|
const s3storage = require("../s3storage");
|
|
13
13
|
const resizer = require("resize-with-sharp-or-jimp");
|
|
14
|
+
const db = require("@saltcorn/data/db");
|
|
14
15
|
|
|
15
16
|
const {
|
|
16
17
|
mkTable,
|
|
@@ -20,7 +21,7 @@ const {
|
|
|
20
21
|
post_delete_btn,
|
|
21
22
|
} = require("@saltcorn/markup");
|
|
22
23
|
const { isAdmin, error_catcher, setTenant } = require("./utils.js");
|
|
23
|
-
const { h1, div, text } = require("@saltcorn/markup/tags");
|
|
24
|
+
const { h1, div, text, button, i, a } = require("@saltcorn/markup/tags");
|
|
24
25
|
// const { csrfField } = require("./utils");
|
|
25
26
|
const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
|
|
26
27
|
const { strictParseInt } = require("@saltcorn/data/plugin-helper");
|
|
@@ -31,6 +32,7 @@ const {
|
|
|
31
32
|
} = require("../markup/admin");
|
|
32
33
|
// const fsp = require("fs").promises;
|
|
33
34
|
const fs = require("fs");
|
|
35
|
+
const path = require("path");
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* @type {object}
|
|
@@ -51,7 +53,7 @@ module.exports = router;
|
|
|
51
53
|
*/
|
|
52
54
|
const editFileRoleForm = (file, roles, req) =>
|
|
53
55
|
editRoleForm({
|
|
54
|
-
url: `/files/setrole/${file.
|
|
56
|
+
url: `/files/setrole/${file.path_to_serve}`,
|
|
55
57
|
current_role: file.min_role_read,
|
|
56
58
|
roles,
|
|
57
59
|
req,
|
|
@@ -68,49 +70,51 @@ router.get(
|
|
|
68
70
|
isAdmin,
|
|
69
71
|
error_catcher(async (req, res) => {
|
|
70
72
|
// todo limit select from file by 10 or 20
|
|
71
|
-
const
|
|
73
|
+
const { dir } = req.query
|
|
74
|
+
const safeDir = File.normalise(dir || "/")
|
|
75
|
+
const rows = await File.find({ folder: dir }, { orderBy: "filename" });
|
|
72
76
|
const roles = await User.get_roles();
|
|
77
|
+
//console.log(rows);
|
|
78
|
+
if (safeDir && safeDir !== "/" && safeDir !== ".") {
|
|
79
|
+
let dirname = path.dirname(safeDir)
|
|
80
|
+
if (dirname === ".") dirname = "/"
|
|
81
|
+
rows.unshift(new File({
|
|
82
|
+
filename: "..",
|
|
83
|
+
location: dirname,
|
|
84
|
+
isDirectory: true,
|
|
85
|
+
mime_super: "",
|
|
86
|
+
mime_sub: "",
|
|
87
|
+
}))
|
|
88
|
+
}
|
|
89
|
+
if (req.xhr) {
|
|
90
|
+
for (const file of rows) {
|
|
91
|
+
file.location = file.path_to_serve
|
|
92
|
+
}
|
|
93
|
+
const directories = await File.allDirectories()
|
|
94
|
+
for (const file of directories) {
|
|
95
|
+
file.location = file.path_to_serve
|
|
96
|
+
}
|
|
97
|
+
res.json({ files: rows, roles, directories })
|
|
98
|
+
return
|
|
99
|
+
}
|
|
73
100
|
send_files_page({
|
|
74
101
|
res,
|
|
75
102
|
req,
|
|
103
|
+
headers: [
|
|
104
|
+
{
|
|
105
|
+
script: `/static_assets/${db.connectObj.version_tag}/bundle.js`,
|
|
106
|
+
defer: true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
css: `/static_assets/${db.connectObj.version_tag}/bundle.css`,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
76
112
|
active_sub: "Files",
|
|
77
113
|
contents: {
|
|
78
114
|
type: "card",
|
|
79
115
|
contents: [
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
{
|
|
83
|
-
label: req.__("Filename"),
|
|
84
|
-
key: (r) =>
|
|
85
|
-
div(
|
|
86
|
-
{ "data-inline-edit-dest-url": `/files/setname/${r.id}` },
|
|
87
|
-
r.filename
|
|
88
|
-
),
|
|
89
|
-
},
|
|
90
|
-
{ label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
|
|
91
|
-
{ label: req.__("Media type"), key: (r) => r.mimetype },
|
|
92
|
-
{
|
|
93
|
-
label: req.__("Role to access"),
|
|
94
|
-
key: (r) => editFileRoleForm(r, roles, req),
|
|
95
|
-
},
|
|
96
|
-
{
|
|
97
|
-
label: req.__("Link"),
|
|
98
|
-
key: (r) => link(`/files/serve/${r.id}`, req.__("Link")),
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
label: req.__("Download"),
|
|
102
|
-
key: (r) => link(`/files/download/${r.id}`, req.__("Download")),
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
label: req.__("Delete"),
|
|
106
|
-
key: (r) =>
|
|
107
|
-
post_delete_btn(`/files/delete/${r.id}`, req, r.filename),
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
|
-
rows,
|
|
111
|
-
{ hover: true }
|
|
112
|
-
),
|
|
113
|
-
fileUploadForm(req),
|
|
116
|
+
div({ id: "saltcorn-file-manager" }),
|
|
117
|
+
fileUploadForm(req, safeDir),
|
|
114
118
|
],
|
|
115
119
|
},
|
|
116
120
|
});
|
|
@@ -124,19 +128,21 @@ router.get(
|
|
|
124
128
|
* @function
|
|
125
129
|
*/
|
|
126
130
|
router.get(
|
|
127
|
-
"/download
|
|
131
|
+
"/download/*",
|
|
128
132
|
error_catcher(async (req, res) => {
|
|
129
133
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
130
134
|
const user_id = req.user && req.user.id;
|
|
131
|
-
const
|
|
132
|
-
const file = await File.findOne(
|
|
133
|
-
|
|
135
|
+
const serve_path = req.params[0];
|
|
136
|
+
const file = await File.findOne(serve_path);
|
|
137
|
+
|
|
138
|
+
if (file && (role <= file.min_role_read || (user_id && user_id === file.user_id))) {
|
|
134
139
|
res.type(file.mimetype);
|
|
135
140
|
if (file.s3_store) s3storage.serveObject(file, res, true);
|
|
136
141
|
else res.download(file.location, file.filename);
|
|
137
142
|
} else {
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
res
|
|
144
|
+
.status(404)
|
|
145
|
+
.sendWrap(req.__("Not found"), h1(req.__("File not found")));
|
|
140
146
|
}
|
|
141
147
|
})
|
|
142
148
|
);
|
|
@@ -148,31 +154,25 @@ router.get(
|
|
|
148
154
|
* @function
|
|
149
155
|
*/
|
|
150
156
|
router.get(
|
|
151
|
-
"/serve
|
|
157
|
+
"/serve/*",
|
|
152
158
|
error_catcher(async (req, res) => {
|
|
153
159
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
154
160
|
const user_id = req.user && req.user.id;
|
|
155
|
-
const
|
|
156
|
-
let file;
|
|
157
|
-
if (typeof strictParseInt(id) !== "undefined")
|
|
158
|
-
|
|
159
|
-
else file = await File.findOne({ filename: id });
|
|
161
|
+
const serve_path = req.params[0];
|
|
162
|
+
//let file;
|
|
163
|
+
//if (typeof strictParseInt(id) !== "undefined")
|
|
164
|
+
const file = await File.findOne(serve_path);
|
|
160
165
|
|
|
161
|
-
if (
|
|
162
|
-
res
|
|
163
|
-
.status(404)
|
|
164
|
-
.sendWrap(req.__("Not found"), h1(req.__("File not found")));
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (role <= file.min_role_read || (user_id && user_id === file.user_id)) {
|
|
166
|
+
if (file && (role <= file.min_role_read || (user_id && user_id === file.user_id))) {
|
|
168
167
|
res.type(file.mimetype);
|
|
169
168
|
const cacheability = file.min_role_read === 10 ? "public" : "private";
|
|
170
169
|
res.set("Cache-Control", `${cacheability}, max-age=86400`);
|
|
171
170
|
if (file.s3_store) s3storage.serveObject(file, res, false);
|
|
172
171
|
else res.sendFile(file.location);
|
|
173
172
|
} else {
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
res
|
|
174
|
+
.status(404)
|
|
175
|
+
.sendWrap(req.__("Not found"), h1(req.__("File not found")));
|
|
176
176
|
}
|
|
177
177
|
})
|
|
178
178
|
);
|
|
@@ -184,23 +184,17 @@ router.get(
|
|
|
184
184
|
* @function
|
|
185
185
|
*/
|
|
186
186
|
router.get(
|
|
187
|
-
"/resize/:
|
|
187
|
+
"/resize/:width_str/:height_str/*",
|
|
188
188
|
error_catcher(async (req, res) => {
|
|
189
189
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
190
190
|
const user_id = req.user && req.user.id;
|
|
191
|
-
const {
|
|
192
|
-
|
|
193
|
-
if (typeof strictParseInt(id) !== "undefined")
|
|
194
|
-
file = await File.findOne({ id });
|
|
195
|
-
else file = await File.findOne({ filename: id });
|
|
191
|
+
const { width_str, height_str } = req.params;
|
|
192
|
+
const serve_path = req.params[0];
|
|
196
193
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (role <= file.min_role_read || (user_id && user_id === file.user_id)) {
|
|
194
|
+
|
|
195
|
+
const file = await File.findOne(serve_path);
|
|
196
|
+
|
|
197
|
+
if (file && (role <= file.min_role_read || (user_id && user_id === file.user_id))) {
|
|
204
198
|
res.type(file.mimetype);
|
|
205
199
|
const cacheability = file.min_role_read === 10 ? "public" : "private";
|
|
206
200
|
res.set("Cache-Control", `${cacheability}, max-age=86400`);
|
|
@@ -208,12 +202,14 @@ router.get(
|
|
|
208
202
|
if (file.s3_store) s3storage.serveObject(file, res, false);
|
|
209
203
|
else {
|
|
210
204
|
const width = strictParseInt(width_str);
|
|
211
|
-
const height = height_str
|
|
205
|
+
const height = height_str && height_str !== "0"
|
|
206
|
+
? strictParseInt(height_str) : null;
|
|
212
207
|
if (!width) {
|
|
213
208
|
res.sendFile(file.location);
|
|
214
209
|
return;
|
|
215
210
|
}
|
|
216
|
-
const
|
|
211
|
+
const basenm = path.join(path.dirname(file.location), '_resized_' + path.basename(file.location))
|
|
212
|
+
const fnm = `${basenm}_w${width}${height ? `_h${height}` : ""}`;
|
|
217
213
|
if (!fs.existsSync(fnm)) {
|
|
218
214
|
await resizer({
|
|
219
215
|
fromFileName: file.location,
|
|
@@ -225,8 +221,9 @@ router.get(
|
|
|
225
221
|
res.sendFile(fnm);
|
|
226
222
|
}
|
|
227
223
|
} else {
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
res
|
|
225
|
+
.status(404)
|
|
226
|
+
.sendWrap(req.__("Not found"), h1(req.__("File not found")));
|
|
230
227
|
}
|
|
231
228
|
})
|
|
232
229
|
);
|
|
@@ -238,23 +235,42 @@ router.get(
|
|
|
238
235
|
* @function
|
|
239
236
|
*/
|
|
240
237
|
router.post(
|
|
241
|
-
"/setrole
|
|
238
|
+
"/setrole/*",
|
|
242
239
|
isAdmin,
|
|
243
240
|
error_catcher(async (req, res) => {
|
|
244
|
-
const
|
|
241
|
+
const serve_path = req.params[0];
|
|
242
|
+
const file = await File.findOne(serve_path);
|
|
245
243
|
const role = req.body.role;
|
|
246
|
-
await File.update(+id, { min_role_read: role });
|
|
247
|
-
const file = await File.findOne({ id });
|
|
248
244
|
const roles = await User.get_roles();
|
|
249
245
|
const roleRow = roles.find((r) => r.id === +role);
|
|
250
|
-
if (roleRow && file)
|
|
251
|
-
req.flash(
|
|
252
|
-
"success",
|
|
253
|
-
req.__(`Minimum role for %s updated to %s`, file.filename, roleRow.role)
|
|
254
|
-
);
|
|
255
|
-
else req.flash("success", req.__(`Minimum role updated`));
|
|
256
246
|
|
|
257
|
-
|
|
247
|
+
if (roleRow && file) {
|
|
248
|
+
await file.set_role(role);
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
res.redirect(file ? `/files?dir=${encodeURIComponent(file.current_folder)}` : "/files");
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
router.post(
|
|
259
|
+
"/move/*",
|
|
260
|
+
isAdmin,
|
|
261
|
+
error_catcher(async (req, res) => {
|
|
262
|
+
const serve_path = req.params[0];
|
|
263
|
+
const file = await File.findOne(serve_path);
|
|
264
|
+
const new_path = req.body.new_path;
|
|
265
|
+
|
|
266
|
+
if (file) {
|
|
267
|
+
await file.move_to_dir(new_path);
|
|
268
|
+
}
|
|
269
|
+
if (req.xhr) {
|
|
270
|
+
res.json({ success: "ok" })
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
res.redirect(file ? `/files?dir=${encodeURIComponent(file.current_folder)}` : "/files");
|
|
258
274
|
})
|
|
259
275
|
);
|
|
260
276
|
|
|
@@ -265,14 +281,28 @@ router.post(
|
|
|
265
281
|
* @function
|
|
266
282
|
*/
|
|
267
283
|
router.post(
|
|
268
|
-
"/setname
|
|
284
|
+
"/setname/*",
|
|
269
285
|
isAdmin,
|
|
270
286
|
error_catcher(async (req, res) => {
|
|
271
|
-
const
|
|
287
|
+
const serve_path = req.params[0];
|
|
272
288
|
const filename = req.body.value;
|
|
273
|
-
await File.update(+id, { filename });
|
|
274
289
|
|
|
275
|
-
|
|
290
|
+
const file = await File.findOne(serve_path);
|
|
291
|
+
await file.rename(filename);
|
|
292
|
+
|
|
293
|
+
res.redirect(`/files?dir=${encodeURIComponent(file.current_folder)}`);
|
|
294
|
+
|
|
295
|
+
})
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
router.post(
|
|
299
|
+
"/new-folder",
|
|
300
|
+
isAdmin,
|
|
301
|
+
error_catcher(async (req, res) => {
|
|
302
|
+
const { name, folder } = req.body
|
|
303
|
+
await File.new_folder(name, folder);
|
|
304
|
+
|
|
305
|
+
res.json({ success: "ok" });
|
|
276
306
|
})
|
|
277
307
|
);
|
|
278
308
|
|
|
@@ -286,9 +316,11 @@ router.post(
|
|
|
286
316
|
"/upload",
|
|
287
317
|
setTenant, // TODO why is this needed?????
|
|
288
318
|
error_catcher(async (req, res) => {
|
|
319
|
+
let { folder } = req.body
|
|
289
320
|
let jsonResp = {};
|
|
290
321
|
const min_role_upload = getState().getConfig("min_role_upload", 1);
|
|
291
322
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
323
|
+
let file_for_redirect
|
|
292
324
|
if (role > +min_role_upload) {
|
|
293
325
|
if (!req.xhr) req.flash("warning", req.__("Not authorized"));
|
|
294
326
|
else jsonResp = { error: "Not authorized" };
|
|
@@ -300,9 +332,11 @@ router.post(
|
|
|
300
332
|
const f = await File.from_req_files(
|
|
301
333
|
req.files.file,
|
|
302
334
|
req.user.id,
|
|
303
|
-
+min_role_read
|
|
335
|
+
+min_role_read,
|
|
336
|
+
folder ? File.normalise(folder) : undefined
|
|
304
337
|
);
|
|
305
338
|
const many = Array.isArray(f);
|
|
339
|
+
file_for_redirect = many ? f[0] : f
|
|
306
340
|
if (!req.xhr)
|
|
307
341
|
req.flash(
|
|
308
342
|
"success",
|
|
@@ -317,14 +351,18 @@ router.post(
|
|
|
317
351
|
jsonResp = {
|
|
318
352
|
success: {
|
|
319
353
|
filename: many ? f.map((fl) => fl.filename) : f.filename,
|
|
320
|
-
|
|
354
|
+
location: many ? f.map((fl) => fl.path_to_serve) : f.path_to_serve,
|
|
321
355
|
url: many
|
|
322
|
-
? f.map((fl) => `/files/serve/${fl.
|
|
323
|
-
: `/files/serve/${f.
|
|
356
|
+
? f.map((fl) => `/files/serve/${fl.path_to_serve}`)
|
|
357
|
+
: `/files/serve/${f.path_to_serve}`,
|
|
324
358
|
},
|
|
325
359
|
};
|
|
326
360
|
}
|
|
327
|
-
if (!req.xhr)
|
|
361
|
+
if (!req.xhr)
|
|
362
|
+
res.redirect(!file_for_redirect
|
|
363
|
+
? '/files'
|
|
364
|
+
: `/files?dir=${encodeURIComponent(file_for_redirect.current_folder)}`);
|
|
365
|
+
|
|
328
366
|
else res.json(jsonResp);
|
|
329
367
|
})
|
|
330
368
|
);
|
|
@@ -336,11 +374,11 @@ router.post(
|
|
|
336
374
|
* @function
|
|
337
375
|
*/
|
|
338
376
|
router.post(
|
|
339
|
-
"/delete
|
|
377
|
+
"/delete/*",
|
|
340
378
|
isAdmin,
|
|
341
379
|
error_catcher(async (req, res) => {
|
|
342
|
-
const
|
|
343
|
-
const f = await File.findOne(
|
|
380
|
+
const serve_path = req.params[0];
|
|
381
|
+
const f = await File.findOne(serve_path);
|
|
344
382
|
if (!f) {
|
|
345
383
|
req.flash("error", "File not found");
|
|
346
384
|
res.redirect("/files");
|
|
@@ -351,10 +389,8 @@ router.post(
|
|
|
351
389
|
);
|
|
352
390
|
if (result && result.error) {
|
|
353
391
|
req.flash("error", result.error);
|
|
354
|
-
} else {
|
|
355
|
-
req.flash("success", req.__(`File %s deleted`, text(f.filename)));
|
|
356
392
|
}
|
|
357
|
-
res.redirect(`/files`);
|
|
393
|
+
res.redirect(`/files?dir=${encodeURIComponent(f.current_folder)}`);
|
|
358
394
|
})
|
|
359
395
|
);
|
|
360
396
|
|
package/routes/homepage.js
CHANGED
|
@@ -194,7 +194,7 @@ const filesTab = async (req) => {
|
|
|
194
194
|
[
|
|
195
195
|
{
|
|
196
196
|
label: req.__("Filename"),
|
|
197
|
-
key: (r) => link(`/files/serve/${r.
|
|
197
|
+
key: (r) => r.isDirectory ? r.filename : link(`/files/serve/${r.path_to_serve}`, r.filename),
|
|
198
198
|
},
|
|
199
199
|
{ label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
|
|
200
200
|
{ label: req.__("Media type"), key: (r) => r.mimetype },
|
|
@@ -352,7 +352,7 @@ const helpCard = (req) =>
|
|
|
352
352
|
const welcome_page = async (req) => {
|
|
353
353
|
const packs_available = await fetch_available_packs();
|
|
354
354
|
const packlist = [
|
|
355
|
-
...packs_available.slice(0, 5),
|
|
355
|
+
...(packs_available || []).slice(0, 5),
|
|
356
356
|
{ name: req.__("More..."), description: "" },
|
|
357
357
|
];
|
|
358
358
|
const tables = await Table.find({}, { cached: true });
|
package/routes/infoarch.js
CHANGED
|
@@ -60,7 +60,7 @@ const languageForm = (req) =>
|
|
|
60
60
|
{
|
|
61
61
|
name: "locale",
|
|
62
62
|
label: req.__("Locale"),
|
|
63
|
-
sublabel: "Locale identifier short code, e.g. en, zh, fr, ar etc. ",
|
|
63
|
+
sublabel: req.__("Locale identifier short code, e.g. en, zh, fr, ar etc. "),
|
|
64
64
|
type: "String",
|
|
65
65
|
required: true,
|
|
66
66
|
},
|
|
@@ -68,7 +68,7 @@ const languageForm = (req) =>
|
|
|
68
68
|
name: "is_default",
|
|
69
69
|
label: req.__("Default language"),
|
|
70
70
|
sublabel:
|
|
71
|
-
"Is this the default language in which the application is built?",
|
|
71
|
+
req.__("Is this the default language in which the application is built?"),
|
|
72
72
|
type: "Bool",
|
|
73
73
|
},
|
|
74
74
|
],
|