@saltcorn/server 1.0.0-beta.15 → 1.0.0-beta.17

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 CHANGED
@@ -306,13 +306,7 @@ const getApp = async (opts = {}) => {
306
306
  ? new Date(u.last_mobile_login).valueOf()
307
307
  : u.last_mobile_login) <= jwt_payload.iat
308
308
  ) {
309
- return done(null, {
310
- email: u.email,
311
- id: u.id,
312
- role_id: u.role_id,
313
- language: u.language,
314
- tenant: db.getTenantSchema(),
315
- });
309
+ return done(null, u.session_object);
316
310
  } else {
317
311
  return done(null, { role_id: 100 });
318
312
  }
@@ -0,0 +1,55 @@
1
+ You can define how a date value should be shown by specifying a date format string
2
+ which supports a range of formatting options. The formatting is provided by the
3
+ [Day.js](https://day.js.org/docs/en/display/format) library with the
4
+ [AdvancedFormat](https://day.js.org/docs/en/plugin/advanced-format) extension. Those links
5
+ give the full definition for the available format options.
6
+
7
+ When a date is rendered with a format string, certain specific character sequences are
8
+ substituted for parts of the date value. Any unrecognized characters are repeated verbatim
9
+ in the output and you can used that for separator charcters such as hyphens (-) or slashes (/).
10
+
11
+ For example, the timepoint 11.15am on Friday, 4 October 2024 is rendered, according to the
12
+ format string:
13
+
14
+ {{# const d = new Date('2024-10-04T11:15:00.000')}}
15
+
16
+ | Format string | Output |
17
+ | ------------------- | ------------ |
18
+ | YYYY-MM-DD | {{ moment(d).format("YYYY-MM-DD")}} |
19
+ | H:mm on dddd, D MMMM YYYY | {{ moment(d).format("H:mm on dddd, D MMMM YYYY")}} |
20
+
21
+ The full list of supported format sequences are:
22
+
23
+ | Format | Output | Description |
24
+ | --- | --- | --- |
25
+ | `YY` | 18 | Two-digit year |
26
+ | `YYYY` | 2018 | Four-digit year |
27
+ | `M` | 1-12 | The month, beginning at 1 |
28
+ | `MM` | 01-12 | The month, 2-digits |
29
+ | `MMM` | Jan-Dec | The abbreviated month name |
30
+ | `MMMM` | January-December | The full month name |
31
+ | `D` | 1-31 | The day of the month |
32
+ | `DD` | 01-31 | The day of the month, 2-digits |
33
+ | `d` | 0-6 | The day of the week, with Sunday as 0 |
34
+ | `dd` | Su-Sa | The min name of the day of the week |
35
+ | `ddd` | Sun-Sat | The short name of the day of the week |
36
+ | `dddd` | Sunday-Saturday | The name of the day of the week |
37
+ | `H` | 0-23 | The hour |
38
+ | `HH` | 00-23 | The hour, 2-digits |
39
+ | `h` | 1-12 | The hour, 12-hour clock |
40
+ | `hh` | 01-12 | The hour, 12-hour clock, 2-digits |
41
+ | `m` | 0-59 | The minute |
42
+ | `mm` | 00-59 | The minute, 2-digits |
43
+ | `s` | 0-59 | The second |
44
+ | `ss` | 00-59 | The second, 2-digits |
45
+ | `SSS` | 000-999 | The millisecond, 3-digits |
46
+ | `Z` | +05:00 | The offset from UTC, ±HH:mm |
47
+ | `ZZ` | +0500 | The offset from UTC, ±HHmm |
48
+ | `A` | AM PM | |
49
+ | `a` | am pm | |
50
+ | `Q` | 1-4 | Quarter |
51
+ | `Do` | 1st 2nd ... 31st | Day of Month with ordinal |
52
+ | `k` | 1-24 | The hour, beginning at 1 |
53
+ | `kk` | 01-24 | The hour, 2-digits, beginning at 1 |
54
+ | `X` | 1360013296 | Unix Timestamp in second |
55
+ | `x` | 1360013296123 | Unix Timestamp in millisecond |
package/help/index.js CHANGED
@@ -5,6 +5,7 @@ const _ = require("underscore");
5
5
  const fs = require("fs").promises;
6
6
  const MarkdownIt = require("markdown-it"),
7
7
  md = new MarkdownIt();
8
+ const moment = require("moment");
8
9
 
9
10
  const { pre } = require("@saltcorn/markup/tags");
10
11
  const path = require("path");
@@ -33,6 +34,7 @@ const get_help_markup = async (topic, query, req) => {
33
34
  scState: getState(),
34
35
  query,
35
36
  oneOf,
37
+ moment,
36
38
  };
37
39
  const mdTemplate = await get_md_file(topic);
38
40
  if (!mdTemplate) return { markup: "Topic not found" };
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.17",
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": "1.0.0-beta.15",
11
- "@saltcorn/builder": "1.0.0-beta.15",
12
- "@saltcorn/data": "1.0.0-beta.15",
13
- "@saltcorn/admin-models": "1.0.0-beta.15",
14
- "@saltcorn/filemanager": "1.0.0-beta.15",
15
- "@saltcorn/markup": "1.0.0-beta.15",
16
- "@saltcorn/plugins-loader": "1.0.0-beta.15",
17
- "@saltcorn/sbadmin2": "1.0.0-beta.15",
10
+ "@saltcorn/base-plugin": "1.0.0-beta.17",
11
+ "@saltcorn/builder": "1.0.0-beta.17",
12
+ "@saltcorn/data": "1.0.0-beta.17",
13
+ "@saltcorn/admin-models": "1.0.0-beta.17",
14
+ "@saltcorn/filemanager": "1.0.0-beta.17",
15
+ "@saltcorn/markup": "1.0.0-beta.17",
16
+ "@saltcorn/plugins-loader": "1.0.0-beta.17",
17
+ "@saltcorn/sbadmin2": "1.0.0-beta.17",
18
18
  "@socket.io/cluster-adapter": "^0.2.1",
19
19
  "@socket.io/sticky": "^1.0.1",
20
20
  "adm-zip": "0.5.10",
@@ -55,7 +55,7 @@
55
55
  "pg": "^8.2.1",
56
56
  "pluralize": "^8.0.0",
57
57
  "qrcode": "1.5.1",
58
- "resize-with-sharp-or-jimp": "0.1.7",
58
+ "resize-with-sharp-or-jimp": "0.1.8",
59
59
  "semver": "^7.6.0",
60
60
  "socket.io": "4.6.0",
61
61
  "systeminformation": "^5.21.7",
@@ -1365,10 +1365,14 @@ function notifyAlert(note, spin) {
1365
1365
  if (typeof note == "string") {
1366
1366
  txt = note;
1367
1367
  type = "info";
1368
- } else {
1368
+ } else if (note.text) {
1369
1369
  txt = note.text;
1370
- type = note.type;
1370
+ type = note.type || "info";
1371
+ } else {
1372
+ type = "info";
1373
+ txt = JSON.stringify(note, null, 2);
1371
1374
  }
1375
+
1372
1376
  const { id, html } = buildToast(txt, type, spin);
1373
1377
  let $modal = $("#scmodal");
1374
1378
  if ($modal.length && $modal.hasClass("show"))
package/routes/actions.js CHANGED
@@ -546,7 +546,10 @@ router.get(
546
546
  let trigger;
547
547
  let id = parseInt(idorname);
548
548
  if (id) trigger = await Trigger.findOne({ id });
549
- else trigger = await Trigger.findOne({ name: idorname });
549
+ else {
550
+ trigger = await Trigger.findOne({ name: idorname });
551
+ id = trigger.id;
552
+ }
550
553
 
551
554
  if (!trigger) {
552
555
  req.flash("warning", req.__("Action not found"));
package/routes/admin.js CHANGED
@@ -3224,8 +3224,8 @@ router.post(
3224
3224
  error_catcher(async (req, res) => {
3225
3225
  const state = getState();
3226
3226
  const child = spawn(
3227
- "docker",
3228
- ["image", "pull", "saltcorn/cordova-builder:latest"],
3227
+ `${process.env.DOCKER_BIN ? `${process.env.DOCKER_BIN}/` : ""}docker`,
3228
+ ["pull", "saltcorn/cordova-builder:latest"],
3229
3229
  {
3230
3230
  stdio: ["ignore", "pipe", "pipe"],
3231
3231
  cwd: ".",
@@ -41,6 +41,7 @@ const {
41
41
  i,
42
42
  th,
43
43
  pre,
44
+ text,
44
45
  } = require("@saltcorn/markup/tags");
45
46
  const Table = require("@saltcorn/data/models/table");
46
47
  const { send_events_page } = require("../markup/admin.js");
@@ -442,7 +443,7 @@ router.get(
442
443
  ) +
443
444
  div(
444
445
  { class: "eventpayload" },
445
- ev.payload ? pre(JSON.stringify(ev.payload, null, 2)) : ""
446
+ ev.payload ? pre(text(JSON.stringify(ev.payload, null, 2))) : ""
446
447
  ),
447
448
  },
448
449
  });
package/routes/fields.js CHANGED
@@ -919,12 +919,19 @@ router.post(
919
919
  "/test-formula",
920
920
  isAdmin,
921
921
  error_catcher(async (req, res) => {
922
- const { formula, tablename, stored } = req.body;
922
+ let { formula, tablename, stored } = req.body;
923
+ if (stored === "false") stored = false;
924
+
923
925
  const table = Table.findOne({ name: tablename });
924
926
  const fields = table.getFields();
925
927
  const freeVars = freeVariables(formula);
926
928
  const joinFields = {};
927
- if (stored) add_free_variables_to_joinfields(freeVars, joinFields, fields);
929
+ add_free_variables_to_joinfields(freeVars, joinFields, fields);
930
+ if (!stored && Object.keys(joinFields).length > 0) {
931
+ return res
932
+ .status(400)
933
+ .send(`Joinfields only permitted in stored calculated fields`);
934
+ }
928
935
  const rows = await table.getJoinedRows({
929
936
  joinFields,
930
937
  orderBy: "RANDOM()",
@@ -950,9 +957,9 @@ router.post(
950
957
  );
951
958
  } catch (e) {
952
959
  console.error(e);
953
- return res.send(
954
- `Error on running on row with id=${rows[0].id}: ${e.message}`
955
- );
960
+ return res
961
+ .status(400)
962
+ .send(`Error on running on row with id=${rows[0].id}: ${e.message}`);
956
963
  }
957
964
  })
958
965
  );
@@ -14,7 +14,7 @@ const {
14
14
  } = require("../markup/admin.js");
15
15
  const { getState } = require("@saltcorn/data/db/state");
16
16
  const { div, a, i, text } = require("@saltcorn/markup/tags");
17
- const { mkTable, renderForm } = require("@saltcorn/markup");
17
+ const { mkTable, renderForm, post_delete_btn } = require("@saltcorn/markup");
18
18
  const Form = require("@saltcorn/data/models/form");
19
19
 
20
20
  /**
@@ -119,6 +119,15 @@ router.get(
119
119
  })
120
120
  : "",
121
121
  },
122
+ {
123
+ label: req.__("Delete"),
124
+ key: (r) =>
125
+ post_delete_btn(
126
+ `/site-structure/localizer/delete-lang/${r.locale}`,
127
+ req,
128
+ r.name
129
+ ),
130
+ },
122
131
  ],
123
132
  Object.values(cfgLangs)
124
133
  ),
@@ -287,3 +296,25 @@ router.post(
287
296
  }
288
297
  })
289
298
  );
299
+
300
+ /**
301
+ * @name post/localizer/save-lang
302
+ * @function
303
+ * @memberof module:routes/infoarch~infoarchRouter
304
+ * @function
305
+ */
306
+ router.post(
307
+ "/localizer/delete-lang/:lang",
308
+ isAdmin,
309
+ error_catcher(async (req, res) => {
310
+ const { lang } = req.params;
311
+
312
+ const cfgLangs = getState().getConfig("localizer_languages");
313
+ if (cfgLangs[lang]) {
314
+ delete cfgLangs[lang];
315
+ await getState().setConfig("localizer_languages", cfgLangs);
316
+ }
317
+ if (!req.xhr) res.redirect(`/site-structure/localizer`);
318
+ else res.json({ success: "ok" });
319
+ })
320
+ );
package/routes/sync.js CHANGED
@@ -335,13 +335,14 @@ router.post(
335
335
  "/clean_sync_dir",
336
336
  error_catcher(async (req, res) => {
337
337
  const { dir_name } = req.body;
338
+ const safe_dir_name = File.normalise(dir_name);
338
339
  try {
339
340
  const rootFolder = await File.rootFolder();
340
341
  const syncDir = path.join(
341
342
  rootFolder.location,
342
343
  "mobile_app",
343
344
  "sync",
344
- dir_name
345
+ safe_dir_name
345
346
  );
346
347
  await fs.rm(syncDir, { recursive: true, force: true });
347
348
  res.status(200).send("");
@@ -350,6 +350,38 @@ describe("Field Endpoints", () => {
350
350
  .set("Cookie", loginCookie)
351
351
  .expect(toInclude(" is: <pre>2</pre>"));
352
352
  });
353
+ it("should show on stored expression with joinfield", async () => {
354
+ const loginCookie = await getAdminLoginCookie();
355
+ const table = Table.findOne({ name: "books" });
356
+
357
+ const ctx = encodeURIComponent(JSON.stringify({ table_id: table.id }));
358
+ const app = await getApp({ disableCsrf: true });
359
+ await request(app)
360
+ .post("/field/test-formula")
361
+ .send({
362
+ formula: "publisher.name",
363
+ tablename: "books",
364
+ stored: true,
365
+ })
366
+ .set("Cookie", loginCookie)
367
+ .expect(toInclude(" is: <pre>"));
368
+ });
369
+ it("should fail on non-stored expression with joinfield", async () => {
370
+ const loginCookie = await getAdminLoginCookie();
371
+ const table = Table.findOne({ name: "books" });
372
+
373
+ const ctx = encodeURIComponent(JSON.stringify({ table_id: table.id }));
374
+ const app = await getApp({ disableCsrf: true });
375
+ await request(app)
376
+ .post("/field/test-formula")
377
+ .send({
378
+ formula: "publisher.name",
379
+ tablename: "books",
380
+ stored: false,
381
+ })
382
+ .set("Cookie", loginCookie)
383
+ .expect(400);
384
+ });
353
385
  it("should show calculated", async () => {
354
386
  const loginCookie = await getAdminLoginCookie();
355
387
  const table = Table.findOne({ name: "books" });