@saltcorn/data 1.1.0-beta.8 → 1.1.0

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 (166) hide show
  1. package/dist/base-plugin/actions.d.ts +149 -80
  2. package/dist/base-plugin/actions.d.ts.map +1 -1
  3. package/dist/base-plugin/actions.js +268 -46
  4. package/dist/base-plugin/actions.js.map +1 -1
  5. package/dist/base-plugin/fieldviews.d.ts +0 -1
  6. package/dist/base-plugin/fieldviews.d.ts.map +1 -1
  7. package/dist/base-plugin/index.d.ts +50 -4
  8. package/dist/base-plugin/index.d.ts.map +1 -1
  9. package/dist/base-plugin/index.js +11 -1
  10. package/dist/base-plugin/index.js.map +1 -1
  11. package/dist/base-plugin/types.d.ts.map +1 -1
  12. package/dist/base-plugin/types.js +17 -9
  13. package/dist/base-plugin/types.js.map +1 -1
  14. package/dist/base-plugin/viewtemplates/edit.d.ts +7 -13
  15. package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
  16. package/dist/base-plugin/viewtemplates/edit.js +90 -12
  17. package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
  18. package/dist/base-plugin/viewtemplates/feed.d.ts +0 -8
  19. package/dist/base-plugin/viewtemplates/feed.d.ts.map +1 -1
  20. package/dist/base-plugin/viewtemplates/filter.d.ts +0 -8
  21. package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
  22. package/dist/base-plugin/viewtemplates/list.d.ts +0 -10
  23. package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
  24. package/dist/base-plugin/viewtemplates/room.d.ts +0 -2
  25. package/dist/base-plugin/viewtemplates/room.d.ts.map +1 -1
  26. package/dist/base-plugin/viewtemplates/room.js +7 -2
  27. package/dist/base-plugin/viewtemplates/room.js.map +1 -1
  28. package/dist/base-plugin/viewtemplates/show.d.ts +0 -9
  29. package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
  30. package/dist/base-plugin/viewtemplates/show.js +7 -4
  31. package/dist/base-plugin/viewtemplates/show.js.map +1 -1
  32. package/dist/base-plugin/viewtemplates/viewable_fields.d.ts.map +1 -1
  33. package/dist/base-plugin/viewtemplates/viewable_fields.js +10 -11
  34. package/dist/base-plugin/viewtemplates/viewable_fields.js.map +1 -1
  35. package/dist/base-plugin/viewtemplates/workflow-room.d.ts +59 -0
  36. package/dist/base-plugin/viewtemplates/workflow-room.d.ts.map +1 -0
  37. package/dist/base-plugin/viewtemplates/workflow-room.js +250 -0
  38. package/dist/base-plugin/viewtemplates/workflow-room.js.map +1 -0
  39. package/dist/db/index.js +1 -1
  40. package/dist/db/index.js.map +1 -1
  41. package/dist/db/state.d.ts +16 -0
  42. package/dist/db/state.d.ts.map +1 -1
  43. package/dist/db/state.js +55 -3
  44. package/dist/db/state.js.map +1 -1
  45. package/dist/diagram/cy_raster.d.ts +1 -1
  46. package/dist/diagram/cy_raster.d.ts.map +1 -1
  47. package/dist/diagram/node_extract_utils.d.ts +2 -2
  48. package/dist/diagram/node_extract_utils.d.ts.map +1 -1
  49. package/dist/diagram/nodes/node.d.ts +1 -1
  50. package/dist/diagram/nodes/node.d.ts.map +1 -1
  51. package/dist/migrations/202412051957.d.ts +3 -0
  52. package/dist/migrations/202412051957.d.ts.map +1 -0
  53. package/dist/migrations/202412051957.js +48 -0
  54. package/dist/migrations/202412051957.js.map +1 -0
  55. package/dist/migrations/202412111526.d.ts +3 -0
  56. package/dist/migrations/202412111526.d.ts.map +1 -0
  57. package/dist/migrations/202412111526.js +38 -0
  58. package/dist/migrations/202412111526.js.map +1 -0
  59. package/dist/models/config.d.ts +2 -4
  60. package/dist/models/config.d.ts.map +1 -1
  61. package/dist/models/config.js +44 -34
  62. package/dist/models/config.js.map +1 -1
  63. package/dist/models/crash.d.ts +1 -1
  64. package/dist/models/crash.d.ts.map +1 -1
  65. package/dist/models/email.d.ts +1 -1
  66. package/dist/models/eventlog.d.ts +1 -1
  67. package/dist/models/eventlog.d.ts.map +1 -1
  68. package/dist/models/expression.d.ts.map +1 -1
  69. package/dist/models/expression.js +16 -2
  70. package/dist/models/expression.js.map +1 -1
  71. package/dist/models/field.d.ts.map +1 -1
  72. package/dist/models/field.js +22 -4
  73. package/dist/models/field.js.map +1 -1
  74. package/dist/models/fieldrepeat.d.ts +1 -1
  75. package/dist/models/fieldrepeat.d.ts.map +1 -1
  76. package/dist/models/file.d.ts +7 -3
  77. package/dist/models/file.d.ts.map +1 -1
  78. package/dist/models/file.js +3 -3
  79. package/dist/models/file.js.map +1 -1
  80. package/dist/models/form.d.ts +1 -1
  81. package/dist/models/form.d.ts.map +1 -1
  82. package/dist/models/form.js +5 -1
  83. package/dist/models/form.js.map +1 -1
  84. package/dist/models/index.d.ts +2 -2
  85. package/dist/models/index.d.ts.map +1 -1
  86. package/dist/models/index.js +1 -1
  87. package/dist/models/index.js.map +1 -1
  88. package/dist/models/layout.d.ts +1 -1
  89. package/dist/models/layout.d.ts.map +1 -1
  90. package/dist/models/model_instance.d.ts +1 -0
  91. package/dist/models/model_instance.d.ts.map +1 -1
  92. package/dist/models/notification.d.ts +1 -1
  93. package/dist/models/notification.d.ts.map +1 -1
  94. package/dist/models/page.d.ts.map +1 -1
  95. package/dist/models/page.js +3 -1
  96. package/dist/models/page.js.map +1 -1
  97. package/dist/models/page_group.d.ts +1 -1
  98. package/dist/models/page_group.d.ts.map +1 -1
  99. package/dist/models/page_group.js +7 -1
  100. package/dist/models/page_group.js.map +1 -1
  101. package/dist/models/page_group_member.d.ts +1 -1
  102. package/dist/models/page_group_member.d.ts.map +1 -1
  103. package/dist/models/plugin.d.ts.map +1 -1
  104. package/dist/models/plugin.js +5 -3
  105. package/dist/models/plugin.js.map +1 -1
  106. package/dist/models/scheduler.d.ts.map +1 -1
  107. package/dist/models/scheduler.js +4 -0
  108. package/dist/models/scheduler.js.map +1 -1
  109. package/dist/models/table.d.ts +4 -3
  110. package/dist/models/table.d.ts.map +1 -1
  111. package/dist/models/table.js +34 -3
  112. package/dist/models/table.js.map +1 -1
  113. package/dist/models/table_constraints.d.ts +2 -2
  114. package/dist/models/table_constraints.d.ts.map +1 -1
  115. package/dist/models/tag.d.ts +1 -1
  116. package/dist/models/tag.d.ts.map +1 -1
  117. package/dist/models/tag_entry.d.ts +1 -1
  118. package/dist/models/tag_entry.d.ts.map +1 -1
  119. package/dist/models/trigger.d.ts +5 -1
  120. package/dist/models/trigger.d.ts.map +1 -1
  121. package/dist/models/trigger.js +38 -7
  122. package/dist/models/trigger.js.map +1 -1
  123. package/dist/models/user.d.ts +1 -1
  124. package/dist/models/user.d.ts.map +1 -1
  125. package/dist/models/view.d.ts +1 -1
  126. package/dist/models/view.d.ts.map +1 -1
  127. package/dist/models/view.js +3 -3
  128. package/dist/models/workflow.d.ts +1 -1
  129. package/dist/models/workflow.d.ts.map +1 -1
  130. package/dist/models/workflow_run.d.ts +78 -0
  131. package/dist/models/workflow_run.d.ts.map +1 -0
  132. package/dist/models/workflow_run.js +433 -0
  133. package/dist/models/workflow_run.js.map +1 -0
  134. package/dist/models/workflow_step.d.ts +58 -0
  135. package/dist/models/workflow_step.d.ts.map +1 -0
  136. package/dist/models/workflow_step.js +144 -0
  137. package/dist/models/workflow_step.js.map +1 -0
  138. package/dist/models/workflow_trace.d.ts +57 -0
  139. package/dist/models/workflow_trace.d.ts.map +1 -0
  140. package/dist/models/workflow_trace.js +90 -0
  141. package/dist/models/workflow_trace.js.map +1 -0
  142. package/dist/plugin-helper.d.ts.map +1 -1
  143. package/dist/plugin-helper.js +8 -9
  144. package/dist/plugin-helper.js.map +1 -1
  145. package/dist/tests/actions.test.js +5 -2
  146. package/dist/tests/actions.test.js.map +1 -1
  147. package/dist/tests/auxtest.test.js +6 -3
  148. package/dist/tests/auxtest.test.js.map +1 -1
  149. package/dist/tests/config.test.js +2 -2
  150. package/dist/tests/config.test.js.map +1 -1
  151. package/dist/tests/field.test.js +47 -0
  152. package/dist/tests/field.test.js.map +1 -1
  153. package/dist/tests/mocks.d.ts +1 -1
  154. package/dist/tests/mocks.js +1 -1
  155. package/dist/tests/mocks.js.map +1 -1
  156. package/dist/tests/workflow.test.js +9 -0
  157. package/dist/tests/workflow.test.js.map +1 -1
  158. package/dist/tests/workflow_run.test.d.ts +2 -0
  159. package/dist/tests/workflow_run.test.d.ts.map +1 -0
  160. package/dist/tests/workflow_run.test.js +109 -0
  161. package/dist/tests/workflow_run.test.js.map +1 -0
  162. package/dist/utils.d.ts +9 -1
  163. package/dist/utils.d.ts.map +1 -1
  164. package/dist/utils.js +32 -6
  165. package/dist/utils.js.map +1 -1
  166. package/package.json +11 -9
@@ -20,7 +20,7 @@ const { get_async_expression_function, recalculate_for_stored, eval_expression,
20
20
  const { div, code, a, span } = require("@saltcorn/markup/tags");
21
21
  const { sleep, getSessionId, urlStringToObject, dollarizeObject, objectToQueryString, interpolate, } = require("../utils");
22
22
  const db = require("../db");
23
- const { isNode, isWeb, ppVal } = require("../utils");
23
+ const { isNode, isWeb, ppVal, getFetchProxyOptions } = require("../utils");
24
24
  const { available_languages } = require("../models/config");
25
25
  //action use cases: field modify, like/rate (insert join), notify, send row to webhook
26
26
  // todo add translation
@@ -143,6 +143,7 @@ module.exports = {
143
143
  blocks: {
144
144
  disableInBuilder: true,
145
145
  disableInList: true,
146
+ disableInWorkflow: true,
146
147
  description: "Build action with drag and drop steps similar to Scratch",
147
148
  configFields: [
148
149
  {
@@ -212,34 +213,115 @@ module.exports = {
212
213
  */
213
214
  webhook: {
214
215
  description: "Make an outbound HTTP POST request",
215
- configFields: [
216
- {
217
- name: "url",
218
- label: "URL",
219
- type: "String",
220
- sublabel: "Trigger will call specified URL",
221
- },
222
- {
223
- name: "body",
224
- label: "JSON body",
225
- sublabel: "Leave blank to use row from table",
226
- type: "String",
227
- fieldview: "textarea", // I think that textarea is better
228
- },
229
- ],
216
+ configFields: async ({ table, mode }) => {
217
+ let field_opts = [];
218
+ if (table) {
219
+ field_opts = table.fields
220
+ .filter((f) => f.type && ["String", "HTML", "JSON"].includes(f.type.name))
221
+ .map((f) => f.name);
222
+ }
223
+ return [
224
+ {
225
+ name: "url",
226
+ label: "URL",
227
+ type: "String",
228
+ sublabel: "Trigger will call specified URL",
229
+ },
230
+ {
231
+ name: "method",
232
+ label: "HTTP Method",
233
+ type: "String",
234
+ required: true,
235
+ attributes: { options: "POST,GET,PUT,DELETE,PATCH" },
236
+ },
237
+ {
238
+ name: "body",
239
+ label: "JSON body",
240
+ sublabel: "Leave blank to use row from table",
241
+ type: "String",
242
+ fieldview: "textarea",
243
+ showIf: { method: ["POST", "PUT", "DELETE", "PATCH"] },
244
+ },
245
+ {
246
+ name: "authorization",
247
+ label: "Authorization header",
248
+ type: "String",
249
+ sublabel: "For example <code>Bearer xxxx</code>",
250
+ },
251
+ ...(field_opts.length
252
+ ? [
253
+ {
254
+ name: "response_field",
255
+ label: "Response into field",
256
+ type: "String",
257
+ attributes: { options: field_opts },
258
+ },
259
+ ]
260
+ : []),
261
+ ...(mode === "workflow"
262
+ ? [
263
+ {
264
+ name: "response_var",
265
+ label: "Response variable",
266
+ sublabel: "Variable in the context to fill with the response",
267
+ type: "String",
268
+ },
269
+ ]
270
+ : []),
271
+ ];
272
+ },
230
273
  /**
231
274
  * @param {object} opts
232
275
  * @param {string} opts.url
233
276
  * @param {object} opts.body
234
277
  * @returns {Promise<object>}
235
278
  */
236
- run: async ({ row, user, configuration: { url, body } }) => {
279
+ run: async ({ row, user, table, configuration: { url, body, authorization, response_field, response_var, method, }, }) => {
237
280
  let url1 = interpolate(url, row, user);
238
- return await fetch(url1, {
239
- method: "post",
240
- body: body || JSON.stringify(row),
281
+ const fetchOpts = {
282
+ method: (method || "post").toLowerCase(),
241
283
  headers: { "Content-Type": "application/json" },
242
- });
284
+ ...getFetchProxyOptions(),
285
+ };
286
+ if (method !== "GET") {
287
+ let postBody;
288
+ if (body && table) {
289
+ const f = get_async_expression_function(body, table.fields, {
290
+ row: row || {},
291
+ user,
292
+ });
293
+ postBody = JSON.stringify(await f(row, user));
294
+ }
295
+ else if (body)
296
+ postBody = body;
297
+ else
298
+ postBody = JSON.stringify(row);
299
+ fetchOpts.body = postBody;
300
+ }
301
+ if (authorization)
302
+ fetchOpts.headers.Authorization = interpolate(authorization, row, user);
303
+ const response = await fetch(url1, fetchOpts);
304
+ const contentType = response.headers.get("content-type");
305
+ const isJSON = contentType && contentType.indexOf("application/json") !== -1;
306
+ if (response_var) {
307
+ const parsedResponse = isJSON
308
+ ? await response.json()
309
+ : await response.text();
310
+ return { [response_var]: parsedResponse };
311
+ }
312
+ else if (table && row && response_field) {
313
+ const field = table.getField(response_field);
314
+ const parsedResponse = isJSON
315
+ ? await response.json()
316
+ : await response.text();
317
+ const saveResponse = isJSON &&
318
+ (field?.type?.name === "String" || field?.type?.sql_name === "text")
319
+ ? JSON.stringify(parsedResponse)
320
+ : parsedResponse;
321
+ await table.updateRow({ [response_field]: saveResponse }, row[table.pk_name]);
322
+ }
323
+ else
324
+ return;
243
325
  },
244
326
  },
245
327
  /**
@@ -317,7 +399,50 @@ module.exports = {
317
399
  * @returns {Promise<object[]>}
318
400
  */
319
401
  description: "Send an email, based on a chosen view for this table",
320
- configFields: async ({ table }) => {
402
+ configFields: async ({ table, mode }) => {
403
+ if (mode === "workflow") {
404
+ return [
405
+ {
406
+ name: "to_email",
407
+ label: "To",
408
+ sublabel: "To addresses, comma separated, <code>{{ }}</code> interpolations usable",
409
+ type: "String",
410
+ },
411
+ {
412
+ name: "cc_email",
413
+ label: "cc",
414
+ sublabel: "cc addresses, comma separated, <code>{{ }}</code> interpolations usable",
415
+ type: "String",
416
+ },
417
+ {
418
+ name: "subject",
419
+ label: "Subject",
420
+ sublabel: "Subject of email, <code>{{ }}</code> interpolations usable",
421
+ type: "String",
422
+ required: true,
423
+ },
424
+ {
425
+ name: "body",
426
+ label: "Body",
427
+ type: "String",
428
+ fieldview: "textarea",
429
+ required: true,
430
+ },
431
+ /* {
432
+ name: "attachment_paths",
433
+ label: "Attachments",
434
+ sublabel:
435
+ "Comma-separated list of files to attach. <code>{{ }}</code> interpolations usable",
436
+ type: "String",
437
+ },*/
438
+ {
439
+ name: "confirm_field",
440
+ label: "Send confirmation variable",
441
+ type: "String",
442
+ sublabel: "Bool variable set in context indicate successful sending of email message",
443
+ },
444
+ ];
445
+ }
321
446
  if (!table)
322
447
  return [];
323
448
  const views = await View.find_table_views_where(table, ({ viewtemplate }) => viewtemplate?.runMany || viewtemplate?.renderRows);
@@ -448,7 +573,24 @@ module.exports = {
448
573
  * @param {object} opts.user
449
574
  * @returns {Promise<object>}
450
575
  */
451
- run: async ({ row, table, configuration: { body_type, body_field, viewname, subject, subject_formula, to_email, to_email_field, to_email_fixed, cc_email, only_if, attachment_path, disable_notify, confirm_field, }, user, }) => {
576
+ run: async ({ row, table, configuration: { body_type, body_field, viewname, subject, subject_formula, to_email, to_email_field, to_email_fixed, cc_email, only_if, attachment_path, disable_notify, confirm_field, body, }, user, mode, }) => {
577
+ const from = getState().getConfig("email_from");
578
+ if (mode === "workflow") {
579
+ const email = {
580
+ from,
581
+ to: interpolate(to_email, row, user),
582
+ cc: interpolate(cc_email, row, user),
583
+ subject: interpolate(subject, row, user),
584
+ html: interpolate(body, row, user),
585
+ // attachments,
586
+ };
587
+ const sendres = await getMailTransport().sendMail(email);
588
+ getState().log(5, `send_email result: ${JSON.stringify(sendres)}`);
589
+ if (confirm_field)
590
+ return { [confirm_field]: sendres.accepted.length > 0 };
591
+ else
592
+ return;
593
+ }
452
594
  let to_addr;
453
595
  let useRow = row;
454
596
  const fvs = [
@@ -510,7 +652,6 @@ module.exports = {
510
652
  const view = await View.findOne({ name: viewname });
511
653
  setBody.html = await viewToEmailHtml(view, { id: row[table.pk_name] });
512
654
  }
513
- const from = getState().getConfig("email_from");
514
655
  const attachments = await loadAttachments(attachment_path, row, user ? user : { role_id: 100 });
515
656
  const the_subject = subject_formula
516
657
  ? eval_expression(subject, useRow, user, "send_email subject formula")
@@ -577,6 +718,7 @@ module.exports = {
577
718
  },
578
719
  ];
579
720
  },
721
+ disableInWorkflow: true,
580
722
  requireRow: true,
581
723
  /**
582
724
  * @param {object} opts
@@ -617,6 +759,7 @@ module.exports = {
617
759
  */
618
760
  description: "Duplicate the current row",
619
761
  configFields: () => [],
762
+ disableInWorkflow: true,
620
763
  requireRow: true,
621
764
  /**
622
765
  * @param {object} opts
@@ -709,7 +852,7 @@ module.exports = {
709
852
  * @returns {Promise<object[]>}
710
853
  */
711
854
  description: "insert a row into any table, using a formula expression",
712
- configFields: async ({ table }) => {
855
+ configFields: async ({ mode }) => {
713
856
  const tables = await Table.find({}, { cached: true });
714
857
  return [
715
858
  {
@@ -726,6 +869,16 @@ module.exports = {
726
869
  type: "String",
727
870
  fieldview: "textarea",
728
871
  },
872
+ ...(mode === "workflow"
873
+ ? [
874
+ {
875
+ name: "id_variable",
876
+ label: "ID variable",
877
+ sublabel: "Variable in the context to fill with the created ID value",
878
+ type: "String",
879
+ },
880
+ ]
881
+ : []),
729
882
  ];
730
883
  },
731
884
  /**
@@ -739,7 +892,7 @@ module.exports = {
739
892
  */
740
893
  run: async ({ row, table, configuration, user, referrer, ...rest }) => {
741
894
  const state = urlStringToObject(referrer);
742
- const f = get_async_expression_function(configuration.row_expr, table?.fields || [], {
895
+ const f = get_async_expression_function(configuration.row_expr, table?.fields || Object.keys(row).map((k) => ({ name: k })), {
743
896
  user,
744
897
  console,
745
898
  session_id: rest.req && getSessionId(rest.req),
@@ -750,6 +903,8 @@ module.exports = {
750
903
  const res = await table_for_insert.tryInsertRow(calcrow, user);
751
904
  if (res.error)
752
905
  return res;
906
+ else if (configuration.id_variable)
907
+ return { [configuration.id_variable]: res.success };
753
908
  else
754
909
  return true;
755
910
  },
@@ -761,11 +916,6 @@ module.exports = {
761
916
  * @subcategory actions
762
917
  */
763
918
  modify_row: {
764
- /**
765
- * @param {object} opts
766
- * @param {*} opts.table
767
- * @returns {Promise<object[]>}
768
- */
769
919
  description: "Modify the triggering row",
770
920
  configFields: async ({ mode, when_trigger }) => {
771
921
  return [
@@ -776,7 +926,10 @@ module.exports = {
776
926
  input_type: "code",
777
927
  attributes: { mode: "application/javascript" },
778
928
  },
779
- ...(mode === "edit" || mode === "filter" || when_trigger === "Validate"
929
+ ...(mode === "edit" ||
930
+ mode === "filter" ||
931
+ when_trigger === "Validate" ||
932
+ mode === "workflow"
780
933
  ? [
781
934
  {
782
935
  name: "where",
@@ -788,22 +941,59 @@ module.exports = {
788
941
  ? ["Row"]
789
942
  : mode === "filter"
790
943
  ? ["Filter state"]
791
- : ["Form", "Database"],
944
+ : mode === "workflow"
945
+ ? ["Database", "Active edit view"]
946
+ : ["Form", "Database"],
792
947
  },
793
948
  },
794
949
  ]
795
950
  : []),
951
+ ...(mode === "workflow"
952
+ ? [
953
+ {
954
+ name: "select_table",
955
+ label: "Table",
956
+ type: "String",
957
+ required: true,
958
+ attributes: {
959
+ options: (await Table.find()).map((t) => t.name),
960
+ showIf: { where: "Database" },
961
+ },
962
+ },
963
+ {
964
+ name: "query",
965
+ label: "Query object",
966
+ type: "String",
967
+ required: true,
968
+ showIf: { where: "Database" },
969
+ },
970
+ ]
971
+ : []),
796
972
  ];
797
973
  },
798
974
  requireRow: true,
799
- run: async ({ row, table, configuration: { row_expr, where }, user, ...rest }) => {
800
- const f = get_async_expression_function(row_expr, table.fields, {
975
+ run: async ({ row, table, configuration: { row_expr, where, select_table, query }, user, ...rest }) => {
976
+ const f = get_async_expression_function(row_expr, table?.fields || Object.keys(row).map((k) => ({ name: k })), {
801
977
  row: row || {},
802
978
  user,
803
979
  });
804
980
  const calcrow = await f(row, user);
805
- if (where === "Form" || where === "Filter state" || where === "Row")
981
+ if (where === "Form" ||
982
+ where === "Filter state" ||
983
+ where === "Row" ||
984
+ where === "Active edit view")
806
985
  return { set_fields: calcrow };
986
+ if (select_table && query) {
987
+ //get table
988
+ const table = Table.findOne(select_table);
989
+ // evaluate query
990
+ const q = eval_expression(query, row, user, "Query expression in modify_row step");
991
+ const rows = await table.getRows(q);
992
+ for (const row of rows) {
993
+ await table.updateRow(calcrow, row[table.pk_name]);
994
+ }
995
+ return;
996
+ }
807
997
  const res = await table.tryUpdateRow(calcrow, row[table.pk_name], user);
808
998
  if (res.error)
809
999
  return res;
@@ -822,11 +1012,15 @@ module.exports = {
822
1012
  configFields: async ({ mode, when_trigger }) => {
823
1013
  const tables = await Table.find({}, { cached: true });
824
1014
  return [
825
- {
826
- name: "delete_triggering_row",
827
- label: "Delete triggering row",
828
- type: "Bool",
829
- },
1015
+ ...(mode === "workflow"
1016
+ ? []
1017
+ : [
1018
+ {
1019
+ name: "delete_triggering_row",
1020
+ label: "Delete triggering row",
1021
+ type: "Bool",
1022
+ },
1023
+ ]),
830
1024
  {
831
1025
  name: "table_name",
832
1026
  label: "Table",
@@ -905,7 +1099,11 @@ module.exports = {
905
1099
  case "Popup modal":
906
1100
  return { popup: url1 };
907
1101
  case "Back":
908
- return { eval_js: isWeb(req) ? "history.back()" : "parent.goBack()" };
1102
+ return {
1103
+ eval_js: isWeb(req)
1104
+ ? "history.back()"
1105
+ : "parent.saltcorn.mobileApp.navigation.goBack()",
1106
+ };
909
1107
  case "Close tab":
910
1108
  return { eval_js: "window.close()" };
911
1109
  case "Close modal":
@@ -925,6 +1123,7 @@ module.exports = {
925
1123
  * @returns {Promise<object[]>}
926
1124
  */
927
1125
  description: "Step control flow",
1126
+ disableInWorkflow: true,
928
1127
  configFields: [
929
1128
  {
930
1129
  name: "control_action",
@@ -1115,6 +1314,7 @@ module.exports = {
1115
1314
  topic: "JavaScript action code",
1116
1315
  },
1117
1316
  showIf: { run_where: "Server" },
1317
+ attributes: { secondColHoriz: true },
1118
1318
  },
1119
1319
  {
1120
1320
  input_type: "section_header",
@@ -1124,6 +1324,7 @@ module.exports = {
1124
1324
  topic: "JavaScript action code",
1125
1325
  },
1126
1326
  showIf: { run_where: "Client page" },
1327
+ attributes: { secondColHoriz: true },
1127
1328
  },
1128
1329
  {
1129
1330
  name: "run_where",
@@ -1147,7 +1348,23 @@ module.exports = {
1147
1348
  * @returns {Promise<object[]>}
1148
1349
  */
1149
1350
  description: "Run arbitrary JavaScript code from a String field",
1150
- configFields: async ({ table }) => {
1351
+ configFields: async ({ table, mode }) => {
1352
+ if (mode === "workflow")
1353
+ return [
1354
+ {
1355
+ name: "code_field",
1356
+ label: "Code field",
1357
+ sublabel: "String variable in context contains the JavaScript code to run",
1358
+ type: "String",
1359
+ required: true,
1360
+ },
1361
+ {
1362
+ name: "run_where",
1363
+ label: "Run where",
1364
+ input_type: "select",
1365
+ options: ["Server", "Client page"],
1366
+ },
1367
+ ];
1151
1368
  const field_opts = table.fields
1152
1369
  .filter((f) => f.type?.name === "String")
1153
1370
  .map((f) => f.name);
@@ -1185,7 +1402,7 @@ module.exports = {
1185
1402
  * @type {base-plugin/actions~run_code}
1186
1403
  * @see base-plugin/actions~run_code
1187
1404
  **/
1188
- run: async ({ table, configuration: { code_field, run_where }, row, ...rest }) => {
1405
+ run: async ({ table, configuration: { code_field, run_where }, row, mode, ...rest }) => {
1189
1406
  let code;
1190
1407
  if (code_field.includes(".")) {
1191
1408
  const [ref, target] = code_field.split(".");
@@ -1232,6 +1449,7 @@ module.exports = {
1232
1449
  ...fldOpts,
1233
1450
  ];
1234
1451
  },
1452
+ disableInWorkflow: true,
1235
1453
  requireRow: true,
1236
1454
  run: async ({ row, table, configuration: { viewname, ...flds }, user }) => {
1237
1455
  const qs = Object.entries(flds)
@@ -1310,14 +1528,18 @@ module.exports = {
1310
1528
  label: "Source table",
1311
1529
  sublabel: "External table to sync from",
1312
1530
  input_type: "select",
1313
- options: tables.filter((t) => t.external || t.provider_name).map((t) => t.name),
1531
+ options: tables
1532
+ .filter((t) => t.external || t.provider_name)
1533
+ .map((t) => t.name),
1314
1534
  },
1315
1535
  {
1316
1536
  name: "table_dest",
1317
1537
  label: "Destination table",
1318
1538
  sublabel: "Table to sync to",
1319
1539
  input_type: "select",
1320
- options: tables.filter((t) => !(t.external || t.provider_name)).map((t) => t.name),
1540
+ options: tables
1541
+ .filter((t) => !(t.external || t.provider_name))
1542
+ .map((t) => t.name),
1321
1543
  },
1322
1544
  {
1323
1545
  name: "pk_field",