@saltcorn/server 1.1.1-rc.4 → 1.1.2-beta.0

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/tables.js CHANGED
@@ -51,7 +51,7 @@ const {
51
51
  pre,
52
52
  button,
53
53
  } = require("@saltcorn/markup/tags");
54
- const stringify = require("csv-stringify");
54
+ const { stringify } = require("csv-stringify");
55
55
  const TableConstraint = require("@saltcorn/data/models/table_constraints");
56
56
  const fs = require("fs").promises;
57
57
  const {
@@ -359,7 +359,7 @@ router.post(
359
359
  error_catcher(async (req, res) => {
360
360
  const tbls = await discoverable_tables();
361
361
  const form = discoverForm(tbls, req);
362
- form.validate(req.body);
362
+ form.validate(req.body || {});
363
363
  const tableNames = tbls
364
364
  .filter((t) => form.values[t.table_name])
365
365
  .map((t) => t.table_name);
@@ -437,8 +437,8 @@ router.post(
437
437
  setTenant,
438
438
  isAdminOrHasConfigMinRole("min_role_edit_tables"),
439
439
  error_catcher(async (req, res) => {
440
- if (req.body.name && req.files && req.files.file) {
441
- const name = req.body.name;
440
+ if ((req.body || {}).name && req.files && req.files.file) {
441
+ const name = (req.body || {}).name;
442
442
  const alltables = await Table.find({});
443
443
  const existing_tables = [
444
444
  "users",
@@ -645,8 +645,8 @@ router.get(
645
645
  div(
646
646
  {
647
647
  id: "erd-wrapper",
648
- style: "height: calc(100vh - 250px);",
649
- class: "overflow-scroll position-relative",
648
+ style:
649
+ "height: calc(100vh - 250px); overflow: hidden !important;",
650
650
  },
651
651
  screenshotPanel(),
652
652
  pre(
@@ -712,6 +712,7 @@ const attribBadges = (f) => {
712
712
  if (
713
713
  [
714
714
  "summary_field",
715
+ "importance",
715
716
  "on_delete_cascade",
716
717
  "on_delete",
717
718
  "unique_error_msg",
@@ -1168,7 +1169,7 @@ router.post(
1168
1169
  "/",
1169
1170
  isAdminOrHasConfigMinRole("min_role_edit_tables"),
1170
1171
  error_catcher(async (req, res) => {
1171
- const v = req.body;
1172
+ const v = req.body || {};
1172
1173
  if (typeof v.id === "undefined" && typeof v.external === "undefined") {
1173
1174
  // insert
1174
1175
  v.name = v.name.trim();
@@ -1633,21 +1634,14 @@ const constraintForm = (req, table, fields, type) => {
1633
1634
  case "Index":
1634
1635
  const fieldopts = fields.map((f) => ({ label: f.label, name: f.name }));
1635
1636
  const hasIncludeFts = fields.filter((f) => f.attributes?.include_fts);
1636
- if (!db.isSQLite && !hasIncludeFts.length)
1637
+ if (!db.isSQLite)
1637
1638
  fieldopts.push({ label: "Full-text search", name: "_fts" });
1638
1639
  return new Form({
1639
1640
  action: `/table/add-constraint/${table.id}/${type}`,
1640
- blurb:
1641
- req.__(
1642
- "Choose the field to be indexed. This make searching the table faster."
1643
- ) +
1644
- " " +
1645
- (hasIncludeFts.length
1646
- ? req.__(
1647
- `Full-text search index is not available as the table contains Key fields (%s) with the "Include in full-text search" option enabled. Disable this before creating a Full-text search index`,
1648
- hasIncludeFts.map((f) => f.name).join(",")
1649
- )
1650
- : ""),
1641
+ blurb: req.__(
1642
+ "Choose the field to be indexed. This make searching the table faster."
1643
+ ),
1644
+
1651
1645
  fields: [
1652
1646
  {
1653
1647
  type: "String",
@@ -1656,6 +1650,11 @@ const constraintForm = (req, table, fields, type) => {
1656
1650
  required: true,
1657
1651
  attributes: {
1658
1652
  options: fieldopts,
1653
+ explainers: hasIncludeFts
1654
+ ? {
1655
+ _fts: "Full text search index is not compatible with Key fields with the 'Include in Full text search' option. A new field will be created for your search context",
1656
+ }
1657
+ : {},
1659
1658
  },
1660
1659
  },
1661
1660
  ],
@@ -1728,7 +1727,7 @@ router.post(
1728
1727
  }
1729
1728
  const fields = table.getFields();
1730
1729
  const form = constraintForm(req, table, fields, type);
1731
- form.validate(req.body);
1730
+ form.validate(req.body || {});
1732
1731
  if (form.hasErrors) req.flash("error", req.__("An error occurred"));
1733
1732
  else {
1734
1733
  let configuration = {};
@@ -1819,7 +1818,7 @@ router.post(
1819
1818
  const table = Table.findOne({ id });
1820
1819
  const form = renameForm(table.id, req);
1821
1820
 
1822
- form.validate(req.body);
1821
+ form.validate(req.body || {});
1823
1822
  if (form.hasErrors) req.flash("error", req.__("An error occurred"));
1824
1823
  else {
1825
1824
  await table.rename(form.values.name);
@@ -2187,7 +2186,7 @@ router.post(
2187
2186
  return;
2188
2187
  }
2189
2188
  const workflow = get_provider_workflow(table, req);
2190
- const wfres = await workflow.run(req.body, req);
2189
+ const wfres = await workflow.run(req.body || {}, req);
2191
2190
  respondWorkflow(table, workflow, wfres, req, res);
2192
2191
  })
2193
2192
  );
@@ -161,7 +161,7 @@ router.post(
161
161
  isAdmin,
162
162
  error_catcher(async (req, res) => {
163
163
  const { entry_type, tag_id } = req.params;
164
- const { ids } = req.body;
164
+ const { ids } = req.body || {};
165
165
  if (!ids) {
166
166
  req.flash("error", req.__("Please select at least one item"));
167
167
  return res.redirect(`/tag-entries/add/${entry_type}/${tag_id}`);
@@ -218,7 +218,7 @@ router.post(
218
218
  isAdmin,
219
219
  error_catcher(async (req, res) => {
220
220
  let { entry_type, object_id } = req.params;
221
- let { tag_ids } = req.body;
221
+ let { tag_ids } = req.body || {};
222
222
  object_id = parseInt(object_id);
223
223
  tag_ids = tag_ids.map((id) => parseInt(id));
224
224
  const tags = (await Tag.find()).filter((tag) => tag_ids.includes(tag.id));
package/routes/tags.js CHANGED
@@ -315,7 +315,7 @@ router.post(
315
315
  "/",
316
316
  isAdmin,
317
317
  error_catcher(async (req, res) => {
318
- const { name } = req.body;
318
+ const { name } = req.body || {};
319
319
  const tag = await Tag.create({ name });
320
320
  req.flash("success", req.__(`Tag %s created`, name));
321
321
  res.redirect(`/tag/${tag.id}?show_list=tables`);
package/routes/tenant.js CHANGED
@@ -265,7 +265,7 @@ router.post(
265
265
  const base_url = get_cfg_tenant_base_url(req);
266
266
  const form = tenant_form(req, base_url);
267
267
  // validate ui form
268
- const valres = form.validate(req.body);
268
+ const valres = form.validate(req.body || {});
269
269
  if (valres.errors)
270
270
  res.sendWrap(
271
271
  req.__("Create application"),
@@ -548,7 +548,7 @@ router.post(
548
548
  isAdmin,
549
549
  error_catcher(async (req, res) => {
550
550
  const form = await tenant_settings_form(req);
551
- form.validate(req.body);
551
+ form.validate(req.body || {});
552
552
  if (form.hasErrors) {
553
553
  send_infoarch_page({
554
554
  res,
@@ -872,11 +872,11 @@ router.post(
872
872
  return;
873
873
  }
874
874
  const { subdomain } = req.params;
875
- const { base_url } = req.body;
875
+ const { base_url } = req.body || {};
876
876
  const saneDomain = domain_sanitize(subdomain);
877
877
 
878
878
  // save description
879
- const { description } = req.body;
879
+ const { description } = req.body || {};
880
880
  await Tenant.update(saneDomain, { description: description });
881
881
 
882
882
  await db.runWithTenant(saneDomain, async () => {
package/routes/utils.js CHANGED
@@ -446,7 +446,7 @@ const admin_config_route = ({
446
446
  isAdmin,
447
447
  error_catcher(async (req, res) => {
448
448
  const form = await getTheForm(req);
449
- form.validate(req.body);
449
+ form.validate(req.body || {});
450
450
  if (form.hasErrors) {
451
451
  response(form, req, res);
452
452
  } else {
@@ -493,7 +493,7 @@ const sendHtmlFile = async (req, res, file) => {
493
493
  path.dirname(fullPath)
494
494
  );
495
495
  if (scFile && role <= scFile.min_role_read) {
496
- res.sendFile(fullPath);
496
+ res.sendFile(fullPath, { dotfiles: "allow" });
497
497
  } else {
498
498
  return res
499
499
  .status(404)
@@ -517,7 +517,7 @@ const sendHtmlFile = async (req, res, file) => {
517
517
  */
518
518
  const setRole = async (req, res, model) => {
519
519
  const { id } = req.params;
520
- const role = req.body.role;
520
+ const role = (req.body || {}).role;
521
521
  await model.update(+id, { min_role: role });
522
522
  const page = model.findOne({ id });
523
523
  const roles = await User.get_roles();
package/routes/view.js CHANGED
@@ -39,7 +39,7 @@ module.exports = router;
39
39
  * @function
40
40
  */
41
41
  router.get(
42
- ["/:viewname", "/:viewname/*"],
42
+ ["/:viewname", "/:viewname/*slug"],
43
43
  error_catcher(async (req, res) => {
44
44
  const { viewname } = req.params;
45
45
  const query = { ...req.query };
@@ -60,7 +60,7 @@ router.get(
60
60
  }
61
61
  const tic = new Date();
62
62
 
63
- view.rewrite_query_from_slug(query, req.params);
63
+ view.rewrite_query_from_slug(query, req.params.slug);
64
64
  if (
65
65
  role > view.min_role &&
66
66
  !(await view.authorise_get({ query, req, ...view }))
@@ -231,7 +231,7 @@ router.post(
231
231
 
232
232
  res.redirect("/");
233
233
  } else {
234
- await view.runRoute(route, req.body, res, { res, req });
234
+ await view.runRoute(route, req.body || {}, res, { res, req });
235
235
  }
236
236
  })
237
237
  );
@@ -243,7 +243,7 @@ router.post(
243
243
  * @function
244
244
  */
245
245
  router.post(
246
- ["/:viewname", "/:viewname/*"],
246
+ ["/:viewname", "/:viewname/*slug"],
247
247
  setTenant,
248
248
  error_catcher(async (req, res) => {
249
249
  const { viewname } = req.params;
@@ -263,11 +263,11 @@ router.post(
263
263
  res.redirect("/");
264
264
  return;
265
265
  }
266
- view.rewrite_query_from_slug(query, req.params);
266
+ view.rewrite_query_from_slug(query, req.params.slug);
267
267
 
268
268
  if (
269
269
  role > view.min_role &&
270
- !(await view.authorise_post({ body: req.body, req, ...view }))
270
+ !(await view.authorise_post({ body: req.body || {}, req, ...view }))
271
271
  ) {
272
272
  req.flash("danger", req.__("Not authorized"));
273
273
  state.log(2, `View ${viewname} POST not authorized`);
@@ -280,7 +280,7 @@ router.post(
280
280
  } does not supply a POST handler`
281
281
  );
282
282
  } else {
283
- await view.runPost(query, req.body, { res, req });
283
+ await view.runPost(query, req.body || {}, { res, req });
284
284
  }
285
285
  })
286
286
  );
@@ -492,7 +492,7 @@ router.post(
492
492
  const roles = await User.get_roles();
493
493
  const pages = await Page.find();
494
494
  const form = await viewForm(req, tableOptions, roles, pages);
495
- const result = form.validate(req.body);
495
+ const result = form.validate(req.body || {});
496
496
  const sendForm = (form) => {
497
497
  res.sendWrap(req.__(`Edit view`), {
498
498
  above: [
@@ -521,7 +521,7 @@ router.post(
521
521
  } else {
522
522
  const existing_view = await View.findOne({ name: result.success.name });
523
523
  if (existing_view)
524
- if (+req.body.id !== existing_view.id) {
524
+ if (+(req.body || {}).id !== existing_view.id) {
525
525
  // may be need !== but doesnt work
526
526
  form.errors.name = req.__("A view with this name already exists");
527
527
  form.hasErrors = true;
@@ -544,8 +544,8 @@ router.post(
544
544
  }
545
545
  //const table = Table.findOne({ name: v.table_name });
546
546
  delete v.table_name;
547
- if (req.body.id) {
548
- await View.update(v, +req.body.id);
547
+ if ((req.body || {}).id) {
548
+ await View.update(v, +(req.body || {}).id);
549
549
  } else {
550
550
  const vt = getState().viewtemplates[v.viewtemplate];
551
551
  if (vt.initial_config) v.configuration = await vt.initial_config(v);
@@ -744,7 +744,7 @@ router.post(
744
744
  entity_name: view.name,
745
745
  });
746
746
  };
747
- const wfres = await configFlow.run(req.body, req);
747
+ const wfres = await configFlow.run(req.body || {}, req);
748
748
 
749
749
  let table;
750
750
  if (view.table_id) table = Table.findOne({ id: view.table_id });
@@ -852,9 +852,9 @@ router.post(
852
852
  error_catcher(async (req, res) => {
853
853
  const { id } = req.params;
854
854
 
855
- if (id && req.body) {
855
+ if (id && (req.body || {})) {
856
856
  const exview = await View.findOne({ id });
857
- let newcfg = { ...exview.configuration, ...req.body };
857
+ let newcfg = { ...exview.configuration, ...(req.body || {}) };
858
858
  await View.update({ configuration: newcfg }, +id);
859
859
  Trigger.emitEvent("AppChange", `View ${exview.name}`, req.user, {
860
860
  entity_type: "View",
@@ -880,11 +880,11 @@ router.post(
880
880
  error_catcher(async (req, res) => {
881
881
  const { viewname } = req.params;
882
882
 
883
- if (viewname && req.body) {
883
+ if (viewname && (req.body || {})) {
884
884
  const view = await View.findOne({ name: viewname });
885
885
  req.staticFieldViewConfig = true;
886
886
  const configFlow = await view.get_config_flow(req);
887
- const step = await configFlow.singleStepForm(req.body, req);
887
+ const step = await configFlow.singleStepForm(req.body || {}, req);
888
888
  if (step?.renderForm) {
889
889
  if (!step.renderForm.hasErrors) {
890
890
  let newcfg;
@@ -926,7 +926,7 @@ router.post(
926
926
  isAdminOrHasConfigMinRole("min_role_edit_views"),
927
927
  error_catcher(async (req, res) => {
928
928
  const { id } = req.params;
929
- const role = req.body.role;
929
+ const role = (req.body || {}).role;
930
930
  await View.update({ min_role: role }, +id);
931
931
  const view = await View.findOne({ id });
932
932
  Trigger.emitEvent("AppChange", `View ${view.name}`, req.user, {
@@ -955,7 +955,7 @@ router.post(
955
955
  "/test/inserter",
956
956
  isAdminOrHasConfigMinRole("min_role_edit_views"),
957
957
  error_catcher(async (req, res) => {
958
- const view = await View.create(req.body);
958
+ const view = await View.create(req.body || {});
959
959
  res.json({ view });
960
960
  })
961
961
  );
package/s3storage.js CHANGED
@@ -159,7 +159,7 @@ module.exports = {
159
159
  .send();
160
160
  } else {
161
161
  // Use legacy file download
162
- res.download(file.location, file.filename);
162
+ res.download(file.location, file.filename, { dotfiles: "allow" });
163
163
  }
164
164
  },
165
165
 
@@ -358,9 +358,19 @@ describe("visible_entries test", () => {
358
358
  .send(`role=${role}`)
359
359
  .expect(toRedirect("/files?dir=_sc_test_subfolder_one"));
360
360
  };
361
+ const setDirRole = async (role, entry) => {
362
+ const app = await getApp({ disableCsrf: true });
363
+ const adminCookie = await getAdminLoginCookie();
364
+ await request(app)
365
+ .post(`/files/setrole/${entry}`)
366
+ .set("Cookie", adminCookie)
367
+ .send(`role=${role}`)
368
+ .expect(toRedirect("/files?dir=."));
369
+ };
361
370
 
362
371
  it("shows allowed files", async () => {
363
372
  await setRole(100, path.join("_sc_test_subfolder_one", "foo_image.png"));
373
+ await setDirRole(100, "_sc_test_subfolder_one");
364
374
 
365
375
  const app = await getApp({ disableCsrf: true });
366
376
  const staffCookie = await getStaffLoginCookie();
@@ -91,9 +91,7 @@ describe("Plugin Endpoints", () => {
91
91
  .get("/plugins/public/any-bootstrap-theme/test.txt")
92
92
  .expect(toInclude("testfilecontents"));
93
93
  await request(app)
94
- .get(
95
- "/plugins/public/sbadmin2@9.9.9/sb-admin-2.min.css"
96
- )
94
+ .get("/plugins/public/sbadmin2@9.9.9/sb-admin-2.min.css")
97
95
  .expect(toInclude("Start Bootstrap"));
98
96
 
99
97
  await request(app)
@@ -101,9 +99,7 @@ describe("Plugin Endpoints", () => {
101
99
  .set("Cookie", loginCookie)
102
100
  .expect(toRedirect("/plugins"));
103
101
  await request(app)
104
- .get(
105
- "/plugins/public/sbadmin2@9.9.9/sb-admin-2.min.css"
106
- )
102
+ .get("/plugins/public/sbadmin2@9.9.9/sb-admin-2.min.css")
107
103
  .expect(toInclude("Start Bootstrap"));
108
104
  });
109
105
  it("should install named without config", async () => {