@saltcorn/server 1.0.0-beta.1 → 1.0.0-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/auth/admin.js +1 -0
- package/help/Android App Signing.tmd +1 -1
- package/load_plugins.js +51 -6
- package/locales/en.json +10 -1
- package/markup/blockly.js +4 -4
- package/package.json +14 -14
- package/public/saltcorn-common.js +101 -65
- package/public/saltcorn.js +40 -7
- package/routes/actions.js +7 -0
- package/routes/admin.js +35 -10
- package/routes/files.js +101 -15
- package/routes/notifications.js +13 -3
- package/routes/plugins.js +56 -18
- package/routes/search.js +6 -1
- package/routes/tables.js +2 -1
- package/routes/viewedit.js +10 -2
- package/serve.js +1 -1
- package/tests/edit.test.js +5 -5
- package/tests/files.test.js +87 -7
- package/tests/plugin_install.test.js +235 -0
- package/tests/plugins.test.js +140 -0
package/routes/files.js
CHANGED
|
@@ -20,7 +20,15 @@ const {
|
|
|
20
20
|
setTenant,
|
|
21
21
|
is_relative_url,
|
|
22
22
|
} = require("./utils.js");
|
|
23
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
h1,
|
|
25
|
+
div,
|
|
26
|
+
text,
|
|
27
|
+
script,
|
|
28
|
+
style,
|
|
29
|
+
link,
|
|
30
|
+
domReady,
|
|
31
|
+
} = require("@saltcorn/markup/tags");
|
|
24
32
|
const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
|
|
25
33
|
const { strictParseInt } = require("@saltcorn/data/plugin-helper");
|
|
26
34
|
const {
|
|
@@ -43,20 +51,98 @@ const { extract } = require("@saltcorn/admin-models/models/backup");
|
|
|
43
51
|
const router = new Router();
|
|
44
52
|
module.exports = router;
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
const send_files_picker = async (folder, noSubdirs, inputId, req, res) => {
|
|
55
|
+
res.set("SaltcornModalWidth", "1200px");
|
|
56
|
+
res.sendWrap(req.__("Please select a file"), {
|
|
57
|
+
above: [
|
|
58
|
+
script({
|
|
59
|
+
src: `/static_assets/${db.connectObj.version_tag}/bundle.js`,
|
|
60
|
+
defer: true,
|
|
61
|
+
}),
|
|
62
|
+
script(
|
|
63
|
+
domReady(
|
|
64
|
+
`$("head").append('${link({
|
|
65
|
+
rel: "stylesheet",
|
|
66
|
+
href: `/static_assets/${db.connectObj.version_tag}/bundle.css`,
|
|
67
|
+
})}')`
|
|
68
|
+
)
|
|
69
|
+
),
|
|
70
|
+
div({
|
|
71
|
+
id: "saltcorn-file-manager",
|
|
72
|
+
full_manager: "false",
|
|
73
|
+
folder: folder,
|
|
74
|
+
input_id: inputId,
|
|
75
|
+
...(noSubdirs ? { no_subdirs: "true" } : {}),
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
59
78
|
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
router.get(
|
|
82
|
+
"/picker",
|
|
83
|
+
error_catcher(async (req, res) => {
|
|
84
|
+
const { folder, input_id, no_subdirs } = req.query;
|
|
85
|
+
send_files_picker(folder, no_subdirs, input_id, req, res);
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
router.get(
|
|
90
|
+
"/visible_entries",
|
|
91
|
+
error_catcher(async (req, res) => {
|
|
92
|
+
const role = req.user?.role_id ? req.user.role_id : 100;
|
|
93
|
+
const userId = req.user?.id;
|
|
94
|
+
const { dir, no_subdirs } = req.query;
|
|
95
|
+
const noSubdirs = no_subdirs === "true";
|
|
96
|
+
const safeDir = File.normalise(dir || "/");
|
|
97
|
+
const absFolder = path.join(
|
|
98
|
+
db.connectObj.file_store,
|
|
99
|
+
db.getTenantSchema(),
|
|
100
|
+
safeDir
|
|
101
|
+
);
|
|
102
|
+
const dirOnDisk = await File.from_file_on_disk(
|
|
103
|
+
path.basename(absFolder),
|
|
104
|
+
path.dirname(absFolder)
|
|
105
|
+
);
|
|
106
|
+
if (dirOnDisk.min_role_read < role) {
|
|
107
|
+
getState().log(5, `Directory denied. path=${dir} role=${role}`);
|
|
108
|
+
res.json({ files: [], roles: [], directories: [] });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const rows = (
|
|
112
|
+
await File.find({ folder: dir }, { orderBy: "filename" })
|
|
113
|
+
).filter((f) => {
|
|
114
|
+
if (noSubdirs && f.isDirectory) return false;
|
|
115
|
+
else return role <= f.min_role_read || (userId && userId === f.user_id);
|
|
116
|
+
});
|
|
117
|
+
const roles = await User.get_roles();
|
|
118
|
+
if (!no_subdirs && safeDir && safeDir !== "/" && safeDir !== ".") {
|
|
119
|
+
let dirname = path.dirname(safeDir);
|
|
120
|
+
if (dirname === ".") dirname = "/";
|
|
121
|
+
rows.unshift(
|
|
122
|
+
new File({
|
|
123
|
+
filename: "..",
|
|
124
|
+
location: dirname,
|
|
125
|
+
isDirectory: true,
|
|
126
|
+
mime_super: "",
|
|
127
|
+
mime_sub: "",
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
for (const file of rows) {
|
|
133
|
+
file.location = file.path_to_serve;
|
|
134
|
+
}
|
|
135
|
+
const directories = !noSubdirs
|
|
136
|
+
? (await File.allDirectories(true)).filter(
|
|
137
|
+
(dir) => role <= dir.min_role_read
|
|
138
|
+
)
|
|
139
|
+
: [];
|
|
140
|
+
for (const dir of directories) {
|
|
141
|
+
dir.location = dir.path_to_serve;
|
|
142
|
+
}
|
|
143
|
+
res.json({ files: rows, roles, directories });
|
|
144
|
+
})
|
|
145
|
+
);
|
|
60
146
|
|
|
61
147
|
/**
|
|
62
148
|
* @name get
|
|
@@ -116,7 +202,7 @@ router.get(
|
|
|
116
202
|
contents: {
|
|
117
203
|
type: "card",
|
|
118
204
|
contents: [
|
|
119
|
-
div({ id: "saltcorn-file-manager" }),
|
|
205
|
+
div({ full_manager: "true", id: "saltcorn-file-manager" }),
|
|
120
206
|
fileUploadForm(req, safeDir),
|
|
121
207
|
],
|
|
122
208
|
},
|
package/routes/notifications.js
CHANGED
|
@@ -14,6 +14,7 @@ const Form = require("@saltcorn/data/models/form");
|
|
|
14
14
|
const File = require("@saltcorn/data/models/file");
|
|
15
15
|
const User = require("@saltcorn/data/models/user");
|
|
16
16
|
const { renderForm, post_btn } = require("@saltcorn/markup");
|
|
17
|
+
const db = require("@saltcorn/data/db");
|
|
17
18
|
|
|
18
19
|
const router = new Router();
|
|
19
20
|
module.exports = router;
|
|
@@ -39,9 +40,18 @@ router.get(
|
|
|
39
40
|
orderDesc: true,
|
|
40
41
|
limit: 20,
|
|
41
42
|
});
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const unreads = nots.filter((n) => !n.read);
|
|
44
|
+
if (unreads.length > 0)
|
|
45
|
+
await Notification.mark_as_read(
|
|
46
|
+
!db.isSQLite
|
|
47
|
+
? {
|
|
48
|
+
id: { in: unreads.map((n) => n.id) },
|
|
49
|
+
}
|
|
50
|
+
: {
|
|
51
|
+
or: unreads.map((n) => ({ id: n.id })),
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
45
55
|
const form = notificationSettingsForm();
|
|
46
56
|
const user = await User.findOne({ id: req.user?.id });
|
|
47
57
|
form.values = { notify_email: user?._attributes?.notify_email };
|
package/routes/plugins.js
CHANGED
|
@@ -59,6 +59,10 @@ const { sleep, removeNonWordChars } = require("@saltcorn/data/utils");
|
|
|
59
59
|
const { loadAllPlugins } = require("../load_plugins");
|
|
60
60
|
const npmFetch = require("npm-registry-fetch");
|
|
61
61
|
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
62
|
+
const {
|
|
63
|
+
supportedVersion,
|
|
64
|
+
isVersionSupported,
|
|
65
|
+
} = require("@saltcorn/plugins-loader/stable_versioning");
|
|
62
66
|
|
|
63
67
|
/**
|
|
64
68
|
* @type {object}
|
|
@@ -177,6 +181,8 @@ const get_store_items = async () => {
|
|
|
177
181
|
has_auth: plugin.has_auth,
|
|
178
182
|
unsafe: plugin.unsafe,
|
|
179
183
|
source: plugin.source,
|
|
184
|
+
ready_for_mobile:
|
|
185
|
+
plugin.ready_for_mobile && plugin.ready_for_mobile(plugin.name),
|
|
180
186
|
}))
|
|
181
187
|
.filter((p) => !p.unsafe || isRoot || tenants_unsafe_plugins);
|
|
182
188
|
const local_logins = installed_plugins
|
|
@@ -190,6 +196,8 @@ const get_store_items = async () => {
|
|
|
190
196
|
github: plugin.source === "github",
|
|
191
197
|
git: plugin.source === "git",
|
|
192
198
|
local: plugin.source === "local",
|
|
199
|
+
ready_for_mobile:
|
|
200
|
+
plugin.ready_for_mobile && plugin.ready_for_mobile(plugin.name),
|
|
193
201
|
}));
|
|
194
202
|
|
|
195
203
|
const pack_items = packs_available.map((pack) => ({
|
|
@@ -274,7 +282,8 @@ const store_item_html = (req) => (item) => ({
|
|
|
274
282
|
item.github && badge("GitHub"),
|
|
275
283
|
item.git && badge("Git"),
|
|
276
284
|
item.local && badge(req.__("Local")),
|
|
277
|
-
item.installed && badge(req.__("Installed"))
|
|
285
|
+
item.installed && badge(req.__("Installed")),
|
|
286
|
+
item.ready_for_mobile && badge(req.__("Mobile"))
|
|
278
287
|
),
|
|
279
288
|
div(item.description || ""),
|
|
280
289
|
item.documentation_link
|
|
@@ -584,7 +593,9 @@ router.get(
|
|
|
584
593
|
error_catcher(async (req, res) => {
|
|
585
594
|
const { name } = req.params;
|
|
586
595
|
const withoutOrg = name.replace(/^@saltcorn\//, "");
|
|
587
|
-
|
|
596
|
+
let plugin = await Plugin.store_by_name(decodeURIComponent(withoutOrg));
|
|
597
|
+
if (!plugin)
|
|
598
|
+
plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
588
599
|
if (!plugin) {
|
|
589
600
|
getState().log(
|
|
590
601
|
2,
|
|
@@ -609,6 +620,7 @@ router.get(
|
|
|
609
620
|
if (mod) selected = mod.version;
|
|
610
621
|
}
|
|
611
622
|
if (!selected) selected = versions[versions.length - 1];
|
|
623
|
+
const packageJson = require("../package.json");
|
|
612
624
|
return res.send(
|
|
613
625
|
form(
|
|
614
626
|
{
|
|
@@ -631,14 +643,18 @@ router.get(
|
|
|
631
643
|
class: "form-control form-select",
|
|
632
644
|
name: "version",
|
|
633
645
|
},
|
|
634
|
-
versions
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
646
|
+
versions
|
|
647
|
+
.filter((v) =>
|
|
648
|
+
isVersionSupported(v, pkgInfo.versions, packageJson.version)
|
|
649
|
+
)
|
|
650
|
+
.map((version) =>
|
|
651
|
+
option({
|
|
652
|
+
id: `${version}_opt`,
|
|
653
|
+
value: version,
|
|
654
|
+
label: version,
|
|
655
|
+
selected: version === selected,
|
|
656
|
+
})
|
|
657
|
+
)
|
|
642
658
|
)
|
|
643
659
|
),
|
|
644
660
|
div(
|
|
@@ -1354,7 +1370,17 @@ router.get(
|
|
|
1354
1370
|
const { name } = req.params;
|
|
1355
1371
|
|
|
1356
1372
|
const plugin = await Plugin.findOne({ name });
|
|
1357
|
-
|
|
1373
|
+
const pkgInfo = await npmFetch.json(
|
|
1374
|
+
`https://registry.npmjs.org/${plugin.location}`
|
|
1375
|
+
);
|
|
1376
|
+
await plugin.upgrade_version(
|
|
1377
|
+
(p, f) => load_plugins.loadPlugin(p, f),
|
|
1378
|
+
supportedVersion(
|
|
1379
|
+
"latest",
|
|
1380
|
+
pkgInfo.versions,
|
|
1381
|
+
require("../package.json").version
|
|
1382
|
+
)
|
|
1383
|
+
);
|
|
1358
1384
|
req.flash("success", req.__(`Module up-to-date`));
|
|
1359
1385
|
|
|
1360
1386
|
res.redirect(`/plugins/info/${encodeURIComponent(plugin.name)}`);
|
|
@@ -1385,11 +1411,12 @@ router.post(
|
|
|
1385
1411
|
res.redirect(`/plugins`);
|
|
1386
1412
|
} else {
|
|
1387
1413
|
try {
|
|
1388
|
-
await load_plugins.loadAndSaveNewPlugin(
|
|
1414
|
+
const msgs = await load_plugins.loadAndSaveNewPlugin(
|
|
1389
1415
|
plugin,
|
|
1390
1416
|
schema === db.connectObj.default_schema || plugin.source === "github"
|
|
1391
1417
|
);
|
|
1392
1418
|
req.flash("success", req.__(`Module %s installed`, plugin.name));
|
|
1419
|
+
for (const msg of msgs) req.flash("warning", msg);
|
|
1393
1420
|
res.redirect(`/plugins`);
|
|
1394
1421
|
} catch (e) {
|
|
1395
1422
|
req.flash("error", `${e.message}`);
|
|
@@ -1488,12 +1515,23 @@ router.post(
|
|
|
1488
1515
|
res.redirect(`/plugins`);
|
|
1489
1516
|
return;
|
|
1490
1517
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1518
|
+
|
|
1519
|
+
let msgs = null;
|
|
1520
|
+
try {
|
|
1521
|
+
msgs = await load_plugins.loadAndSaveNewPlugin(
|
|
1522
|
+
plugin,
|
|
1523
|
+
forceReInstall,
|
|
1524
|
+
undefined,
|
|
1525
|
+
req.__
|
|
1526
|
+
);
|
|
1527
|
+
} catch (e) {
|
|
1528
|
+
req.flash(
|
|
1529
|
+
"error",
|
|
1530
|
+
e.message || req.__("Error installing module %s", plugin.name)
|
|
1531
|
+
);
|
|
1532
|
+
res.redirect(`/plugins`);
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1497
1535
|
const plugin_module = getState().plugins[name];
|
|
1498
1536
|
await sleep(1000); // Allow other workers to load this plugin
|
|
1499
1537
|
await getState().refresh_views();
|
package/routes/search.js
CHANGED
|
@@ -202,7 +202,12 @@ const runSearch = async ({ q, _page, table }, req, res) => {
|
|
|
202
202
|
let tablesWithResults = [];
|
|
203
203
|
let tablesConfigured = 0;
|
|
204
204
|
for (const [tableName, viewName] of Object.entries(cfg)) {
|
|
205
|
-
if (
|
|
205
|
+
if (
|
|
206
|
+
!viewName ||
|
|
207
|
+
viewName === "" ||
|
|
208
|
+
viewName === "search_table_description" ||
|
|
209
|
+
tableName === "search_table_description"
|
|
210
|
+
)
|
|
206
211
|
continue;
|
|
207
212
|
tablesConfigured += 1;
|
|
208
213
|
if (table && tableName !== table) continue;
|
package/routes/tables.js
CHANGED
|
@@ -708,6 +708,7 @@ const attribBadges = (f) => {
|
|
|
708
708
|
].includes(k)
|
|
709
709
|
)
|
|
710
710
|
return;
|
|
711
|
+
if(Array.isArray(v) && !v.length) return;
|
|
711
712
|
if (v || v === 0) s += badge("secondary", k);
|
|
712
713
|
});
|
|
713
714
|
}
|
|
@@ -1911,7 +1912,7 @@ router.post(
|
|
|
1911
1912
|
const table = Table.findOne({ name });
|
|
1912
1913
|
|
|
1913
1914
|
try {
|
|
1914
|
-
await table.deleteRows({});
|
|
1915
|
+
await table.deleteRows({}, req.user);
|
|
1915
1916
|
req.flash("success", req.__("Deleted all rows"));
|
|
1916
1917
|
} catch (e) {
|
|
1917
1918
|
req.flash("error", e.message);
|
package/routes/viewedit.js
CHANGED
|
@@ -186,7 +186,11 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
186
186
|
sublabel: req.__("Display data from this table"),
|
|
187
187
|
options: tableOptions,
|
|
188
188
|
disabled: isEdit,
|
|
189
|
-
showIf:
|
|
189
|
+
showIf: isEdit
|
|
190
|
+
? hasTable.includes(values.viewtemplate)
|
|
191
|
+
? undefined
|
|
192
|
+
: { nosuchvar: true }
|
|
193
|
+
: { viewtemplate: hasTable },
|
|
190
194
|
}),
|
|
191
195
|
new Field({
|
|
192
196
|
name: "min_role",
|
|
@@ -242,7 +246,11 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
242
246
|
mapObjectValues(slugOptions, (lvs) => lvs.map((lv) => lv.label)),
|
|
243
247
|
],
|
|
244
248
|
},
|
|
245
|
-
showIf:
|
|
249
|
+
showIf: isEdit
|
|
250
|
+
? hasTable.includes(values.viewtemplate)
|
|
251
|
+
? undefined
|
|
252
|
+
: { nosuchvar: true }
|
|
253
|
+
: { viewtemplate: hasTable },
|
|
246
254
|
}),
|
|
247
255
|
new Field({
|
|
248
256
|
name: "no_menu",
|
package/serve.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @category server
|
|
5
5
|
* @module serve
|
|
6
6
|
*/
|
|
7
|
-
const runScheduler = require("@saltcorn/data/models/scheduler");
|
|
7
|
+
const { runScheduler } = require("@saltcorn/data/models/scheduler");
|
|
8
8
|
const User = require("@saltcorn/data/models/user");
|
|
9
9
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
10
10
|
const db = require("@saltcorn/data/db");
|
package/tests/edit.test.js
CHANGED
|
@@ -346,7 +346,7 @@ describe("JSDOM-E2E edit test", () => {
|
|
|
346
346
|
showIfFormula: 'publisher?.name == "AK Press"',
|
|
347
347
|
});
|
|
348
348
|
const dom = await load_url_dom("/view/AuthorEditForTest");
|
|
349
|
-
await sleep(
|
|
349
|
+
await sleep(2000);
|
|
350
350
|
const pubwarn = dom.window.document.querySelector("div.pubwarn");
|
|
351
351
|
//console.log(dom.serialize());
|
|
352
352
|
expect(pubwarn.style.display).toBe("none");
|
|
@@ -362,7 +362,7 @@ describe("JSDOM-E2E edit test", () => {
|
|
|
362
362
|
select.value = "1";
|
|
363
363
|
select.dispatchEvent(newEvent(dom, "change"));
|
|
364
364
|
|
|
365
|
-
await sleep(
|
|
365
|
+
await sleep(2000);
|
|
366
366
|
expect([...select_seq.options].map((o) => o.text)).toStrictEqual([
|
|
367
367
|
"",
|
|
368
368
|
"Leo Tolstoy",
|
|
@@ -381,7 +381,7 @@ describe("JSDOM-E2E edit test", () => {
|
|
|
381
381
|
const input = dom.window.document.querySelector("input[name=pages]");
|
|
382
382
|
input.value = "13";
|
|
383
383
|
input.dispatchEvent(newEvent(dom, "change"));
|
|
384
|
-
await sleep(
|
|
384
|
+
await sleep(2000);
|
|
385
385
|
const cf = dom.window.document.querySelector(
|
|
386
386
|
`div[data-source-url="/field/show-calculated/books/pagesp1/show?input_type=text"]`
|
|
387
387
|
);
|
|
@@ -394,7 +394,7 @@ describe("JSDOM-E2E edit test", () => {
|
|
|
394
394
|
showIfFormula: "publisher == 1",
|
|
395
395
|
});
|
|
396
396
|
const dom = await load_url_dom("/view/AuthorEditForTest1");
|
|
397
|
-
await sleep(
|
|
397
|
+
await sleep(2000);
|
|
398
398
|
const pubwarn = dom.window.document.querySelector("div.pubwarn");
|
|
399
399
|
|
|
400
400
|
expect(pubwarn.style.display).toBe("none");
|
|
@@ -410,7 +410,7 @@ describe("JSDOM-E2E edit test", () => {
|
|
|
410
410
|
select.value = "1";
|
|
411
411
|
select.dispatchEvent(newEvent(dom, "change"));
|
|
412
412
|
|
|
413
|
-
await sleep(
|
|
413
|
+
await sleep(2000);
|
|
414
414
|
expect([...select_seq.options].map((o) => o.text)).toStrictEqual([
|
|
415
415
|
"",
|
|
416
416
|
"Leo Tolstoy",
|
package/tests/files.test.js
CHANGED
|
@@ -30,6 +30,14 @@ const createTestFile = async (folder, name, mimetype, content) => {
|
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
const checkFiles = (files, expecteds) =>
|
|
34
|
+
files.length >= expecteds.length &&
|
|
35
|
+
expecteds.every(({ filename, location }) =>
|
|
36
|
+
files.find(
|
|
37
|
+
(file) => file.filename === filename && file.location === location
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
|
|
33
41
|
beforeAll(async () => {
|
|
34
42
|
await resetToFixtures();
|
|
35
43
|
await File.ensure_file_store();
|
|
@@ -186,13 +194,7 @@ describe("files admin", () => {
|
|
|
186
194
|
it("search files by name", async () => {
|
|
187
195
|
const app = await getApp({ disableCsrf: true });
|
|
188
196
|
const loginCookie = await getAdminLoginCookie();
|
|
189
|
-
|
|
190
|
-
files.length >= expecteds.length &&
|
|
191
|
-
expecteds.every(({ filename, location }) =>
|
|
192
|
-
files.find(
|
|
193
|
-
(file) => file.filename === filename && file.location === location
|
|
194
|
-
)
|
|
195
|
-
);
|
|
197
|
+
|
|
196
198
|
const searchTestHelper = async (dir, search, expected) => {
|
|
197
199
|
await request(app)
|
|
198
200
|
.get("/files")
|
|
@@ -345,3 +347,81 @@ describe("files edit", () => {
|
|
|
345
347
|
expect(!!file).toBe(true);
|
|
346
348
|
});
|
|
347
349
|
});
|
|
350
|
+
|
|
351
|
+
describe("visible_entries test", () => {
|
|
352
|
+
const setRole = async (role, entry) => {
|
|
353
|
+
const app = await getApp({ disableCsrf: true });
|
|
354
|
+
const adminCookie = await getAdminLoginCookie();
|
|
355
|
+
await request(app)
|
|
356
|
+
.post(`/files/setrole/${entry}`)
|
|
357
|
+
.set("Cookie", adminCookie)
|
|
358
|
+
.send(`role=${role}`)
|
|
359
|
+
.expect(toRedirect("/files?dir=_sc_test_subfolder_one"));
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
it("shows allowed files", async () => {
|
|
363
|
+
await setRole(100, path.join("_sc_test_subfolder_one", "foo_image.png"));
|
|
364
|
+
|
|
365
|
+
const app = await getApp({ disableCsrf: true });
|
|
366
|
+
const staffCookie = await getStaffLoginCookie();
|
|
367
|
+
await request(app)
|
|
368
|
+
.get("/files/visible_entries?dir=_sc_test_subfolder_one")
|
|
369
|
+
.set("Cookie", staffCookie)
|
|
370
|
+
.expect(
|
|
371
|
+
respondJsonWith(200, (data) =>
|
|
372
|
+
checkFiles(data.files, [
|
|
373
|
+
{
|
|
374
|
+
filename: "foo_image.png",
|
|
375
|
+
location: path.join("_sc_test_subfolder_one", "foo_image.png"),
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
filename: "bar_image.png",
|
|
379
|
+
location: path.join("_sc_test_subfolder_one", "bar_image.png"),
|
|
380
|
+
},
|
|
381
|
+
])
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("shows no disallowed files", async () => {
|
|
387
|
+
await setRole(1, path.join("_sc_test_subfolder_one", "foo_image.png"));
|
|
388
|
+
const app = await getApp({ disableCsrf: true });
|
|
389
|
+
const staffCookie = await getStaffLoginCookie();
|
|
390
|
+
const resp = await request(app)
|
|
391
|
+
.get("/files/visible_entries?dir=_sc_test_subfolder_one")
|
|
392
|
+
.set("Cookie", staffCookie);
|
|
393
|
+
expect(resp.statusCode).toBe(200);
|
|
394
|
+
const files = resp.body.files;
|
|
395
|
+
expect(
|
|
396
|
+
files.find((file) => file.filename === "foo_image.png")
|
|
397
|
+
).toBeUndefined();
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("shows allowed directories", async () => {
|
|
401
|
+
const dir = path.join("_sc_test_subfolder_one", "subsubfolder");
|
|
402
|
+
await setRole(80, dir);
|
|
403
|
+
const app = await getApp({ disableCsrf: true });
|
|
404
|
+
const staffCookie = await getStaffLoginCookie();
|
|
405
|
+
const resp = await request(app)
|
|
406
|
+
.get(`/files/visible_entries?dir=${dir}`)
|
|
407
|
+
.set("Cookie", staffCookie);
|
|
408
|
+
expect(resp.statusCode).toBe(200);
|
|
409
|
+
expect(
|
|
410
|
+
resp.body.directories.find((file) => file.filename === "subsubfolder")
|
|
411
|
+
).toBeDefined();
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it("shows no disallowed directories", async () => {
|
|
415
|
+
const dir = path.join("_sc_test_subfolder_one", "subsubfolder");
|
|
416
|
+
await setRole(1, dir);
|
|
417
|
+
const app = await getApp({ disableCsrf: true });
|
|
418
|
+
const staffCookie = await getStaffLoginCookie();
|
|
419
|
+
const resp = await request(app)
|
|
420
|
+
.get(`/files/visible_entries?dir=${dir}`)
|
|
421
|
+
.set("Cookie", staffCookie);
|
|
422
|
+
expect(resp.statusCode).toBe(200);
|
|
423
|
+
const body = resp.body;
|
|
424
|
+
expect(body.files.length).toBe(0);
|
|
425
|
+
expect(body.directories.length).toBe(0);
|
|
426
|
+
});
|
|
427
|
+
});
|