@saltcorn/server 1.0.0-beta.9 → 1.0.0-rc.2

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.
@@ -190,7 +190,8 @@ function pjax_to(href, e) {
190
190
  initialize_page();
191
191
  },
192
192
  error: function (res) {
193
- notifyAlert({ type: "danger", text: res.responseText });
193
+ if (!checkNetworkError(res))
194
+ notifyAlert({ type: "danger", text: res.responseText });
194
195
  },
195
196
  });
196
197
  }
@@ -224,8 +225,10 @@ function ajax_done(res, viewname) {
224
225
  function spin_action_link(e) {
225
226
  const $e = $(e);
226
227
  const width = $e.width();
228
+ const height = $e.height();
229
+
227
230
  $e.attr("data-innerhtml-prespin", $e.html());
228
- $e.html('<i class="fas fa-spinner fa-spin"></i>').width(width);
231
+ $e.html('<i class="fas fa-spinner fa-spin"></i>').width(width).height(height);
229
232
  }
230
233
 
231
234
  function reset_spinners() {
@@ -264,7 +267,8 @@ function view_post(viewnameOrElem, route, data, onDone, sendState) {
264
267
  reset_spinners();
265
268
  })
266
269
  .fail(function (res) {
267
- notifyAlert({ type: "danger", text: res.responseText });
270
+ if (!checkNetworkError(res))
271
+ notifyAlert({ type: "danger", text: res.responseText });
268
272
  reset_spinners();
269
273
  });
270
274
  }
@@ -346,16 +350,18 @@ function expand_thumbnail(img_id, filename) {
346
350
  }
347
351
 
348
352
  function ajax_modal(url, opts = {}) {
349
- ensure_modal_exists_and_closed();
350
- $("#scmodal").removeClass("no-submit-reload");
351
- $("#scmodal").attr("data-on-close-reload-view", opts.reload_view || null);
352
-
353
- if (opts.submitReload === false) $("#scmodal").addClass("no-submit-reload");
354
353
  $.ajax(url, {
355
354
  headers: {
356
355
  SaltcornModalRequest: "true",
357
356
  },
358
357
  success: function (res, textStatus, request) {
358
+ ensure_modal_exists_and_closed();
359
+ $("#scmodal").removeClass("no-submit-reload");
360
+ $("#scmodal").attr("data-on-close-reload-view", opts.reload_view || null);
361
+
362
+ if (opts.submitReload === false)
363
+ $("#scmodal").addClass("no-submit-reload");
364
+
359
365
  var title = request.getResponseHeader("Page-Title");
360
366
  var width = request.getResponseHeader("SaltcornModalWidth");
361
367
  var minwidth = request.getResponseHeader("SaltcornModalMinWidth");
@@ -388,7 +394,7 @@ function ajax_modal(url, opts = {}) {
388
394
  ? {
389
395
  error: opts.onError,
390
396
  }
391
- : {}),
397
+ : { error: checkNetworkError }),
392
398
  });
393
399
  }
394
400
  function closeModal() {
@@ -451,7 +457,8 @@ function saveAndContinue(e, k, event) {
451
457
  },
452
458
  error: function (request) {
453
459
  var ct = request.getResponseHeader("content-type") || "";
454
- if (ct.startsWith && ct.startsWith("application/json")) {
460
+ if (checkNetworkError(request)) {
461
+ } else if (ct.startsWith && ct.startsWith("application/json")) {
455
462
  notifyAlert({ type: "danger", text: request.responseJSON.error });
456
463
  } else {
457
464
  $("#page-inner-content").html(request.responseText);
@@ -500,6 +507,7 @@ function applyViewConfig(e, url, k, event) {
500
507
  },
501
508
  data: JSON.stringify(cfg),
502
509
  error: function (request) {
510
+ checkNetworkError(request);
503
511
  window.savingViewConfig = false;
504
512
  ajax_indicate_error(e, request);
505
513
  },
@@ -578,6 +586,7 @@ function ajaxSubmitForm(e, force_no_reload) {
578
586
  else common_done(res, form.attr("data-viewname"));
579
587
  },
580
588
  error: function (request) {
589
+ checkNetworkError(request);
581
590
  var title = request.getResponseHeader("Page-Title");
582
591
  if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
583
592
  var body = request.responseText;
@@ -594,6 +603,9 @@ function ajax_post_json(url, data, args = {}) {
594
603
  ...args,
595
604
  });
596
605
  }
606
+
607
+ let scNetworkErrorSignaled = false;
608
+
597
609
  function ajax_post(url, args) {
598
610
  $.ajax(url, {
599
611
  type: "POST",
@@ -603,10 +615,31 @@ function ajax_post(url, args) {
603
615
  ...(args || {}),
604
616
  })
605
617
  .done(ajax_done)
606
- .fail((e) =>
607
- ajax_done(e.responseJSON || { error: "Unknown error: " + e.responseText })
608
- );
618
+ .fail((e, ...more) => {
619
+ if (!checkNetworkError(e))
620
+ return ajax_done(
621
+ e.responseJSON || { error: "Unknown error: " + e.responseText }
622
+ );
623
+ });
624
+ }
625
+
626
+ function checkNetworkError(e) {
627
+ if (e.readyState == 0 && !e.responseText && !e.responseJSON) {
628
+ //network error
629
+ if (scNetworkErrorSignaled) return true;
630
+ scNetworkErrorSignaled = true;
631
+ setTimeout(() => {
632
+ scNetworkErrorSignaled = false;
633
+ }, 1000);
634
+ console.error("Network error", e);
635
+ notifyAlert({
636
+ type: "danger",
637
+ text: "Network connection error",
638
+ });
639
+ return true;
640
+ }
609
641
  }
642
+
610
643
  function ajax_post_btn(e, reload_on_done, reload_delay) {
611
644
  var form = $(e).closest("form");
612
645
  var url = form.attr("action");
@@ -620,6 +653,7 @@ function ajax_post_btn(e, reload_on_done, reload_delay) {
620
653
  success: function () {
621
654
  if (reload_on_done) location.reload();
622
655
  },
656
+ error: checkNetworkError,
623
657
  complete: function () {
624
658
  if (reload_delay)
625
659
  setTimeout(function () {
@@ -641,6 +675,7 @@ function api_action_call(name, body) {
641
675
  success: function (res) {
642
676
  common_done(res.data);
643
677
  },
678
+ error: checkNetworkError,
644
679
  });
645
680
  }
646
681
 
@@ -840,7 +875,7 @@ function build_mobile_app(button) {
840
875
 
841
876
  if (
842
877
  params.useDocker &&
843
- !cordovaBuilderAvailable &&
878
+ !window.cordovaBuilderAvailable &&
844
879
  !confirm(
845
880
  "Docker is selected but the Cordova builder seems not to be installed. " +
846
881
  "Do you really want to continue?"
@@ -848,6 +883,30 @@ function build_mobile_app(button) {
848
883
  ) {
849
884
  return;
850
885
  }
886
+ if (
887
+ isSbadmin2 &&
888
+ !confirm(
889
+ "It seems you are using the standard sbadmin2 layout. " +
890
+ "This layout is not optimized for mobile, consider using any-bootstrap-theme. " +
891
+ "Do you really want to continue?"
892
+ )
893
+ ) {
894
+ return;
895
+ }
896
+ const notSupportedPlugins = params.includedPlugins.filter(
897
+ (plugin) => !window.pluginsReadyForMobile.includes(plugin)
898
+ );
899
+ if (
900
+ notSupportedPlugins.length > 0 &&
901
+ !confirm(
902
+ `It seems that the plugins '${notSupportedPlugins.join(
903
+ ", "
904
+ )}' are not ready for mobile. Do you really want to continue?`
905
+ )
906
+ ) {
907
+ return;
908
+ }
909
+
851
910
  ajax_post("/admin/build-mobile-app", {
852
911
  data: params,
853
912
  success: (data) => {
@@ -919,8 +978,8 @@ function check_cordova_builder() {
919
978
  $.ajax("/admin/mobile-app/check-cordova-builder", {
920
979
  type: "GET",
921
980
  success: function (res) {
922
- cordovaBuilderAvailable = !!res.installed;
923
- if (cordovaBuilderAvailable) {
981
+ window.cordovaBuilderAvailable = !!res.installed;
982
+ if (window.cordovaBuilderAvailable) {
924
983
  $("#dockerBuilderStatusId").html(
925
984
  `<span>
926
985
  installed<i class="ps-2 fas fa-check text-success"></i>
@@ -1014,7 +1073,7 @@ function toggle_tbl_sync() {
1014
1073
  function toggle_android_platform() {
1015
1074
  if ($("#androidCheckboxId")[0].checked === true) {
1016
1075
  $("#dockerCheckboxId").attr("hidden", false);
1017
- $("#dockerCheckboxId").attr("checked", cordovaBuilderAvailable);
1076
+ $("#dockerCheckboxId").attr("checked", window.cordovaBuilderAvailable);
1018
1077
  $("#dockerLabelId").removeClass("d-none");
1019
1078
  } else {
1020
1079
  $("#dockerCheckboxId").attr("hidden", true);
package/routes/actions.js CHANGED
@@ -57,21 +57,6 @@ const {
57
57
  blocklyToolbox,
58
58
  } = require("../markup/blockly.js");
59
59
 
60
- /**
61
- * @returns {Promise<object>}
62
- */
63
- const getActions = async () => {
64
- return Object.entries(getState().actions).map(([k, v]) => {
65
- const hasConfig = !!v.configFields;
66
- const requireRow = !!v.requireRow;
67
- return {
68
- name: k,
69
- hasConfig,
70
- requireRow,
71
- };
72
- });
73
- };
74
-
75
60
  /**
76
61
  * Show list of Actions (Triggers) (HTTP GET)
77
62
  * @name get
@@ -96,7 +81,7 @@ router.get(
96
81
  triggers = triggers.filter((t) => tagged_trigger_ids.has(t.id));
97
82
  filterOnTag = await Tag.findOne({ id: +req.query._tag });
98
83
  }
99
- const actions = await getActions();
84
+ const actions = Trigger.abbreviated_actions;
100
85
  send_events_page({
101
86
  res,
102
87
  req,
@@ -156,7 +141,7 @@ const triggerForm = async (req, trigger) => {
156
141
  value: r.id,
157
142
  label: r.role,
158
143
  }));
159
- const actions = await getActions();
144
+ const actions = Trigger.abbreviated_actions;
160
145
  const tables = await Table.find({});
161
146
  let id;
162
147
  let form_action;
@@ -168,14 +153,13 @@ const triggerForm = async (req, trigger) => {
168
153
  const hasChannel = Object.entries(getState().eventTypes)
169
154
  .filter(([k, v]) => v.hasChannel)
170
155
  .map(([k, v]) => k);
171
- const allActions = actions.map((t) => t.name);
172
- allActions.push("Multi-step action");
156
+
157
+ const allActions = Trigger.action_options({ notRequireRow: false });
173
158
  const table_triggers = ["Insert", "Update", "Delete", "Validate"];
174
159
  const action_options = {};
175
- const actionsNotRequiringRow = actions
176
- .filter((a) => !a.requireRow)
177
- .map((t) => t.name);
178
- actionsNotRequiringRow.push("Multi-step action");
160
+ const actionsNotRequiringRow = Trigger.action_options({
161
+ notRequireRow: true,
162
+ });
179
163
 
180
164
  Trigger.when_options.forEach((t) => {
181
165
  if (table_triggers.includes(t)) action_options[t] = allActions;
@@ -237,6 +221,13 @@ const triggerForm = async (req, trigger) => {
237
221
  showIf: { when_trigger: "Daily" },
238
222
  sublabel: req.__("UTC timezone"),
239
223
  },
224
+ {
225
+ name: "channel",
226
+ label: req.__("Time to run"),
227
+ input_type: "time_of_week",
228
+ showIf: { when_trigger: "Weekly" },
229
+ sublabel: req.__("UTC timezone"),
230
+ },
240
231
  {
241
232
  name: "channel",
242
233
  label: req.__("Channel"),
@@ -555,7 +546,10 @@ router.get(
555
546
  let trigger;
556
547
  let id = parseInt(idorname);
557
548
  if (id) trigger = await Trigger.findOne({ id });
558
- else trigger = await Trigger.findOne({ name: idorname });
549
+ else {
550
+ trigger = await Trigger.findOne({ name: idorname });
551
+ id = trigger.id;
552
+ }
559
553
 
560
554
  if (!trigger) {
561
555
  req.flash("warning", req.__("Action not found"));
@@ -761,7 +755,7 @@ router.post(
761
755
  req.flash("success", req.__("Action configuration saved"));
762
756
  res.redirect(
763
757
  req.query.on_done_redirect &&
764
- is_relative_url(req.query.on_done_redirect)
758
+ is_relative_url("/" + req.query.on_done_redirect)
765
759
  ? `/${req.query.on_done_redirect}`
766
760
  : "/actions/"
767
761
  );