@saltcorn/server 1.0.0-beta.9 → 1.0.0-rc.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.
@@ -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
  }
@@ -388,7 +392,7 @@ function ajax_modal(url, opts = {}) {
388
392
  ? {
389
393
  error: opts.onError,
390
394
  }
391
- : {}),
395
+ : { error: checkNetworkError }),
392
396
  });
393
397
  }
394
398
  function closeModal() {
@@ -451,7 +455,8 @@ function saveAndContinue(e, k, event) {
451
455
  },
452
456
  error: function (request) {
453
457
  var ct = request.getResponseHeader("content-type") || "";
454
- if (ct.startsWith && ct.startsWith("application/json")) {
458
+ if (checkNetworkError(request)) {
459
+ } else if (ct.startsWith && ct.startsWith("application/json")) {
455
460
  notifyAlert({ type: "danger", text: request.responseJSON.error });
456
461
  } else {
457
462
  $("#page-inner-content").html(request.responseText);
@@ -500,6 +505,7 @@ function applyViewConfig(e, url, k, event) {
500
505
  },
501
506
  data: JSON.stringify(cfg),
502
507
  error: function (request) {
508
+ checkNetworkError(request);
503
509
  window.savingViewConfig = false;
504
510
  ajax_indicate_error(e, request);
505
511
  },
@@ -578,6 +584,7 @@ function ajaxSubmitForm(e, force_no_reload) {
578
584
  else common_done(res, form.attr("data-viewname"));
579
585
  },
580
586
  error: function (request) {
587
+ checkNetworkError(request);
581
588
  var title = request.getResponseHeader("Page-Title");
582
589
  if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
583
590
  var body = request.responseText;
@@ -594,6 +601,9 @@ function ajax_post_json(url, data, args = {}) {
594
601
  ...args,
595
602
  });
596
603
  }
604
+
605
+ let scNetworkErrorSignaled = false;
606
+
597
607
  function ajax_post(url, args) {
598
608
  $.ajax(url, {
599
609
  type: "POST",
@@ -603,10 +613,31 @@ function ajax_post(url, args) {
603
613
  ...(args || {}),
604
614
  })
605
615
  .done(ajax_done)
606
- .fail((e) =>
607
- ajax_done(e.responseJSON || { error: "Unknown error: " + e.responseText })
608
- );
616
+ .fail((e, ...more) => {
617
+ if (!checkNetworkError(e))
618
+ return ajax_done(
619
+ e.responseJSON || { error: "Unknown error: " + e.responseText }
620
+ );
621
+ });
609
622
  }
623
+
624
+ function checkNetworkError(e) {
625
+ if (e.readyState == 0 && !e.responseText && !e.responseJSON) {
626
+ //network error
627
+ if (scNetworkErrorSignaled) return true;
628
+ scNetworkErrorSignaled = true;
629
+ setTimeout(() => {
630
+ scNetworkErrorSignaled = false;
631
+ }, 1000);
632
+ console.error("Network error", e);
633
+ notifyAlert({
634
+ type: "danger",
635
+ text: "Network connection error",
636
+ });
637
+ return true;
638
+ }
639
+ }
640
+
610
641
  function ajax_post_btn(e, reload_on_done, reload_delay) {
611
642
  var form = $(e).closest("form");
612
643
  var url = form.attr("action");
@@ -620,6 +651,7 @@ function ajax_post_btn(e, reload_on_done, reload_delay) {
620
651
  success: function () {
621
652
  if (reload_on_done) location.reload();
622
653
  },
654
+ error: checkNetworkError,
623
655
  complete: function () {
624
656
  if (reload_delay)
625
657
  setTimeout(function () {
@@ -641,6 +673,7 @@ function api_action_call(name, body) {
641
673
  success: function (res) {
642
674
  common_done(res.data);
643
675
  },
676
+ error: checkNetworkError,
644
677
  });
645
678
  }
646
679
 
@@ -840,7 +873,7 @@ function build_mobile_app(button) {
840
873
 
841
874
  if (
842
875
  params.useDocker &&
843
- !cordovaBuilderAvailable &&
876
+ !window.cordovaBuilderAvailable &&
844
877
  !confirm(
845
878
  "Docker is selected but the Cordova builder seems not to be installed. " +
846
879
  "Do you really want to continue?"
@@ -848,6 +881,30 @@ function build_mobile_app(button) {
848
881
  ) {
849
882
  return;
850
883
  }
884
+ if (
885
+ isSbadmin2 &&
886
+ !confirm(
887
+ "It seems you are using the standard sbadmin2 layout. " +
888
+ "This layout is not optimized for mobile, consider using any-bootstrap-theme. " +
889
+ "Do you really want to continue?"
890
+ )
891
+ ) {
892
+ return;
893
+ }
894
+ const notSupportedPlugins = params.includedPlugins.filter(
895
+ (plugin) => !window.pluginsReadyForMobile.includes(plugin)
896
+ );
897
+ if (
898
+ notSupportedPlugins.length > 0 &&
899
+ !confirm(
900
+ `It seems that the plugins '${notSupportedPlugins.join(
901
+ ", "
902
+ )}' are not ready for mobile. Do you really want to continue?`
903
+ )
904
+ ) {
905
+ return;
906
+ }
907
+
851
908
  ajax_post("/admin/build-mobile-app", {
852
909
  data: params,
853
910
  success: (data) => {
@@ -919,8 +976,8 @@ function check_cordova_builder() {
919
976
  $.ajax("/admin/mobile-app/check-cordova-builder", {
920
977
  type: "GET",
921
978
  success: function (res) {
922
- cordovaBuilderAvailable = !!res.installed;
923
- if (cordovaBuilderAvailable) {
979
+ window.cordovaBuilderAvailable = !!res.installed;
980
+ if (window.cordovaBuilderAvailable) {
924
981
  $("#dockerBuilderStatusId").html(
925
982
  `<span>
926
983
  installed<i class="ps-2 fas fa-check text-success"></i>
@@ -1014,7 +1071,7 @@ function toggle_tbl_sync() {
1014
1071
  function toggle_android_platform() {
1015
1072
  if ($("#androidCheckboxId")[0].checked === true) {
1016
1073
  $("#dockerCheckboxId").attr("hidden", false);
1017
- $("#dockerCheckboxId").attr("checked", cordovaBuilderAvailable);
1074
+ $("#dockerCheckboxId").attr("checked", window.cordovaBuilderAvailable);
1018
1075
  $("#dockerLabelId").removeClass("d-none");
1019
1076
  } else {
1020
1077
  $("#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"));