@saltcorn/server 0.7.4 → 0.8.0-beta.1

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.
Files changed (50) hide show
  1. package/app.js +18 -11
  2. package/auth/admin.js +370 -120
  3. package/auth/roleadmin.js +5 -23
  4. package/auth/routes.js +40 -15
  5. package/locales/de.json +1049 -273
  6. package/locales/en.json +58 -3
  7. package/locales/es.json +134 -134
  8. package/locales/it.json +6 -1
  9. package/locales/ru.json +44 -7
  10. package/markup/admin.js +46 -42
  11. package/markup/forms.js +4 -3
  12. package/package.json +8 -7
  13. package/public/blockly.js +19 -31
  14. package/public/diagram_utils.js +530 -0
  15. package/public/gridedit.js +4 -1
  16. package/public/jquery-menu-editor.min.js +112 -112
  17. package/public/saltcorn-common.js +31 -8
  18. package/public/saltcorn.css +11 -0
  19. package/public/saltcorn.js +211 -70
  20. package/restart_watcher.js +1 -0
  21. package/routes/actions.js +6 -14
  22. package/routes/admin.js +229 -79
  23. package/routes/api.js +19 -2
  24. package/routes/common_lists.js +137 -134
  25. package/routes/delete.js +6 -5
  26. package/routes/diagram.js +43 -117
  27. package/routes/edit.js +5 -10
  28. package/routes/fields.js +63 -29
  29. package/routes/files.js +137 -101
  30. package/routes/homepage.js +2 -2
  31. package/routes/infoarch.js +2 -2
  32. package/routes/list.js +12 -13
  33. package/routes/page.js +16 -3
  34. package/routes/pageedit.js +13 -8
  35. package/routes/scapi.js +1 -1
  36. package/routes/search.js +1 -1
  37. package/routes/tables.js +9 -14
  38. package/routes/tag_entries.js +31 -10
  39. package/routes/tags.js +10 -10
  40. package/routes/tenant.js +114 -50
  41. package/routes/utils.js +12 -0
  42. package/routes/view.js +3 -4
  43. package/routes/viewedit.js +57 -55
  44. package/serve.js +5 -0
  45. package/tests/admin.test.js +6 -2
  46. package/tests/auth.test.js +20 -0
  47. package/tests/fields.test.js +1 -0
  48. package/tests/files.test.js +11 -20
  49. package/tests/tenant.test.js +12 -2
  50. package/tests/viewedit.test.js +15 -1
package/routes/list.js CHANGED
@@ -11,9 +11,8 @@
11
11
  const Router = require("express-promise-router");
12
12
 
13
13
  const db = require("@saltcorn/data/db");
14
- const { mkTable, h, link, post_btn } = require("@saltcorn/markup");
14
+ const { mkTable, link, post_btn } = require("@saltcorn/markup");
15
15
  const {
16
- a,
17
16
  script,
18
17
  domReady,
19
18
  div,
@@ -27,7 +26,6 @@ const {
27
26
  const Table = require("@saltcorn/data/models/table");
28
27
  const { isAdmin, error_catcher } = require("./utils");
29
28
  const moment = require("moment");
30
- const { readState } = require("@saltcorn/data/plugin-helper");
31
29
 
32
30
  /**
33
31
  * @type {object}
@@ -49,11 +47,11 @@ module.exports = router;
49
47
  * @function
50
48
  */
51
49
  router.get(
52
- "/_versions/:name/:id",
50
+ "/_versions/:tableName/:id",
53
51
  isAdmin,
54
52
  error_catcher(async (req, res) => {
55
- const { name, id } = req.params;
56
- const table = await Table.findOne({ name });
53
+ const { tableName, id } = req.params;
54
+ const table = await Table.findOne({ name : tableName });
57
55
 
58
56
  const fields = await table.getFields();
59
57
  var tfields = fields.map((f) => ({ label: f.label, key: f.listKey }));
@@ -97,11 +95,11 @@ router.get(
97
95
  * @function
98
96
  */
99
97
  router.post(
100
- "/_restore/:name/:id/:_version",
98
+ "/_restore/:tableName/:id/:_version",
101
99
  isAdmin,
102
100
  error_catcher(async (req, res) => {
103
- const { name, id, _version } = req.params;
104
- const table = await Table.findOne({ name });
101
+ const { tableName, id, _version } = req.params;
102
+ const table = await Table.findOne({ name : tableName });
105
103
 
106
104
  const fields = await table.getFields();
107
105
  const row = await db.selectOne(`${db.sqlsanitize(table.name)}__history`, {
@@ -217,7 +215,8 @@ jsGrid.fields.versions = VersionsField;
217
215
  const arrangeIdFirst = (flds) => {
218
216
  const noId = flds.filter((f) => f.name !== "id");
219
217
  const id = flds.find((f) => f.name === "id");
220
- return [id, ...noId];
218
+ if (id) return [id, ...noId];
219
+ else return flds
221
220
  };
222
221
 
223
222
  /**
@@ -245,6 +244,7 @@ router.get(
245
244
  }
246
245
 
247
246
  //console.log(fields);
247
+ // todo remove keyfields - unused
248
248
  const keyfields = fields
249
249
  .filter((f) => f.type === "Key" || f.type === "File")
250
250
  .map((f) => ({ name: f.name, type: f.reftype }));
@@ -369,9 +369,8 @@ router.get(
369
369
  })
370
370
  })
371
371
  window.tabulator_table = new Tabulator("#jsGrid", {
372
- ajaxURL:"/api/${table.name}${
373
- table.versioned ? "?versioncount=on" : ""
374
- }",
372
+ ajaxURL:"/api/${table.name}${table.versioned ? "?versioncount=on" : ""
373
+ }",
375
374
  layout:"fitColumns",
376
375
  columns,
377
376
  height:"100%",
package/routes/page.js CHANGED
@@ -7,10 +7,8 @@
7
7
  const Router = require("express-promise-router");
8
8
 
9
9
  const Page = require("@saltcorn/data/models/page");
10
- const { div, a, i } = require("@saltcorn/markup/tags");
11
- const { renderForm } = require("@saltcorn/markup");
12
10
  const { getState } = require("@saltcorn/data/db/state");
13
- const { error_catcher, scan_for_page_title } = require("../routes/utils.js");
11
+ const { error_catcher, scan_for_page_title, isAdmin } = require("../routes/utils.js");
14
12
  const { add_edit_bar } = require("../markup/admin.js");
15
13
  const { traverseSync } = require("@saltcorn/data/models/layout");
16
14
  const { run_action_column } = require("@saltcorn/data/plugin-helper");
@@ -67,6 +65,21 @@ router.get(
67
65
  })
68
66
  );
69
67
 
68
+ router.post(
69
+ "/:pagename/preview",
70
+ isAdmin,
71
+ error_catcher(async (req, res) => {
72
+ const { pagename } = req.params;
73
+ const page = await Page.findOne({ name: pagename });
74
+ if (!page) {
75
+ res.send("");
76
+ return;
77
+ }
78
+ const contents = await page.run(req.query, { res, req });
79
+ res.sendWrap({}, contents);
80
+ })
81
+ );
82
+
70
83
  /**
71
84
  * @name post/:pagename/action/:rndid
72
85
  * @function
@@ -100,6 +100,7 @@ const pageBuilderData = async (req, context) => {
100
100
  const views = await View.find();
101
101
  const pages = await Page.find();
102
102
  const images = await File.find({ mime_super: "image" });
103
+ images.forEach(im => im.location = im.path_to_serve)
103
104
  const roles = await User.get_roles();
104
105
  const stateActions = getState().actions;
105
106
  const actions = [
@@ -130,6 +131,9 @@ const pageBuilderData = async (req, context) => {
130
131
  const fs = await view.get_state_fields();
131
132
  for (const frec of fs) {
132
133
  const f = new Field(frec);
134
+ if (f.input_type === "hidden") continue;
135
+ if (f.name === "_fts") continue;
136
+
133
137
  f.required = false;
134
138
  if (f.type && f.type.name === "Bool") f.fieldview = "tristate";
135
139
 
@@ -156,6 +160,7 @@ const pageBuilderData = async (req, context) => {
156
160
  }
157
161
  }
158
162
  }
163
+ //console.log(fixed_state_fields.ListTasks);
159
164
  return {
160
165
  views,
161
166
  images,
@@ -569,13 +574,13 @@ router.post(
569
574
  const page = await Page.findOne({ id });
570
575
  const roles = await User.get_roles();
571
576
  const roleRow = roles.find((r) => r.id === +role);
572
- if (roleRow && page)
573
- req.flash(
574
- "success",
575
- req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
576
- );
577
- else req.flash("success", req.__(`Minimum role updated`));
578
-
579
- res.redirect("/pageedit");
577
+ const message =
578
+ roleRow && page
579
+ ? req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
580
+ : req.__(`Minimum role updated`);
581
+ if (!req.xhr) {
582
+ req.flash("success", message);
583
+ res.redirect("/pageedit");
584
+ } else res.json({ okay: true, responseText: message });
580
585
  })
581
586
  );
package/routes/scapi.js CHANGED
@@ -185,7 +185,7 @@ router.get(
185
185
  { session: false },
186
186
  async function (err, user, info) {
187
187
  if (accessAllowedRead(req, user)) {
188
- const triggers = await Trigger.find({});
188
+ const triggers = Trigger.find({});
189
189
 
190
190
  res.json({ success: triggers });
191
191
  } else {
package/routes/search.js CHANGED
@@ -188,7 +188,7 @@ const runSearch = async ({ q, _page, table }, req, res) => {
188
188
  pages: current_page + (vresps.length === 20 ? 1 : 0),
189
189
  trailing_ellipsis: vresps.length === 20,
190
190
  get_page_link: (n) =>
191
- `javascript:gopage(${n}, 20, {table:'${tableName}'})`,
191
+ `javascript:gopage(${n}, 20, undefined, {table:'${tableName}'})`,
192
192
  });
193
193
  }
194
194
 
package/routes/tables.js CHANGED
@@ -8,7 +8,6 @@ const Router = require("express-promise-router");
8
8
 
9
9
  const db = require("@saltcorn/data/db");
10
10
  const Table = require("@saltcorn/data/models/table");
11
- const Field = require("@saltcorn/data/models/field");
12
11
  const File = require("@saltcorn/data/models/file");
13
12
  const View = require("@saltcorn/data/models/view");
14
13
  const User = require("@saltcorn/data/models/user");
@@ -16,7 +15,6 @@ const {
16
15
  mkTable,
17
16
  renderForm,
18
17
  link,
19
- post_btn,
20
18
  settingsDropdown,
21
19
  post_delete_btn,
22
20
  post_dropdown_item,
@@ -29,10 +27,7 @@ const { isAdmin, error_catcher, setTenant } = require("./utils.js");
29
27
  const Form = require("@saltcorn/data/models/form");
30
28
  const {
31
29
  span,
32
- h5,
33
30
  h4,
34
- h3,
35
- nbsp,
36
31
  p,
37
32
  a,
38
33
  div,
@@ -41,7 +36,6 @@ const {
41
36
  label,
42
37
  input,
43
38
  text,
44
- tr,
45
39
  script,
46
40
  domReady,
47
41
  code,
@@ -135,6 +129,7 @@ const tableForm = async (table, req) => {
135
129
  name: "min_role_read",
136
130
  input_type: "select",
137
131
  options: roleOptions,
132
+ attributes: { asideNext: !table.external }
138
133
  },
139
134
  ...(table.external
140
135
  ? []
@@ -551,14 +546,13 @@ router.get(
551
546
  res.redirect(`/table`);
552
547
  return;
553
548
  }
554
- id = table.id;
555
549
  const nrows = await table.countRows();
556
550
  const fields = await table.getFields();
557
551
  const { child_relations } = await table.get_child_relations();
558
552
  const inbound_refs = [
559
553
  ...new Set(child_relations.map(({ table }) => table.name)),
560
554
  ];
561
- var fieldCard;
555
+ let fieldCard;
562
556
  if (fields.length === 0) {
563
557
  fieldCard = [
564
558
  h4(req.__(`No fields defined in %s table`, table.name)),
@@ -865,8 +859,10 @@ router.post(
865
859
  res.redirect(`/table/${table.id}`);
866
860
  }
867
861
  } else if (v.external) {
862
+ // todo check that works after where change
863
+ // todo findOne can be have parameter for external table here
868
864
  //we can only save min role
869
- const table = await Table.findOne(v.name);
865
+ const table = await Table.findOne( { name : v.name });
870
866
  if (table) {
871
867
  const exttables_min_role_read = getState().getConfigCopy(
872
868
  "exttables_min_role_read",
@@ -885,15 +881,13 @@ router.post(
885
881
  const table = await Table.findOne({ id: parseInt(id) });
886
882
  const old_versioned = table.versioned;
887
883
  let hasError = false;
884
+ let notify = ""
888
885
  if (!rest.versioned) rest.versioned = false;
889
886
  if (rest.ownership_field_id === "_formula") {
890
887
  rest.ownership_field_id = null;
891
888
  const fmlValidRes = expressionValidator(rest.ownership_formula);
892
889
  if (typeof fmlValidRes === "string") {
893
- req.flash(
894
- "error",
895
- req.__(`Invalid ownership formula: %s`, fmlValidRes)
896
- );
890
+ notify = req.__(`Invalid ownership formula: %s`, fmlValidRes)
897
891
  hasError = true;
898
892
  }
899
893
  } else rest.ownership_formula = null;
@@ -911,7 +905,7 @@ router.post(
911
905
  else if (!hasError) req.flash("success", req.__("Table saved"));
912
906
 
913
907
  if (!req.xhr) res.redirect(`/table/${id}`);
914
- else res.json({ success: "ok" });
908
+ else res.json({ success: "ok", notify });
915
909
  }
916
910
  })
917
911
  );
@@ -1149,6 +1143,7 @@ router.get(
1149
1143
  /**
1150
1144
  * Constraint Fields Edition Form
1151
1145
  * Choosing fields for adding to contrain
1146
+ * @param req
1152
1147
  * @param {string} table_id
1153
1148
  * @param {object[]} fields
1154
1149
  * @returns {Form}
@@ -1,9 +1,6 @@
1
1
  const {
2
- a,
3
2
  div,
4
- text,
5
3
  button,
6
- i,
7
4
  form,
8
5
  select,
9
6
  option,
@@ -81,10 +78,10 @@ const formOptions = async (type, tag_id) => {
81
78
  ),
82
79
  };
83
80
  }
84
- case "trigger": {
81
+ case "triggers": {
85
82
  const ids = await tag.getTriggerIds();
86
83
  return {
87
- trigger: (await Trigger.find()).filter(
84
+ triggers: (Trigger.find()).filter(
88
85
  (value) => ids.indexOf(value.id) === -1
89
86
  ),
90
87
  };
@@ -101,7 +98,7 @@ router.get(
101
98
  above: [
102
99
  {
103
100
  type: "breadcrumbs",
104
- crumbs: [{ text: `Tag entry` }],
101
+ crumbs: [{ text: req.__(`Tag entry`) }],
105
102
  },
106
103
  {
107
104
  type: "card",
@@ -120,15 +117,19 @@ router.get(
120
117
 
121
118
  const idField = (entryType) => {
122
119
  switch (entryType) {
123
- case "tables": {
120
+ case "tables":
121
+ case "table": {
124
122
  return "table_id";
125
123
  }
126
- case "views": {
124
+ case "views":
125
+ case "view": {
127
126
  return "view_id";
128
127
  }
129
- case "pages": {
128
+ case "pages":
129
+ case "page": {
130
130
  return "page_id";
131
131
  }
132
+ case "triggers":
132
133
  case "trigger": {
133
134
  return "trigger_id";
134
135
  }
@@ -136,6 +137,7 @@ const idField = (entryType) => {
136
137
  return null;
137
138
  };
138
139
 
140
+ // add multiple objects to one tag
139
141
  router.post(
140
142
  "/add/:entry_type/:tag_id",
141
143
  isAdmin,
@@ -155,6 +157,24 @@ router.post(
155
157
  })
156
158
  );
157
159
 
160
+ // add one object to multiple tags
161
+ router.post(
162
+ "/add/multiple_tags/:entry_type/:object_id",
163
+ isAdmin,
164
+ error_catcher(async (req, res) => {
165
+ let { entry_type, object_id } = req.params;
166
+ let { tag_ids } = req.body;
167
+ object_id = parseInt(object_id);
168
+ tag_ids = tag_ids.map((id) => parseInt(id));
169
+ const tags = (await Tag.find()).filter((tag) => tag_ids.includes(tag.id));
170
+ const fieldName = idField(entry_type);
171
+ for (const tag of tags) {
172
+ await tag.addEntry({ [fieldName]: object_id });
173
+ }
174
+ res.json({ tags });
175
+ })
176
+ );
177
+
158
178
  router.post(
159
179
  "/remove/:entry_type/:entry_id/:tag_id",
160
180
  isAdmin,
@@ -168,6 +188,7 @@ router.post(
168
188
  } else {
169
189
  await TagEntry.update(entry.id, { [fieldName]: null });
170
190
  }
171
- res.redirect(`/tag/${tag_id}?show_list=${entry_type}`);
191
+ if (!req.xhr) res.redirect(`/tag/${tag_id}?show_list=${entry_type}`);
192
+ else res.json({ okay: true });
172
193
  })
173
194
  );
package/routes/tags.js CHANGED
@@ -128,18 +128,18 @@ router.get(
128
128
  const views = await tag.getViews();
129
129
  await setTableRefs(views);
130
130
  const pages = await tag.getPages();
131
- const trigger = await tag.getTrigger();
131
+ const triggers = await tag.getTriggers();
132
132
  const roles = await User.get_roles();
133
133
 
134
134
  const tablesDomId = "tablesListId";
135
135
  const viewsDomId = "viewsListId";
136
136
  const pagesDomId = "pagesDomId";
137
- const triggerDomId = "triggerDomId";
137
+ const triggersDomId = "triggerDomId";
138
138
  res.sendWrap(req.__("%s Tag", tag.name), {
139
139
  above: [
140
140
  {
141
141
  type: "breadcrumbs",
142
- crumbs: [{ text: `Tag: ${tag.name}` }],
142
+ crumbs: [{ text: req.__(`Tag: %s`, tag.name) }],
143
143
  },
144
144
  {
145
145
  type: "card",
@@ -211,19 +211,19 @@ router.get(
211
211
  type: "card",
212
212
  bodyId: "collapseTriggerCard",
213
213
  title: headerWithCollapser(
214
- req.__("Trigger"),
215
- triggerDomId,
216
- isShowList(show_list, "trigger")
214
+ req.__("Triggers"),
215
+ triggersDomId,
216
+ isShowList(show_list, "triggers")
217
217
  ),
218
218
  contents: [
219
- getTriggerList(trigger, req, {
219
+ getTriggerList(triggers, req, {
220
220
  tagId: tag.id,
221
- domId: triggerDomId,
222
- showList: isShowList(show_list, "trigger"),
221
+ domId: triggersDomId,
222
+ showList: isShowList(show_list, "triggers"),
223
223
  }),
224
224
  a(
225
225
  {
226
- href: `/tag-entries/add/trigger/${tag.id}`,
226
+ href: `/tag-entries/add/triggers/${tag.id}`,
227
227
  class: "btn btn-primary",
228
228
  },
229
229
  req.__("Add triggers")