@saltcorn/server 0.8.0-beta.4 → 0.8.1-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 +7 -6
- package/auth/admin.js +260 -217
- package/auth/index.js +20 -20
- package/auth/roleadmin.js +2 -9
- package/auth/routes.js +193 -139
- package/auth/testhelp.js +62 -55
- package/fixture_persons.js +1 -1
- package/index.js +22 -22
- package/locales/en.json +13 -1
- package/locales/fr.json +14 -2
- package/locales/ru.json +25 -19
- package/markup/admin.js +22 -15
- package/markup/blockly.js +1 -1
- package/markup/expression_blurb.js +15 -15
- package/markup/forms.js +21 -22
- package/markup/index.js +20 -20
- package/markup/plugin-store.js +4 -4
- package/package.json +8 -8
- package/public/diagram_utils.js +22 -9
- package/public/saltcorn-common.js +128 -68
- package/public/saltcorn.css +6 -0
- package/public/saltcorn.js +68 -20
- package/restart_watcher.js +157 -157
- package/routes/actions.js +4 -11
- package/routes/admin.js +14 -6
- package/routes/api.js +11 -18
- package/routes/common_lists.js +127 -130
- package/routes/delete.js +2 -2
- package/routes/edit.js +1 -1
- package/routes/fields.js +48 -2
- package/routes/files.js +112 -94
- package/routes/homepage.js +1 -1
- package/routes/infoarch.js +1 -1
- package/routes/list.js +6 -5
- package/routes/packs.js +1 -2
- package/routes/pageedit.js +1 -1
- package/routes/tag_entries.js +1 -1
- package/routes/tenant.js +2 -1
- package/routes/utils.js +3 -1
- package/routes/view.js +14 -2
- package/routes/viewedit.js +35 -0
- package/s3storage.js +13 -11
- package/serve.js +35 -31
- package/systemd.js +23 -21
- package/tests/fields.test.js +23 -0
- package/wrapper.js +46 -45
package/routes/files.js
CHANGED
|
@@ -18,7 +18,11 @@ const { isAdmin, error_catcher, setTenant } = require("./utils.js");
|
|
|
18
18
|
const { h1, div, text } = require("@saltcorn/markup/tags");
|
|
19
19
|
const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
|
|
20
20
|
const { strictParseInt } = require("@saltcorn/data/plugin-helper");
|
|
21
|
-
const {
|
|
21
|
+
const {
|
|
22
|
+
send_files_page,
|
|
23
|
+
config_fields_form,
|
|
24
|
+
save_config_from_form,
|
|
25
|
+
} = require("../markup/admin");
|
|
22
26
|
const fs = require("fs");
|
|
23
27
|
const path = require("path");
|
|
24
28
|
|
|
@@ -58,31 +62,33 @@ router.get(
|
|
|
58
62
|
isAdmin,
|
|
59
63
|
error_catcher(async (req, res) => {
|
|
60
64
|
// todo limit select from file by 10 or 20
|
|
61
|
-
const { dir } = req.query
|
|
62
|
-
const safeDir = File.normalise(dir || "/")
|
|
65
|
+
const { dir } = req.query;
|
|
66
|
+
const safeDir = File.normalise(dir || "/");
|
|
63
67
|
const rows = await File.find({ folder: dir }, { orderBy: "filename" });
|
|
64
68
|
const roles = await User.get_roles();
|
|
65
69
|
if (safeDir && safeDir !== "/" && safeDir !== ".") {
|
|
66
|
-
let dirname = path.dirname(safeDir)
|
|
67
|
-
if (dirname === ".") dirname = "/"
|
|
68
|
-
rows.unshift(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
let dirname = path.dirname(safeDir);
|
|
71
|
+
if (dirname === ".") dirname = "/";
|
|
72
|
+
rows.unshift(
|
|
73
|
+
new File({
|
|
74
|
+
filename: "..",
|
|
75
|
+
location: dirname,
|
|
76
|
+
isDirectory: true,
|
|
77
|
+
mime_super: "",
|
|
78
|
+
mime_sub: "",
|
|
79
|
+
})
|
|
80
|
+
);
|
|
75
81
|
}
|
|
76
82
|
if (req.xhr) {
|
|
77
83
|
for (const file of rows) {
|
|
78
|
-
file.location = file.path_to_serve
|
|
84
|
+
file.location = file.path_to_serve;
|
|
79
85
|
}
|
|
80
|
-
const directories = await File.allDirectories()
|
|
86
|
+
const directories = await File.allDirectories();
|
|
81
87
|
for (const file of directories) {
|
|
82
|
-
file.location = file.path_to_serve
|
|
88
|
+
file.location = file.path_to_serve;
|
|
83
89
|
}
|
|
84
|
-
res.json({ files: rows, roles, directories })
|
|
85
|
-
return
|
|
90
|
+
res.json({ files: rows, roles, directories });
|
|
91
|
+
return;
|
|
86
92
|
}
|
|
87
93
|
send_files_page({
|
|
88
94
|
res,
|
|
@@ -90,7 +96,7 @@ router.get(
|
|
|
90
96
|
headers: [
|
|
91
97
|
{
|
|
92
98
|
script: `/static_assets/${db.connectObj.version_tag}/bundle.js`,
|
|
93
|
-
defer: true
|
|
99
|
+
defer: true,
|
|
94
100
|
},
|
|
95
101
|
{
|
|
96
102
|
css: `/static_assets/${db.connectObj.version_tag}/bundle.css`,
|
|
@@ -122,7 +128,10 @@ router.get(
|
|
|
122
128
|
const serve_path = req.params[0];
|
|
123
129
|
const file = await File.findOne(serve_path);
|
|
124
130
|
|
|
125
|
-
if (
|
|
131
|
+
if (
|
|
132
|
+
file &&
|
|
133
|
+
(role <= file.min_role_read || (user_id && user_id === file.user_id))
|
|
134
|
+
) {
|
|
126
135
|
res.type(file.mimetype);
|
|
127
136
|
if (file.s3_store) s3storage.serveObject(file, res, true);
|
|
128
137
|
else res.download(file.location, file.filename);
|
|
@@ -150,7 +159,10 @@ router.get(
|
|
|
150
159
|
//if (typeof strictParseInt(id) !== "undefined")
|
|
151
160
|
const file = await File.findOne(serve_path);
|
|
152
161
|
|
|
153
|
-
if (
|
|
162
|
+
if (
|
|
163
|
+
file &&
|
|
164
|
+
(role <= file.min_role_read || (user_id && user_id === file.user_id))
|
|
165
|
+
) {
|
|
154
166
|
res.type(file.mimetype);
|
|
155
167
|
const cacheability = file.min_role_read === 10 ? "public" : "private";
|
|
156
168
|
res.set("Cache-Control", `${cacheability}, max-age=86400`);
|
|
@@ -178,10 +190,12 @@ router.get(
|
|
|
178
190
|
const { width_str, height_str } = req.params;
|
|
179
191
|
const serve_path = req.params[0];
|
|
180
192
|
|
|
181
|
-
|
|
182
193
|
const file = await File.findOne(serve_path);
|
|
183
194
|
|
|
184
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
file &&
|
|
197
|
+
(role <= file.min_role_read || (user_id && user_id === file.user_id))
|
|
198
|
+
) {
|
|
185
199
|
res.type(file.mimetype);
|
|
186
200
|
const cacheability = file.min_role_read === 10 ? "public" : "private";
|
|
187
201
|
res.set("Cache-Control", `${cacheability}, max-age=86400`);
|
|
@@ -189,13 +203,16 @@ router.get(
|
|
|
189
203
|
if (file.s3_store) s3storage.serveObject(file, res, false);
|
|
190
204
|
else {
|
|
191
205
|
const width = strictParseInt(width_str);
|
|
192
|
-
const height =
|
|
193
|
-
? strictParseInt(height_str) : null;
|
|
206
|
+
const height =
|
|
207
|
+
height_str && height_str !== "0" ? strictParseInt(height_str) : null;
|
|
194
208
|
if (!width) {
|
|
195
209
|
res.sendFile(file.location);
|
|
196
210
|
return;
|
|
197
211
|
}
|
|
198
|
-
const basenm = path.join(
|
|
212
|
+
const basenm = path.join(
|
|
213
|
+
path.dirname(file.location),
|
|
214
|
+
"_resized_" + path.basename(file.location)
|
|
215
|
+
);
|
|
199
216
|
const fnm = `${basenm}_w${width}${height ? `_h${height}` : ""}`;
|
|
200
217
|
if (!fs.existsSync(fnm)) {
|
|
201
218
|
await resizer({
|
|
@@ -233,15 +250,14 @@ router.post(
|
|
|
233
250
|
|
|
234
251
|
if (roleRow && file) {
|
|
235
252
|
await file.set_role(role);
|
|
236
|
-
|
|
237
253
|
}
|
|
238
254
|
|
|
239
|
-
|
|
240
|
-
|
|
255
|
+
res.redirect(
|
|
256
|
+
file ? `/files?dir=${encodeURIComponent(file.current_folder)}` : "/files"
|
|
257
|
+
);
|
|
241
258
|
})
|
|
242
259
|
);
|
|
243
260
|
|
|
244
|
-
|
|
245
261
|
router.post(
|
|
246
262
|
"/move/*",
|
|
247
263
|
isAdmin,
|
|
@@ -254,10 +270,12 @@ router.post(
|
|
|
254
270
|
await file.move_to_dir(new_path);
|
|
255
271
|
}
|
|
256
272
|
if (req.xhr) {
|
|
257
|
-
res.json({ success: "ok" })
|
|
258
|
-
return
|
|
273
|
+
res.json({ success: "ok" });
|
|
274
|
+
return;
|
|
259
275
|
}
|
|
260
|
-
res.redirect(
|
|
276
|
+
res.redirect(
|
|
277
|
+
file ? `/files?dir=${encodeURIComponent(file.current_folder)}` : "/files"
|
|
278
|
+
);
|
|
261
279
|
})
|
|
262
280
|
);
|
|
263
281
|
|
|
@@ -278,7 +296,6 @@ router.post(
|
|
|
278
296
|
await file.rename(filename);
|
|
279
297
|
|
|
280
298
|
res.redirect(`/files?dir=${encodeURIComponent(file.current_folder)}`);
|
|
281
|
-
|
|
282
299
|
})
|
|
283
300
|
);
|
|
284
301
|
|
|
@@ -286,7 +303,7 @@ router.post(
|
|
|
286
303
|
"/new-folder",
|
|
287
304
|
isAdmin,
|
|
288
305
|
error_catcher(async (req, res) => {
|
|
289
|
-
const { name, folder } = req.body
|
|
306
|
+
const { name, folder } = req.body;
|
|
290
307
|
await File.new_folder(name, folder);
|
|
291
308
|
|
|
292
309
|
res.json({ success: "ok" });
|
|
@@ -303,11 +320,11 @@ router.post(
|
|
|
303
320
|
"/upload",
|
|
304
321
|
setTenant,
|
|
305
322
|
error_catcher(async (req, res) => {
|
|
306
|
-
let { folder } = req.body
|
|
323
|
+
let { folder } = req.body;
|
|
307
324
|
let jsonResp = {};
|
|
308
325
|
const min_role_upload = getState().getConfig("min_role_upload", 1);
|
|
309
326
|
const role = req.user && req.user.id ? req.user.role_id : 10;
|
|
310
|
-
let file_for_redirect
|
|
327
|
+
let file_for_redirect;
|
|
311
328
|
if (role > +min_role_upload) {
|
|
312
329
|
if (!req.xhr) req.flash("warning", req.__("Not authorized"));
|
|
313
330
|
else jsonResp = { error: "Not authorized" };
|
|
@@ -323,7 +340,7 @@ router.post(
|
|
|
323
340
|
folder ? File.normalise(folder) : undefined
|
|
324
341
|
);
|
|
325
342
|
const many = Array.isArray(f);
|
|
326
|
-
file_for_redirect = many ? f[0] : f
|
|
343
|
+
file_for_redirect = many ? f[0] : f;
|
|
327
344
|
if (!req.xhr)
|
|
328
345
|
req.flash(
|
|
329
346
|
"success",
|
|
@@ -346,10 +363,11 @@ router.post(
|
|
|
346
363
|
};
|
|
347
364
|
}
|
|
348
365
|
if (!req.xhr)
|
|
349
|
-
res.redirect(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
366
|
+
res.redirect(
|
|
367
|
+
!file_for_redirect
|
|
368
|
+
? "/files"
|
|
369
|
+
: `/files?dir=${encodeURIComponent(file_for_redirect.current_folder)}`
|
|
370
|
+
);
|
|
353
371
|
else res.json(jsonResp);
|
|
354
372
|
})
|
|
355
373
|
);
|
|
@@ -438,7 +456,7 @@ router.post(
|
|
|
438
456
|
const form = await storage_form(req);
|
|
439
457
|
form.validate(req.body);
|
|
440
458
|
if (form.hasErrors) {
|
|
441
|
-
|
|
459
|
+
send_files_page({
|
|
442
460
|
res,
|
|
443
461
|
req,
|
|
444
462
|
active_sub: "Storage",
|
|
@@ -465,17 +483,17 @@ router.post(
|
|
|
465
483
|
* @returns {Promise<Form>} form
|
|
466
484
|
*/
|
|
467
485
|
const files_settings_form = async (req) => {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
486
|
+
return await config_fields_form({
|
|
487
|
+
req,
|
|
488
|
+
field_names: [
|
|
489
|
+
"min_role_upload",
|
|
490
|
+
"file_accept_filter_default",
|
|
491
|
+
"file_upload_debug",
|
|
492
|
+
"file_upload_limit",
|
|
493
|
+
"file_upload_timeout",
|
|
494
|
+
],
|
|
495
|
+
action: "/files/settings",
|
|
496
|
+
});
|
|
479
497
|
};
|
|
480
498
|
|
|
481
499
|
/**
|
|
@@ -484,21 +502,21 @@ const files_settings_form = async (req) => {
|
|
|
484
502
|
* @memberof module:routes/admin~routes/adminRouter
|
|
485
503
|
*/
|
|
486
504
|
router.get(
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
505
|
+
"/settings",
|
|
506
|
+
isAdmin,
|
|
507
|
+
error_catcher(async (req, res) => {
|
|
508
|
+
const form = await files_settings_form(req);
|
|
509
|
+
send_files_page({
|
|
510
|
+
res,
|
|
511
|
+
req,
|
|
512
|
+
active_sub: "Settings",
|
|
513
|
+
contents: {
|
|
514
|
+
type: "card",
|
|
515
|
+
title: req.__("Files settings"),
|
|
516
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
})
|
|
502
520
|
);
|
|
503
521
|
|
|
504
522
|
/**
|
|
@@ -507,29 +525,29 @@ router.get(
|
|
|
507
525
|
* @memberof module:routes/admin~routes/adminRouter
|
|
508
526
|
*/
|
|
509
527
|
router.post(
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
);
|
|
528
|
+
"/settings",
|
|
529
|
+
isAdmin,
|
|
530
|
+
error_catcher(async (req, res) => {
|
|
531
|
+
const form = await files_settings_form(req);
|
|
532
|
+
form.validate(req.body);
|
|
533
|
+
if (form.hasErrors) {
|
|
534
|
+
send_files_page({
|
|
535
|
+
res,
|
|
536
|
+
req,
|
|
537
|
+
active_sub: "Settings",
|
|
538
|
+
contents: {
|
|
539
|
+
type: "card",
|
|
540
|
+
title: req.__("Files settings"),
|
|
541
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
} else {
|
|
545
|
+
await save_config_from_form(form);
|
|
546
|
+
|
|
547
|
+
if (!req.xhr) {
|
|
548
|
+
req.flash("success", req.__("Files settings updated"));
|
|
549
|
+
res.redirect("/files/settings");
|
|
550
|
+
} else res.json({ success: "ok" });
|
|
551
|
+
}
|
|
552
|
+
})
|
|
553
|
+
);
|
package/routes/homepage.js
CHANGED
|
@@ -481,7 +481,7 @@ const get_config_response = async (role_id, res, req) => {
|
|
|
481
481
|
title: db_page.title,
|
|
482
482
|
description: db_page.description,
|
|
483
483
|
bodyClass: "page_" + db.sqlsanitize(homeCfg),
|
|
484
|
-
}
|
|
484
|
+
},
|
|
485
485
|
contents
|
|
486
486
|
);
|
|
487
487
|
} else res.redirect(homeCfg);
|
package/routes/infoarch.js
CHANGED
package/routes/list.js
CHANGED
|
@@ -51,7 +51,7 @@ router.get(
|
|
|
51
51
|
isAdmin,
|
|
52
52
|
error_catcher(async (req, res) => {
|
|
53
53
|
const { tableName, id } = req.params;
|
|
54
|
-
const table = await Table.findOne({ name
|
|
54
|
+
const table = await Table.findOne({ name: tableName });
|
|
55
55
|
|
|
56
56
|
const fields = await table.getFields();
|
|
57
57
|
var tfields = fields.map((f) => ({ label: f.label, key: f.listKey }));
|
|
@@ -99,7 +99,7 @@ router.post(
|
|
|
99
99
|
isAdmin,
|
|
100
100
|
error_catcher(async (req, res) => {
|
|
101
101
|
const { tableName, id, _version } = req.params;
|
|
102
|
-
const table = await Table.findOne({ name
|
|
102
|
+
const table = await Table.findOne({ name: tableName });
|
|
103
103
|
|
|
104
104
|
const fields = await table.getFields();
|
|
105
105
|
const row = await db.selectOne(`${db.sqlsanitize(table.name)}__history`, {
|
|
@@ -216,7 +216,7 @@ const arrangeIdFirst = (flds) => {
|
|
|
216
216
|
const noId = flds.filter((f) => f.name !== "id");
|
|
217
217
|
const id = flds.find((f) => f.name === "id");
|
|
218
218
|
if (id) return [id, ...noId];
|
|
219
|
-
else return flds
|
|
219
|
+
else return flds;
|
|
220
220
|
};
|
|
221
221
|
|
|
222
222
|
/**
|
|
@@ -369,8 +369,9 @@ router.get(
|
|
|
369
369
|
})
|
|
370
370
|
})
|
|
371
371
|
window.tabulator_table = new Tabulator("#jsGrid", {
|
|
372
|
-
ajaxURL:"/api/${table.name}${
|
|
373
|
-
|
|
372
|
+
ajaxURL:"/api/${table.name}${
|
|
373
|
+
table.versioned ? "?versioncount=on" : ""
|
|
374
|
+
}",
|
|
374
375
|
layout:"fitColumns",
|
|
375
376
|
columns,
|
|
376
377
|
height:"100%",
|
package/routes/packs.js
CHANGED
|
@@ -335,8 +335,7 @@ router.post(
|
|
|
335
335
|
const can_install = await can_install_pack(pack.pack);
|
|
336
336
|
|
|
337
337
|
if (can_install.error) {
|
|
338
|
-
error
|
|
339
|
-
req.flash("error", error);
|
|
338
|
+
req.flash("error", can_install.error);
|
|
340
339
|
res.redirect(`/plugins`);
|
|
341
340
|
return;
|
|
342
341
|
} else if (can_install.warning) {
|
package/routes/pageedit.js
CHANGED
|
@@ -100,7 +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
|
+
images.forEach((im) => (im.location = im.path_to_serve));
|
|
104
104
|
const roles = await User.get_roles();
|
|
105
105
|
const stateActions = getState().actions;
|
|
106
106
|
const actions = [
|
package/routes/tag_entries.js
CHANGED
package/routes/tenant.js
CHANGED
|
@@ -21,6 +21,7 @@ const {
|
|
|
21
21
|
renderForm,
|
|
22
22
|
link,
|
|
23
23
|
post_delete_btn,
|
|
24
|
+
localeDateTime,
|
|
24
25
|
mkTable,
|
|
25
26
|
} = require("@saltcorn/markup");
|
|
26
27
|
const {
|
|
@@ -384,7 +385,7 @@ router.get(
|
|
|
384
385
|
},
|
|
385
386
|
{
|
|
386
387
|
label: req.__("Created"),
|
|
387
|
-
key: (r) =>
|
|
388
|
+
key: (r) => localeDateTime(r.created),
|
|
388
389
|
},
|
|
389
390
|
{
|
|
390
391
|
label: req.__("Information"),
|
package/routes/utils.js
CHANGED
|
@@ -218,7 +218,9 @@ const scan_for_page_title = (contents, viewname) => {
|
|
|
218
218
|
try {
|
|
219
219
|
scanstr =
|
|
220
220
|
typeof contents === "string" ? contents : JSON.stringify(contents);
|
|
221
|
-
} catch {
|
|
221
|
+
} catch {
|
|
222
|
+
//ignore
|
|
223
|
+
}
|
|
222
224
|
if (scanstr.includes("<!--SCPT:")) {
|
|
223
225
|
const start = scanstr.indexOf("<!--SCPT:");
|
|
224
226
|
const end = scanstr.indexOf("-->", start);
|
package/routes/view.js
CHANGED
|
@@ -9,7 +9,7 @@ const Router = require("express-promise-router");
|
|
|
9
9
|
const View = require("@saltcorn/data/models/view");
|
|
10
10
|
const Table = require("@saltcorn/data/models/table");
|
|
11
11
|
|
|
12
|
-
const { text } = require("@saltcorn/markup/tags");
|
|
12
|
+
const { text, style } = require("@saltcorn/markup/tags");
|
|
13
13
|
const {
|
|
14
14
|
isAdmin,
|
|
15
15
|
error_catcher,
|
|
@@ -62,8 +62,20 @@ router.get(
|
|
|
62
62
|
res.redirect("/");
|
|
63
63
|
return;
|
|
64
64
|
}
|
|
65
|
+
const isModal = req.headers?.saltcornmodalrequest;
|
|
66
|
+
|
|
65
67
|
const contents = await view.run_possibly_on_page(query, req, res);
|
|
66
|
-
const title =
|
|
68
|
+
const title =
|
|
69
|
+
isModal && view.attributes?.popup_title
|
|
70
|
+
? view.attributes?.popup_title
|
|
71
|
+
: scan_for_page_title(contents, view.name);
|
|
72
|
+
if (isModal && view.attributes?.popup_width)
|
|
73
|
+
res.set(
|
|
74
|
+
"SaltcornModalWidth",
|
|
75
|
+
`${view.attributes?.popup_width}${
|
|
76
|
+
view.attributes?.popup_width_units || "px"
|
|
77
|
+
}`
|
|
78
|
+
);
|
|
67
79
|
res.sendWrap(
|
|
68
80
|
title,
|
|
69
81
|
add_edit_bar({
|
package/routes/viewedit.js
CHANGED
|
@@ -134,6 +134,7 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
134
134
|
action: addOnDoneRedirect("/viewedit/save", req),
|
|
135
135
|
submitLabel: req.__("Configure") + " »",
|
|
136
136
|
blurb: req.__("First, please give some basic information about the view."),
|
|
137
|
+
tabs: { tabsStyle: "Accordion" },
|
|
137
138
|
fields: [
|
|
138
139
|
new Field({
|
|
139
140
|
label: req.__("View name"),
|
|
@@ -191,6 +192,7 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
191
192
|
"Requests to render this view directly will instead show the chosen page, if any. The chosen page should embed this view. Use this to decorate the view with additional elements."
|
|
192
193
|
),
|
|
193
194
|
input_type: "select",
|
|
195
|
+
tab: "View settings",
|
|
194
196
|
options: [
|
|
195
197
|
{ value: "", label: "" },
|
|
196
198
|
...pages.map((p) => ({ value: p.name, label: p.name })),
|
|
@@ -201,6 +203,7 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
201
203
|
label: req.__("Slug"),
|
|
202
204
|
sublabel: req.__("Field that can be used for a prettier URL structure"),
|
|
203
205
|
type: "String",
|
|
206
|
+
tab: "View settings",
|
|
204
207
|
attributes: {
|
|
205
208
|
calcOptions: [
|
|
206
209
|
"table_name",
|
|
@@ -209,6 +212,33 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
209
212
|
},
|
|
210
213
|
showIf: { viewtemplate: hasTable },
|
|
211
214
|
}),
|
|
215
|
+
new Field({
|
|
216
|
+
name: "popup_title",
|
|
217
|
+
label: req.__("Title"),
|
|
218
|
+
type: "String",
|
|
219
|
+
parent_field: "attributes",
|
|
220
|
+
tab: "Popup settings",
|
|
221
|
+
}),
|
|
222
|
+
{
|
|
223
|
+
name: "popup_width",
|
|
224
|
+
label: req.__("Column width"),
|
|
225
|
+
type: "Integer",
|
|
226
|
+
tab: "Popup settings",
|
|
227
|
+
parent_field: "attributes",
|
|
228
|
+
attributes: { asideNext: true },
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
name: "popup_width_units",
|
|
232
|
+
label: req.__("Units"),
|
|
233
|
+
type: "String",
|
|
234
|
+
tab: "Popup settings",
|
|
235
|
+
fieldview: "radio_group",
|
|
236
|
+
parent_field: "attributes",
|
|
237
|
+
attributes: {
|
|
238
|
+
inline: true,
|
|
239
|
+
options: ["px", "%", "vw", "em", "rem"],
|
|
240
|
+
},
|
|
241
|
+
},
|
|
212
242
|
...(isEdit
|
|
213
243
|
? [
|
|
214
244
|
new Field({
|
|
@@ -396,6 +426,7 @@ router.post(
|
|
|
396
426
|
const vt = getState().viewtemplates[v.viewtemplate];
|
|
397
427
|
if (vt.initial_config) v.configuration = await vt.initial_config(v);
|
|
398
428
|
else v.configuration = {};
|
|
429
|
+
//console.log(v);
|
|
399
430
|
await View.create(v);
|
|
400
431
|
}
|
|
401
432
|
res.redirect(
|
|
@@ -503,6 +534,9 @@ router.get(
|
|
|
503
534
|
res.redirect("/viewedit");
|
|
504
535
|
return;
|
|
505
536
|
}
|
|
537
|
+
(view.configuration?.columns || []).forEach((c) => {
|
|
538
|
+
c._columndef = JSON.stringify(c);
|
|
539
|
+
});
|
|
506
540
|
const configFlow = await view.get_config_flow(req);
|
|
507
541
|
const hasConfig =
|
|
508
542
|
view.configuration && Object.keys(view.configuration).length > 0;
|
|
@@ -644,6 +678,7 @@ router.post(
|
|
|
644
678
|
|
|
645
679
|
if (viewname && req.body) {
|
|
646
680
|
const view = await View.findOne({ name: viewname });
|
|
681
|
+
req.staticFieldViewConfig = true;
|
|
647
682
|
const configFlow = await view.get_config_flow(req);
|
|
648
683
|
const step = await configFlow.singleStepForm(req.body, req);
|
|
649
684
|
if (step?.renderForm) {
|
package/s3storage.js
CHANGED
|
@@ -5,7 +5,7 @@ const { getState } = require("@saltcorn/data/db/state");
|
|
|
5
5
|
const fileUpload = require("express-fileupload");
|
|
6
6
|
const { v4: uuidv4 } = require("uuid");
|
|
7
7
|
const contentDisposition = require("content-disposition");
|
|
8
|
-
|
|
8
|
+
const fs = require("fs");
|
|
9
9
|
function createS3Client() {
|
|
10
10
|
return new aws.S3({
|
|
11
11
|
secretAccessKey: getState().getConfig("storage_s3_access_secret"),
|
|
@@ -46,25 +46,27 @@ module.exports = {
|
|
|
46
46
|
s3upload(req, res, next);
|
|
47
47
|
} else {
|
|
48
48
|
// Use regular file upload https://www.npmjs.com/package/express-fileupload
|
|
49
|
-
const fileSizeLimit =
|
|
49
|
+
const fileSizeLimit =
|
|
50
|
+
1024 * +getState().getConfig("file_upload_limit", 0);
|
|
50
51
|
fileUpload({
|
|
51
52
|
useTempFiles: true,
|
|
52
53
|
createParentPath: true,
|
|
53
54
|
tempFileDir: "/tmp/",
|
|
54
55
|
// set to true - if you want to have debug
|
|
55
|
-
debug: getState().getConfig("file_upload_debug",false),
|
|
56
|
+
debug: getState().getConfig("file_upload_debug", false),
|
|
56
57
|
//uriDecodeFileNames: true,
|
|
57
58
|
//safeFileNames: true,
|
|
58
|
-
defCharset:
|
|
59
|
-
defParamCharset:
|
|
59
|
+
defCharset: "utf8",
|
|
60
|
+
defParamCharset: "utf8",
|
|
60
61
|
// 0 - means no upload limit check
|
|
61
|
-
limits:
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
limits: fileSizeLimit
|
|
63
|
+
? {
|
|
64
|
+
fileSize: fileSizeLimit,
|
|
65
|
+
}
|
|
66
|
+
: {},
|
|
64
67
|
abortOnLimit: fileSizeLimit !== 0,
|
|
65
68
|
// 0 - means no upload limit check
|
|
66
|
-
uploadTimeout: getState().getConfig("file_upload_timeout",0),
|
|
67
|
-
|
|
69
|
+
uploadTimeout: getState().getConfig("file_upload_timeout", 0),
|
|
68
70
|
})(req, res, next);
|
|
69
71
|
}
|
|
70
72
|
},
|
|
@@ -146,7 +148,7 @@ module.exports = {
|
|
|
146
148
|
// Forward the object
|
|
147
149
|
s3.getObject(params)
|
|
148
150
|
.on("httpHeaders", function (statusCode, headers) {
|
|
149
|
-
if (
|
|
151
|
+
if (download)
|
|
150
152
|
res.set("Content-Disposition", contentDisposition(file.filename));
|
|
151
153
|
res.set("Content-Length", headers["content-length"]);
|
|
152
154
|
this.response.httpResponse.createUnbufferedStream().pipe(res);
|