@saltcorn/data 0.7.1-beta.3 → 0.7.2-beta.10

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 (261) hide show
  1. package/dist/base-plugin/actions.d.ts.map +1 -1
  2. package/dist/base-plugin/actions.js +5 -1
  3. package/dist/base-plugin/actions.js.map +1 -1
  4. package/dist/base-plugin/fieldviews.d.ts +29 -0
  5. package/dist/base-plugin/fieldviews.d.ts.map +1 -1
  6. package/dist/base-plugin/fieldviews.js +60 -5
  7. package/dist/base-plugin/fieldviews.js.map +1 -1
  8. package/dist/base-plugin/fileviews.d.ts.map +1 -1
  9. package/dist/base-plugin/fileviews.js +20 -5
  10. package/dist/base-plugin/fileviews.js.map +1 -1
  11. package/dist/base-plugin/index.d.ts +309 -84
  12. package/dist/base-plugin/index.d.ts.map +1 -1
  13. package/dist/base-plugin/types.d.ts +127 -65
  14. package/dist/base-plugin/types.d.ts.map +1 -1
  15. package/dist/base-plugin/types.js +152 -36
  16. package/dist/base-plugin/types.js.map +1 -1
  17. package/dist/base-plugin/viewtemplates/edit.d.ts +47 -9
  18. package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
  19. package/dist/base-plugin/viewtemplates/edit.js +266 -113
  20. package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
  21. package/dist/base-plugin/viewtemplates/feed.d.ts +14 -1
  22. package/dist/base-plugin/viewtemplates/feed.d.ts.map +1 -1
  23. package/dist/base-plugin/viewtemplates/feed.js +20 -8
  24. package/dist/base-plugin/viewtemplates/feed.js.map +1 -1
  25. package/dist/base-plugin/viewtemplates/filter.d.ts +17 -1
  26. package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
  27. package/dist/base-plugin/viewtemplates/filter.js +66 -45
  28. package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
  29. package/dist/base-plugin/viewtemplates/list.d.ts +21 -1
  30. package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
  31. package/dist/base-plugin/viewtemplates/list.js +63 -40
  32. package/dist/base-plugin/viewtemplates/list.js.map +1 -1
  33. package/dist/base-plugin/viewtemplates/listshowlist.d.ts +14 -1
  34. package/dist/base-plugin/viewtemplates/listshowlist.d.ts.map +1 -1
  35. package/dist/base-plugin/viewtemplates/listshowlist.js +15 -4
  36. package/dist/base-plugin/viewtemplates/listshowlist.js.map +1 -1
  37. package/dist/base-plugin/viewtemplates/room.d.ts +41 -1
  38. package/dist/base-plugin/viewtemplates/room.d.ts.map +1 -1
  39. package/dist/base-plugin/viewtemplates/room.js +107 -80
  40. package/dist/base-plugin/viewtemplates/room.js.map +1 -1
  41. package/dist/base-plugin/viewtemplates/show.d.ts +38 -4
  42. package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
  43. package/dist/base-plugin/viewtemplates/show.js +125 -76
  44. package/dist/base-plugin/viewtemplates/show.js.map +1 -1
  45. package/dist/base-plugin/viewtemplates/viewable_fields.d.ts +26 -7
  46. package/dist/base-plugin/viewtemplates/viewable_fields.d.ts.map +1 -1
  47. package/dist/base-plugin/viewtemplates/viewable_fields.js +60 -48
  48. package/dist/base-plugin/viewtemplates/viewable_fields.js.map +1 -1
  49. package/dist/db/connect.d.ts.map +1 -1
  50. package/dist/db/connect.js +8 -1
  51. package/dist/db/connect.js.map +1 -1
  52. package/dist/db/connect_mobile.d.ts +9 -0
  53. package/dist/db/connect_mobile.d.ts.map +1 -0
  54. package/dist/db/connect_mobile.js +15 -0
  55. package/dist/db/connect_mobile.js.map +1 -0
  56. package/dist/db/fixtures.d.ts.map +1 -1
  57. package/dist/db/fixtures.js +6 -1
  58. package/dist/db/fixtures.js.map +1 -1
  59. package/dist/db/index.d.ts.map +1 -1
  60. package/dist/db/index.js +16 -2
  61. package/dist/db/index.js.map +1 -1
  62. package/dist/db/state.d.ts +214 -52
  63. package/dist/db/state.d.ts.map +1 -1
  64. package/dist/db/state.js +115 -104
  65. package/dist/db/state.js.map +1 -1
  66. package/dist/index.d.ts +8 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +32 -3
  69. package/dist/index.js.map +1 -1
  70. package/dist/migrate.d.ts.map +1 -1
  71. package/dist/migrate.js +54 -40
  72. package/dist/migrate.js.map +1 -1
  73. package/dist/mobile-mocks/db/postgres.d.ts +1 -0
  74. package/dist/mobile-mocks/db/postgres.d.ts.map +1 -0
  75. package/dist/mobile-mocks/db/postgres.js +2 -0
  76. package/dist/mobile-mocks/db/postgres.js.map +1 -0
  77. package/dist/mobile-mocks/db/sqlite.d.ts +1 -0
  78. package/dist/mobile-mocks/db/sqlite.d.ts.map +1 -0
  79. package/dist/mobile-mocks/db/sqlite.js +2 -0
  80. package/dist/mobile-mocks/db/sqlite.js.map +1 -0
  81. package/dist/mobile-mocks/models/email.d.ts +1 -0
  82. package/dist/mobile-mocks/models/email.d.ts.map +1 -0
  83. package/dist/mobile-mocks/models/email.js +2 -0
  84. package/dist/mobile-mocks/models/email.js.map +1 -0
  85. package/dist/mobile-mocks/node/async_hooks.d.ts +4 -0
  86. package/dist/mobile-mocks/node/async_hooks.d.ts.map +1 -0
  87. package/dist/mobile-mocks/node/async_hooks.js +8 -0
  88. package/dist/mobile-mocks/node/async_hooks.js.map +1 -0
  89. package/dist/mobile-mocks/node/child_process.d.ts +3 -0
  90. package/dist/mobile-mocks/node/child_process.d.ts.map +1 -0
  91. package/dist/mobile-mocks/node/child_process.js +6 -0
  92. package/dist/mobile-mocks/node/child_process.js.map +1 -0
  93. package/dist/mobile-mocks/node/fs/promises.d.ts +6 -0
  94. package/dist/mobile-mocks/node/fs/promises.d.ts.map +1 -0
  95. package/dist/mobile-mocks/node/fs/promises.js +16 -0
  96. package/dist/mobile-mocks/node/fs/promises.js.map +1 -0
  97. package/dist/mobile-mocks/node/fs.d.ts +4 -0
  98. package/dist/mobile-mocks/node/fs.d.ts.map +1 -0
  99. package/dist/mobile-mocks/node/fs.js +15 -0
  100. package/dist/mobile-mocks/node/fs.js.map +1 -0
  101. package/dist/mobile-mocks/node/latest-version.d.ts +2 -0
  102. package/dist/mobile-mocks/node/latest-version.d.ts.map +1 -0
  103. package/dist/mobile-mocks/node/latest-version.js +7 -0
  104. package/dist/mobile-mocks/node/latest-version.js.map +1 -0
  105. package/dist/mobile-mocks/node/v8.d.ts +3 -0
  106. package/dist/mobile-mocks/node/v8.d.ts.map +1 -0
  107. package/dist/mobile-mocks/node/v8.js +8 -0
  108. package/dist/mobile-mocks/node/v8.js.map +1 -0
  109. package/dist/mobile-mocks/npm/env-paths.d.ts +6 -0
  110. package/dist/mobile-mocks/npm/env-paths.d.ts.map +1 -0
  111. package/dist/mobile-mocks/npm/env-paths.js +9 -0
  112. package/dist/mobile-mocks/npm/env-paths.js.map +1 -0
  113. package/dist/mobile-mocks/saltcorn/plugin-testing.d.ts +1 -0
  114. package/dist/mobile-mocks/saltcorn/plugin-testing.d.ts.map +1 -0
  115. package/dist/mobile-mocks/saltcorn/plugin-testing.js +2 -0
  116. package/dist/mobile-mocks/saltcorn/plugin-testing.js.map +1 -0
  117. package/dist/models/config.d.ts +9 -10
  118. package/dist/models/config.d.ts.map +1 -1
  119. package/dist/models/config.js +35 -12
  120. package/dist/models/config.js.map +1 -1
  121. package/dist/models/crash.js +5 -1
  122. package/dist/models/crash.js.map +1 -1
  123. package/dist/models/discovery.d.ts.map +1 -1
  124. package/dist/models/discovery.js +5 -1
  125. package/dist/models/discovery.js.map +1 -1
  126. package/dist/models/email.d.ts +2 -0
  127. package/dist/models/email.d.ts.map +1 -1
  128. package/dist/models/email.js +2 -2
  129. package/dist/models/email.js.map +1 -1
  130. package/dist/models/field.d.ts +6 -4
  131. package/dist/models/field.d.ts.map +1 -1
  132. package/dist/models/field.js +41 -21
  133. package/dist/models/field.js.map +1 -1
  134. package/dist/models/fieldrepeat.d.ts +5 -0
  135. package/dist/models/fieldrepeat.d.ts.map +1 -1
  136. package/dist/models/fieldrepeat.js +2 -0
  137. package/dist/models/fieldrepeat.js.map +1 -1
  138. package/dist/models/file.d.ts +6 -0
  139. package/dist/models/file.d.ts.map +1 -1
  140. package/dist/models/file.js +25 -1
  141. package/dist/models/file.js.map +1 -1
  142. package/dist/models/form.d.ts +2 -0
  143. package/dist/models/form.d.ts.map +1 -1
  144. package/dist/models/form.js +1 -0
  145. package/dist/models/form.js.map +1 -1
  146. package/dist/models/index.d.ts +2 -13
  147. package/dist/models/index.d.ts.map +1 -1
  148. package/dist/models/layout.d.ts.map +1 -1
  149. package/dist/models/layout.js +12 -2
  150. package/dist/models/layout.js.map +1 -1
  151. package/dist/models/page.d.ts +1 -1
  152. package/dist/models/page.d.ts.map +1 -1
  153. package/dist/models/page.js +17 -9
  154. package/dist/models/page.js.map +1 -1
  155. package/dist/models/plugin.d.ts +1 -1
  156. package/dist/models/plugin.d.ts.map +1 -1
  157. package/dist/models/plugin.js +10 -6
  158. package/dist/models/plugin.js.map +1 -1
  159. package/dist/models/scheduler.d.ts.map +1 -1
  160. package/dist/models/scheduler.js +3 -1
  161. package/dist/models/scheduler.js.map +1 -1
  162. package/dist/models/table.d.ts +2 -1
  163. package/dist/models/table.d.ts.map +1 -1
  164. package/dist/models/table.js +48 -12
  165. package/dist/models/table.js.map +1 -1
  166. package/dist/models/trigger.d.ts +3 -3
  167. package/dist/models/trigger.d.ts.map +1 -1
  168. package/dist/models/trigger.js +6 -5
  169. package/dist/models/trigger.js.map +1 -1
  170. package/dist/models/user.d.ts +1 -1
  171. package/dist/models/user.d.ts.map +1 -1
  172. package/dist/models/user.js +7 -15
  173. package/dist/models/user.js.map +1 -1
  174. package/dist/models/view.d.ts +25 -14
  175. package/dist/models/view.d.ts.map +1 -1
  176. package/dist/models/view.js +84 -25
  177. package/dist/models/view.js.map +1 -1
  178. package/dist/models/workflow.d.ts +1 -0
  179. package/dist/models/workflow.d.ts.map +1 -1
  180. package/dist/models/workflow.js +58 -2
  181. package/dist/models/workflow.js.map +1 -1
  182. package/dist/package.json +127 -0
  183. package/dist/plugin-helper.d.ts +19 -14
  184. package/dist/plugin-helper.d.ts.map +1 -1
  185. package/dist/plugin-helper.js +158 -80
  186. package/dist/plugin-helper.js.map +1 -1
  187. package/dist/plugin-testing.d.ts +1 -0
  188. package/dist/plugin-testing.d.ts.map +1 -1
  189. package/dist/plugin-testing.js +84 -1
  190. package/dist/plugin-testing.js.map +1 -1
  191. package/dist/tests/actions.test.js +30 -5
  192. package/dist/tests/actions.test.js.map +1 -1
  193. package/dist/tests/auxtest.test.js +100 -3
  194. package/dist/tests/auxtest.test.js.map +1 -1
  195. package/dist/tests/db.test.d.ts +2 -0
  196. package/dist/tests/db.test.d.ts.map +1 -0
  197. package/dist/tests/db.test.js +32 -0
  198. package/dist/tests/db.test.js.map +1 -0
  199. package/dist/tests/exact_views.test.js +277 -22
  200. package/dist/tests/exact_views.test.js.map +1 -1
  201. package/dist/tests/field.test.js +36 -0
  202. package/dist/tests/field.test.js.map +1 -1
  203. package/dist/tests/mocks.d.ts +2 -0
  204. package/dist/tests/mocks.d.ts.map +1 -1
  205. package/dist/tests/mocks.js +2 -0
  206. package/dist/tests/mocks.js.map +1 -1
  207. package/dist/tests/models.test.js +46 -1
  208. package/dist/tests/models.test.js.map +1 -1
  209. package/dist/tests/remote_query_helper.d.ts +341 -0
  210. package/dist/tests/remote_query_helper.d.ts.map +1 -0
  211. package/dist/tests/remote_query_helper.js +446 -0
  212. package/dist/tests/remote_query_helper.js.map +1 -0
  213. package/dist/tests/state.test.d.ts +2 -0
  214. package/dist/tests/state.test.d.ts.map +1 -0
  215. package/dist/tests/state.test.js +78 -0
  216. package/dist/tests/state.test.js.map +1 -0
  217. package/dist/tests/table.test.js +19 -0
  218. package/dist/tests/table.test.js.map +1 -1
  219. package/dist/tests/view.test.js +29 -0
  220. package/dist/tests/view.test.js.map +1 -1
  221. package/dist/tsconfig.json +24 -0
  222. package/dist/tsconfig.ref.json +9 -0
  223. package/dist/tsconfig.ref.tsbuildinfo +1 -1
  224. package/dist/utils.d.ts +3 -1
  225. package/dist/utils.d.ts.map +1 -1
  226. package/dist/utils.js +14 -23
  227. package/dist/utils.js.map +1 -1
  228. package/dist/webpack.config.d.ts +57 -0
  229. package/dist/webpack.config.d.ts.map +1 -0
  230. package/dist/webpack.config.js +75 -0
  231. package/dist/webpack.config.js.map +1 -0
  232. package/package.json +39 -15
  233. package/CHANGELOG.md +0 -8
  234. package/dist/models/backup.d.ts +0 -9
  235. package/dist/models/backup.d.ts.map +0 -1
  236. package/dist/models/backup.js +0 -278
  237. package/dist/models/backup.js.map +0 -1
  238. package/dist/models/pack.d.ts +0 -42
  239. package/dist/models/pack.d.ts.map +0 -1
  240. package/dist/models/pack.js +0 -419
  241. package/dist/models/pack.js.map +0 -1
  242. package/dist/models/tenant.d.ts +0 -17
  243. package/dist/models/tenant.d.ts.map +0 -1
  244. package/dist/models/tenant.js +0 -152
  245. package/dist/models/tenant.js.map +0 -1
  246. package/dist/tests/backup.test.d.ts +0 -2
  247. package/dist/tests/backup.test.d.ts.map +0 -1
  248. package/dist/tests/backup.test.js +0 -101
  249. package/dist/tests/backup.test.js.map +0 -1
  250. package/dist/tests/pack.test.d.ts +0 -2
  251. package/dist/tests/pack.test.d.ts.map +0 -1
  252. package/dist/tests/pack.test.js +0 -338
  253. package/dist/tests/pack.test.js.map +0 -1
  254. package/dist/tests/random.test.d.ts +0 -2
  255. package/dist/tests/random.test.d.ts.map +0 -1
  256. package/dist/tests/random.test.js +0 -149
  257. package/dist/tests/random.test.js.map +0 -1
  258. package/dist/tests/tenant.test.d.ts +0 -2
  259. package/dist/tests/tenant.test.d.ts.map +0 -1
  260. package/dist/tests/tenant.test.js +0 -52
  261. package/dist/tests/tenant.test.js.map +0 -1
@@ -16,12 +16,13 @@ const { text, text_attr } = require("@saltcorn/markup/tags");
16
16
  const { renderForm } = require("@saltcorn/markup");
17
17
  const FieldRepeat = require("../../models/fieldrepeat");
18
18
  const { get_expression_function, expressionChecker, } = require("../../models/expression");
19
- const { InvalidConfiguration } = require("../../utils");
19
+ const { InvalidConfiguration, isNode } = require("../../utils");
20
20
  const Library = require("../../models/library");
21
+ const { check_view_columns } = require("../../plugin-testing");
21
22
  const { initial_config_all_fields, calcfldViewOptions, calcfldViewConfig, get_parent_views, get_link_view_opts, picked_fields_to_query, stateFieldsToWhere, stateFieldsToQuery, strictParseInt, } = require("../../plugin-helper");
22
23
  const { splitUniques, getForm, fill_presets, parse_view_select, get_view_link_query, objToQueryString, } = require("./viewable_fields");
23
- const { traverse, getStringsForI18n, translateLayout, } = require("../../models/layout");
24
- const { asyncMap } = require("../../utils");
24
+ const { traverse, getStringsForI18n, translateLayout, traverseSync, } = require("../../models/layout");
25
+ const { asyncMap, isWeb } = require("../../utils");
25
26
  /**
26
27
  * @param {object} req
27
28
  * @returns {Workflow}
@@ -42,7 +43,7 @@ const configuration_workflow = (req) => new Workflow({
42
43
  await field.reftable.getFields();
43
44
  }
44
45
  }
45
- const { field_view_options, handlesTextStyle } = calcfldViewOptions(fields, "edit");
46
+ const { field_view_options, handlesTextStyle, blockDisplay } = calcfldViewOptions(fields, "edit");
46
47
  const fieldViewConfigForms = await calcfldViewConfig(fields, true);
47
48
  const roles = await User.get_roles();
48
49
  const images = await File.find({ mime_super: "image" });
@@ -115,6 +116,7 @@ const configuration_workflow = (req) => new Workflow({
115
116
  field_view_options,
116
117
  parent_field_list,
117
118
  handlesTextStyle,
119
+ blockDisplay,
118
120
  roles,
119
121
  actions,
120
122
  fieldViewConfigForms,
@@ -185,7 +187,6 @@ const configuration_workflow = (req) => new Workflow({
185
187
  done_view_opts.push(`${v.name}.${relation.name}`);
186
188
  }));
187
189
  return new Form({
188
- blurb: req.__("The view you choose here can be ignored depending on the context of the form, for instance if it appears in a pop-up the redirect will not take place."),
189
190
  fields: [
190
191
  {
191
192
  name: "auto_save",
@@ -194,24 +195,36 @@ const configuration_workflow = (req) => new Workflow({
194
195
  type: "Bool",
195
196
  },
196
197
  {
197
- name: "view_when_done",
198
- label: req.__("Default view when done"),
199
- sublabel: req.__("This is the view to which the user will be sent when the form is submitted, unless a formula below is true."),
198
+ name: "destination_type",
199
+ label: "Destination type",
200
200
  type: "String",
201
201
  required: true,
202
+ sublabel: req.__("This is the view to which the user will be sent when the form is submitted. The view you specify here can be ignored depending on the context of the form, for instance if it appears in a pop-up the redirect will not take place."),
203
+ //fieldview: "radio_group",
202
204
  attributes: {
203
- options: done_view_opts,
205
+ options: ["View", "Formula", "Back to referer"],
204
206
  },
205
207
  },
206
208
  {
207
- label: req.__("Alternative destinations if formula evaluates to true"),
208
- sublabel: req.__("You can send the user to an different view depending on the day the user has submitted. Ignore this option if you always want to send the user to the same destination"),
209
- input_type: "section_header",
209
+ name: "view_when_done",
210
+ label: req.__("Destination view"),
211
+ type: "String",
212
+ required: true,
213
+ attributes: {
214
+ options: done_view_opts,
215
+ },
216
+ showIf: { destination_type: "View" },
210
217
  },
211
218
  new FieldRepeat({
212
219
  name: "formula_destinations",
220
+ showIf: { destination_type: "Formula" },
213
221
  fields: [
214
- { type: "String", name: "expression", label: "Formula" },
222
+ {
223
+ type: "String",
224
+ name: "expression",
225
+ label: "Formula",
226
+ sublabel: "if this formula evaluates to true, use the following view",
227
+ },
215
228
  {
216
229
  name: "view",
217
230
  label: req.__("View"),
@@ -268,26 +281,8 @@ const initial_config = initial_config_all_fields(true);
268
281
  * @param {object} optsTwo.res
269
282
  * @returns {Promise<Form>}
270
283
  */
271
- const run = async (table_id, viewname, { columns, layout, auto_save }, state, { res, req }) => {
272
- const table = await Table.findOne({ id: table_id });
273
- const fields = await table.getFields();
274
- const { uniques, nonUniques } = splitUniques(fields, state);
275
- let row = null;
276
- if (Object.keys(uniques).length > 0) {
277
- row = await table.getRow(uniques);
278
- }
279
- return await render({
280
- table,
281
- fields,
282
- viewname,
283
- columns,
284
- layout,
285
- row,
286
- req,
287
- res,
288
- state,
289
- auto_save,
290
- });
284
+ const run = async (table_id, viewname, {}, state, { res, req }, { editQuery }) => {
285
+ return await editQuery(state);
291
286
  };
292
287
  /**
293
288
  * @param {number} table_id
@@ -299,22 +294,8 @@ const run = async (table_id, viewname, { columns, layout, auto_save }, state, {
299
294
  * @param {object} extra
300
295
  * @returns {Promise<Form[]>}
301
296
  */
302
- const runMany = async (table_id, viewname, { columns, layout, auto_save }, state, extra) => {
303
- const table = await Table.findOne({ id: table_id });
304
- const fields = await table.getFields();
305
- const { joinFields, aggregations } = picked_fields_to_query(columns, fields);
306
- const qstate = await stateFieldsToWhere({ fields, state });
307
- const q = await stateFieldsToQuery({ state, fields });
308
- const rows = await table.getJoinedRows({
309
- where: qstate,
310
- joinFields,
311
- aggregations,
312
- ...(extra && extra.limit && { limit: extra.limit }),
313
- ...(extra && extra.offset && { offset: extra.offset }),
314
- ...(extra && extra.orderBy && { orderBy: extra.orderBy }),
315
- ...(extra && extra.orderDesc && { orderDesc: extra.orderDesc }),
316
- ...q,
317
- });
297
+ const runMany = async (table_id, viewname, { columns, layout, auto_save }, state, extra, { editManyQuery, getRowQuery }) => {
298
+ const { table, fields, rows } = await editManyQuery();
318
299
  return await asyncMap(rows, async (row) => {
319
300
  const html = await render({
320
301
  table,
@@ -327,6 +308,7 @@ const runMany = async (table_id, viewname, { columns, layout, auto_save }, state
327
308
  res: extra.res,
328
309
  state,
329
310
  auto_save,
311
+ getRowQuery,
330
312
  });
331
313
  return { html, row };
332
314
  });
@@ -341,7 +323,7 @@ const runMany = async (table_id, viewname, { columns, layout, auto_save }, state
341
323
  * @throws {InvalidConfiguration}
342
324
  * @returns {Promise<void>}
343
325
  */
344
- const transformForm = async ({ form, table, req, row, res }) => {
326
+ const transformForm = async ({ form, table, req, row, res, getRowQuery }) => {
345
327
  await traverse(form.layout, {
346
328
  action(segment) {
347
329
  if (segment.action_name === "Delete") {
@@ -359,13 +341,53 @@ const transformForm = async ({ form, table, req, row, res }) => {
359
341
  segment.sourceURL = `/field/show-calculated/${table.name}/${segment.join_field}/${segment.fieldview}?${qs}`;
360
342
  },
361
343
  async view(segment) {
344
+ //console.log(segment);
345
+ const view_select = parse_view_select(segment.view);
346
+ //console.log({ view_select });
347
+ const view = await View.findOne({ name: view_select.viewname });
348
+ if (!view)
349
+ throw new InvalidConfiguration(`Cannot find embedded view: ${view_select.viewname}`);
350
+ if (view.viewtemplate === "Edit" && view_select.type === "ChildList") {
351
+ const childTable = Table.findOne({ id: view.table_id });
352
+ const childForm = await getForm(childTable, view.name, view.configuration.columns, view.configuration.layout, row?.id, req);
353
+ traverseSync(childForm.layout, {
354
+ field(segment) {
355
+ segment.field_name = `${view_select.field_name}.${segment.field_name}`;
356
+ },
357
+ });
358
+ const fr = new FieldRepeat({
359
+ name: view_select.field_name,
360
+ label: view_select.field_name,
361
+ fields: childForm.fields,
362
+ layout: childForm.layout,
363
+ metadata: {
364
+ table_id: childTable.id,
365
+ relation: view_select.field_name,
366
+ },
367
+ });
368
+ if (row?.id) {
369
+ const childRows = getRowQuery
370
+ ? await getRowQuery(view.table_id, view_select, row.id)
371
+ : await childTable.getRows({
372
+ [view_select.field_name]: row.id,
373
+ });
374
+ fr.metadata.rows = childRows;
375
+ if (!fr.fields.map((f) => f.name).includes(childTable.pk_name))
376
+ fr.fields.push({
377
+ name: childTable.pk_name,
378
+ input_type: "hidden",
379
+ });
380
+ }
381
+ form.fields.push(fr);
382
+ segment.type = "field_repeat";
383
+ segment.field_repeat = fr;
384
+ return;
385
+ }
362
386
  if (!row) {
363
387
  segment.type = "blank";
364
388
  segment.contents = "";
365
389
  return;
366
390
  }
367
- const view_select = parse_view_select(segment.view);
368
- const view = await View.findOne({ name: view_select.viewname });
369
391
  if (!view)
370
392
  throw new InvalidConfiguration(`Edit view incorrectly configured: cannot find embedded view ${view_select.viewname}`);
371
393
  let state;
@@ -405,10 +427,10 @@ const transformForm = async ({ form, table, req, row, res }) => {
405
427
  * @param {object} opts.res
406
428
  * @returns {Promise<Form>}
407
429
  */
408
- const render = async ({ table, fields, viewname, columns, layout, row, req, state, res, auto_save, }) => {
409
- const form = await getForm(table, viewname, columns, layout, state.id, req);
430
+ const render = async ({ table, fields, viewname, columns, layout, row, req, state, res, auto_save, destination_type, isRemote, getRowQuery, }) => {
431
+ const form = await getForm(table, viewname, columns, layout, state.id, req, isRemote);
410
432
  if (auto_save)
411
- form.onChange = `saveAndContinue(this)`;
433
+ form.onChange = `saveAndContinue(this, ${!isWeb(req) ? `'${form.action}'` : undefined})`;
412
434
  if (row) {
413
435
  form.values = row;
414
436
  const file_fields = form.fields.filter((f) => f.type === "File");
@@ -420,6 +442,10 @@ const render = async ({ table, fields, viewname, columns, layout, row, req, stat
420
442
  }
421
443
  form.hidden(table.pk_name);
422
444
  }
445
+ if (destination_type === "Back to referer") {
446
+ form.hidden("_referer");
447
+ form.values._referer = req.headers?.referer;
448
+ }
423
449
  Object.entries(state).forEach(([k, v]) => {
424
450
  const field = form.fields.find((f) => f.name === k);
425
451
  if (field && ((field.type && field.type.read) || field.is_fkey)) {
@@ -434,8 +460,8 @@ const render = async ({ table, fields, viewname, columns, layout, row, req, stat
434
460
  }
435
461
  });
436
462
  await form.fill_fkey_options();
437
- await transformForm({ form, table, req, row, res });
438
- return renderForm(form, req.csrfToken());
463
+ await transformForm({ form, table, req, row, res, getRowQuery });
464
+ return renderForm(form, !isRemote ? req.csrfToken() : false);
439
465
  };
440
466
  /**
441
467
  * @param {number} table_id
@@ -454,12 +480,12 @@ const render = async ({ table, fields, viewname, columns, layout, row, req, stat
454
480
  * @param {string} optsTwo.redirect
455
481
  * @returns {Promise<void>}
456
482
  */
457
- const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_done, formula_destinations, auto_save }, state, body, { res, req, redirect }) => {
483
+ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_done, formula_destinations, auto_save, destination_type, }, state, body, { res, req, redirect }, { tryInsertQuery, tryUpdateQuery, getRowQuery }, remote) => {
458
484
  const table = await Table.findOne({ id: table_id });
459
485
  const fields = await table.getFields();
460
486
  const form = await getForm(table, viewname, columns, layout, body.id, req);
461
487
  if (auto_save)
462
- form.onChange = `saveAndContinue(this)`;
488
+ form.onChange = `saveAndContinue(this, ${!isWeb(req) ? `'${form.action}'` : undefined})`;
463
489
  Object.entries(body).forEach(([k, v]) => {
464
490
  const form_field = form.fields.find((f) => f.name === k);
465
491
  const tbl_field = fields.find((f) => f.name === k);
@@ -468,13 +494,21 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
468
494
  }
469
495
  });
470
496
  setDateLocales(form, req.getLocale());
497
+ await transformForm({
498
+ form,
499
+ table,
500
+ req,
501
+ row: body[table.pk_name]
502
+ ? { [table.pk_name]: body[table.pk_name] }
503
+ : undefined,
504
+ getRowQuery,
505
+ });
471
506
  form.validate(body);
472
507
  if (form.hasErrors) {
473
508
  if (req.xhr)
474
509
  res.status(422);
475
510
  await form.fill_fkey_options();
476
- await transformForm({ form, table, req });
477
- res.sendWrap(viewname, renderForm(form, req.csrfToken()));
511
+ res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
478
512
  }
479
513
  else {
480
514
  let row;
@@ -485,12 +519,22 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
485
519
  row = { ...use_fixed, ...form.values };
486
520
  }
487
521
  else {
488
- row = form.values;
522
+ row = { ...form.values };
523
+ }
524
+ for (const field of form.fields.filter((f) => f.isRepeat)) {
525
+ delete row[field.name];
489
526
  }
490
527
  const file_fields = form.fields.filter((f) => f.type === "File");
491
528
  for (const field of file_fields) {
492
529
  if (req.files && req.files[field.name]) {
493
- const file = await File.from_req_files(req.files[field.name], req.user ? req.user.id : null, (field.attributes && +field.attributes.min_role_read) || 1);
530
+ if (!isNode() && !remote) {
531
+ req.flash("error", "The mobile-app supports no local files, please use a remote table.");
532
+ res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
533
+ return;
534
+ }
535
+ const file = isNode()
536
+ ? await File.from_req_files(req.files[field.name], req.user ? req.user.id : null, (field.attributes && +field.attributes.min_role_read) || 1)
537
+ : await File.upload(req.files[field.name]);
494
538
  row[field.name] = file.id;
495
539
  }
496
540
  else {
@@ -499,77 +543,102 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
499
543
  }
500
544
  const originalID = id;
501
545
  if (typeof id === "undefined") {
502
- const ins_res = await table.tryInsertRow(row, req.user ? +req.user.id : undefined);
546
+ const ins_res = await tryInsertQuery(row);
503
547
  if (ins_res.success) {
504
548
  id = ins_res.success;
505
549
  row[pk.name] = id;
506
550
  }
507
551
  else {
508
552
  req.flash("error", text_attr(ins_res.error));
509
- res.sendWrap(viewname, renderForm(form, req.csrfToken()));
553
+ res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
510
554
  return;
511
555
  }
512
556
  }
513
557
  else {
514
- const upd_res = await table.tryUpdateRow(row, id, req.user ? +req.user.id : undefined);
558
+ const upd_res = await tryUpdateQuery(row, id);
515
559
  if (upd_res.error) {
516
560
  req.flash("error", text_attr(upd_res.error));
517
561
  res.sendWrap(viewname, renderForm(form, req.csrfToken()));
518
562
  return;
519
563
  }
520
564
  }
521
- if (req.xhr && !originalID) {
522
- res.json({ id });
565
+ for (const field of form.fields.filter((f) => f.isRepeat)) {
566
+ const childTable = Table.findOne({ id: field.metadata?.table_id });
567
+ for (const childRow of form.values[field.name]) {
568
+ childRow[field.metadata?.relation] = id;
569
+ if (childRow[childTable.pk_name]) {
570
+ const upd_res = await childTable.tryUpdateRow(childRow, childRow[childTable.pk_name]);
571
+ if (upd_res.error) {
572
+ req.flash("error", text_attr(upd_res.error));
573
+ res.sendWrap(viewname, renderForm(form, req.csrfToken()));
574
+ return;
575
+ }
576
+ }
577
+ else {
578
+ const ins_res = await childTable.tryInsertRow(childRow, req.user ? +req.user.id : undefined);
579
+ if (ins_res.error) {
580
+ req.flash("error", text_attr(ins_res.error));
581
+ res.sendWrap(viewname, renderForm(form, req.csrfToken()));
582
+ return;
583
+ }
584
+ }
585
+ }
586
+ }
587
+ if (req.xhr && !originalID && !req.smr) {
588
+ res.json({ id, view_when_done });
523
589
  return;
524
590
  }
525
591
  if (redirect) {
526
592
  res.redirect(redirect);
527
593
  return;
528
594
  }
529
- if (!view_when_done) {
530
- res.redirect(`/`);
595
+ let use_view_when_done = view_when_done;
596
+ if (destination_type === "Back to referer" && body._referer) {
597
+ res.redirect(body._referer);
531
598
  return;
532
599
  }
533
- let use_view_when_done = view_when_done;
534
- for (const { view, expression } of formula_destinations || []) {
535
- if (expression) {
536
- const f = get_expression_function(expression, fields);
537
- if (f(row)) {
538
- use_view_when_done = view;
539
- continue;
600
+ else if (destination_type !== "View")
601
+ for (const { view, expression } of formula_destinations || []) {
602
+ if (expression) {
603
+ const f = get_expression_function(expression, fields);
604
+ if (f(row)) {
605
+ use_view_when_done = view;
606
+ continue;
607
+ }
540
608
  }
541
609
  }
610
+ if (!use_view_when_done) {
611
+ res.redirect(`/`);
612
+ return;
542
613
  }
543
614
  const [viewname_when_done, relation] = use_view_when_done.split(".");
544
615
  const nxview = await View.findOne({ name: viewname_when_done });
545
- //console.log()
546
616
  if (!nxview) {
547
617
  req.flash("warning", `View "${use_view_when_done}" not found - change "View when done" in "${viewname}" view`);
548
618
  res.redirect(`/`);
549
619
  }
550
620
  else {
551
621
  const state_fields = await nxview.get_state_fields();
622
+ let target = `/view/${text(viewname_when_done)}`;
623
+ let query = "";
552
624
  if ((nxview.table_id === table_id || relation) &&
553
625
  state_fields.some((sf) => sf.name === pk.name)) {
554
626
  const get_query = get_view_link_query(fields);
555
- const query = relation
627
+ query = relation
556
628
  ? `?${pk.name}=${text(row[relation])}`
557
629
  : get_query(row);
558
- res.redirect(`/view/${text(viewname_when_done)}${query}`);
559
630
  }
560
- else
561
- res.redirect(`/view/${text(viewname_when_done)}`);
631
+ const redirectPath = `${target}${query}`;
632
+ if (!isWeb(req)) {
633
+ res.json({ redirect: `get${redirectPath}` });
634
+ }
635
+ else {
636
+ res.redirect(redirectPath);
637
+ }
562
638
  }
563
639
  }
564
640
  };
565
- /**
566
- * @param {object} opts
567
- * @param {object} opts.body
568
- * @param {string} opts.table_id
569
- * @param {object} opts.req
570
- * @returns {Promise<boolean>}
571
- */
572
- const authorise_post = async ({ body, table_id, req }) => {
641
+ const doAuthPost = async ({ body, table_id, req }) => {
573
642
  const table = await Table.findOne({ id: table_id });
574
643
  const user_id = req.user ? req.user.id : null;
575
644
  if (table.ownership_field_id && user_id) {
@@ -592,6 +661,16 @@ const authorise_post = async ({ body, table_id, req }) => {
592
661
  return true;
593
662
  return false;
594
663
  };
664
+ /**
665
+ * @param {object} opts
666
+ * @param {object} opts.body
667
+ * @param {string} opts.table_id
668
+ * @param {object} opts.req
669
+ * @returns {Promise<boolean>}
670
+ */
671
+ const authorise_post = async ({ body, table_id, req }, { authorizePostQuery }) => {
672
+ return await authorizePostQuery(body, table_id);
673
+ };
595
674
  module.exports = {
596
675
  /** @type {string} */
597
676
  name: "Edit",
@@ -612,20 +691,8 @@ module.exports = {
612
691
  * @param {...*} opts.rest
613
692
  * @returns {Promise<boolean>}
614
693
  */
615
- authorise_get: async ({ query, table_id, req }) => {
616
- let body = query || {};
617
- if (Object.keys(body).length == 1) {
618
- const table = await Table.findOne({ id: table_id });
619
- if (table.ownership_field_id || table.ownership_formula) {
620
- const fields = await table.getFields();
621
- const { uniques } = splitUniques(fields, body);
622
- if (Object.keys(uniques).length > 0) {
623
- body = await table.getRow(uniques);
624
- return table.is_owner(req.user, body);
625
- }
626
- }
627
- }
628
- return authorise_post({ body, table_id, req });
694
+ authorise_get: async ({ query, table_id, req }, { authorizeGetQuery }) => {
695
+ return await authorizeGetQuery(query, table_id);
629
696
  },
630
697
  /**
631
698
  * @param {object} opts
@@ -635,17 +702,103 @@ module.exports = {
635
702
  getStringsForI18n({ layout }) {
636
703
  return getStringsForI18n(layout);
637
704
  },
638
- configCheck: async ({ name, configuration: { view_when_done, formula_destinations }, }) => {
705
+ queries: ({ table_id, name, configuration: { columns, default_state, layout, auto_save, destination_type, }, req, res, }) => ({
706
+ async editQuery(state) {
707
+ const table = await Table.findOne({ id: table_id });
708
+ const fields = await table.getFields();
709
+ const { uniques } = splitUniques(fields, state);
710
+ let row = null;
711
+ if (Object.keys(uniques).length > 0) {
712
+ row = await table.getRow(uniques);
713
+ }
714
+ const isRemote = !isWeb(req);
715
+ return await render({
716
+ table,
717
+ fields,
718
+ viewname: name,
719
+ columns,
720
+ layout,
721
+ row,
722
+ req,
723
+ res,
724
+ state,
725
+ auto_save,
726
+ destination_type,
727
+ isRemote,
728
+ });
729
+ },
730
+ async editManyQuery(state) {
731
+ const table = await Table.findOne({ id: table_id });
732
+ const fields = await table.getFields();
733
+ const { joinFields, aggregations } = picked_fields_to_query(columns, fields);
734
+ const qstate = await stateFieldsToWhere({ fields, state });
735
+ const q = await stateFieldsToQuery({ state, fields });
736
+ const rows = await table.getJoinedRows({
737
+ where: qstate,
738
+ joinFields,
739
+ aggregations,
740
+ ...(extra && extra.limit && { limit: extra.limit }),
741
+ ...(extra && extra.offset && { offset: extra.offset }),
742
+ ...(extra && extra.orderBy && { orderBy: extra.orderBy }),
743
+ ...(extra && extra.orderDesc && { orderDesc: extra.orderDesc }),
744
+ ...q,
745
+ });
746
+ return {
747
+ table,
748
+ fields,
749
+ rows,
750
+ };
751
+ },
752
+ async tryInsertQuery(row) {
753
+ const table = await Table.findOne({ id: table_id });
754
+ const ins_res = await table.tryInsertRow(row, req.user ? +req.user.id : undefined);
755
+ return ins_res;
756
+ },
757
+ async tryUpdateQuery(row, id) {
758
+ const table = await Table.findOne({ id: table_id });
759
+ const upd_res = await table.tryUpdateRow(row, id, req.user ? +req.user.id : undefined);
760
+ return upd_res;
761
+ },
762
+ async authorizePostQuery(body, table_id /*overwrites*/) {
763
+ return await doAuthPost({ body, table_id, req });
764
+ },
765
+ async authorizeGetQuery(query, table_id) {
766
+ let body = query || {};
767
+ if (Object.keys(body).length == 1) {
768
+ const table = await Table.findOne({ id: table_id });
769
+ if (table.ownership_field_id || table.ownership_formula) {
770
+ const fields = await table.getFields();
771
+ const { uniques } = splitUniques(fields, body);
772
+ if (Object.keys(uniques).length > 0) {
773
+ body = await table.getRow(uniques);
774
+ return table.is_owner(req.user, body);
775
+ }
776
+ }
777
+ }
778
+ return doAuthPost({ body, table_id, req });
779
+ },
780
+ async getRowQuery(table_id, view_select, row_id) {
781
+ const childTable = Table.findOne({ id: table_id });
782
+ return await childTable.getRows({
783
+ [view_select.field_name]: row_id,
784
+ });
785
+ },
786
+ }),
787
+ configCheck: async (view) => {
788
+ const { name, configuration: { view_when_done, destination_type, formula_destinations }, } = view;
639
789
  const errs = [];
640
- const vwd = await View.findOne({
641
- name: (view_when_done || "").split(".")[0],
642
- });
643
- if (!vwd)
644
- errs.push(`In View ${name}, view when done ${view_when_done} not found`);
645
- for (const { expression } of formula_destinations || []) {
646
- if (expression)
647
- expressionChecker(expression, `In View ${name}, destination formula ${expression} error: `, errs);
790
+ if (destination_type !== "Back to referer") {
791
+ const vwd = await View.findOne({
792
+ name: (view_when_done || "").split(".")[0],
793
+ });
794
+ if (!vwd)
795
+ errs.push(`In View ${name}, view when done ${view_when_done} not found`);
796
+ for (const { expression } of formula_destinations || []) {
797
+ if (expression)
798
+ expressionChecker(expression, `In View ${name}, destination formula ${expression} error: `, errs);
799
+ }
648
800
  }
801
+ errs.push(...(await check_view_columns(view, view.configuration.columns)));
649
802
  return errs;
650
803
  },
651
804
  };