@saltcorn/data 0.7.1-beta.3 → 0.7.2-beta.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.
Files changed (260) hide show
  1. package/dist/app-locales/en.json +6 -0
  2. package/dist/app-locales/public/da.json +3 -0
  3. package/dist/app-locales/public/tlh.json +3 -0
  4. package/dist/base-plugin/actions.d.ts.map +1 -1
  5. package/dist/base-plugin/actions.js +4 -1
  6. package/dist/base-plugin/actions.js.map +1 -1
  7. package/dist/base-plugin/fieldviews.d.ts +29 -0
  8. package/dist/base-plugin/fieldviews.d.ts.map +1 -1
  9. package/dist/base-plugin/fieldviews.js +60 -5
  10. package/dist/base-plugin/fieldviews.js.map +1 -1
  11. package/dist/base-plugin/index.d.ts +245 -20
  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 +46 -8
  18. package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
  19. package/dist/base-plugin/viewtemplates/edit.js +258 -112
  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 +64 -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 +120 -75
  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/coverage/coverage-final.json +46 -0
  50. package/dist/coverage/lcov-report/block-navigation.d.ts +2 -0
  51. package/dist/coverage/lcov-report/block-navigation.d.ts.map +1 -0
  52. package/dist/coverage/lcov-report/block-navigation.js +71 -0
  53. package/dist/coverage/lcov-report/block-navigation.js.map +1 -0
  54. package/dist/coverage/lcov-report/prettify.d.ts +1 -0
  55. package/dist/coverage/lcov-report/prettify.d.ts.map +1 -0
  56. package/dist/coverage/lcov-report/prettify.js +478 -0
  57. package/dist/coverage/lcov-report/prettify.js.map +1 -0
  58. package/dist/coverage/lcov-report/sorter.d.ts +2 -0
  59. package/dist/coverage/lcov-report/sorter.d.ts.map +1 -0
  60. package/dist/coverage/lcov-report/sorter.js +164 -0
  61. package/dist/coverage/lcov-report/sorter.js.map +1 -0
  62. package/dist/db/connect.d.ts.map +1 -1
  63. package/dist/db/connect.js +8 -1
  64. package/dist/db/connect.js.map +1 -1
  65. package/dist/db/connect_mobile.d.ts +9 -0
  66. package/dist/db/connect_mobile.d.ts.map +1 -0
  67. package/dist/db/connect_mobile.js +15 -0
  68. package/dist/db/connect_mobile.js.map +1 -0
  69. package/dist/db/fixtures.d.ts.map +1 -1
  70. package/dist/db/fixtures.js +6 -1
  71. package/dist/db/fixtures.js.map +1 -1
  72. package/dist/db/index.d.ts.map +1 -1
  73. package/dist/db/index.js +16 -2
  74. package/dist/db/index.js.map +1 -1
  75. package/dist/db/state.d.ts +207 -52
  76. package/dist/db/state.d.ts.map +1 -1
  77. package/dist/db/state.js +87 -84
  78. package/dist/db/state.js.map +1 -1
  79. package/dist/index.d.ts +8 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +27 -2
  82. package/dist/index.js.map +1 -1
  83. package/dist/migrate.d.ts.map +1 -1
  84. package/dist/migrate.js +54 -40
  85. package/dist/migrate.js.map +1 -1
  86. package/dist/mobile-mocks/db/postgres.d.ts +1 -0
  87. package/dist/mobile-mocks/db/postgres.d.ts.map +1 -0
  88. package/dist/mobile-mocks/db/postgres.js +2 -0
  89. package/dist/mobile-mocks/db/postgres.js.map +1 -0
  90. package/dist/mobile-mocks/db/sqlite.d.ts +1 -0
  91. package/dist/mobile-mocks/db/sqlite.d.ts.map +1 -0
  92. package/dist/mobile-mocks/db/sqlite.js +2 -0
  93. package/dist/mobile-mocks/db/sqlite.js.map +1 -0
  94. package/dist/mobile-mocks/models/email.d.ts +1 -0
  95. package/dist/mobile-mocks/models/email.d.ts.map +1 -0
  96. package/dist/mobile-mocks/models/email.js +2 -0
  97. package/dist/mobile-mocks/models/email.js.map +1 -0
  98. package/dist/mobile-mocks/node/async_hooks.d.ts +4 -0
  99. package/dist/mobile-mocks/node/async_hooks.d.ts.map +1 -0
  100. package/dist/mobile-mocks/node/async_hooks.js +8 -0
  101. package/dist/mobile-mocks/node/async_hooks.js.map +1 -0
  102. package/dist/mobile-mocks/node/child_process.d.ts +3 -0
  103. package/dist/mobile-mocks/node/child_process.d.ts.map +1 -0
  104. package/dist/mobile-mocks/node/child_process.js +6 -0
  105. package/dist/mobile-mocks/node/child_process.js.map +1 -0
  106. package/dist/mobile-mocks/node/fs/promises.d.ts +6 -0
  107. package/dist/mobile-mocks/node/fs/promises.d.ts.map +1 -0
  108. package/dist/mobile-mocks/node/fs/promises.js +16 -0
  109. package/dist/mobile-mocks/node/fs/promises.js.map +1 -0
  110. package/dist/mobile-mocks/node/fs.d.ts +4 -0
  111. package/dist/mobile-mocks/node/fs.d.ts.map +1 -0
  112. package/dist/mobile-mocks/node/fs.js +15 -0
  113. package/dist/mobile-mocks/node/fs.js.map +1 -0
  114. package/dist/mobile-mocks/node/latest-version.d.ts +2 -0
  115. package/dist/mobile-mocks/node/latest-version.d.ts.map +1 -0
  116. package/dist/mobile-mocks/node/latest-version.js +7 -0
  117. package/dist/mobile-mocks/node/latest-version.js.map +1 -0
  118. package/dist/mobile-mocks/node/v8.d.ts +3 -0
  119. package/dist/mobile-mocks/node/v8.d.ts.map +1 -0
  120. package/dist/mobile-mocks/node/v8.js +8 -0
  121. package/dist/mobile-mocks/node/v8.js.map +1 -0
  122. package/dist/mobile-mocks/npm/env-paths.d.ts +6 -0
  123. package/dist/mobile-mocks/npm/env-paths.d.ts.map +1 -0
  124. package/dist/mobile-mocks/npm/env-paths.js +9 -0
  125. package/dist/mobile-mocks/npm/env-paths.js.map +1 -0
  126. package/dist/mobile-mocks/saltcorn/plugin-testing.d.ts +1 -0
  127. package/dist/mobile-mocks/saltcorn/plugin-testing.d.ts.map +1 -0
  128. package/dist/mobile-mocks/saltcorn/plugin-testing.js +2 -0
  129. package/dist/mobile-mocks/saltcorn/plugin-testing.js.map +1 -0
  130. package/dist/models/config.d.ts +9 -10
  131. package/dist/models/config.d.ts.map +1 -1
  132. package/dist/models/config.js +8 -7
  133. package/dist/models/config.js.map +1 -1
  134. package/dist/models/discovery.d.ts.map +1 -1
  135. package/dist/models/discovery.js +5 -1
  136. package/dist/models/discovery.js.map +1 -1
  137. package/dist/models/email.d.ts +2 -0
  138. package/dist/models/email.d.ts.map +1 -1
  139. package/dist/models/field.d.ts +6 -4
  140. package/dist/models/field.d.ts.map +1 -1
  141. package/dist/models/field.js +40 -21
  142. package/dist/models/field.js.map +1 -1
  143. package/dist/models/fieldrepeat.d.ts +5 -0
  144. package/dist/models/fieldrepeat.d.ts.map +1 -1
  145. package/dist/models/fieldrepeat.js +2 -0
  146. package/dist/models/fieldrepeat.js.map +1 -1
  147. package/dist/models/form.d.ts +2 -0
  148. package/dist/models/form.d.ts.map +1 -1
  149. package/dist/models/form.js +1 -0
  150. package/dist/models/form.js.map +1 -1
  151. package/dist/models/index.d.ts +2 -13
  152. package/dist/models/index.d.ts.map +1 -1
  153. package/dist/models/layout.d.ts.map +1 -1
  154. package/dist/models/layout.js +12 -2
  155. package/dist/models/layout.js.map +1 -1
  156. package/dist/models/page.d.ts +1 -1
  157. package/dist/models/page.d.ts.map +1 -1
  158. package/dist/models/page.js +17 -9
  159. package/dist/models/page.js.map +1 -1
  160. package/dist/models/scheduler.d.ts.map +1 -1
  161. package/dist/models/scheduler.js +3 -1
  162. package/dist/models/scheduler.js.map +1 -1
  163. package/dist/models/table.d.ts +2 -1
  164. package/dist/models/table.d.ts.map +1 -1
  165. package/dist/models/table.js +32 -11
  166. package/dist/models/table.js.map +1 -1
  167. package/dist/models/trigger.d.ts +3 -3
  168. package/dist/models/trigger.d.ts.map +1 -1
  169. package/dist/models/trigger.js +4 -5
  170. package/dist/models/trigger.js.map +1 -1
  171. package/dist/models/user.d.ts +1 -1
  172. package/dist/models/user.d.ts.map +1 -1
  173. package/dist/models/user.js +7 -15
  174. package/dist/models/user.js.map +1 -1
  175. package/dist/models/view.d.ts +16 -12
  176. package/dist/models/view.d.ts.map +1 -1
  177. package/dist/models/view.js +63 -15
  178. package/dist/models/view.js.map +1 -1
  179. package/dist/models/workflow.d.ts +1 -0
  180. package/dist/models/workflow.d.ts.map +1 -1
  181. package/dist/models/workflow.js +58 -2
  182. package/dist/models/workflow.js.map +1 -1
  183. package/dist/package.json +127 -0
  184. package/dist/plugin-helper.d.ts +17 -13
  185. package/dist/plugin-helper.d.ts.map +1 -1
  186. package/dist/plugin-helper.js +122 -74
  187. package/dist/plugin-helper.js.map +1 -1
  188. package/dist/plugin-testing.d.ts +1 -0
  189. package/dist/plugin-testing.d.ts.map +1 -1
  190. package/dist/plugin-testing.js +84 -1
  191. package/dist/plugin-testing.js.map +1 -1
  192. package/dist/tests/actions.test.js +30 -5
  193. package/dist/tests/actions.test.js.map +1 -1
  194. package/dist/tests/db.test.d.ts +2 -0
  195. package/dist/tests/db.test.d.ts.map +1 -0
  196. package/dist/tests/db.test.js +32 -0
  197. package/dist/tests/db.test.js.map +1 -0
  198. package/dist/tests/exact_views.test.js +274 -19
  199. package/dist/tests/exact_views.test.js.map +1 -1
  200. package/dist/tests/field.test.js +36 -0
  201. package/dist/tests/field.test.js.map +1 -1
  202. package/dist/tests/mocks.d.ts +2 -0
  203. package/dist/tests/mocks.d.ts.map +1 -1
  204. package/dist/tests/mocks.js +2 -0
  205. package/dist/tests/mocks.js.map +1 -1
  206. package/dist/tests/models.test.js +46 -1
  207. package/dist/tests/models.test.js.map +1 -1
  208. package/dist/tests/remote_query_helper.d.ts +341 -0
  209. package/dist/tests/remote_query_helper.d.ts.map +1 -0
  210. package/dist/tests/remote_query_helper.js +446 -0
  211. package/dist/tests/remote_query_helper.js.map +1 -0
  212. package/dist/tests/state.test.d.ts +2 -0
  213. package/dist/tests/state.test.d.ts.map +1 -0
  214. package/dist/tests/state.test.js +78 -0
  215. package/dist/tests/state.test.js.map +1 -0
  216. package/dist/tests/table.test.js +19 -0
  217. package/dist/tests/table.test.js.map +1 -1
  218. package/dist/tests/view.test.js +29 -0
  219. package/dist/tests/view.test.js.map +1 -1
  220. package/dist/tsconfig.json +24 -0
  221. package/dist/tsconfig.ref.json +9 -0
  222. package/dist/tsconfig.ref.tsbuildinfo +1 -1
  223. package/dist/utils.d.ts +3 -1
  224. package/dist/utils.d.ts.map +1 -1
  225. package/dist/utils.js +14 -23
  226. package/dist/utils.js.map +1 -1
  227. package/dist/webpack.config.d.ts +57 -0
  228. package/dist/webpack.config.d.ts.map +1 -0
  229. package/dist/webpack.config.js +75 -0
  230. package/dist/webpack.config.js.map +1 -0
  231. package/package.json +38 -13
  232. package/CHANGELOG.md +0 -8
  233. package/dist/models/backup.d.ts +0 -9
  234. package/dist/models/backup.d.ts.map +0 -1
  235. package/dist/models/backup.js +0 -278
  236. package/dist/models/backup.js.map +0 -1
  237. package/dist/models/pack.d.ts +0 -42
  238. package/dist/models/pack.d.ts.map +0 -1
  239. package/dist/models/pack.js +0 -419
  240. package/dist/models/pack.js.map +0 -1
  241. package/dist/models/tenant.d.ts +0 -17
  242. package/dist/models/tenant.d.ts.map +0 -1
  243. package/dist/models/tenant.js +0 -152
  244. package/dist/models/tenant.js.map +0 -1
  245. package/dist/tests/backup.test.d.ts +0 -2
  246. package/dist/tests/backup.test.d.ts.map +0 -1
  247. package/dist/tests/backup.test.js +0 -101
  248. package/dist/tests/backup.test.js.map +0 -1
  249. package/dist/tests/pack.test.d.ts +0 -2
  250. package/dist/tests/pack.test.d.ts.map +0 -1
  251. package/dist/tests/pack.test.js +0 -338
  252. package/dist/tests/pack.test.js.map +0 -1
  253. package/dist/tests/random.test.d.ts +0 -2
  254. package/dist/tests/random.test.d.ts.map +0 -1
  255. package/dist/tests/random.test.js +0 -149
  256. package/dist/tests/random.test.js.map +0 -1
  257. package/dist/tests/tenant.test.d.ts +0 -2
  258. package/dist/tests/tenant.test.d.ts.map +0 -1
  259. package/dist/tests/tenant.test.js +0 -52
  260. 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 } = 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 }) => {
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,7 +519,10 @@ 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) {
@@ -499,77 +536,102 @@ const runPost = async (table_id, viewname, { columns, layout, fixed, view_when_d
499
536
  }
500
537
  const originalID = id;
501
538
  if (typeof id === "undefined") {
502
- const ins_res = await table.tryInsertRow(row, req.user ? +req.user.id : undefined);
539
+ const ins_res = await tryInsertQuery(row);
503
540
  if (ins_res.success) {
504
541
  id = ins_res.success;
505
542
  row[pk.name] = id;
506
543
  }
507
544
  else {
508
545
  req.flash("error", text_attr(ins_res.error));
509
- res.sendWrap(viewname, renderForm(form, req.csrfToken()));
546
+ res.sendWrap(viewname, renderForm(form, req.csrfToken ? req.csrfToken() : false));
510
547
  return;
511
548
  }
512
549
  }
513
550
  else {
514
- const upd_res = await table.tryUpdateRow(row, id, req.user ? +req.user.id : undefined);
551
+ const upd_res = await tryUpdateQuery(row, id);
515
552
  if (upd_res.error) {
516
553
  req.flash("error", text_attr(upd_res.error));
517
554
  res.sendWrap(viewname, renderForm(form, req.csrfToken()));
518
555
  return;
519
556
  }
520
557
  }
521
- if (req.xhr && !originalID) {
522
- res.json({ id });
558
+ for (const field of form.fields.filter((f) => f.isRepeat)) {
559
+ const childTable = Table.findOne({ id: field.metadata?.table_id });
560
+ for (const childRow of form.values[field.name]) {
561
+ childRow[field.metadata?.relation] = id;
562
+ if (childRow[childTable.pk_name]) {
563
+ const upd_res = await childTable.tryUpdateRow(childRow, childRow[childTable.pk_name]);
564
+ if (upd_res.error) {
565
+ req.flash("error", text_attr(upd_res.error));
566
+ res.sendWrap(viewname, renderForm(form, req.csrfToken()));
567
+ return;
568
+ }
569
+ }
570
+ else {
571
+ const ins_res = await childTable.tryInsertRow(childRow, req.user ? +req.user.id : undefined);
572
+ if (ins_res.error) {
573
+ req.flash("error", text_attr(ins_res.error));
574
+ res.sendWrap(viewname, renderForm(form, req.csrfToken()));
575
+ return;
576
+ }
577
+ }
578
+ }
579
+ }
580
+ if (req.xhr && !originalID && !req.smr) {
581
+ res.json({ id, view_when_done });
523
582
  return;
524
583
  }
525
584
  if (redirect) {
526
585
  res.redirect(redirect);
527
586
  return;
528
587
  }
529
- if (!view_when_done) {
530
- res.redirect(`/`);
588
+ let use_view_when_done = view_when_done;
589
+ if (destination_type === "Back to referer" && body._referer) {
590
+ res.redirect(body._referer);
531
591
  return;
532
592
  }
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;
593
+ else if (destination_type !== "View")
594
+ for (const { view, expression } of formula_destinations || []) {
595
+ if (expression) {
596
+ const f = get_expression_function(expression, fields);
597
+ if (f(row)) {
598
+ use_view_when_done = view;
599
+ continue;
600
+ }
540
601
  }
541
602
  }
603
+ if (!use_view_when_done) {
604
+ res.redirect(`/`);
605
+ return;
542
606
  }
543
607
  const [viewname_when_done, relation] = use_view_when_done.split(".");
544
608
  const nxview = await View.findOne({ name: viewname_when_done });
545
- //console.log()
546
609
  if (!nxview) {
547
610
  req.flash("warning", `View "${use_view_when_done}" not found - change "View when done" in "${viewname}" view`);
548
611
  res.redirect(`/`);
549
612
  }
550
613
  else {
551
614
  const state_fields = await nxview.get_state_fields();
615
+ let target = `/view/${text(viewname_when_done)}`;
616
+ let query = "";
552
617
  if ((nxview.table_id === table_id || relation) &&
553
618
  state_fields.some((sf) => sf.name === pk.name)) {
554
619
  const get_query = get_view_link_query(fields);
555
- const query = relation
620
+ query = relation
556
621
  ? `?${pk.name}=${text(row[relation])}`
557
622
  : get_query(row);
558
- res.redirect(`/view/${text(viewname_when_done)}${query}`);
559
623
  }
560
- else
561
- res.redirect(`/view/${text(viewname_when_done)}`);
624
+ const redirectPath = `${target}${query}`;
625
+ if (!isWeb(req)) {
626
+ res.json({ redirect: `get${redirectPath}` });
627
+ }
628
+ else {
629
+ res.redirect(redirectPath);
630
+ }
562
631
  }
563
632
  }
564
633
  };
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 }) => {
634
+ const doAuthPost = async ({ body, table_id, req }) => {
573
635
  const table = await Table.findOne({ id: table_id });
574
636
  const user_id = req.user ? req.user.id : null;
575
637
  if (table.ownership_field_id && user_id) {
@@ -592,6 +654,16 @@ const authorise_post = async ({ body, table_id, req }) => {
592
654
  return true;
593
655
  return false;
594
656
  };
657
+ /**
658
+ * @param {object} opts
659
+ * @param {object} opts.body
660
+ * @param {string} opts.table_id
661
+ * @param {object} opts.req
662
+ * @returns {Promise<boolean>}
663
+ */
664
+ const authorise_post = async ({ body, table_id, req }, { authorizePostQuery }) => {
665
+ return await authorizePostQuery(body, table_id);
666
+ };
595
667
  module.exports = {
596
668
  /** @type {string} */
597
669
  name: "Edit",
@@ -612,20 +684,8 @@ module.exports = {
612
684
  * @param {...*} opts.rest
613
685
  * @returns {Promise<boolean>}
614
686
  */
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 });
687
+ authorise_get: async ({ query, table_id, req }, { authorizeGetQuery }) => {
688
+ return await authorizeGetQuery(query, table_id);
629
689
  },
630
690
  /**
631
691
  * @param {object} opts
@@ -635,17 +695,103 @@ module.exports = {
635
695
  getStringsForI18n({ layout }) {
636
696
  return getStringsForI18n(layout);
637
697
  },
638
- configCheck: async ({ name, configuration: { view_when_done, formula_destinations }, }) => {
698
+ queries: ({ table_id, name, configuration: { columns, default_state, layout, auto_save, destination_type, }, req, res, }) => ({
699
+ async editQuery(state) {
700
+ const table = await Table.findOne({ id: table_id });
701
+ const fields = await table.getFields();
702
+ const { uniques } = splitUniques(fields, state);
703
+ let row = null;
704
+ if (Object.keys(uniques).length > 0) {
705
+ row = await table.getRow(uniques);
706
+ }
707
+ const isRemote = !isWeb(req);
708
+ return await render({
709
+ table,
710
+ fields,
711
+ viewname: name,
712
+ columns,
713
+ layout,
714
+ row,
715
+ req,
716
+ res,
717
+ state,
718
+ auto_save,
719
+ destination_type,
720
+ isRemote,
721
+ });
722
+ },
723
+ async editManyQuery(state) {
724
+ const table = await Table.findOne({ id: table_id });
725
+ const fields = await table.getFields();
726
+ const { joinFields, aggregations } = picked_fields_to_query(columns, fields);
727
+ const qstate = await stateFieldsToWhere({ fields, state });
728
+ const q = await stateFieldsToQuery({ state, fields });
729
+ const rows = await table.getJoinedRows({
730
+ where: qstate,
731
+ joinFields,
732
+ aggregations,
733
+ ...(extra && extra.limit && { limit: extra.limit }),
734
+ ...(extra && extra.offset && { offset: extra.offset }),
735
+ ...(extra && extra.orderBy && { orderBy: extra.orderBy }),
736
+ ...(extra && extra.orderDesc && { orderDesc: extra.orderDesc }),
737
+ ...q,
738
+ });
739
+ return {
740
+ table,
741
+ fields,
742
+ rows,
743
+ };
744
+ },
745
+ async tryInsertQuery(row) {
746
+ const table = await Table.findOne({ id: table_id });
747
+ const ins_res = await table.tryInsertRow(row, req.user ? +req.user.id : undefined);
748
+ return ins_res;
749
+ },
750
+ async tryUpdateQuery(row, id) {
751
+ const table = await Table.findOne({ id: table_id });
752
+ const upd_res = await table.tryUpdateRow(row, id, req.user ? +req.user.id : undefined);
753
+ return upd_res;
754
+ },
755
+ async authorizePostQuery(body, table_id /*overwrites*/) {
756
+ return await doAuthPost({ body, table_id, req });
757
+ },
758
+ async authorizeGetQuery(query, table_id) {
759
+ let body = query || {};
760
+ if (Object.keys(body).length == 1) {
761
+ const table = await Table.findOne({ id: table_id });
762
+ if (table.ownership_field_id || table.ownership_formula) {
763
+ const fields = await table.getFields();
764
+ const { uniques } = splitUniques(fields, body);
765
+ if (Object.keys(uniques).length > 0) {
766
+ body = await table.getRow(uniques);
767
+ return table.is_owner(req.user, body);
768
+ }
769
+ }
770
+ }
771
+ return doAuthPost({ body, table_id, req });
772
+ },
773
+ async getRowQuery(table_id, view_select, row_id) {
774
+ const childTable = Table.findOne({ id: table_id });
775
+ return await childTable.getRows({
776
+ [view_select.field_name]: row_id,
777
+ });
778
+ },
779
+ }),
780
+ configCheck: async (view) => {
781
+ const { name, configuration: { view_when_done, destination_type, formula_destinations }, } = view;
639
782
  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);
783
+ if (destination_type !== "Back to referer") {
784
+ const vwd = await View.findOne({
785
+ name: (view_when_done || "").split(".")[0],
786
+ });
787
+ if (!vwd)
788
+ errs.push(`In View ${name}, view when done ${view_when_done} not found`);
789
+ for (const { expression } of formula_destinations || []) {
790
+ if (expression)
791
+ expressionChecker(expression, `In View ${name}, destination formula ${expression} error: `, errs);
792
+ }
648
793
  }
794
+ errs.push(...(await check_view_columns(view, view.configuration.columns)));
649
795
  return errs;
650
796
  },
651
797
  };