@saltcorn/server 0.7.4-beta.0 → 0.7.4-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.
package/routes/menu.js CHANGED
@@ -22,6 +22,9 @@ const { renderForm } = require("@saltcorn/markup");
22
22
  const { script, domReady, div, ul } = require("@saltcorn/markup/tags");
23
23
  const { send_infoarch_page } = require("../markup/admin.js");
24
24
  const Table = require("@saltcorn/data/models/table");
25
+ const Trigger = require("@saltcorn/data/models/trigger");
26
+ const { run_action_column } = require("@saltcorn/data/plugin-helper");
27
+
25
28
 
26
29
  /**
27
30
  * @type {object}
@@ -61,6 +64,18 @@ const menuForm = async (req) => {
61
64
  dynSectionFieldOptions[table.name].push(field.name);
62
65
  }
63
66
  }
67
+ const stateActions = getState().actions;
68
+ const actions = [
69
+ ...Object.entries(stateActions)
70
+ .filter(([k, v]) => !v.requireRow && !v.disableInBuilder)
71
+ .map(([k, v]) => k),
72
+ ];
73
+ const triggers = await Trigger.find({
74
+ when_trigger: { or: ["API call", "Never"] },
75
+ });
76
+ triggers.forEach((tr) => {
77
+ actions.push(tr.name);
78
+ });
64
79
 
65
80
  return new Form({
66
81
  action: "/menu/",
@@ -92,6 +107,7 @@ const menuForm = async (req) => {
92
107
  "Dynamic",
93
108
  "Search",
94
109
  "Separator",
110
+ "Action"
95
111
  ],
96
112
  },
97
113
  {
@@ -101,7 +117,7 @@ const menuForm = async (req) => {
101
117
  input_type: "text",
102
118
  required: true,
103
119
  showIf: {
104
- type: ["View", "Page", "Link", "Header", "Dynamic", "Search"],
120
+ type: ["View", "Page", "Link", "Header", "Dynamic", "Search", "Action"],
105
121
  },
106
122
  },
107
123
  {
@@ -111,7 +127,7 @@ const menuForm = async (req) => {
111
127
  attributes: {
112
128
  html: `<button type="button" id="myEditor_icon" class="btn btn-outline-secondary"></button>`,
113
129
  },
114
- showIf: { type: ["View", "Page", "Link", "Header"] },
130
+ showIf: { type: ["View", "Page", "Link", "Header", "Action"] },
115
131
  },
116
132
  {
117
133
  name: "icon",
@@ -149,6 +165,17 @@ const menuForm = async (req) => {
149
165
  attributes: { options: views.map((r) => r.select_option) },
150
166
  showIf: { type: "View" },
151
167
  },
168
+ {
169
+ name: "action_name",
170
+ label: req.__("Action"),
171
+ type: "String",
172
+ class: "item-menu",
173
+ required: true,
174
+ attributes: {
175
+ options: actions,
176
+ },
177
+ showIf: { type: "Action" },
178
+ },
152
179
  {
153
180
  name: "dyn_table",
154
181
  label: req.__("Table"),
@@ -217,7 +244,7 @@ const menuForm = async (req) => {
217
244
  class: "item-menu",
218
245
  type: "String",
219
246
  required: true,
220
- showIf: { type: ["View", "Page", "Link", "Header", "Dynamic"] },
247
+ showIf: { type: ["View", "Page", "Link", "Header", "Dynamic", "Action"] },
221
248
  attributes: {
222
249
  options: [
223
250
  { name: "", label: "Link" },
@@ -239,7 +266,7 @@ const menuForm = async (req) => {
239
266
  {
240
267
  name: "location",
241
268
  label: req.__("Location"),
242
- showIf: { type: ["View", "Page", "Link", "Header", "Dynamic"] },
269
+ showIf: { type: ["View", "Page", "Link", "Header", "Dynamic", "Action"] },
243
270
  sublabel: req.__("Not all themes support all locations"),
244
271
  class: "item-menu",
245
272
  type: "String",
@@ -404,3 +431,37 @@ router.post(
404
431
  res.json({ success: true });
405
432
  })
406
433
  );
434
+
435
+ router.post(
436
+ "/runaction/:name",
437
+ error_catcher(async (req, res) => {
438
+ const { name } = req.params;
439
+ const role = (req.user || {}).role_id || 10;
440
+ const state = getState();
441
+ const menu_items = state.getConfig("menu_items");
442
+ let menu_item;
443
+ const search = items =>
444
+ items
445
+ .filter((item) => role <= +item.min_role)
446
+ .forEach(item => {
447
+ if (item.type === "Action" && item.action_name === name)
448
+ menu_item = item;
449
+ else if (item.subitems)
450
+ search(item.subitems);
451
+ })
452
+ search(menu_items);
453
+ if (menu_item)
454
+ try {
455
+ const result = await run_action_column({
456
+ col: menu_item,
457
+ referrer: req.get("Referrer"),
458
+ req,
459
+ });
460
+ res.json({ success: "ok", ...(result || {}) });
461
+ } catch (e) {
462
+ res.status(400).json({ error: e.message || e });
463
+ }
464
+
465
+ else res.status(404).json({ error: "Action not found" });
466
+ })
467
+ );
package/routes/packs.js CHANGED
@@ -116,7 +116,7 @@ router.get(
116
116
  type: "breadcrumbs",
117
117
  crumbs: [
118
118
  { text: req.__("Settings") },
119
- { text: req.__("Plugins"), href: "/plugins" },
119
+ { text: req.__("Modules"), href: "/plugins" },
120
120
  { text: req.__("Create pack") },
121
121
  ],
122
122
  },
@@ -184,7 +184,7 @@ router.post(
184
184
  type: "breadcrumbs",
185
185
  crumbs: [
186
186
  { text: req.__("Settings") },
187
- { text: req.__("Plugins"), href: "/plugins" },
187
+ { text: req.__("Modules"), href: "/plugins" },
188
188
  { text: req.__("Create pack") },
189
189
  ],
190
190
  },
@@ -242,7 +242,7 @@ router.get(
242
242
  type: "breadcrumbs",
243
243
  crumbs: [
244
244
  { text: req.__("Settings") },
245
- { text: req.__("Plugins"), href: "/plugins" },
245
+ { text: req.__("Modules"), href: "/plugins" },
246
246
  { text: req.__("Install pack") },
247
247
  ],
248
248
  },
@@ -293,7 +293,7 @@ router.post(
293
293
  type: "breadcrumbs",
294
294
  crumbs: [
295
295
  { text: req.__("Settings") },
296
- { text: req.__("Plugins"), href: "/plugins" },
296
+ { text: req.__("Modules"), href: "/plugins" },
297
297
  { text: req.__("Install pack") },
298
298
  ],
299
299
  },
package/routes/plugins.js CHANGED
@@ -79,7 +79,7 @@ const pluginForm = (req, plugin) => {
79
79
  label: req.__("Name"),
80
80
  name: "name",
81
81
  input_type: "text",
82
- sublabel: req.__("Plugin name"),
82
+ sublabel: req.__("Module name"),
83
83
  }),
84
84
  new Field({
85
85
  label: req.__("Source"),
@@ -88,11 +88,11 @@ const pluginForm = (req, plugin) => {
88
88
  required: true,
89
89
  attributes: { options: "npm,local,github,git" },
90
90
  sublabel: req.__(
91
- "Source of plugin for install. Few options:" +
92
- "npm - download from npm repository," +
93
- "local - get from local file system," +
94
- "github - download from github," +
95
- "git - get from git"
91
+ "Source of module for install. Few options:" +
92
+ "npm - download from npm repository," +
93
+ "local - get from local file system," +
94
+ "github - download from github," +
95
+ "git - get from git"
96
96
  ),
97
97
  }),
98
98
  new Field({
@@ -101,19 +101,19 @@ const pluginForm = (req, plugin) => {
101
101
  input_type: "text",
102
102
  sublabel: req.__(
103
103
  "For npm - name of npm package, e.g. @saltcorn/html or saltcorn-gantt, check at npmjs.com, " +
104
- "for local - absolute path to plugin folder in file system, e.g.C:\\gitsrc\\any-bootstrap-theme\\, " +
105
- "for github - name of github project."
104
+ "for local - absolute path to module folder in file system, e.g. C:\\gitsrc\\any-bootstrap-theme\\, " +
105
+ "for github - name of github project."
106
106
  ),
107
107
  }),
108
108
  ...(schema === db.connectObj.default_schema
109
109
  ? [
110
- new Field({
111
- label: req.__("Version"),
112
- name: "version",
113
- input_type: "text",
114
- sublabel: req.__("Version of plugin, latest is default value"),
115
- }),
116
- ]
110
+ new Field({
111
+ label: req.__("Version"),
112
+ name: "version",
113
+ input_type: "text",
114
+ sublabel: req.__("Version of module, latest is default value"),
115
+ }),
116
+ ]
117
117
  : []),
118
118
  new Field({
119
119
  label: req.__("Private SSH key"),
@@ -257,7 +257,7 @@ const store_item_html = (req) => (item) => ({
257
257
  title: item.name,
258
258
  contents: div(
259
259
  div(
260
- item.plugin && badge(req.__("Plugin")),
260
+ item.plugin && badge(req.__("Module")),
261
261
  item.pack && badge(req.__("Pack")),
262
262
  item.has_theme && badge(req.__("Theme")),
263
263
  item.has_auth && badge(req.__("Authentication")),
@@ -269,72 +269,72 @@ const store_item_html = (req) => (item) => ({
269
269
  div(item.description || ""),
270
270
  item.documentation_link
271
271
  ? div(
272
- a(
273
- { href: item.documentation_link, target: "_blank" },
274
- req.__("Documentation")
275
- )
272
+ a(
273
+ { href: item.documentation_link, target: "_blank" },
274
+ req.__("Documentation")
276
275
  )
276
+ )
277
277
  : ""
278
278
  ),
279
279
  footer: div(
280
280
  div(
281
281
  !item.installed &&
282
- item.plugin &&
283
- post_btn(
284
- `/plugins/install/${encodeURIComponent(item.name)}`,
285
- req.__("Install"),
286
- req.csrfToken(),
287
- {
288
- klass: "store-install",
289
- small: true,
290
- onClick: "press_store_button(this)",
291
- }
292
- ),
282
+ item.plugin &&
283
+ post_btn(
284
+ `/plugins/install/${encodeURIComponent(item.name)}`,
285
+ req.__("Install"),
286
+ req.csrfToken(),
287
+ {
288
+ klass: "store-install",
289
+ small: true,
290
+ onClick: "press_store_button(this)",
291
+ }
292
+ ),
293
293
  !item.installed &&
294
- item.pack &&
295
- post_btn(
296
- `/packs/install-named/${encodeURIComponent(item.name)}`,
297
- req.__("Install"),
298
- req.csrfToken(),
299
- {
300
- klass: "store-install",
301
- small: true,
302
- onClick: "press_store_button(this)",
303
- }
304
- ),
294
+ item.pack &&
295
+ post_btn(
296
+ `/packs/install-named/${encodeURIComponent(item.name)}`,
297
+ req.__("Install"),
298
+ req.csrfToken(),
299
+ {
300
+ klass: "store-install",
301
+ small: true,
302
+ onClick: "press_store_button(this)",
303
+ }
304
+ ),
305
305
 
306
306
  item.installed && item.plugin && cfg_link(req, item),
307
307
  item.installed && item.plugin && info_link(req, item),
308
308
 
309
309
  item.installed &&
310
- item.pack &&
311
- post_btn(
312
- `/packs/uninstall/${encodeURIComponent(item.name)}`,
313
- req.__("Uninstall"),
314
- req.csrfToken(),
315
- {
316
- klass: "store-install",
317
- small: true,
318
- btnClass: "btn-danger",
319
- formClass: "d-inline",
320
- onClick: "press_store_button(this)",
321
- }
322
- ),
310
+ item.pack &&
311
+ post_btn(
312
+ `/packs/uninstall/${encodeURIComponent(item.name)}`,
313
+ req.__("Uninstall"),
314
+ req.csrfToken(),
315
+ {
316
+ klass: "store-install",
317
+ small: true,
318
+ btnClass: "btn-danger",
319
+ formClass: "d-inline",
320
+ onClick: "press_store_button(this)",
321
+ }
322
+ ),
323
323
  item.installed &&
324
- item.plugin &&
325
- item.name !== "base" &&
326
- post_btn(
327
- `/plugins/delete/${encodeURIComponent(item.name)}`,
328
- req.__("Remove"),
329
- req.csrfToken(),
330
- {
331
- klass: "store-install",
332
- small: true,
333
- btnClass: "btn-danger",
334
- formClass: "d-inline",
335
- onClick: "press_store_button(this)",
336
- }
337
- )
324
+ item.plugin &&
325
+ item.name !== "base" &&
326
+ post_btn(
327
+ `/plugins/delete/${encodeURIComponent(item.name)}`,
328
+ req.__("Remove"),
329
+ req.csrfToken(),
330
+ {
331
+ klass: "store-install",
332
+ small: true,
333
+ btnClass: "btn-danger",
334
+ formClass: "d-inline",
335
+ onClick: "press_store_button(this)",
336
+ }
337
+ )
338
338
  )
339
339
  ),
340
340
  });
@@ -354,7 +354,7 @@ const storeNavPills = (req) => {
354
354
  "nav-link",
355
355
  (req.query.set === txt.toLowerCase() ||
356
356
  (txt === "All" && !req.query.set)) &&
357
- "active",
357
+ "active",
358
358
  ],
359
359
  },
360
360
  req.__(txt)
@@ -363,7 +363,7 @@ const storeNavPills = (req) => {
363
363
  return ul(
364
364
  { class: "nav nav-pills plugin-section" },
365
365
  link("All"),
366
- link("Plugins"),
366
+ link("Modules"),
367
367
  link("Packs"),
368
368
  link("Themes"),
369
369
  link("Installed")
@@ -407,7 +407,7 @@ const satisfy_q = (p, q) => {
407
407
  */
408
408
  const filter_items_set = (items, query) => {
409
409
  switch (query.set) {
410
- case "plugins":
410
+ case "modules":
411
411
  return items.filter((item) => item.plugin && !item.has_theme);
412
412
  case "packs":
413
413
  return items.filter((item) => item.pack);
@@ -451,23 +451,23 @@ const store_actions_dropdown = (req) =>
451
451
  '<i class="fas fa-sync"></i>&nbsp;' + req.__("Refresh")
452
452
  ),
453
453
  db.getTenantSchema() === db.connectObj.default_schema &&
454
- a(
455
- {
456
- class: "dropdown-item",
457
- href: `/plugins/upgrade`,
458
- onClick: `notifyAlert('Upgrading plugins...', true)`,
459
- },
460
- '<i class="far fa-arrow-alt-circle-up"></i>&nbsp;' +
461
- req.__("Upgrade installed plugins")
462
- ),
454
+ a(
455
+ {
456
+ class: "dropdown-item",
457
+ href: `/plugins/upgrade`,
458
+ onClick: `notifyAlert('Upgrading modules...', true)`,
459
+ },
460
+ '<i class="far fa-arrow-alt-circle-up"></i>&nbsp;' +
461
+ req.__("Upgrade installed modules")
462
+ ),
463
463
  db.getTenantSchema() === db.connectObj.default_schema &&
464
- a(
465
- {
466
- class: "dropdown-item",
467
- href: `/plugins/new`,
468
- },
469
- '<i class="fas fa-plus"></i>&nbsp;' + req.__("Add another plugin")
470
- ),
464
+ a(
465
+ {
466
+ class: "dropdown-item",
467
+ href: `/plugins/new`,
468
+ },
469
+ '<i class="fas fa-plus"></i>&nbsp;' + req.__("Add another module")
470
+ ),
471
471
 
472
472
  a(
473
473
  {
@@ -501,7 +501,7 @@ const plugin_store_html = (items, req) => {
501
501
  type: "breadcrumbs",
502
502
  crumbs: [
503
503
  { text: req.__("Settings"), href: "/settings" },
504
- { text: req.__("Plugin and pack store") },
504
+ { text: req.__("Module store") },
505
505
  ],
506
506
  },
507
507
  {
@@ -534,7 +534,7 @@ router.get(
534
534
  error_catcher(async (req, res) => {
535
535
  const items = await get_store_items();
536
536
  const relevant_items = filter_items(items, req.query);
537
- res.sendWrap(req.__("Plugins"), plugin_store_html(relevant_items, req));
537
+ res.sendWrap(req.__("Module store"), plugin_store_html(relevant_items, req));
538
538
  })
539
539
  );
540
540
 
@@ -574,9 +574,8 @@ router.get(
574
574
  onclick: "location.reload()",
575
575
  },
576
576
  ];
577
- wfres.renderForm.onChange = `${
578
- wfres.renderForm.onChange || ""
579
- };$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
577
+ wfres.renderForm.onChange = `${wfres.renderForm.onChange || ""
578
+ };$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
580
579
  }
581
580
 
582
581
  res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
@@ -620,9 +619,8 @@ router.post(
620
619
  onclick: "location.reload()",
621
620
  },
622
621
  ];
623
- wfres.renderForm.onChange = `${
624
- wfres.renderForm.onChange || ""
625
- };$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
622
+ wfres.renderForm.onChange = `${wfres.renderForm.onChange || ""
623
+ };$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
626
624
  }
627
625
  res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
628
626
  type: "card",
@@ -694,13 +692,13 @@ router.get(
694
692
  type: "breadcrumbs",
695
693
  crumbs: [
696
694
  { text: req.__("Settings"), href: "/settings" },
697
- { text: req.__("Plugin and pack store"), href: "/plugins" },
695
+ { text: req.__("Module store"), href: "/plugins" },
698
696
  { text: req.__("New") },
699
697
  ],
700
698
  },
701
699
  {
702
700
  type: "card",
703
- title: req.__(`Add plugin`),
701
+ title: req.__(`Add module`),
704
702
  contents: renderForm(pluginForm(req), req.csrfToken()),
705
703
  },
706
704
  ],
@@ -808,35 +806,35 @@ router.get(
808
806
  latest || "",
809
807
  can_update
810
808
  ? a(
811
- {
812
- href: `/plugins/upgrade-plugin/${plugin_db.name}`,
813
- class: "btn btn-primary btn-sm ms-2",
814
- },
815
- req.__("Upgrade")
816
- )
809
+ {
810
+ href: `/plugins/upgrade-plugin/${plugin_db.name}`,
811
+ class: "btn btn-primary btn-sm ms-2",
812
+ },
813
+ req.__("Upgrade")
814
+ )
817
815
  : ""
818
816
  )
819
817
  ),
820
818
  mod.plugin_module.dependencies
821
819
  ? tr(
822
- th(req.__("Plugin dependencies")),
823
- td(
824
- mod.plugin_module.dependencies.map((d) =>
825
- span({ class: "badge bg-primary me-1" }, d)
826
- )
820
+ th(req.__("Plugin dependencies")),
821
+ td(
822
+ mod.plugin_module.dependencies.map((d) =>
823
+ span({ class: "badge bg-primary me-1" }, d)
827
824
  )
828
825
  )
826
+ )
829
827
  : null,
830
828
  store_item && store_item.documentation_link
831
829
  ? tr(
832
- th(req.__("Documentation")),
833
- td(
834
- link(
835
- store_item.documentation_link,
836
- store_item.documentation_link
837
- )
830
+ th(req.__("Documentation")),
831
+ td(
832
+ link(
833
+ store_item.documentation_link,
834
+ store_item.documentation_link
838
835
  )
839
836
  )
837
+ )
840
838
  : null,
841
839
  pkgjson && pkgjson.repository
842
840
  ? tr(th(req.__("Repository")), td(showRepository(pkgjson.repository)))
@@ -861,13 +859,13 @@ router.get(
861
859
  type: "breadcrumbs",
862
860
  crumbs: [
863
861
  { text: req.__("Settings"), href: "/settings" },
864
- { text: req.__("Plugin and pack store"), href: "/plugins" },
862
+ { text: req.__("Module store"), href: "/plugins" },
865
863
  { text: plugin_db.name },
866
864
  ],
867
865
  },
868
866
  {
869
867
  type: "card",
870
- title: req.__(`%s plugin information`, plugin_db.name),
868
+ title: req.__(`%s module information`, plugin_db.name),
871
869
  contents: p(store_item.description) + infoTable,
872
870
  },
873
871
  ...cards,
@@ -913,7 +911,7 @@ router.get(
913
911
  for (const plugin of installed_plugins) {
914
912
  await plugin.upgrade_version((p, f) => load_plugins.loadPlugin(p, f));
915
913
  }
916
- req.flash("success", req.__(`Plugins up-to-date`));
914
+ req.flash("success", req.__(`Modules up-to-date`));
917
915
  await restart_tenant(loadAllPlugins);
918
916
  process.send &&
919
917
  process.send({ restart_tenant: true, tenant: db.getTenantSchema() });
@@ -63,9 +63,9 @@ router.get(
63
63
  href: "/admin",
64
64
  }),
65
65
  settingsCard({
66
- title: req.__("Plugins"),
67
- icon: "fas fa-plug",
68
- blurb: req.__("Plugin and pack installation and control"),
66
+ title: req.__("Modules"),
67
+ icon: "fas fa-cubes",
68
+ blurb: req.__("Module installation and control"),
69
69
  href: "/plugins",
70
70
  }),
71
71
  settingsCard({