@saltcorn/server 0.7.3-beta.7 → 0.7.3

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/auth/admin.js CHANGED
@@ -381,7 +381,8 @@ router.post(
381
381
  } else {
382
382
  await save_config_from_form(form);
383
383
  req.flash("success", req.__("User settings updated"));
384
- res.redirect("/useradmin/settings");
384
+ if (!req.xhr) res.redirect("/useradmin/settings");
385
+ else res.json({ success: "ok" });
385
386
  }
386
387
  })
387
388
  );
@@ -530,7 +531,7 @@ router.get(
530
531
  send_users_page({
531
532
  res,
532
533
  req,
533
- active_sub: "Settings",
534
+ active_sub: "SSL",
534
535
  contents: {
535
536
  type: "card",
536
537
  title: req.__("Authentication settings"),
@@ -556,7 +557,7 @@ router.post(
556
557
  send_users_page({
557
558
  res,
558
559
  req,
559
- active_sub: "Settings",
560
+ active_sub: "SSL",
560
561
  contents: {
561
562
  type: "card",
562
563
  title: req.__("Authentication settings"),
@@ -572,7 +573,9 @@ router.post(
572
573
  " " +
573
574
  a({ href: "/admin/system" }, req.__("Restart here"))
574
575
  );
575
- res.redirect("/useradmin/ssl");
576
+ if (!req.xhr) {
577
+ res.redirect("/useradmin/ssl");
578
+ } else res.json({ success: "ok" });
576
579
  }
577
580
  })
578
581
  );
package/locales/en.json CHANGED
@@ -920,5 +920,6 @@
920
920
  "Download automated backup": "Download automated backup",
921
921
  "Restoring automated backup": "Restoring automated backup",
922
922
  "No errors detected during configuration check": "No errors detected during configuration check",
923
- "%s view - %s on %s": "%s view - %s on %s"
923
+ "%s view - %s on %s": "%s view - %s on %s",
924
+ "Back": "Back"
924
925
  }
package/markup/admin.js CHANGED
@@ -345,7 +345,7 @@ const flash_restart = (req) => {
345
345
  * @param {*} opts.formArgs
346
346
  * @returns {Promise<Form>}
347
347
  */
348
- const config_fields_form = async ({ field_names, req, ...formArgs }) => {
348
+ const config_fields_form = async ({ field_names, req, action, ...formArgs }) => {
349
349
  const values = {};
350
350
  const state = getState();
351
351
  const fields = [];
@@ -396,8 +396,9 @@ const config_fields_form = async ({ field_names, req, ...formArgs }) => {
396
396
  const form = new Form({
397
397
  fields,
398
398
  values,
399
- submitButtonClass: "btn-outline-primary",
400
- onChange: "remove_outline(this)",
399
+ action,
400
+ noSubmitButton: true,
401
+ onChange: `saveAndContinue(this)`,
401
402
  ...formArgs,
402
403
  });
403
404
  await form.fill_fkey_options();
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.7.3-beta.7",
3
+ "version": "0.7.3",
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
- "@saltcorn/base-plugin": "0.7.3-beta.7",
10
- "@saltcorn/builder": "0.7.3-beta.7",
11
- "@saltcorn/data": "0.7.3-beta.7",
12
- "@saltcorn/admin-models": "0.7.3-beta.7",
13
- "@saltcorn/markup": "0.7.3-beta.7",
14
- "@saltcorn/sbadmin2": "0.7.3-beta.7",
9
+ "@saltcorn/base-plugin": "0.7.3",
10
+ "@saltcorn/builder": "0.7.3",
11
+ "@saltcorn/data": "0.7.3",
12
+ "@saltcorn/admin-models": "0.7.3",
13
+ "@saltcorn/markup": "0.7.3",
14
+ "@saltcorn/sbadmin2": "0.7.3",
15
15
  "@socket.io/cluster-adapter": "^0.1.0",
16
16
  "@socket.io/sticky": "^1.0.1",
17
17
  "aws-sdk": "^2.1037.0",
@@ -502,7 +502,7 @@ function common_done(res, isWeb = true) {
502
502
  }
503
503
  }
504
504
 
505
- const repeaterCopyValuesToForm = (form, editor) => {
505
+ const repeaterCopyValuesToForm = (form, editor, noTriggerChange) => {
506
506
  const vs = JSON.parse(editor.getString());
507
507
 
508
508
  const setVal = (k, ix, v) => {
@@ -533,6 +533,7 @@ const repeaterCopyValuesToForm = (form, editor) => {
533
533
  if (typeof ix !== "number" || isNaN(ix)) return;
534
534
  if (ix >= vs.length) $(this).remove();
535
535
  });
536
+ !noTriggerChange && form.trigger("change");
536
537
  };
537
538
  function align_dropdown(id) {
538
539
  setTimeout(() => {
@@ -213,6 +213,7 @@ function ajax_modal(url, opts = {}) {
213
213
  (opts.onOpen || function () {})(res);
214
214
  $("#scmodal").on("hidden.bs.modal", function (e) {
215
215
  (opts.onClose || function () {})(res);
216
+ $("body").css("overflow", "");
216
217
  });
217
218
  },
218
219
  });
@@ -248,7 +249,7 @@ function saveAndContinue(e, k) {
248
249
  return false;
249
250
  }
250
251
 
251
- function applyViewConfig(e, url) {
252
+ function applyViewConfig(e, url, k) {
252
253
  var form = $(e).closest("form");
253
254
  var form_data = form.serializeArray();
254
255
  const cfg = {};
@@ -264,6 +265,9 @@ function applyViewConfig(e, url) {
264
265
  },
265
266
  data: JSON.stringify(cfg),
266
267
  error: function (request) {},
268
+ success: function (res) {
269
+ k && k(res);
270
+ },
267
271
  });
268
272
 
269
273
  return false;
package/routes/admin.js CHANGED
@@ -46,6 +46,8 @@ const {
46
46
  ul,
47
47
  li,
48
48
  ol,
49
+ script,
50
+ domReady,
49
51
  } = require("@saltcorn/markup/tags");
50
52
  const db = require("@saltcorn/data/db");
51
53
  const {
@@ -141,10 +143,6 @@ const email_form = async (req) => {
141
143
  ],
142
144
  action: "/admin/email",
143
145
  });
144
- form.submitButtonClass = "btn-outline-primary";
145
- form.submitLabel = req.__("Save");
146
- form.onChange =
147
- "remove_outline(this);$('#testemail').attr('href','#').removeClass('btn-primary').addClass('btn-outline-primary')";
148
146
  return form;
149
147
  };
150
148
 
@@ -215,8 +213,10 @@ router.post(
215
213
  flash_restart_if_required(form, req);
216
214
  await save_config_from_form(form);
217
215
 
218
- req.flash("success", req.__("Site identity settings updated"));
219
- res.redirect("/admin");
216
+ if (!req.xhr) {
217
+ req.flash("success", req.__("Site identity settings updated"));
218
+ res.redirect("/admin");
219
+ } else res.json({ success: "ok" });
220
220
  }
221
221
  })
222
222
  );
@@ -309,7 +309,8 @@ router.post(
309
309
  } else {
310
310
  await save_config_from_form(form);
311
311
  req.flash("success", req.__("Email settings updated"));
312
- res.redirect("/admin/email");
312
+ if (!req.xhr) res.redirect("/admin/email");
313
+ else res.json({ success: "ok" });
313
314
  }
314
315
  })
315
316
  );
@@ -379,6 +380,11 @@ router.get(
379
380
  a(
380
381
  { href: "/admin/auto-backup-list" },
381
382
  "Restore/download automated backups &raquo;"
383
+ ),
384
+ script(
385
+ domReady(
386
+ `$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
387
+ )
382
388
  )
383
389
  ),
384
390
  }
@@ -484,9 +490,8 @@ router.get(
484
490
  const autoBackupForm = (req) =>
485
491
  new Form({
486
492
  action: "/admin/set-auto-backup",
487
- submitButtonClass: "btn-outline-primary",
488
- onChange: "remove_outline(this)",
489
- submitLabel: "Save settings",
493
+ onChange: `saveAndContinue(this);$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`,
494
+ noSubmitButton: true,
490
495
  additionalButtons: [
491
496
  {
492
497
  label: "Backup now",
@@ -555,7 +560,8 @@ router.post(
555
560
  } else {
556
561
  await save_config_from_form(form);
557
562
  req.flash("success", req.__("Backup settings updated"));
558
- res.redirect("/admin/backup");
563
+ if (!req.xhr) res.redirect("/admin/backup");
564
+ else res.json({ success: "ok" });
559
565
  }
560
566
  })
561
567
  );
@@ -73,8 +73,9 @@ const logSettingsForm = async (req) => {
73
73
  fields.push({
74
74
  name: w + "_channel",
75
75
  label: w + " channel",
76
- sublabel:
77
- req.__("Channels to create events for. Separate by comma; leave blank for all"),
76
+ sublabel: req.__(
77
+ "Channels to create events for. Separate by comma; leave blank for all"
78
+ ),
78
79
  type: "String",
79
80
  showIf: { [w]: true },
80
81
  });
@@ -82,8 +83,8 @@ const logSettingsForm = async (req) => {
82
83
  return new Form({
83
84
  action: "/eventlog/settings",
84
85
  blurb: req.__("Which events should be logged?"),
85
- submitButtonClass: "btn-outline-primary",
86
- onChange: "remove_outline(this)",
86
+ noSubmitButton: true,
87
+ onChange: "saveAndContinue(this)",
87
88
  fields,
88
89
  });
89
90
  };
@@ -169,23 +170,23 @@ router.get(
169
170
  * @returns {Form}
170
171
  */
171
172
  const customEventForm = async (req) => {
172
- return new Form({
173
- action: "/eventlog/custom/new",
174
- submitButtonClass: "btn-outline-primary",
175
- onChange: "remove_outline(this)",
176
- fields: [
177
- {
178
- name: "name",
179
- label: req.__("Event Name"),
180
- type: "String",
181
- },
182
- {
183
- name: "hasChannel",
184
- label: req.__("Has channels?"),
185
- type: "Bool",
186
- },
187
- ],
188
- });
173
+ return new Form({
174
+ action: "/eventlog/custom/new",
175
+ submitButtonClass: "btn-outline-primary",
176
+ onChange: "remove_outline(this)",
177
+ fields: [
178
+ {
179
+ name: "name",
180
+ label: req.__("Event Name"),
181
+ type: "String",
182
+ },
183
+ {
184
+ name: "hasChannel",
185
+ label: req.__("Has channels?"),
186
+ type: "Bool",
187
+ },
188
+ ],
189
+ });
189
190
  };
190
191
  /**
191
192
  * @name get/custom/new
@@ -297,7 +298,8 @@ router.post(
297
298
  } else {
298
299
  await getState().setConfig("event_log_settings", form.values);
299
300
 
300
- res.redirect(`/eventlog/settings`);
301
+ if (!req.xhr) res.redirect(`/eventlog/settings`);
302
+ else res.json({ success: "ok" });
301
303
  }
302
304
  })
303
305
  );
package/routes/files.js CHANGED
@@ -378,9 +378,6 @@ const storage_form = async (req) => {
378
378
  ],
379
379
  action: "/files/storage",
380
380
  });
381
- form.submitButtonClass = "btn-outline-primary";
382
- form.submitLabel = req.__("Save");
383
- form.onChange = "remove_outline(this)";
384
381
  return form;
385
382
  };
386
383
 
@@ -431,8 +428,11 @@ router.post(
431
428
  });
432
429
  } else {
433
430
  await save_config_from_form(form);
434
- req.flash("success", req.__("Storage settings updated"));
435
- res.redirect("/files/storage");
431
+
432
+ if (!req.xhr) {
433
+ req.flash("success", req.__("Storage settings updated"));
434
+ res.redirect("/files/storage");
435
+ } else res.json({ success: "ok" });
436
436
  }
437
437
  })
438
438
  );
@@ -48,8 +48,8 @@ router.get(
48
48
  const languageForm = (req) =>
49
49
  new Form({
50
50
  action: "/site-structure/localizer/save-lang",
51
- submitButtonClass: "btn-outline-primary",
52
- onChange: "remove_outline(this)",
51
+ onChange: "saveAndContinue(this)",
52
+ noSubmitButton: true,
53
53
  fields: [
54
54
  {
55
55
  name: "name",
@@ -270,7 +270,10 @@ router.post(
270
270
  ...cfgLangs,
271
271
  [lang.locale]: lang,
272
272
  });
273
- res.redirect(`/site-structure/localizer/edit/${lang.locale}`);
273
+
274
+ if (!req.xhr)
275
+ res.redirect(`/site-structure/localizer/edit/${lang.locale}`);
276
+ else res.json({ success: "ok" });
274
277
  }
275
278
  })
276
279
  );
package/routes/plugins.js CHANGED
@@ -561,12 +561,30 @@ router.get(
561
561
  }
562
562
  const flow = module.configuration_workflow();
563
563
  flow.action = `/plugins/configure/${encodeURIComponent(plugin.name)}`;
564
+ flow.autoSave = true;
565
+ flow.saveURL = `/plugins/saveconfig/${encodeURIComponent(plugin.name)}`;
564
566
  const wfres = await flow.run(plugin.configuration || {});
567
+ if (module.layout) {
568
+ wfres.renderForm.additionalButtons = [
569
+ ...(wfres.renderForm.additionalButtons || []),
570
+ {
571
+ label: "Reload page to see changes",
572
+ id: "btnReloadNow",
573
+ class: "btn btn-outline-secondary",
574
+ onclick: "location.reload()",
575
+ },
576
+ ];
577
+ wfres.renderForm.onChange = `${
578
+ wfres.renderForm.onChange || ""
579
+ };$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
580
+ }
565
581
 
566
- res.sendWrap(
567
- req.__(`Configure %s Plugin`, plugin.name),
568
- renderForm(wfres.renderForm, req.csrfToken())
569
- );
582
+ res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
583
+ type: "card",
584
+ class: "mt-0",
585
+ title: req.__(`Configure %s Plugin`, plugin.name),
586
+ contents: renderForm(wfres.renderForm, req.csrfToken()),
587
+ });
570
588
  })
571
589
  );
572
590
 
@@ -588,13 +606,31 @@ router.post(
588
606
  }
589
607
  const flow = module.configuration_workflow();
590
608
  flow.action = `/plugins/configure/${encodeURIComponent(plugin.name)}`;
609
+ flow.autoSave = true;
610
+ flow.saveURL = `/plugins/saveconfig/${encodeURIComponent(plugin.name)}`;
591
611
  const wfres = await flow.run(req.body);
592
- if (wfres.renderForm)
593
- res.sendWrap(
594
- req.__(`Configure %s Plugin`, plugin.name),
595
- renderForm(wfres.renderForm, req.csrfToken())
596
- );
597
- else {
612
+ if (wfres.renderForm) {
613
+ if (module.layout) {
614
+ wfres.renderForm.additionalButtons = [
615
+ ...(wfres.renderForm.additionalButtons || []),
616
+ {
617
+ label: "Reload page to see changes",
618
+ id: "btnReloadNow",
619
+ class: "btn btn-outline-secondary",
620
+ onclick: "location.reload()",
621
+ },
622
+ ];
623
+ wfres.renderForm.onChange = `${
624
+ wfres.renderForm.onChange || ""
625
+ };$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
626
+ }
627
+ res.sendWrap(req.__(`Configure %s Plugin`, plugin.name), {
628
+ type: "card",
629
+ class: "mt-0",
630
+ title: req.__(`Configure %s Plugin`, plugin.name),
631
+ contents: renderForm(wfres.renderForm, req.csrfToken()),
632
+ });
633
+ } else {
598
634
  plugin.configuration = wfres;
599
635
  await plugin.upsert();
600
636
  await load_plugins.loadPlugin(plugin);
@@ -606,12 +642,42 @@ router.post(
606
642
  refresh_plugin_cfg: plugin.name,
607
643
  tenant: db.getTenantSchema(),
608
644
  });
609
- await sleep(500); // Allow other workers to reload this plugin
645
+ if (module.layout) await sleep(500); // Allow other workers to reload this plugin
610
646
  res.redirect("/plugins");
611
647
  }
612
648
  })
613
649
  );
614
650
 
651
+ router.post(
652
+ "/saveconfig/:name",
653
+ isAdmin,
654
+ error_catcher(async (req, res) => {
655
+ const { name } = req.params;
656
+ const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
657
+ let module = getState().plugins[plugin.name];
658
+ if (!module) {
659
+ module = getState().plugins[getState().plugin_module_names[plugin.name]];
660
+ }
661
+ const flow = module.configuration_workflow();
662
+ const step = await flow.singleStepForm(req.body, req);
663
+ if (step?.renderForm) {
664
+ if (!step.renderForm.hasErrors) {
665
+ plugin.configuration = {
666
+ ...plugin.configuration,
667
+ ...step.renderForm.values,
668
+ };
669
+ await plugin.upsert();
670
+ await load_plugins.loadPlugin(plugin);
671
+ process.send &&
672
+ process.send({
673
+ refresh_plugin_cfg: plugin.name,
674
+ tenant: db.getTenantSchema(),
675
+ });
676
+ res.json({ success: "ok" });
677
+ }
678
+ }
679
+ })
680
+ );
615
681
  /**
616
682
  * @name get/new
617
683
  * @function
package/routes/search.js CHANGED
@@ -55,7 +55,8 @@ const searchConfigForm = (tables, views, req) => {
55
55
  );
56
56
  return new Form({
57
57
  action: "/search/config",
58
- submitLabel: req.__("Save"),
58
+ noSubmitButton: true,
59
+ onChange: `saveAndContinue(this)`,
59
60
  blurb:
60
61
  blurb1 +
61
62
  (tbls_noviews.length > 0
@@ -111,7 +112,8 @@ router.post(
111
112
 
112
113
  if (result.success) {
113
114
  await getState().setConfig("globalSearch", result.success);
114
- res.redirect("/search/config");
115
+ if (!req.xhr) res.redirect("/search/config");
116
+ else res.json({ success: "ok" });
115
117
  } else {
116
118
  send_infoarch_page({
117
119
  res,
package/routes/tables.js CHANGED
@@ -83,8 +83,8 @@ const tableForm = async (table, req) => {
83
83
  .map((f) => ({ value: f.id, label: f.name }));
84
84
  const form = new Form({
85
85
  action: "/table",
86
- submitButtonClass: "btn-outline-primary",
87
- onChange: "remove_outline(this)",
86
+ noSubmitButton: true,
87
+ onChange: "saveAndContinue(this)",
88
88
  fields: [
89
89
  ...(!table.external
90
90
  ? [
@@ -910,7 +910,8 @@ router.post(
910
910
  );
911
911
  else if (!hasError) req.flash("success", req.__("Table saved"));
912
912
 
913
- res.redirect(`/table/${id}`);
913
+ if (!req.xhr) res.redirect(`/table/${id}`);
914
+ else res.json({ success: "ok" });
914
915
  }
915
916
  })
916
917
  );
package/routes/tenant.js CHANGED
@@ -452,8 +452,10 @@ router.post(
452
452
  } else {
453
453
  await save_config_from_form(form);
454
454
 
455
- req.flash("success", req.__("Tenant settings updated"));
456
- res.redirect("/tenant/settings");
455
+ if (!req.xhr) {
456
+ req.flash("success", req.__("Tenant settings updated"));
457
+ res.redirect("/tenant/settings");
458
+ } else res.json({ success: "ok" });
457
459
  }
458
460
  })
459
461
  );
@@ -584,7 +584,7 @@ router.get(
584
584
  isAdmin,
585
585
  error_catcher(async (req, res) => {
586
586
  const { name } = req.params;
587
-
587
+ const { step } = req.query;
588
588
  const view = await View.findOne({ name });
589
589
  if (!view) {
590
590
  req.flash("error", `View not found: ${text(name)}`);
@@ -601,6 +601,7 @@ router.get(
601
601
  table_id: view.table_id,
602
602
  exttable_name: view.exttable_name,
603
603
  viewname: name,
604
+ ...(step ? { stepName: step } : {}),
604
605
  },
605
606
  req
606
607
  );