@saltcorn/server 0.9.3-beta.4 → 0.9.3-beta.5

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.
@@ -9,7 +9,8 @@ const View = require("@saltcorn/data/models/view");
9
9
  const Field = require("@saltcorn/data/models/field");
10
10
  const Table = require("@saltcorn/data/models/table");
11
11
  const Page = require("@saltcorn/data/models/page");
12
- const { div, a, iframe, script } = require("@saltcorn/markup/tags");
12
+ const PageGroup = require("@saltcorn/data/models/page_group");
13
+ const { div, a, iframe, script, p } = require("@saltcorn/markup/tags");
13
14
  const { getState } = require("@saltcorn/data/db/state");
14
15
  const User = require("@saltcorn/data/models/user");
15
16
  const Workflow = require("@saltcorn/data/models/workflow");
@@ -19,13 +20,14 @@ const Trigger = require("@saltcorn/data/models/trigger");
19
20
  const { getViews, traverseSync } = require("@saltcorn/data/models/layout");
20
21
  const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
21
22
  const db = require("@saltcorn/data/db");
22
- const { getPageList } = require("./common_lists");
23
+ const { getPageList, getPageGroupList } = require("./common_lists");
23
24
 
24
25
  const {
25
26
  isAdmin,
26
27
  error_catcher,
27
28
  addOnDoneRedirect,
28
29
  is_relative_url,
30
+ setRole,
29
31
  } = require("./utils.js");
30
32
  const { asyncMap } = require("@saltcorn/data/utils");
31
33
  const {
@@ -61,6 +63,7 @@ module.exports = router;
61
63
  const pagePropertiesForm = async (req, isNew) => {
62
64
  const roles = await User.get_roles();
63
65
  const pages = (await Page.find()).map((p) => p.name);
66
+ const groups = (await PageGroup.find()).map((g) => g.name);
64
67
  const htmlFiles = await File.find(
65
68
  {
66
69
  mime_super: "text",
@@ -86,6 +89,8 @@ const pagePropertiesForm = async (req, isNew) => {
86
89
  if (s.length < 1) return req.__("Missing name");
87
90
  if (pages.includes(s) && isNew)
88
91
  return req.__("A page with this name already exists");
92
+ if (groups.includes(s) && isNew)
93
+ return req.__("A page group with this name already exists");
89
94
  },
90
95
  sublabel: req.__("A short name that will be in your URL"),
91
96
  type: "String",
@@ -148,6 +153,7 @@ const pagePropertiesForm = async (req, isNew) => {
148
153
  const pageBuilderData = async (req, context) => {
149
154
  const views = await View.find();
150
155
  const pages = await Page.find();
156
+ const page_groups = (await PageGroup.find()).map((g) => ({ name: g.name }));
151
157
  const images = await File.find({ mime_super: "image" });
152
158
  images.forEach((im) => (im.location = im.path_to_serve));
153
159
  const roles = await User.get_roles();
@@ -216,6 +222,7 @@ const pageBuilderData = async (req, context) => {
216
222
  views,
217
223
  images,
218
224
  pages,
225
+ page_groups,
219
226
  actions,
220
227
  builtInActions: ["GoBack"],
221
228
  library,
@@ -278,6 +285,10 @@ router.get(
278
285
  isAdmin,
279
286
  error_catcher(async (req, res) => {
280
287
  const pages = await Page.find({}, { orderBy: "name", nocase: true });
288
+ const pageGroups = await PageGroup.find(
289
+ {},
290
+ { orderBy: "name", nocase: true }
291
+ );
281
292
  const roles = await User.get_roles();
282
293
 
283
294
  res.sendWrap(req.__("Pages"), {
@@ -301,6 +312,27 @@ router.get(
301
312
  )
302
313
  ),
303
314
  },
315
+ {
316
+ type: "card",
317
+ title: req.__("Your page groups"),
318
+ contents: div(
319
+ p(
320
+ req.__(
321
+ "A group has pages with an eligible formula. " +
322
+ "When you request a group, then the first page where the formula matches gets served. " +
323
+ "This way, you can choose a page depending on the screen of the device."
324
+ )
325
+ ),
326
+ getPageGroupList(pageGroups, roles, req),
327
+ a(
328
+ {
329
+ href: `/page_groupedit/new`,
330
+ class: "btn btn-primary",
331
+ },
332
+ req.__("Create page group")
333
+ )
334
+ ),
335
+ },
304
336
  {
305
337
  type: "card",
306
338
  title: req.__("Root pages"),
@@ -352,7 +384,7 @@ router.get(
352
384
  isAdmin,
353
385
  error_catcher(async (req, res) => {
354
386
  const { pagename } = req.params;
355
- const page = await Page.findOne({ name: pagename });
387
+ const page = Page.findOne({ name: pagename });
356
388
  if (!page) {
357
389
  req.flash("error", req.__(`Page %s not found`, pagename));
358
390
  res.redirect(`/pageedit`);
@@ -699,7 +731,7 @@ router.post(
699
731
  isAdmin,
700
732
  error_catcher(async (req, res) => {
701
733
  const { id } = req.params;
702
- const page = await Page.findOne({ id });
734
+ const page = Page.findOne({ id });
703
735
  await add_to_menu({
704
736
  label: page.name,
705
737
  type: "Page",
@@ -748,19 +780,6 @@ router.post(
748
780
  "/setrole/:id",
749
781
  isAdmin,
750
782
  error_catcher(async (req, res) => {
751
- const { id } = req.params;
752
- const role = req.body.role;
753
- await Page.update(+id, { min_role: role });
754
- const page = await Page.findOne({ id });
755
- const roles = await User.get_roles();
756
- const roleRow = roles.find((r) => r.id === +role);
757
- const message =
758
- roleRow && page
759
- ? req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
760
- : req.__(`Minimum role updated`);
761
- if (!req.xhr) {
762
- req.flash("success", message);
763
- res.redirect("/pageedit");
764
- } else res.json({ okay: true, responseText: message });
783
+ await setRole(req, res, Page);
765
784
  })
766
785
  );
package/routes/utils.js CHANGED
@@ -19,6 +19,7 @@ const is = require("contractis/is");
19
19
  const { validateHeaderName, validateHeaderValue } = require("http");
20
20
  const Crash = require("@saltcorn/data/models/crash");
21
21
  const File = require("@saltcorn/data/models/file");
22
+ const User = require("@saltcorn/data/models/user");
22
23
  const si = require("systeminformation");
23
24
  const {
24
25
  config_fields_form,
@@ -415,6 +416,23 @@ const sendHtmlFile = async (req, res, file) => {
415
416
  }
416
417
  };
417
418
 
419
+ const setRole = async (req, res, model) => {
420
+ const { id } = req.params;
421
+ const role = req.body.role;
422
+ await model.update(+id, { min_role: role });
423
+ const page = model.findOne({ id });
424
+ const roles = await User.get_roles();
425
+ const roleRow = roles.find((r) => r.id === +role);
426
+ const message =
427
+ roleRow && page
428
+ ? req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
429
+ : req.__(`Minimum role updated`);
430
+ if (!req.xhr) {
431
+ req.flash("success", message);
432
+ res.redirect("/pageedit");
433
+ } else res.json({ okay: true, responseText: message });
434
+ };
435
+
418
436
  module.exports = {
419
437
  sqlsanitize,
420
438
  csrfField,
@@ -432,4 +450,5 @@ module.exports = {
432
450
  get_sys_info,
433
451
  admin_config_route,
434
452
  sendHtmlFile,
453
+ setRole,
435
454
  };
@@ -625,6 +625,7 @@ describe("clear all page", () => {
625
625
  .set("Cookie", loginCookie)
626
626
  .send("tables=on")
627
627
  .send("views=on")
628
+ .send("page_groups=on")
628
629
  .send("pages=on")
629
630
  .send("files=on")
630
631
  .send("users=on")
@@ -0,0 +1,366 @@
1
+ const request = require("supertest");
2
+ const getApp = require("../app");
3
+ const {
4
+ getAdminLoginCookie,
5
+ prepScreenInfoCookie,
6
+ prepUserAgent,
7
+ toRedirect,
8
+ toInclude,
9
+ notFound,
10
+ toNotInclude,
11
+ resetToFixtures,
12
+ } = require("../auth/testhelp");
13
+ const db = require("@saltcorn/data/db");
14
+ const Page = require("@saltcorn/data/models/page");
15
+ const PageGroup = require("@saltcorn/data/models/page_group");
16
+ const PageGroupMember = require("@saltcorn/data/models/page_group_member");
17
+
18
+ beforeAll(async () => {
19
+ await resetToFixtures();
20
+ });
21
+ afterAll(db.close);
22
+
23
+ describe("edit Page groups", () => {
24
+ it("shows the create new page group form", async () => {
25
+ const app = await getApp({ disableCsrf: true });
26
+ const loginCookie = await getAdminLoginCookie();
27
+ await request(app)
28
+ .get("/page_groupedit/new")
29
+ .set("Cookie", loginCookie)
30
+ .expect(toInclude("New"))
31
+ .expect(toInclude(`action="/page_groupedit/edit-properties"`))
32
+ .expect(toInclude("Save"));
33
+ });
34
+
35
+ let pageGroupName = "new page group";
36
+ let nameAfterUpdate = "updated page group";
37
+ it("creates a new page group", async () => {
38
+ const app = await getApp({ disableCsrf: true });
39
+ const loginCookie = await getAdminLoginCookie();
40
+ await request(app)
41
+ .post("/page_groupedit/edit-properties")
42
+ .set("Cookie", loginCookie)
43
+ .send({ name: pageGroupName })
44
+ .expect(
45
+ toRedirect(`/page_groupedit/${encodeURIComponent(pageGroupName)}`)
46
+ );
47
+ const fromDb = PageGroup.findOne({ name: pageGroupName });
48
+ expect(fromDb).toBeTruthy();
49
+ });
50
+
51
+ it("shows the page group editor", async () => {
52
+ const app = await getApp({ disableCsrf: true });
53
+ const loginCookie = await getAdminLoginCookie();
54
+ await request(app)
55
+ .get(`/page_groupedit/${encodeURIComponent(pageGroupName)}`)
56
+ .set("Cookie", loginCookie)
57
+ .expect(toInclude("Members"))
58
+ .expect(toInclude("Edit group properties"))
59
+ .expect(toNotInclude("Save"))
60
+ .expect(toInclude(pageGroupName));
61
+ });
62
+
63
+ it("updates the page group properties", async () => {
64
+ const oldGroup = PageGroup.findOne({ name: pageGroupName });
65
+ const app = await getApp({ disableCsrf: true });
66
+ const loginCookie = await getAdminLoginCookie();
67
+ const resp = await request(app)
68
+ .post("/page_groupedit/edit-properties")
69
+ .set("Cookie", loginCookie)
70
+ .send({ id: oldGroup.id, name: nameAfterUpdate, min_role: 100 });
71
+ expect(resp.statusCode).toEqual(200);
72
+ expect(resp._body.row).toEqual({
73
+ name: nameAfterUpdate,
74
+ description: null,
75
+ min_role: 100,
76
+ });
77
+ });
78
+
79
+ it("shows the add-member form", async () => {
80
+ const app = await getApp({ disableCsrf: true });
81
+ const loginCookie = await getAdminLoginCookie();
82
+ const res = await request(app)
83
+ .get(`/page_groupedit/add-member/${nameAfterUpdate}`)
84
+ .set("Cookie", loginCookie)
85
+ .expect(toInclude("Page to be served"))
86
+ .expect(toInclude("Eligible Formula"))
87
+ .expect(toInclude("Cancel"))
88
+ .expect(toInclude("Save"));
89
+ });
90
+
91
+ it("adds members to the page group", async () => {
92
+ const app = await getApp({ disableCsrf: true });
93
+ const loginCookie = await getAdminLoginCookie();
94
+ await request(app)
95
+ .post(`/page_groupedit/add-member/${nameAfterUpdate}`)
96
+ .set("Cookie", loginCookie)
97
+ .send({ page_name: "a_page", eligible_formula: "true" })
98
+ .expect(
99
+ toRedirect(`/page_groupedit/${encodeURIComponent(nameAfterUpdate)}`)
100
+ );
101
+ await request(app)
102
+ .post(`/page_groupedit/add-member/${nameAfterUpdate}`)
103
+ .set("Cookie", loginCookie)
104
+ .send({ page_name: "page_with_html_file", eligible_formula: "true" })
105
+ .expect(
106
+ toRedirect(`/page_groupedit/${encodeURIComponent(nameAfterUpdate)}`)
107
+ );
108
+ });
109
+
110
+ it("shows members in the page group editor", async () => {
111
+ const app = await getApp({ disableCsrf: true });
112
+ const loginCookie = await getAdminLoginCookie();
113
+ await request(app)
114
+ .get(`/page_groupedit/${encodeURIComponent(nameAfterUpdate)}`)
115
+ .set("Cookie", loginCookie)
116
+ .expect(toInclude("Members"))
117
+ .expect(toInclude("a_page"))
118
+ .expect(toInclude("page_with_html_file"))
119
+ .expect(toInclude("Edit group properties"))
120
+ .expect(toNotInclude("Save"))
121
+ .expect(toInclude(nameAfterUpdate));
122
+ });
123
+
124
+ it("updates members of the page group", async () => {
125
+ const page = Page.findOne({ name: "a_page" });
126
+ const group = PageGroup.findOne({ name: nameAfterUpdate });
127
+ const members = await PageGroupMember.find({
128
+ page_group_id: group.id,
129
+ page_id: page.id,
130
+ });
131
+ expect(members.length).toBe(1);
132
+ const member = members[0];
133
+ const app = await getApp({ disableCsrf: true });
134
+ const loginCookie = await getAdminLoginCookie();
135
+ await request(app)
136
+ .post(`/page_groupedit/edit-member/${member.id}`)
137
+ .set("Cookie", loginCookie)
138
+ .send({ id: member.id, page_name: "a_page", eligible_formula: "1>0" })
139
+ .expect(
140
+ toRedirect(`/page_groupedit/${encodeURIComponent(nameAfterUpdate)}`)
141
+ );
142
+ await request(app)
143
+ .get(`/page_groupedit/edit-member/${member.id}`)
144
+ .set("Cookie", loginCookie)
145
+ .expect(toInclude("Page to be served"))
146
+ .expect(toInclude("Eligible Formula"))
147
+ .expect(toInclude("Cancel"))
148
+ .expect(toInclude("Save"))
149
+ .expect(toInclude("1&gt;0"));
150
+ });
151
+
152
+ it("removes members from the page group", async () => {
153
+ const page = Page.findOne({ name: "a_page" });
154
+ const group = PageGroup.findOne({ name: nameAfterUpdate });
155
+ const members = await PageGroupMember.find({
156
+ page_group_id: group.id,
157
+ page_id: page.id,
158
+ });
159
+ expect(members.length).toBe(1);
160
+ const member = members[0];
161
+ const app = await getApp({ disableCsrf: true });
162
+ const loginCookie = await getAdminLoginCookie();
163
+ await request(app)
164
+ .post(`/page_groupedit/remove-member/${member.id}`)
165
+ .set("Cookie", loginCookie)
166
+ .expect(toRedirect(`/page_groupedit/${encodeURIComponent(group.name)}`));
167
+ await request(app)
168
+ .get(`/page_groupedit/${encodeURIComponent(nameAfterUpdate)}`)
169
+ .set("Cookie", loginCookie)
170
+ .expect(toInclude("Members"))
171
+ .expect(toNotInclude(page.name));
172
+ });
173
+
174
+ it("deletes the page group", async () => {
175
+ const group = PageGroup.findOne({ name: nameAfterUpdate });
176
+ const app = await getApp({ disableCsrf: true });
177
+ const loginCookie = await getAdminLoginCookie();
178
+ await request(app)
179
+ .post(`/page_groupedit/delete/${group.id}`)
180
+ .set("Cookie", loginCookie)
181
+ .expect(toRedirect("/pageedit"));
182
+ });
183
+ });
184
+
185
+ describe("run page group", () => {
186
+ it("width and height with Cookie", async () => {
187
+ const app = await getApp({ disableCsrf: true });
188
+ const loginCookie = await getAdminLoginCookie();
189
+ await request(app)
190
+ .get(`/page/page_group`)
191
+ .set("Cookie", `${loginCookie}; ${prepScreenInfoCookie(912, 1360)}`)
192
+ .set("accept-language", "en")
193
+ .expect(toInclude("Surface Pro 7"))
194
+ .expect(toNotInclude("iPhone XR"))
195
+ .expect(toNotInclude("iPhone SE"));
196
+ await request(app)
197
+ .get(`/page/page_group`)
198
+ .set("Cookie", `${loginCookie}; ${prepScreenInfoCookie(414, 896)};`)
199
+ .set("accept-language", "en")
200
+ .expect(toNotInclude("Surface Pro 7"))
201
+ .expect(toInclude("iPhone XR"))
202
+ .expect(toNotInclude("iPhone SE"));
203
+ await request(app)
204
+ .get(`/page/page_group`)
205
+ .set("Cookie", `${loginCookie}; ${prepScreenInfoCookie(370, 660)};`)
206
+ .set("accept-language", "en")
207
+ .expect(toNotInclude("Surface Pro 7"))
208
+ .expect(toNotInclude("iPhone XR"))
209
+ .expect(toInclude("iPhone SE"));
210
+ });
211
+
212
+ it("locale mismatch", async () => {
213
+ const app = await getApp({ disableCsrf: true });
214
+ const loginCookie = await getAdminLoginCookie();
215
+ await request(app)
216
+ .get(`/page/page_group`)
217
+ .set("Cookie", `${loginCookie}; ${prepScreenInfoCookie(912, 1360)}`)
218
+ .set("accept-language", "es")
219
+ .expect(notFound);
220
+ await request(app)
221
+ .get(`/page/page_group`)
222
+ .set("Cookie", `${loginCookie}; ${prepScreenInfoCookie(414, 896)};`)
223
+ .set("accept-language", "es")
224
+ .expect(notFound);
225
+ await request(app)
226
+ .get(`/page/page_group`)
227
+ .set("Cookie", `${loginCookie}; ${prepScreenInfoCookie(370, 660)};`)
228
+ .set("accept-language", "es")
229
+ .expect(notFound);
230
+ });
231
+
232
+ it("width an height without Cookie", async () => {
233
+ const app = await getApp({ disableCsrf: true });
234
+ const loginCookie = await getAdminLoginCookie();
235
+ await request(app)
236
+ .get(`/page/page_group`)
237
+ .set("Cookie", `${loginCookie}`)
238
+ .set("User-Agent", prepUserAgent())
239
+ .expect(toInclude("Laptop"))
240
+ .expect(toNotInclude("Surface Pro 7"))
241
+ .expect(toNotInclude("iPhone XR"))
242
+ .expect(toNotInclude("iPhone SE"));
243
+ });
244
+ });
245
+
246
+ describe("page group settings", () => {
247
+ it("shows the settings page", async () => {
248
+ const app = await getApp({ disableCsrf: true });
249
+ const loginCookie = await getAdminLoginCookie();
250
+ await request(app)
251
+ .get("/page_group/settings")
252
+ .set("Cookie", loginCookie)
253
+ .expect(toInclude("User Agent screen infos"))
254
+ .expect(toInclude("web"))
255
+ .expect(toNotInclude("mobile"))
256
+ .expect(toInclude("Add screen info"))
257
+ .expect(toInclude("Page Group settings"))
258
+ .expect(toInclude("Missing screen info"))
259
+ .expect(toInclude(`value="guess_from_user_agent" selected`))
260
+ .expect(toInclude("reload"));
261
+ });
262
+
263
+ it("loads the add-screen info form", async () => {
264
+ const app = await getApp({ disableCsrf: true });
265
+ const loginCookie = await getAdminLoginCookie();
266
+ await request(app)
267
+ .get("/page_group/settings/add-device")
268
+ .set("Cookie", loginCookie)
269
+ .expect(toInclude("Add screen info"));
270
+ });
271
+
272
+ it("adds a screen-info", async () => {
273
+ const app = await getApp({ disableCsrf: true });
274
+ const loginCookie = await getAdminLoginCookie();
275
+ await request(app)
276
+ .post("/page_group/settings/add-device")
277
+ .set("Cookie", loginCookie)
278
+ .send({
279
+ device: "mobile",
280
+ width: "100", // check type Number ?
281
+ height: "200",
282
+ innerWidth: "100",
283
+ innerHeight: "200",
284
+ })
285
+ .expect(toRedirect("/page_group/settings"));
286
+
287
+ await request(app)
288
+ .get("/page_group/settings")
289
+ .set("Cookie", loginCookie)
290
+ .expect(toInclude("web"))
291
+ .expect(toInclude("mobile"));
292
+ });
293
+
294
+ it("removes a screen-info", async () => {
295
+ const app = await getApp({ disableCsrf: true });
296
+ const loginCookie = await getAdminLoginCookie();
297
+ await request(app)
298
+ .post("/page_group/settings/remove-device/mobile")
299
+ .set("Cookie", loginCookie)
300
+ .expect(toRedirect("/page_group/settings"));
301
+
302
+ await request(app)
303
+ .get("/page_group/settings")
304
+ .set("Cookie", loginCookie)
305
+ .expect(toInclude("web"))
306
+ .expect(toNotInclude("mobile"));
307
+ });
308
+
309
+ it("loads the edit-screen info form", async () => {
310
+ const app = await getApp({ disableCsrf: true });
311
+ const loginCookie = await getAdminLoginCookie();
312
+ await request(app)
313
+ .get("/page_group/settings/edit-device/web")
314
+ .set("Cookie", loginCookie)
315
+ .expect(toInclude("Edit screen info"))
316
+ .expect(toInclude("web"))
317
+ .expect(toInclude(`value="1920"`))
318
+ .expect(toInclude(`value="1000"`))
319
+ .expect(toInclude(`value="1848"`))
320
+ .expect(toInclude(`value="980"`));
321
+ });
322
+
323
+ it("edits a screen-info", async () => {
324
+ const app = await getApp({ disableCsrf: true });
325
+ const loginCookie = await getAdminLoginCookie();
326
+ await request(app)
327
+ .post("/page_group/settings/edit-device/web")
328
+ .set("Cookie", loginCookie)
329
+ .send({
330
+ device: "web",
331
+ width: "1000",
332
+ height: "201",
333
+ innerWidth: "100",
334
+ innerHeight: "202",
335
+ })
336
+ .expect(toRedirect("/page_group/settings"));
337
+
338
+ await request(app)
339
+ .get("/page_group/settings/edit-device/web")
340
+ .set("Cookie", loginCookie)
341
+ .expect(toInclude("Edit screen info"))
342
+ .expect(toInclude("web"))
343
+ .expect(toInclude(`value="1000"`))
344
+ .expect(toInclude(`value="201"`))
345
+ .expect(toInclude(`value="100"`))
346
+ .expect(toInclude(`value="202"`));
347
+ });
348
+
349
+ it("edits the missing_screen_info_strategy", async () => {
350
+ const app = await getApp({ disableCsrf: true });
351
+ const loginCookie = await getAdminLoginCookie();
352
+ await request(app)
353
+ .post("/page_group/settings/config")
354
+ .set("Cookie", loginCookie)
355
+ .send({
356
+ missing_screen_info_strategy: "reload",
357
+ })
358
+ .expect(toRedirect("/page_group/settings"));
359
+
360
+ await request(app)
361
+ .get("/page_group/settings")
362
+ .set("Cookie", loginCookie)
363
+ .expect(toInclude(`value="reload" selected`))
364
+ .expect(toInclude("guess_from_user_agent"));
365
+ });
366
+ });