@saltcorn/server 0.7.4-beta.3 → 0.8.0-beta.0
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 +43 -22
- package/auth/admin.js +173 -74
- package/auth/routes.js +67 -28
- package/locales/en.json +54 -2
- package/locales/es.json +134 -134
- package/locales/ru.json +32 -5
- package/markup/admin.js +40 -38
- package/markup/forms.js +4 -3
- package/package.json +8 -7
- 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 +114 -26
- package/public/saltcorn.css +27 -10
- package/public/saltcorn.js +223 -76
- package/restart_watcher.js +1 -0
- package/routes/actions.js +20 -6
- package/routes/admin.js +243 -82
- package/routes/api.js +19 -2
- package/routes/common_lists.js +137 -134
- package/routes/diagram.js +362 -35
- package/routes/fields.js +4 -1
- package/routes/files.js +137 -101
- package/routes/homepage.js +2 -2
- package/routes/infoarch.js +2 -2
- package/routes/list.js +4 -4
- package/routes/page.js +16 -3
- package/routes/pageedit.js +22 -14
- package/routes/scapi.js +1 -1
- package/routes/search.js +1 -1
- package/routes/tables.js +4 -5
- package/routes/tag_entries.js +31 -10
- package/routes/tags.js +36 -32
- package/routes/tenant.js +98 -36
- package/routes/utils.js +72 -20
- package/routes/view.js +0 -1
- package/routes/viewedit.js +55 -22
- package/serve.js +5 -0
- package/tests/admin.test.js +2 -0
- package/tests/auth.test.js +20 -0
- package/tests/files.test.js +11 -20
- package/tests/tenant.test.js +4 -2
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
|
],
|
package/routes/list.js
CHANGED
|
@@ -217,7 +217,8 @@ jsGrid.fields.versions = VersionsField;
|
|
|
217
217
|
const arrangeIdFirst = (flds) => {
|
|
218
218
|
const noId = flds.filter((f) => f.name !== "id");
|
|
219
219
|
const id = flds.find((f) => f.name === "id");
|
|
220
|
-
return [id, ...noId];
|
|
220
|
+
if (id) return [id, ...noId];
|
|
221
|
+
else return flds
|
|
221
222
|
};
|
|
222
223
|
|
|
223
224
|
/**
|
|
@@ -369,9 +370,8 @@ router.get(
|
|
|
369
370
|
})
|
|
370
371
|
})
|
|
371
372
|
window.tabulator_table = new Tabulator("#jsGrid", {
|
|
372
|
-
ajaxURL:"/api/${table.name}${
|
|
373
|
-
|
|
374
|
-
}",
|
|
373
|
+
ajaxURL:"/api/${table.name}${table.versioned ? "?versioncount=on" : ""
|
|
374
|
+
}",
|
|
375
375
|
layout:"fitColumns",
|
|
376
376
|
columns,
|
|
377
377
|
height:"100%",
|
package/routes/page.js
CHANGED
|
@@ -7,10 +7,8 @@
|
|
|
7
7
|
const Router = require("express-promise-router");
|
|
8
8
|
|
|
9
9
|
const Page = require("@saltcorn/data/models/page");
|
|
10
|
-
const { div, a, i } = require("@saltcorn/markup/tags");
|
|
11
|
-
const { renderForm } = require("@saltcorn/markup");
|
|
12
10
|
const { getState } = require("@saltcorn/data/db/state");
|
|
13
|
-
const { error_catcher, scan_for_page_title } = require("../routes/utils.js");
|
|
11
|
+
const { error_catcher, scan_for_page_title, isAdmin } = require("../routes/utils.js");
|
|
14
12
|
const { add_edit_bar } = require("../markup/admin.js");
|
|
15
13
|
const { traverseSync } = require("@saltcorn/data/models/layout");
|
|
16
14
|
const { run_action_column } = require("@saltcorn/data/plugin-helper");
|
|
@@ -67,6 +65,21 @@ router.get(
|
|
|
67
65
|
})
|
|
68
66
|
);
|
|
69
67
|
|
|
68
|
+
router.post(
|
|
69
|
+
"/:pagename/preview",
|
|
70
|
+
isAdmin,
|
|
71
|
+
error_catcher(async (req, res) => {
|
|
72
|
+
const { pagename } = req.params;
|
|
73
|
+
const page = await Page.findOne({ name: pagename });
|
|
74
|
+
if (!page) {
|
|
75
|
+
res.send("");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const contents = await page.run(req.query, { res, req });
|
|
79
|
+
res.sendWrap({}, contents);
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
|
|
70
83
|
/**
|
|
71
84
|
* @name post/:pagename/action/:rndid
|
|
72
85
|
* @function
|
package/routes/pageedit.js
CHANGED
|
@@ -21,7 +21,7 @@ const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
|
|
|
21
21
|
const db = require("@saltcorn/data/db");
|
|
22
22
|
const { getPageList } = require("./common_lists");
|
|
23
23
|
|
|
24
|
-
const { isAdmin, error_catcher } = require("./utils.js");
|
|
24
|
+
const { isAdmin, error_catcher, addOnDoneRedirect } = require("./utils.js");
|
|
25
25
|
const {
|
|
26
26
|
mkTable,
|
|
27
27
|
renderForm,
|
|
@@ -54,7 +54,7 @@ const pagePropertiesForm = async (req) => {
|
|
|
54
54
|
const roles = await User.get_roles();
|
|
55
55
|
|
|
56
56
|
const form = new Form({
|
|
57
|
-
action: "/pageedit/edit-properties",
|
|
57
|
+
action: addOnDoneRedirect("/pageedit/edit-properties", req),
|
|
58
58
|
fields: [
|
|
59
59
|
new Field({
|
|
60
60
|
label: req.__("Name"),
|
|
@@ -100,6 +100,7 @@ const pageBuilderData = async (req, context) => {
|
|
|
100
100
|
const views = await View.find();
|
|
101
101
|
const pages = await Page.find();
|
|
102
102
|
const images = await File.find({ mime_super: "image" });
|
|
103
|
+
images.forEach(im => im.location = im.path_to_serve)
|
|
103
104
|
const roles = await User.get_roles();
|
|
104
105
|
const stateActions = getState().actions;
|
|
105
106
|
const actions = [
|
|
@@ -130,6 +131,9 @@ const pageBuilderData = async (req, context) => {
|
|
|
130
131
|
const fs = await view.get_state_fields();
|
|
131
132
|
for (const frec of fs) {
|
|
132
133
|
const f = new Field(frec);
|
|
134
|
+
if (f.input_type === "hidden") continue;
|
|
135
|
+
if (f.name === "_fts") continue;
|
|
136
|
+
|
|
133
137
|
f.required = false;
|
|
134
138
|
if (f.type && f.type.name === "Bool") f.fieldview = "tristate";
|
|
135
139
|
|
|
@@ -156,6 +160,7 @@ const pageBuilderData = async (req, context) => {
|
|
|
156
160
|
}
|
|
157
161
|
}
|
|
158
162
|
}
|
|
163
|
+
//console.log(fixed_state_fields.ListTasks);
|
|
159
164
|
return {
|
|
160
165
|
views,
|
|
161
166
|
images,
|
|
@@ -357,7 +362,7 @@ router.post(
|
|
|
357
362
|
if (!pageRow.fixed_states) pageRow.fixed_states = {};
|
|
358
363
|
if (!pageRow.layout) pageRow.layout = {};
|
|
359
364
|
await Page.create(pageRow);
|
|
360
|
-
res.redirect(`/pageedit/edit/${pageRow.name}
|
|
365
|
+
res.redirect(addOnDoneRedirect(`/pageedit/edit/${pageRow.name}`, req));
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
368
|
})
|
|
@@ -416,20 +421,23 @@ router.post(
|
|
|
416
421
|
error_catcher(async (req, res) => {
|
|
417
422
|
const { pagename } = req.params;
|
|
418
423
|
|
|
424
|
+
let redirectTarget = req.query.on_done_redirect
|
|
425
|
+
? `/${req.query.on_done_redirect}`
|
|
426
|
+
: "/pageedit";
|
|
419
427
|
const page = await Page.findOne({ name: pagename });
|
|
420
428
|
if (!page) {
|
|
421
429
|
req.flash("error", req.__(`Page %s not found`, pagename));
|
|
422
|
-
res.redirect(
|
|
430
|
+
res.redirect(redirectTarget);
|
|
423
431
|
} else if (req.body.layout) {
|
|
424
432
|
await Page.update(page.id, {
|
|
425
433
|
layout: decodeURIComponent(req.body.layout),
|
|
426
434
|
});
|
|
427
435
|
|
|
428
436
|
req.flash("success", req.__(`Page %s saved`, pagename));
|
|
429
|
-
res.redirect(
|
|
437
|
+
res.redirect(redirectTarget);
|
|
430
438
|
} else {
|
|
431
439
|
req.flash("error", req.__(`Error processing page`));
|
|
432
|
-
res.redirect(
|
|
440
|
+
res.redirect(redirectTarget);
|
|
433
441
|
}
|
|
434
442
|
})
|
|
435
443
|
);
|
|
@@ -566,13 +574,13 @@ router.post(
|
|
|
566
574
|
const page = await Page.findOne({ id });
|
|
567
575
|
const roles = await User.get_roles();
|
|
568
576
|
const roleRow = roles.find((r) => r.id === +role);
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
req.__(`Minimum role
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
res.
|
|
577
|
+
const message =
|
|
578
|
+
roleRow && page
|
|
579
|
+
? req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
|
|
580
|
+
: req.__(`Minimum role updated`);
|
|
581
|
+
if (!req.xhr) {
|
|
582
|
+
req.flash("success", message);
|
|
583
|
+
res.redirect("/pageedit");
|
|
584
|
+
} else res.json({ okay: true, responseText: message });
|
|
577
585
|
})
|
|
578
586
|
);
|
package/routes/scapi.js
CHANGED
|
@@ -185,7 +185,7 @@ router.get(
|
|
|
185
185
|
{ session: false },
|
|
186
186
|
async function (err, user, info) {
|
|
187
187
|
if (accessAllowedRead(req, user)) {
|
|
188
|
-
const triggers =
|
|
188
|
+
const triggers = Trigger.find({});
|
|
189
189
|
|
|
190
190
|
res.json({ success: triggers });
|
|
191
191
|
} else {
|
package/routes/search.js
CHANGED
|
@@ -188,7 +188,7 @@ const runSearch = async ({ q, _page, table }, req, res) => {
|
|
|
188
188
|
pages: current_page + (vresps.length === 20 ? 1 : 0),
|
|
189
189
|
trailing_ellipsis: vresps.length === 20,
|
|
190
190
|
get_page_link: (n) =>
|
|
191
|
-
`javascript:gopage(${n}, 20, {table:'${tableName}'})`,
|
|
191
|
+
`javascript:gopage(${n}, 20, undefined, {table:'${tableName}'})`,
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
194
|
|
package/routes/tables.js
CHANGED
|
@@ -135,6 +135,7 @@ const tableForm = async (table, req) => {
|
|
|
135
135
|
name: "min_role_read",
|
|
136
136
|
input_type: "select",
|
|
137
137
|
options: roleOptions,
|
|
138
|
+
attributes: { asideNext: !table.external }
|
|
138
139
|
},
|
|
139
140
|
...(table.external
|
|
140
141
|
? []
|
|
@@ -885,15 +886,13 @@ router.post(
|
|
|
885
886
|
const table = await Table.findOne({ id: parseInt(id) });
|
|
886
887
|
const old_versioned = table.versioned;
|
|
887
888
|
let hasError = false;
|
|
889
|
+
let notify = ""
|
|
888
890
|
if (!rest.versioned) rest.versioned = false;
|
|
889
891
|
if (rest.ownership_field_id === "_formula") {
|
|
890
892
|
rest.ownership_field_id = null;
|
|
891
893
|
const fmlValidRes = expressionValidator(rest.ownership_formula);
|
|
892
894
|
if (typeof fmlValidRes === "string") {
|
|
893
|
-
req.
|
|
894
|
-
"error",
|
|
895
|
-
req.__(`Invalid ownership formula: %s`, fmlValidRes)
|
|
896
|
-
);
|
|
895
|
+
notify = req.__(`Invalid ownership formula: %s`, fmlValidRes)
|
|
897
896
|
hasError = true;
|
|
898
897
|
}
|
|
899
898
|
} else rest.ownership_formula = null;
|
|
@@ -911,7 +910,7 @@ router.post(
|
|
|
911
910
|
else if (!hasError) req.flash("success", req.__("Table saved"));
|
|
912
911
|
|
|
913
912
|
if (!req.xhr) res.redirect(`/table/${id}`);
|
|
914
|
-
else res.json({ success: "ok" });
|
|
913
|
+
else res.json({ success: "ok", notify });
|
|
915
914
|
}
|
|
916
915
|
})
|
|
917
916
|
);
|