@saltcorn/server 0.8.0-beta.2 → 0.8.0-beta.4

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/routes/admin.js CHANGED
@@ -366,7 +366,7 @@ router.get(
366
366
  post_btn(
367
367
  "/admin/backup",
368
368
  i({ class: "fas fa-download me-2" }) +
369
- req.__("Download a backup"),
369
+ req.__("Download a backup"),
370
370
  req.csrfToken(),
371
371
  {
372
372
  btnClass: "btn-outline-primary",
@@ -385,21 +385,21 @@ router.get(
385
385
  },
386
386
  isRoot
387
387
  ? {
388
- type: "card",
389
- title: req.__("Automated backup"),
390
- contents: div(
391
- renderForm(backupForm, req.csrfToken()),
392
- a(
393
- { href: "/admin/auto-backup-list" },
394
- req.__("Restore/download automated backups »")
395
- ),
396
- script(
397
- domReady(
398
- `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
388
+ type: "card",
389
+ title: req.__("Automated backup"),
390
+ contents: div(
391
+ renderForm(backupForm, req.csrfToken()),
392
+ a(
393
+ { href: "/admin/auto-backup-list" },
394
+ req.__("Restore/download automated backups »")
395
+ ),
396
+ script(
397
+ domReady(
398
+ `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
399
+ )
399
400
  )
400
- )
401
- ),
402
- }
401
+ ),
402
+ }
403
403
  : { type: "blank", contents: "" },
404
404
  {
405
405
  type: "card",
@@ -407,7 +407,9 @@ router.get(
407
407
  contents: div(
408
408
  p(
409
409
  i(
410
- req.__("Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).")
410
+ req.__(
411
+ "Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns)."
412
+ )
411
413
  )
412
414
  ),
413
415
  renderForm(aSnapshotForm, req.csrfToken()),
@@ -475,12 +477,17 @@ router.get(
475
477
  ol(
476
478
  li(req.__("Download one of the backups above")),
477
479
  li(
478
- a({ href: "/admin/clear-all" }, req.__("Clear this application")),
480
+ a(
481
+ { href: "/admin/clear-all" },
482
+ req.__("Clear this application")
483
+ ),
479
484
  " ",
480
485
  req.__("(tick all boxes)")
481
486
  ),
482
487
  li(
483
- req.__("When prompted to create the first user, click the link to restore a backup")
488
+ req.__(
489
+ "When prompted to create the first user, click the link to restore a backup"
490
+ )
484
491
  ),
485
492
  li(req.__("Select the downloaded backup file"))
486
493
  )
@@ -768,7 +775,8 @@ router.get(
768
775
  isAdmin,
769
776
  error_catcher(async (req, res) => {
770
777
  const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
771
- const latest = isRoot && (await get_latest_npm_version("@saltcorn/cli"));
778
+ const latest =
779
+ isRoot && (await get_latest_npm_version("@saltcorn/cli", 1000));
772
780
  const is_latest = packagejson.version === latest;
773
781
  const git_commit = getGitRevision();
774
782
  const can_update =
@@ -832,83 +840,71 @@ router.get(
832
840
  th(req.__("Saltcorn version")),
833
841
  td(
834
842
  packagejson.version +
835
- (isRoot && can_update
836
- ? post_btn(
837
- "/admin/upgrade",
838
- req.__("Upgrade"),
839
- req.csrfToken(),
840
- {
841
- btnClass: "btn-primary btn-sm",
842
- formClass: "d-inline",
843
- }
844
- )
845
- : isRoot && is_latest
843
+ (isRoot && can_update
844
+ ? post_btn(
845
+ "/admin/upgrade",
846
+ req.__("Upgrade"),
847
+ req.csrfToken(),
848
+ {
849
+ btnClass: "btn-primary btn-sm",
850
+ formClass: "d-inline",
851
+ }
852
+ )
853
+ : isRoot && is_latest
846
854
  ? span(
847
- { class: "badge bg-primary ms-2" },
848
- req.__("Latest")
849
- ) +
850
- post_btn(
851
- "/admin/check-for-upgrade",
852
- req.__("Check for updates"),
853
- req.csrfToken(),
854
- {
855
- btnClass: "btn-primary btn-sm px-1 py-0",
856
- formClass: "d-inline",
857
- }
858
- )
855
+ { class: "badge bg-primary ms-2" },
856
+ req.__("Latest")
857
+ ) +
858
+ post_btn(
859
+ "/admin/check-for-upgrade",
860
+ req.__("Check for updates"),
861
+ req.csrfToken(),
862
+ {
863
+ btnClass: "btn-primary btn-sm px-1 py-0",
864
+ formClass: "d-inline",
865
+ }
866
+ )
859
867
  : "")
860
868
  )
861
869
  ),
862
870
  git_commit &&
863
- tr(
864
- th(req.__("git commit")),
865
- td(
866
- a(
867
- {
868
- href:
869
- "https://github.com/saltcorn/saltcorn/commit/" +
870
- git_commit,
871
- },
872
- git_commit.substring(0, 6)
871
+ tr(
872
+ th(req.__("git commit")),
873
+ td(
874
+ a(
875
+ {
876
+ href:
877
+ "https://github.com/saltcorn/saltcorn/commit/" +
878
+ git_commit,
879
+ },
880
+ git_commit.substring(0, 6)
881
+ )
873
882
  )
874
- )
875
- ),
883
+ ),
876
884
  tr(th(req.__("Node.js version")), td(process.version)),
877
885
  tr(
878
886
  th(req.__("Database type")),
879
887
  td(db.isSQLite ? "SQLite " : "PostgreSQL ", dbversion)
880
888
  ),
881
- (isRoot?
882
- tr(
883
- th(req.__("Database host")),
884
- td(db.connectObj.host)
885
- )
886
- : ""),
887
- (isRoot?
888
- tr(
889
- th(req.__("Database port")),
890
- td(db.connectObj.port)
891
- )
892
- : ""),
893
- (isRoot?
894
- tr(
889
+ isRoot
890
+ ? tr(th(req.__("Database host")), td(db.connectObj.host))
891
+ : "",
892
+ isRoot
893
+ ? tr(th(req.__("Database port")), td(db.connectObj.port))
894
+ : "",
895
+ isRoot
896
+ ? tr(
895
897
  th(req.__("Database name")),
896
898
  td(db.connectObj.database)
897
- )
898
- : ""),
899
- (isRoot?
900
- tr(
901
- th(req.__("Database user")),
902
- td(db.connectObj.user)
903
- )
904
- : ""),
905
- tr(
906
- th(req.__("Database schema")),
907
- td(db.getTenantSchema())
908
- ),
899
+ )
900
+ : "",
901
+ isRoot
902
+ ? tr(th(req.__("Database user")), td(db.connectObj.user))
903
+ : "",
904
+ tr(th(req.__("Database schema")), td(db.getTenantSchema())),
909
905
  tr(
910
- th(req.__("Process uptime")),
911
- td(moment(get_process_init_time()).fromNow(true))
906
+ th(req.__("Process uptime")),
907
+ td(moment(get_process_init_time()).fromNow(true))
912
908
  )
913
909
  )
914
910
  ),
@@ -974,7 +970,9 @@ router.post(
974
970
  });
975
971
  child.on("exit", function (code, signal) {
976
972
  res.end(
977
- req.__(`Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`)
973
+ req.__(
974
+ `Upgrade done (if it was available) with code ${code}.\n\nPress the BACK button in your browser, then RELOAD the page.`
975
+ )
978
976
  );
979
977
  setTimeout(() => {
980
978
  if (process.send) process.send("RestartServer");
@@ -1178,8 +1176,8 @@ router.post(
1178
1176
  req.__(
1179
1177
  "LetsEncrypt SSL enabled. Restart for changes to take effect."
1180
1178
  ) +
1181
- " " +
1182
- a({ href: "/admin/system" }, req.__("Restart here"))
1179
+ " " +
1180
+ a({ href: "/admin/system" }, req.__("Restart here"))
1183
1181
  );
1184
1182
  res.redirect("/useradmin/ssl");
1185
1183
  } catch (e) {
@@ -1255,13 +1253,13 @@ router.get(
1255
1253
  contents: div(
1256
1254
  pass
1257
1255
  ? div(
1258
- { class: "alert alert-success", role: "alert" },
1259
- i({ class: "fas fa-check-circle fa-lg me-2" }),
1260
- h5(
1261
- { class: "d-inline" },
1262
- req.__("No errors detected during configuration check")
1256
+ { class: "alert alert-success", role: "alert" },
1257
+ i({ class: "fas fa-check-circle fa-lg me-2" }),
1258
+ h5(
1259
+ { class: "d-inline" },
1260
+ req.__("No errors detected during configuration check")
1261
+ )
1263
1262
  )
1264
- )
1265
1263
  : errors.map(mkError),
1266
1264
  (warnings || []).map(mkWarning)
1267
1265
  ),
@@ -1819,15 +1817,11 @@ router.post(
1819
1817
  * @returns {Promise<Form>} form
1820
1818
  */
1821
1819
  const dev_form = async (req) => {
1822
- return await config_fields_form({
1823
- req,
1824
- field_names: [
1825
- "development_mode",
1826
- "log_sql",
1827
- "log_level",
1828
- ],
1829
- action: "/admin/dev",
1830
- });
1820
+ return await config_fields_form({
1821
+ req,
1822
+ field_names: ["development_mode", "log_sql", "log_level"],
1823
+ action: "/admin/dev",
1824
+ });
1831
1825
  };
1832
1826
  /**
1833
1827
  * Developer Mode page
@@ -1836,19 +1830,19 @@ const dev_form = async (req) => {
1836
1830
  * @memberof module:routes/admin~routes/adminRouter
1837
1831
  */
1838
1832
  router.get(
1839
- "/dev",
1840
- isAdmin,
1841
- error_catcher(async (req, res) => {
1842
- const form = await dev_form(req);
1843
- send_admin_page({
1844
- res,
1845
- req,
1846
- active_sub: "Development",
1847
- contents: {
1848
- type: "card",
1849
- title: req.__("Development settings"),
1850
- contents: [
1851
- renderForm(form, req.csrfToken())/*,
1833
+ "/dev",
1834
+ isAdmin,
1835
+ error_catcher(async (req, res) => {
1836
+ const form = await dev_form(req);
1837
+ send_admin_page({
1838
+ res,
1839
+ req,
1840
+ active_sub: "Development",
1841
+ contents: {
1842
+ type: "card",
1843
+ title: req.__("Development settings"),
1844
+ contents: [
1845
+ renderForm(form, req.csrfToken()) /*,
1852
1846
  a(
1853
1847
  {
1854
1848
  id: "testemail",
@@ -1856,11 +1850,11 @@ router.get(
1856
1850
  class: "btn btn-primary",
1857
1851
  },
1858
1852
  req.__("Send test email")
1859
- ),*/
1860
- ],
1861
- },
1862
- });
1863
- })
1853
+ ),*/,
1854
+ ],
1855
+ },
1856
+ });
1857
+ })
1864
1858
  );
1865
1859
 
1866
1860
  /**
@@ -1870,27 +1864,27 @@ router.get(
1870
1864
  * @memberof module:routes/admin~routes/adminRouter
1871
1865
  */
1872
1866
  router.post(
1873
- "/dev",
1874
- isAdmin,
1875
- error_catcher(async (req, res) => {
1876
- const form = await dev_form(req);
1877
- form.validate(req.body);
1878
- if (form.hasErrors) {
1879
- send_admin_page({
1880
- res,
1881
- req,
1882
- active_sub: "Development",
1883
- contents: {
1884
- type: "card",
1885
- title: req.__("Development settings"),
1886
- contents: [renderForm(form, req.csrfToken())],
1887
- },
1888
- });
1889
- } else {
1890
- await save_config_from_form(form);
1891
- req.flash("success", req.__("Development mode settings updated"));
1892
- if (!req.xhr) res.redirect("/admin/dev");
1893
- else res.json({ success: "ok" });
1894
- }
1895
- })
1896
- );
1867
+ "/dev",
1868
+ isAdmin,
1869
+ error_catcher(async (req, res) => {
1870
+ const form = await dev_form(req);
1871
+ form.validate(req.body);
1872
+ if (form.hasErrors) {
1873
+ send_admin_page({
1874
+ res,
1875
+ req,
1876
+ active_sub: "Development",
1877
+ contents: {
1878
+ type: "card",
1879
+ title: req.__("Development settings"),
1880
+ contents: [renderForm(form, req.csrfToken())],
1881
+ },
1882
+ });
1883
+ } else {
1884
+ await save_config_from_form(form);
1885
+ req.flash("success", req.__("Development mode settings updated"));
1886
+ if (!req.xhr) res.redirect("/admin/dev");
1887
+ else res.json({ success: "ok" });
1888
+ }
1889
+ })
1890
+ );
package/routes/fields.js CHANGED
@@ -66,7 +66,7 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
66
66
  return new Form({
67
67
  action: "/field",
68
68
  validator: (vs) => {
69
- if (vs.calculated && vs.type == "File")
69
+ if (vs.calculated && vs.type === "File")
70
70
  return req.__("Calculated fields cannot have File type");
71
71
  if (vs.calculated && vs.type.startsWith("Key to"))
72
72
  return req.__("Calculated fields cannot have Key type");
@@ -182,8 +182,8 @@ const fieldFlow = (req) =>
182
182
  new Workflow({
183
183
  action: "/field",
184
184
  onDone: async (context) => {
185
- const thetype = getState().types[context.type];
186
- var attributes = context.attributes || {};
185
+ //const thetype = getState().types[context.type];
186
+ const attributes = context.attributes || {};
187
187
  attributes.default = context.default;
188
188
  attributes.summary_field = context.summary_field;
189
189
  attributes.include_fts = context.include_fts;
@@ -291,6 +291,8 @@ const fieldFlow = (req) =>
291
291
  form: async (context) => {
292
292
  if (context.type === "File") {
293
293
  const roles = await User.get_roles();
294
+ const default_file_accept_filter = await getState().getConfig("files_accept_filter_default");
295
+ //console.log("default_file_accept_filter",default_file_accept_filter);
294
296
  return new Form({
295
297
  fields: [
296
298
  {
@@ -310,6 +312,15 @@ const fieldFlow = (req) =>
310
312
  "Deleting a row will also delete the file referenced by this field"
311
313
  ),
312
314
  },
315
+ {
316
+ name: "files_accept_filter",
317
+ type: "String",
318
+ label: req.__("Files accept filter"),
319
+ sublabel: req.__(
320
+ "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`"
321
+ ),
322
+ default: default_file_accept_filter,
323
+ },
313
324
  ],
314
325
  });
315
326
  } else {
@@ -634,7 +645,7 @@ router.post(
634
645
  );
635
646
 
636
647
  /**
637
- * @name post/test-formula
648
+ * Test formula
638
649
  * @function
639
650
  * @memberof module:routes/fields~fieldsRouter
640
651
  * @function
package/routes/files.js CHANGED
@@ -13,24 +13,12 @@ const s3storage = require("../s3storage");
13
13
  const resizer = require("resize-with-sharp-or-jimp");
14
14
  const db = require("@saltcorn/data/db");
15
15
 
16
- const {
17
- mkTable,
18
- renderForm,
19
- link,
20
- //post_btn,
21
- post_delete_btn,
22
- } = require("@saltcorn/markup");
16
+ const { renderForm } = require("@saltcorn/markup");
23
17
  const { isAdmin, error_catcher, setTenant } = require("./utils.js");
24
- const { h1, div, text, button, i, a } = require("@saltcorn/markup/tags");
25
- // const { csrfField } = require("./utils");
18
+ const { h1, div, text } = require("@saltcorn/markup/tags");
26
19
  const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
27
20
  const { strictParseInt } = require("@saltcorn/data/plugin-helper");
28
- const {
29
- send_files_page,
30
- config_fields_form,
31
- save_config_from_form,
32
- } = require("../markup/admin");
33
- // const fsp = require("fs").promises;
21
+ const { send_files_page, config_fields_form, save_config_from_form } = require("../markup/admin");
34
22
  const fs = require("fs");
35
23
  const path = require("path");
36
24
 
@@ -74,7 +62,6 @@ router.get(
74
62
  const safeDir = File.normalise(dir || "/")
75
63
  const rows = await File.find({ folder: dir }, { orderBy: "filename" });
76
64
  const roles = await User.get_roles();
77
- //console.log(rows);
78
65
  if (safeDir && safeDir !== "/" && safeDir !== ".") {
79
66
  let dirname = path.dirname(safeDir)
80
67
  if (dirname === ".") dirname = "/"
@@ -314,7 +301,7 @@ router.post(
314
301
  */
315
302
  router.post(
316
303
  "/upload",
317
- setTenant, // TODO why is this needed?????
304
+ setTenant,
318
305
  error_catcher(async (req, res) => {
319
306
  let { folder } = req.body
320
307
  let jsonResp = {};
@@ -395,12 +382,12 @@ router.post(
395
382
  );
396
383
 
397
384
  /**
398
- * Storage settings form definition
385
+ * S3 Storage settings form definition
399
386
  * @param {object} req request
400
387
  * @returns {Promise<Form>} form
401
388
  */
402
389
  const storage_form = async (req) => {
403
- const form = await config_fields_form({
390
+ return await config_fields_form({
404
391
  req,
405
392
  field_names: [
406
393
  "storage_s3_enabled",
@@ -414,11 +401,10 @@ const storage_form = async (req) => {
414
401
  ],
415
402
  action: "/files/storage",
416
403
  });
417
- return form;
418
404
  };
419
405
 
420
406
  /**
421
- * @name get/storage
407
+ * Show S3 Settings
422
408
  * @function
423
409
  * @memberof module:routes/admin~routes/adminRouter
424
410
  */
@@ -441,7 +427,7 @@ router.get(
441
427
  );
442
428
 
443
429
  /**
444
- * @name post/email
430
+ * Update S3 Settings
445
431
  * @function
446
432
  * @memberof module:routes/admin~routes/adminRouter
447
433
  */
@@ -472,3 +458,78 @@ router.post(
472
458
  }
473
459
  })
474
460
  );
461
+
462
+ /**
463
+ * Files settings form definition
464
+ * @param {object} req request
465
+ * @returns {Promise<Form>} form
466
+ */
467
+ const files_settings_form = async (req) => {
468
+ return await config_fields_form({
469
+ req,
470
+ field_names: [
471
+ "min_role_upload",
472
+ "file_accept_filter_default",
473
+ "file_upload_debug",
474
+ "file_upload_limit",
475
+ "file_upload_timeout",
476
+ ],
477
+ action: "/files/settings",
478
+ });
479
+ };
480
+
481
+ /**
482
+ * Show Files Settings
483
+ * @function
484
+ * @memberof module:routes/admin~routes/adminRouter
485
+ */
486
+ router.get(
487
+ "/settings",
488
+ isAdmin,
489
+ error_catcher(async (req, res) => {
490
+ const form = await files_settings_form(req);
491
+ send_files_page({
492
+ res,
493
+ req,
494
+ active_sub: "Settings",
495
+ contents: {
496
+ type: "card",
497
+ title: req.__("Files settings"),
498
+ contents: [renderForm(form, req.csrfToken())],
499
+ },
500
+ });
501
+ })
502
+ );
503
+
504
+ /**
505
+ * Update Files Settings
506
+ * @function
507
+ * @memberof module:routes/admin~routes/adminRouter
508
+ */
509
+ router.post(
510
+ "/settings",
511
+ isAdmin,
512
+ error_catcher(async (req, res) => {
513
+ const form = await files_settings_form(req);
514
+ form.validate(req.body);
515
+ if (form.hasErrors) {
516
+ send_admin_page({
517
+ res,
518
+ req,
519
+ active_sub: "Settings",
520
+ contents: {
521
+ type: "card",
522
+ title: req.__("Files settings"),
523
+ contents: [renderForm(form, req.csrfToken())],
524
+ },
525
+ });
526
+ } else {
527
+ await save_config_from_form(form);
528
+
529
+ if (!req.xhr) {
530
+ req.flash("success", req.__("Files settings updated"));
531
+ res.redirect("/files/settings");
532
+ } else res.json({ success: "ok" });
533
+ }
534
+ })
535
+ );