@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.
package/auth/testhelp.js CHANGED
@@ -156,6 +156,27 @@ const getAdminLoginCookie = async () => {
156
156
  return resToLoginCookie(res);
157
157
  };
158
158
 
159
+ /**
160
+ *
161
+ * @param {*} width
162
+ * @param {*} height
163
+ * @param {*} innerWidth
164
+ * @param {*} innerHeight
165
+ * @returns
166
+ */
167
+ const prepScreenInfoCookie = (width, height, innerWidth, innerHeight) => {
168
+ return `_sc_screen_info_=${JSON.stringify({
169
+ width,
170
+ height,
171
+ innerWidth: innerWidth || width,
172
+ innerHeight: innerHeight || height,
173
+ })}; Path=/;`;
174
+ };
175
+
176
+ const prepUserAgent = () => {
177
+ return `user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.2 (KHTML, like Gecko) Ubuntu/11.10 Chromium/15.0.874.106 Chrome/15.0.874.106 Safari/535.2"; Path=/;`;
178
+ };
179
+
159
180
  /**
160
181
  *
161
182
  * @param {string} path
@@ -258,10 +279,20 @@ const notAuthorized = (res) => {
258
279
  throw new Error(`Expected status 401, received ${res.statusCode}`);
259
280
  }
260
281
  };
282
+
283
+ const notFound = (res) => {
284
+ if (res.statusCode !== 404) {
285
+ console.log(res.text);
286
+ throw new Error(`Expected status 404, received ${res.statusCode}`);
287
+ }
288
+ };
289
+
261
290
  module.exports = {
262
291
  getStaffLoginCookie,
263
292
  getAdminLoginCookie,
264
293
  getUserLoginCookie,
294
+ prepScreenInfoCookie,
295
+ prepUserAgent,
265
296
  itShouldRedirectUnauthToLogin,
266
297
  toRedirect,
267
298
  toInclude,
@@ -270,6 +301,7 @@ module.exports = {
270
301
  resetToFixtures,
271
302
  succeedJsonWith,
272
303
  notAuthorized,
304
+ notFound,
273
305
  respondJsonWith,
274
306
  toSucceedWithImage,
275
307
  succeedJsonWithWholeBody,
@@ -0,0 +1,69 @@
1
+ Here you can write a formula to decide if the page is eligible for the request.
2
+
3
+ ### `Variables in scope`
4
+ You have access to the following variables:
5
+
6
+ ```
7
+ width, height, innerWidth, innerHeight,
8
+ user, locale,
9
+ device
10
+ ```
11
+
12
+ #### `width and height`
13
+ This is the size of the whole screen.
14
+
15
+ Example:
16
+ ```
17
+ width < 380 && height < 670
18
+ ```
19
+ Note: Depending on your browser, this can get scaled with the zoom factor.
20
+
21
+ ##### `innerWidth and innerHeight`
22
+ This is the size of your window.
23
+
24
+ Example:
25
+ ```
26
+ innerWidth <= 375 && innerHeight <= 667
27
+ ```
28
+ Note: Depending on your browser, this can get scaled with the zoom factor.
29
+
30
+ #### `user`
31
+ Here you'll find the user object or
32
+ ```
33
+ {
34
+ role_id: 100
35
+ }
36
+ ```
37
+ for public requests.
38
+
39
+ Example:
40
+ ```
41
+ user.id === 1 || user.role_id === 1
42
+ ```
43
+
44
+ #### `locale`
45
+ In locale, you'll find the locale of your browser.
46
+
47
+ Example:
48
+ ```
49
+ locale === 'en' || locale === 'es' || local === 'de'
50
+ ```
51
+
52
+ ### `device`
53
+ The device type is extracted from the user agent. The following values are valid:
54
+ ```
55
+ web, mobile, tablet,
56
+ console, smarttv, wearable,
57
+ ```
58
+ Example:
59
+ ```
60
+ device === "mobile" || innerWidth <= 375 && innerHeight <= 667
61
+ ```
62
+
63
+ Note: This value is independent of the browser zoom.
64
+
65
+ ## `Default member`
66
+ If no member matches, you'll get an error message, or to catch this, add a member at the lowest level with just
67
+ ```
68
+ true
69
+ ```
package/locales/en.json CHANGED
@@ -1294,5 +1294,48 @@
1294
1294
  "Hoverable rows": "Hoverable rows",
1295
1295
  "Highlight row under cursor": "Highlight row under cursor",
1296
1296
  "Striped rows": "Striped rows",
1297
- "Add zebra stripes to rows": "Add zebra stripes to rows"
1298
- }
1297
+ "Add zebra stripes to rows": "Add zebra stripes to rows",
1298
+ "New page group": "New page group",
1299
+ "Page Groups": "Page Groups",
1300
+ "%s edit": "%s edit",
1301
+ "Members": "Members",
1302
+ "Add member": "Add member",
1303
+ "Edit group properties": "Edit group properties",
1304
+ "Page to be served": "Page to be served",
1305
+ "A description of the group member": "A description of the group member",
1306
+ "Eligible Formula": "Eligible Formula",
1307
+ "Formula to determine if this page should be served.": "Formula to determine if this page should be served.",
1308
+ "Cancel": "Cancel",
1309
+ "%s add-member": "%s add-member",
1310
+ "add-member": "add-member",
1311
+ "add member to %s": "add member to %s",
1312
+ "Added member": "Added member",
1313
+ "Member %s": "Member %s",
1314
+ "Updated member": "Updated member",
1315
+ "%s edit-member": "%s edit-member",
1316
+ "edit member of %s": "edit member of %s",
1317
+ "Removed member %s": "Removed member %s",
1318
+ "Deleted page group %s": "Deleted page group %s",
1319
+ "Missing screen info": "Missing screen info",
1320
+ "What to do if no screen info is given. Reload with parmeters or guess it from the user-agent.": "What to do if no screen info is given. Reload with parmeters or guess it from the user-agent.",
1321
+ "Guess from user agent": "Guess from user agent",
1322
+ "Reload": "Reload",
1323
+ "User Agent screen infos": "User Agent screen infos",
1324
+ "This screen infos are used when the browser does not send them. With 'Missing screen info' set to 'Guess from user agent', the user agent gets mapped to a device with the following values.": "This screen infos are used when the browser does not send them. With 'Missing screen info' set to 'Guess from user agent', the user agent gets mapped to a device with the following values.",
1325
+ "Add screen info": "Add screen info",
1326
+ "Page Group settings": "Page Group settings",
1327
+ "Pagegroups": "Pagegroups",
1328
+ "width": "width",
1329
+ "height": "height",
1330
+ "innerWidth": "innerWidth",
1331
+ "innerHeight": "innerHeight",
1332
+ "Screen info added": "Screen info added",
1333
+ "Screen info removed": "Screen info removed",
1334
+ "Edit screen info": "Edit screen info",
1335
+ "Screen info saved": "Screen info saved",
1336
+ "Settings saved": "Settings saved",
1337
+ "Destination page group": "Destination page group",
1338
+ "Your page groups": "Your page groups",
1339
+ "A group has pages with an eligible formula. When you request a group, then the first page where the formula matches gets served. This way, you can choose a page depending on the screen of the device.": "A group has pages with an eligible formula. When you request a group, then the first page where the formula matches gets served. This way, you can choose a page depending on the screen of the device.",
1340
+ "Create page group": "Create page group"
1341
+ }
package/markup/admin.js CHANGED
@@ -232,6 +232,7 @@ const send_infoarch_page = (args) => {
232
232
  { text: "Multitenancy", href: "/tenant/settings" },
233
233
  ]
234
234
  : []),
235
+ { text: "Pagegroups", href: "/page_group/settings"},
235
236
  { text: "Tags", href: "/tag" },
236
237
  { text: "Diagram", href: "/diagram" },
237
238
  ],
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.9.3-beta.4",
3
+ "version": "0.9.3-beta.5",
4
4
  "description": "Server app for Saltcorn, open-source no-code platform",
5
5
  "homepage": "https://saltcorn.com",
6
6
  "main": "index.js",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
9
  "@aws-sdk/client-s3": "^3.451.0",
10
- "@saltcorn/base-plugin": "0.9.3-beta.4",
11
- "@saltcorn/builder": "0.9.3-beta.4",
12
- "@saltcorn/data": "0.9.3-beta.4",
13
- "@saltcorn/admin-models": "0.9.3-beta.4",
14
- "@saltcorn/filemanager": "0.9.3-beta.4",
15
- "@saltcorn/markup": "0.9.3-beta.4",
16
- "@saltcorn/sbadmin2": "0.9.3-beta.4",
10
+ "@saltcorn/base-plugin": "0.9.3-beta.5",
11
+ "@saltcorn/builder": "0.9.3-beta.5",
12
+ "@saltcorn/data": "0.9.3-beta.5",
13
+ "@saltcorn/admin-models": "0.9.3-beta.5",
14
+ "@saltcorn/filemanager": "0.9.3-beta.5",
15
+ "@saltcorn/markup": "0.9.3-beta.5",
16
+ "@saltcorn/sbadmin2": "0.9.3-beta.5",
17
17
  "@socket.io/cluster-adapter": "^0.2.1",
18
18
  "@socket.io/sticky": "^1.0.1",
19
19
  "adm-zip": "0.5.10",
@@ -59,6 +59,7 @@
59
59
  "systeminformation": "^5.21.7",
60
60
  "thirty-two": "1.0.2",
61
61
  "tmp-promise": "^3.0.2",
62
+ "ua-parser-js": "^1.0.37",
62
63
  "underscore": "1.13.6",
63
64
  "uuid": "^8.2.0"
64
65
  },
@@ -8,6 +8,21 @@ jQuery.fn.swapWith = function (to) {
8
8
  });
9
9
  };
10
10
 
11
+ function setScreenInfoCookie() {
12
+ document.cookie = `_sc_screen_info_=${JSON.stringify({
13
+ width: window.screen.width,
14
+ height: window.screen.height,
15
+ innerWidth: window.innerWidth,
16
+ innerHeight: window.innerHeight,
17
+ })}; expires=Thu, 01 Jan 2100 00:00:00 GMT; path=/; domain=.${
18
+ window.location.hostname
19
+ }`;
20
+ }
21
+ setScreenInfoCookie();
22
+ $(window).resize(() => {
23
+ setScreenInfoCookie();
24
+ });
25
+
11
26
  //avoids hiding in overflow:hidden
12
27
  function init_bs5_dropdowns() {
13
28
  $("body").on(
@@ -732,6 +747,7 @@ function initialize_page() {
732
747
  }
733
748
  }
734
749
  });
750
+
735
751
  function setExplainer(that) {
736
752
  var id = $(that).attr("id") + "_explainer";
737
753
 
@@ -97,6 +97,9 @@ div[data-inline-edit-dest-url]:hover .editicon {
97
97
  .search-bar button.search-bar {
98
98
  border-color: #95a5a6;
99
99
  background-color: #ffffff;
100
+ border-left: 1px solid #95a5a6 !important;
101
+ border-top: 1px solid #95a5a6 !important;
102
+ border-bottom: 1px solid #95a5a6 !important;
100
103
  }
101
104
  .search-bar button.dropdown-toggle {
102
105
  border-left: none;
@@ -57,6 +57,14 @@ function updateQueryStringParameter(uri1, key, value) {
57
57
  }
58
58
  }
59
59
 
60
+ function updateQueryStringParameters(uri1, kvs) {
61
+ let uri = uri1;
62
+ Object.entries(kvs).forEach((kv) => {
63
+ uri = updateQueryStringParameter(uri, kv[0], kv[1]);
64
+ });
65
+ return uri;
66
+ }
67
+
60
68
  function removeQueryStringParameter(uri1, key) {
61
69
  let hash = "";
62
70
  let uri = uri1;
@@ -885,6 +893,11 @@ function toggle_android_platform() {
885
893
  }
886
894
  }
887
895
 
896
+ function cancelMemberEdit(groupName) {
897
+ const url = new URL(location.href);
898
+ location.href = `${url.origin}/page_groupedit/${groupName}`;
899
+ }
900
+
888
901
  function join_field_clicked(e, fieldPath) {
889
902
  $("#inputjoin_field").val(fieldPath);
890
903
  apply_showif();
package/routes/admin.js CHANGED
@@ -99,6 +99,7 @@ const {
99
99
  } = require("../markup/admin");
100
100
  const moment = require("moment");
101
101
  const View = require("@saltcorn/data/models/view");
102
+ const PageGroup = require("@saltcorn/data/models/page_group");
102
103
  const { getConfigFile } = require("@saltcorn/data/db/connect");
103
104
  const os = require("os");
104
105
  const Page = require("@saltcorn/data/models/page");
@@ -1225,6 +1226,12 @@ const clearAllForm = (req) =>
1225
1226
  label: req.__("Pages"),
1226
1227
  default: true,
1227
1228
  },
1229
+ {
1230
+ type: "Bool",
1231
+ name: "page_groups",
1232
+ label: req.__("Page groups"),
1233
+ default: true,
1234
+ },
1228
1235
  {
1229
1236
  type: "Bool",
1230
1237
  name: "files",
@@ -2370,7 +2377,10 @@ router.post(
2370
2377
  error_catcher(async (req, res) => {
2371
2378
  const form = clearAllForm(req);
2372
2379
  form.validate(req.body);
2373
- //order: pages, views, user fields, tableconstraints, fields, table triggers, table history, tables, plugins, config+crashes+nontable triggers, users
2380
+ //order: page_groups, pages, views, user fields, tableconstraints, fields, table triggers, table history, tables, plugins, config+crashes+nontable triggers, users
2381
+ if (form.values.page_groups) {
2382
+ await PageGroup.delete({});
2383
+ }
2374
2384
  if (form.values.pages) {
2375
2385
  await db.deleteWhere("_sc_pages");
2376
2386
  }
@@ -9,7 +9,7 @@ const {
9
9
  post_dropdown_item,
10
10
  } = require("@saltcorn/markup");
11
11
  const { get_base_url } = require("./utils.js");
12
- const { h4, p, div, a, i, input, text } = require("@saltcorn/markup/tags");
12
+ const { h4, p, div, a, i, text } = require("@saltcorn/markup/tags");
13
13
 
14
14
  /**
15
15
  * @param {string} col
@@ -273,6 +273,36 @@ const viewsList = async (
273
273
  );
274
274
  };
275
275
 
276
+ const page_group_dropdown = (page_group, req) =>
277
+ settingsDropdown(`groupDropdownMenuButton${page_group.id}`, [
278
+ post_dropdown_item(
279
+ `/page_groupedit/add-to-menu/${page_group.id}`,
280
+ '<i class="fas fa-bars"></i>&nbsp;' + req.__("Add to menu"),
281
+ req
282
+ ),
283
+ post_dropdown_item(
284
+ `/page_groupedit/clone/${page_group.id}`,
285
+ '<i class="far fa-copy"></i>&nbsp;' + req.__("Duplicate"),
286
+ req
287
+ ),
288
+ a(
289
+ {
290
+ class: "dropdown-item",
291
+ // TODO check url why view for page, what do we need for page group
292
+ href: `javascript:ajax_modal('/admin/snapshot-restore/pagegroup/${page_group.name}')`,
293
+ },
294
+ '<i class="fas fa-undo-alt"></i>&nbsp;' + req.__("Restore")
295
+ ),
296
+ div({ class: "dropdown-divider" }),
297
+ post_dropdown_item(
298
+ `/page_groupedit/delete/${page_group.id}`,
299
+ '<i class="far fa-trash-alt"></i>&nbsp;' + req.__("Delete"),
300
+ req,
301
+ true,
302
+ page_group.name
303
+ ),
304
+ ]);
305
+
276
306
  /**
277
307
  * @param {object} page
278
308
  * @param {object} req
@@ -327,9 +357,9 @@ const page_dropdown = (page, req) =>
327
357
  * @param {object} req
328
358
  * @returns {Form}
329
359
  */
330
- const editPageRoleForm = (page, roles, req) =>
360
+ const editPageRoleForm = (page, roles, req, isGroup) =>
331
361
  editRoleForm({
332
- url: `/pageedit/setrole/${page.id}`,
362
+ url: `/${!isGroup ? "page" : "page_group"}edit/setrole/${page.id}`,
333
363
  current_role: page.min_role,
334
364
  roles,
335
365
  req,
@@ -380,6 +410,38 @@ const getPageList = (rows, roles, req, { tagId, domId, showList } = {}) => {
380
410
  );
381
411
  };
382
412
 
413
+ /**
414
+ * @param {*} rows
415
+ * @param {*} roles
416
+ * @param {*} req
417
+ */
418
+ const getPageGroupList = (rows, roles, req) => {
419
+ return mkTable(
420
+ [
421
+ {
422
+ label: req.__("Name"),
423
+ key: (r) => link(`/page/${r.name}`, r.name),
424
+ },
425
+ {
426
+ label: req.__("Role to access"),
427
+ key: (row) => editPageRoleForm(row, roles, req, true),
428
+ },
429
+ {
430
+ label: req.__("Edit"),
431
+ key: (r) => link(`/page_groupedit/${r.name}`, req.__("Edit")),
432
+ },
433
+ {
434
+ label: "",
435
+ key: (r) => page_group_dropdown(r, req),
436
+ },
437
+ ],
438
+ rows,
439
+ {
440
+ hover: true,
441
+ }
442
+ );
443
+ };
444
+
383
445
  const getTriggerList = (triggers, req, { tagId, domId, showList } = {}) => {
384
446
  const base_url = get_base_url(req);
385
447
  return mkTable(
@@ -450,5 +512,6 @@ module.exports = {
450
512
  setTableRefs,
451
513
  viewsList,
452
514
  getPageList,
515
+ getPageGroupList,
453
516
  getTriggerList,
454
517
  };
package/routes/index.js CHANGED
@@ -7,6 +7,8 @@ const field = require("./fields");
7
7
  const list = require("./list");
8
8
  const view = require("./view");
9
9
  const page = require("./page");
10
+ const pagegroup = require("./page_group");
11
+ const pagegroupedit = require("./page_groupedit");
10
12
  const pageedit = require("./pageedit");
11
13
  const search = require("./search");
12
14
  const files = require("./files");
@@ -58,6 +60,8 @@ module.exports =
58
60
  app.use("/models", models);
59
61
  app.use("/settings", settings);
60
62
  app.use("/pageedit", pageedit);
63
+ app.use("/page_group", pagegroup);
64
+ app.use("/page_groupedit", pagegroupedit);
61
65
  app.use("/actions", actions);
62
66
  app.use("/eventlog", eventlog);
63
67
  app.use("/library", library);
package/routes/packs.js CHANGED
@@ -12,6 +12,7 @@ const Form = require("@saltcorn/data/models/form");
12
12
  const View = require("@saltcorn/data/models/view");
13
13
  const Plugin = require("@saltcorn/data/models/plugin");
14
14
  const Page = require("@saltcorn/data/models/page");
15
+ const PageGroup = require("@saltcorn/data/models/page_group");
15
16
  const Tag = require("@saltcorn/data/models/tag");
16
17
  const EventLog = require("@saltcorn/data/models/eventlog");
17
18
  const Model = require("@saltcorn/data/models/model");
@@ -24,6 +25,7 @@ const {
24
25
  view_pack,
25
26
  plugin_pack,
26
27
  page_pack,
28
+ page_group_pack,
27
29
  role_pack,
28
30
  library_pack,
29
31
  trigger_pack,
@@ -86,6 +88,12 @@ router.get(
86
88
  name: `page.${t.name}`,
87
89
  type: "Bool",
88
90
  }));
91
+ const pageGroups = await PageGroup.find({});
92
+ const pageGroupFields = pageGroups.map((t) => ({
93
+ label: `${t.name} page group`,
94
+ name: `page_group.${t.name}`,
95
+ type: "Bool",
96
+ }));
89
97
  const libs = await Library.find({});
90
98
  const libFields = libs.map((l) => ({
91
99
  label: `${l.name} library item`,
@@ -157,6 +165,7 @@ router.get(
157
165
  ...viewFields,
158
166
  ...pluginFields,
159
167
  ...pageFields,
168
+ ...pageGroupFields,
160
169
  ...trigFields,
161
170
  ...roleFields,
162
171
  ...libFields,
@@ -205,6 +214,7 @@ router.post(
205
214
  views: [],
206
215
  plugins: [],
207
216
  pages: [],
217
+ page_groups: [],
208
218
  roles: [],
209
219
  library: [],
210
220
  triggers: [],
@@ -228,6 +238,9 @@ router.post(
228
238
  case "page":
229
239
  pack.pages.push(await page_pack(name));
230
240
  break;
241
+ case "page_group":
242
+ pack.page_groups.push(await page_group_pack(name));
243
+ break;
231
244
  case "library":
232
245
  pack.library.push(await library_pack(name));
233
246
  break;