@saltcorn/server 0.8.0-beta.3 → 0.8.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.
package/auth/testhelp.js CHANGED
@@ -3,6 +3,7 @@
3
3
  * @module auth/testhelp
4
4
  * @subcategory auth
5
5
  */
6
+ /*global it, expect*/
6
7
  const request = require("supertest");
7
8
  const app = require("../app");
8
9
  const getApp = require("../app");
@@ -10,8 +11,8 @@ const fixtures = require("@saltcorn/data/db/fixtures");
10
11
  const reset = require("@saltcorn/data/db/reset_schema");
11
12
 
12
13
  /**
13
- *
14
- * @param {string} loc
14
+ *
15
+ * @param {string} loc
15
16
  * @returns {void}
16
17
  * @throws {Error}
17
18
  */
@@ -27,62 +28,68 @@ const toRedirect = (loc) => (res) => {
27
28
  };
28
29
 
29
30
  /**
30
- *
31
- * @param {number} txt
32
- * @param {number} expCode
31
+ *
32
+ * @param {number} txt
33
+ * @param {number} expCode
33
34
  * @returns {void}
34
35
  * @throws {Error}
35
36
  */
36
- const toInclude = (txt, expCode = 200) => (res) => {
37
- if (res.statusCode !== expCode) {
38
- console.log(res.text);
39
- throw new Error(
40
- `Expected status ${expCode} when lookinng for "${txt}", received ${res.statusCode}`
41
- );
42
- }
43
-
44
- if (!res.text.includes(txt)) {
45
- console.log(res.text);
46
- throw new Error(`Expected text ${txt} not found`);
47
- }
48
- };
37
+ const toInclude =
38
+ (txt, expCode = 200) =>
39
+ (res) => {
40
+ if (res.statusCode !== expCode) {
41
+ console.log(res.text);
42
+ throw new Error(
43
+ `Expected status ${expCode} when lookinng for "${txt}", received ${res.statusCode}`
44
+ );
45
+ }
46
+
47
+ if (!res.text.includes(txt)) {
48
+ console.log(res.text);
49
+ throw new Error(`Expected text ${txt} not found`);
50
+ }
51
+ };
49
52
 
50
53
  /**
51
- *
52
- * @param {number} expCode
54
+ *
55
+ * @param {number} expCode
53
56
  * @returns {void}
54
57
  * @throws {Error}
55
58
  */
56
- const toSucceed = (expCode = 200) => (res) => {
57
- if (res.statusCode !== expCode) {
58
- console.log(res.text);
59
- throw new Error(`Expected status ${expCode}, received ${res.statusCode}`);
60
- }
61
- };
59
+ const toSucceed =
60
+ (expCode = 200) =>
61
+ (res) => {
62
+ if (res.statusCode !== expCode) {
63
+ console.log(res.text);
64
+ throw new Error(`Expected status ${expCode}, received ${res.statusCode}`);
65
+ }
66
+ };
62
67
 
63
68
  /**
64
- *
65
- * @param {number} txt
66
- * @param {number} expCode
69
+ *
70
+ * @param {number} txt
71
+ * @param {number} expCode
67
72
  * @returns {void}
68
73
  * @throws {Error}
69
- */
70
- const toNotInclude = (txt, expCode = 200) => (res) => {
71
- if (res.statusCode !== expCode) {
72
- console.log(res.text);
73
- throw new Error(
74
- `Expected status ${expCode} when not lookinng for "${txt}", received ${res.statusCode}`
75
- );
76
- }
77
-
78
- if (res.text.includes(txt)) {
79
- console.log(res.text);
80
- throw new Error(`Expected text ${txt} to be absent, but was present`);
81
- }
82
- };
74
+ */
75
+ const toNotInclude =
76
+ (txt, expCode = 200) =>
77
+ (res) => {
78
+ if (res.statusCode !== expCode) {
79
+ console.log(res.text);
80
+ throw new Error(
81
+ `Expected status ${expCode} when not lookinng for "${txt}", received ${res.statusCode}`
82
+ );
83
+ }
84
+
85
+ if (res.text.includes(txt)) {
86
+ console.log(res.text);
87
+ throw new Error(`Expected text ${txt} to be absent, but was present`);
88
+ }
89
+ };
83
90
 
84
91
  /**
85
- *
92
+ *
86
93
  * @returns {Promise<void>}
87
94
  */
88
95
  const getStaffLoginCookie = async () => {
@@ -96,7 +103,7 @@ const getStaffLoginCookie = async () => {
96
103
  };
97
104
 
98
105
  /**
99
- *
106
+ *
100
107
  * @returns {Promise<void>}
101
108
  */
102
109
  const getAdminLoginCookie = async () => {
@@ -111,9 +118,9 @@ const getAdminLoginCookie = async () => {
111
118
  };
112
119
 
113
120
  /**
114
- *
115
- * @param {string} path
116
- * @param {string} dest
121
+ *
122
+ * @param {string} path
123
+ * @param {string} dest
117
124
  * @returns {void}
118
125
  */
119
126
  const itShouldRedirectUnauthToLogin = (path, dest) => {
@@ -137,8 +144,8 @@ const resetToFixtures = async () => {
137
144
  };
138
145
 
139
146
  /**
140
- *
141
- * @param {*} pred
147
+ *
148
+ * @param {*} pred
142
149
  * @returns {void}
143
150
  * @throws {Error}
144
151
  */
@@ -155,9 +162,9 @@ const succeedJsonWith = (pred) => (res) => {
155
162
  };
156
163
 
157
164
  /**
158
- *
159
- * @param {number} code
160
- * @param {number} pred
165
+ *
166
+ * @param {number} code
167
+ * @param {number} pred
161
168
  * @returns {void}
162
169
  * @throws {Error}
163
170
  */
@@ -174,8 +181,8 @@ const respondJsonWith = (code, pred) => (res) => {
174
181
  };
175
182
 
176
183
  /**
177
- *
178
- * @param {object} res
184
+ *
185
+ * @param {object} res
179
186
  * @returns {void}
180
187
  * @throws {Error}
181
188
  */
@@ -13,7 +13,7 @@ const basePlugin = require("@saltcorn/base-plugin");
13
13
  getState().registerPlugin("base", basePlugin);
14
14
 
15
15
  /**
16
- * @param {object[]} vs
16
+ * @param {object[]} vs
17
17
  * @returns {object}
18
18
  */
19
19
  const rndElem = (vs) => vs[Math.floor(Math.random() * vs.length)];
package/index.js CHANGED
@@ -1,22 +1,22 @@
1
- // for the jsdoc documentation
2
- /**
3
- *
4
- * @category server
5
- * @module server/index
6
- */
7
-
8
- /**
9
- * All files and sub-modules in the saltcorn-markup package.
10
- * @namespace server_overview
11
- * @property {module:auth/index~auth_overview} auth
12
- * @property {module:markup/index~markup_overview} markup
13
- * @property {module:routes/index~routes_overview} routes
14
- *
15
- * @property {module:app} app
16
- * @property {module:errors} errors
17
- * @property {module:load_plugins} load_plugins
18
- * @property {module:serve} serve
19
- * @property {module:systemd} systemd
20
- * @property {module:wrapper} wrapper
21
- * @category server
22
- */
1
+ // for the jsdoc documentation
2
+ /**
3
+ *
4
+ * @category server
5
+ * @module server/index
6
+ */
7
+
8
+ /**
9
+ * All files and sub-modules in the saltcorn-markup package.
10
+ * @namespace server_overview
11
+ * @property {module:auth/index~auth_overview} auth
12
+ * @property {module:markup/index~markup_overview} markup
13
+ * @property {module:routes/index~routes_overview} routes
14
+ *
15
+ * @property {module:app} app
16
+ * @property {module:errors} errors
17
+ * @property {module:load_plugins} load_plugins
18
+ * @property {module:serve} serve
19
+ * @property {module:systemd} systemd
20
+ * @property {module:wrapper} wrapper
21
+ * @category server
22
+ */
package/locales/en.json CHANGED
@@ -1055,5 +1055,7 @@
1055
1055
  "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`",
1056
1056
  "Default Files accept filter": "Default Files accept filter",
1057
1057
  "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`": "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`",
1058
- "Destination page": "Destination page"
1058
+ "Destination page": "Destination page",
1059
+ "Module Store endpoint": "Module Store endpoint",
1060
+ "Authentication settings updated": "Authentication settings updated"
1059
1061
  }
package/locales/ru.json CHANGED
@@ -920,28 +920,34 @@
920
920
  "Permissions settings": "Настройки разрешений",
921
921
  "Permissions": "Разрешения",
922
922
  "Permissions settings updated": "Настройки разрешений обновлены",
923
- "Upload file(s)": "Upload file(s)",
924
- "Split paste": "Split paste",
925
- "Separate paste content into separate inputs": "Separate paste content into separate inputs",
926
- "Preview": "Preview",
927
- "Create new row": "Create new row",
928
- "Descending?": "Descending?",
929
- "Only include rows where this formula is true. ": "Only include rows where this formula is true. ",
930
- "Use %s to access current user ID": "Use %s to access current user ID",
931
- "Formula value": "Formula value",
932
- "Units": "Units",
923
+ "Upload file(s)": "Загрузка файл(ов)",
924
+ "Split paste": "Раздельная вставка",
925
+ "Separate paste content into separate inputs": "Разделять выставляемый конент на отдельные элементы",
926
+ "Preview": "Превью",
927
+ "Create new row": "Создать новую строку",
928
+ "Descending?": "В обратном порядке?",
929
+ "Only include rows where this formula is true. ": "Включать только строки, для который формула возвращает true. ",
930
+ "Use %s to access current user ID": "Использовать %s для доступа к ID пользователя",
931
+ "Formula value": "Значение формулы",
932
+ "Units": "Единицы",
933
933
  "%s view - %s on %s": "%s view - %s on %s",
934
- "Password Repeat": "Password Repeat",
935
- "Remember me": "Remember me",
936
- "Files accept filter ": "Files accept filter ",
934
+ "Password Repeat": "Повторите пароль",
935
+ "Remember me": "Запомнить меня",
937
936
  "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `text/csv,audio/*,video/*,image/*`",
938
- "Home Page by Role": "Home Page by Role",
939
- "Files accept filter": "Files accept filter",
940
- "Specify how to create a new row": "Specify how to create a new row",
941
- "Cascade delete to file": "Cascade delete to file",
937
+ "Home Page by Role": "Домашняя страница в соответствие с ролью",
938
+ "Files accept filter": "Фильтр типов файлов при загрузке",
939
+ "Specify how to create a new row": "Укажите как создавать новую строку",
940
+ "Cascade delete to file": "Каскадное удаление к файлу",
942
941
  "Deleting a row will also delete the file referenced by this field": "Deleting a row will also delete the file referenced by this field",
943
942
  "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`": "Specifies a filter for what file types the user can pick from the file input dialog box. Example is `.doc,audio/*,video/*,image/*`",
944
- "Default Files accept filter": "Default Files accept filter",
945
943
  "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`": "Specifies a default filter for what file types the user can pick from the file input dialog box. Example is `.doc, text/csv,audio/*,video/*,image/*`",
946
- "No file found": "No file found"
944
+ "No file found": "Файл не найден",
945
+ "Default File accept filter": "Фильтр типов файлов для загрузки",
946
+ "File upload debug": "Флаг отладки загрузки файлов",
947
+ "Turn on to debug file upload in express-fileupload.": "Turn on to debug file upload in express-fileupload.",
948
+ "File upload size limit in bytes": "Ограничение на размер файла при загрузке (в байтах)",
949
+ "Defines in bytes limit for upload files in express-fileupload.": "Defines in bytes limit for upload files in express-fileupload.",
950
+ "File upload timeout": "Таймаут загрузки файлов",
951
+ "Defines how long to wait for data before aborting for express-fileupload. Set to 0 if you want to turn off timeout checks. ": "Defines how long to wait for data before aborting for express-fileupload. Set to 0 if you want to turn off timeout checks. ",
952
+ "Files settings": "Настройки Файлов"
947
953
  }
package/markup/admin.js CHANGED
@@ -68,14 +68,40 @@ const restore_backup = (csrf, inner, action = `/admin/restore`) =>
68
68
  * @param {*} opts.req
69
69
  * @returns {object}
70
70
  */
71
- const add_edit_bar = ({ role, title, contents, what, url, req }) => {
71
+ const add_edit_bar = ({
72
+ role,
73
+ title,
74
+ contents,
75
+ what,
76
+ url,
77
+ req,
78
+ viewtemplate,
79
+ table,
80
+ cfgUrl,
81
+ }) => {
72
82
  if (role > 1 && req && req.xhr) return { above: [contents] }; //make sure not put in card
73
83
  if (role > 1) return contents;
84
+ let viewSpec = "";
85
+ if (viewtemplate) viewSpec = viewtemplate;
86
+ if (table) {
87
+ const tbl = Table.findOne(table);
88
+ if (tbl)
89
+ viewSpec = `${viewSpec} on <a href="/table/${table}">${tbl.name}</a>`;
90
+ }
91
+
74
92
  const bar = div(
75
93
  { class: "alert alert-light d-print-none admin-edit-bar" },
76
94
  title,
77
- what && span({ class: "ms-1 badge bg-primary" }, what),
78
- a({ class: "ms-4", href: url }, "Edit&nbsp;", i({ class: "fas fa-edit" }))
95
+ what && span({ class: "ms-1 me-2 badge bg-primary" }, what),
96
+ a({ class: "ms-2", href: url }, "Edit&nbsp;", i({ class: "fas fa-edit" })),
97
+ cfgUrl
98
+ ? a(
99
+ { class: "ms-1 me-3", href: cfgUrl },
100
+ "Configure&nbsp;",
101
+ i({ class: "fas fa-cog" })
102
+ )
103
+ : "",
104
+ viewSpec
79
105
  );
80
106
 
81
107
  if (contents.above) {
@@ -113,35 +139,35 @@ const send_settings_page = ({
113
139
  const pillCard = no_nav_pills
114
140
  ? []
115
141
  : [
116
- {
117
- type: "card",
118
- class: "mt-0",
119
- contents: div(
120
- { class: "d-flex" },
121
- ul(
122
- { class: "nav nav-pills plugin-section" },
123
- sub_sections.map(({ text, href }) =>
124
- li(
125
- { class: "nav-item" },
126
- a(
127
- {
128
- href,
129
- class: ["nav-link", active_sub === text && "active"],
130
- },
131
- req.__(text)
142
+ {
143
+ type: "card",
144
+ class: "mt-0",
145
+ contents: div(
146
+ { class: "d-flex" },
147
+ ul(
148
+ { class: "nav nav-pills plugin-section" },
149
+ sub_sections.map(({ text, href }) =>
150
+ li(
151
+ { class: "nav-item" },
152
+ a(
153
+ {
154
+ href,
155
+ class: ["nav-link", active_sub === text && "active"],
156
+ },
157
+ req.__(text)
158
+ )
132
159
  )
133
160
  )
134
161
  )
135
- )
136
- ),
137
- },
138
- ];
162
+ ),
163
+ },
164
+ ];
139
165
  // headers
140
166
  const title = headers
141
167
  ? {
142
- title: req.__(active_sub),
143
- headers,
144
- }
168
+ title: req.__(active_sub),
169
+ headers,
170
+ }
145
171
  : req.__(active_sub);
146
172
  res.sendWrap(title, {
147
173
  above: [
@@ -158,10 +184,10 @@ const send_settings_page = ({
158
184
  },
159
185
  ...(sub2_page
160
186
  ? [
161
- {
162
- text: sub2_page,
163
- },
164
- ]
187
+ {
188
+ text: sub2_page,
189
+ },
190
+ ]
165
191
  : []),
166
192
  ],
167
193
  },
@@ -190,9 +216,9 @@ const send_infoarch_page = (args) => {
190
216
  { text: "Languages", href: "/site-structure/localizer" },
191
217
  ...(tenant_list
192
218
  ? [
193
- { text: "Tenants", href: "/tenant/list" },
194
- { text: "Multitenancy", href: "/tenant/settings" },
195
- ]
219
+ { text: "Tenants", href: "/tenant/list" },
220
+ { text: "Multitenancy", href: "/tenant/settings" },
221
+ ]
196
222
  : []),
197
223
  { text: "Tags", href: "/tag" },
198
224
  { text: "Diagram", href: "/diagram" },
@@ -318,7 +344,7 @@ const viewAttributes = async (key) => {
318
344
  const flash_restart_if_required = (cfgForm, req) => {
319
345
  let restart = false;
320
346
  cfgForm.fields.forEach((f) => {
321
- if (configTypes[f.name].restart_required) {
347
+ if (configTypes[f.name]?.restart_required) {
322
348
  const current = getState().getConfig(f.name);
323
349
  if (current !== cfgForm.values[f.name]) restart = true;
324
350
  }
@@ -335,8 +361,8 @@ const flash_restart = (req) => {
335
361
  req.flash(
336
362
  "warning",
337
363
  req.__(`Restart required for changes to take effect.`) +
338
- " " +
339
- a({ href: "/admin/system" }, req.__("Restart here"))
364
+ " " +
365
+ a({ href: "/admin/system" }, req.__("Restart here"))
340
366
  );
341
367
  };
342
368
 
@@ -358,8 +384,24 @@ const config_fields_form = async ({
358
384
  const state = getState();
359
385
  const fields = [];
360
386
  const tenant = db.getTenantSchema();
361
-
387
+ const roleAttribs = {
388
+ options: (await User.get_roles()).map((r) => ({
389
+ label: r.role,
390
+ name: `${r.id}`,
391
+ })),
392
+ };
393
+ const getTenants = async () => {
394
+ const tens = await db.select("_sc_tenants");
395
+ return { options: tens.map((t) => t.subdomain) };
396
+ };
362
397
  for (const name of field_names) {
398
+ if (typeof name === "object" && name.section_header) {
399
+ fields.push({
400
+ input_type: "section_header",
401
+ label: name.section_header,
402
+ });
403
+ continue;
404
+ }
363
405
  values[name] = state.getConfig(name);
364
406
  // console.log(`config field name: %s`,name);
365
407
  if (configTypes[name].root_only && tenant !== db.connectObj.default_schema)
@@ -369,16 +411,7 @@ const config_fields_form = async ({
369
411
  const isTenant = configTypes[name].type === "Tenant";
370
412
  const label = configTypes[name].label || name;
371
413
  const sublabel = configTypes[name].sublabel || configTypes[name].blurb;
372
- const roleAttribs = {
373
- options: (await User.get_roles()).map((r) => ({
374
- label: r.role,
375
- name: `${r.id}`,
376
- })),
377
- };
378
- const getTenants = async () => {
379
- const tens = await db.select("_sc_tenants");
380
- return { options: tens.map((t) => t.subdomain) };
381
- };
414
+
382
415
  fields.push({
383
416
  name,
384
417
  ...configTypes[name],
@@ -389,16 +422,16 @@ const config_fields_form = async ({
389
422
  isView || isRole || isTenant
390
423
  ? "String"
391
424
  : configTypes[name].input_type
392
- ? undefined
393
- : configTypes[name].type,
425
+ ? undefined
426
+ : configTypes[name].type,
394
427
  input_type: configTypes[name].input_type,
395
428
  attributes: isView
396
429
  ? await viewAttributes(name)
397
430
  : isRole
398
- ? roleAttribs
399
- : isTenant
400
- ? await getTenants()
401
- : configTypes[name].attributes,
431
+ ? roleAttribs
432
+ : isTenant
433
+ ? await getTenants()
434
+ : configTypes[name].attributes,
402
435
  });
403
436
  }
404
437
  const form = new Form({
package/markup/blockly.js CHANGED
@@ -38,7 +38,7 @@ const blocklyImportScripts = ({ locale }) =>
38
38
  });
39
39
 
40
40
  /**
41
- * @param {boolean} hasActions
41
+ * @param {boolean} hasActions
42
42
  * @returns {string}
43
43
  */
44
44
  const blocklyToolbox = (hasActions) => `
@@ -9,7 +9,7 @@ const { contract, is } = require("contractis");
9
9
  const { getState } = require("@saltcorn/data/db/state");
10
10
 
11
11
  /**
12
- * @param {*} type
12
+ * @param {*} type
13
13
  * @returns {*}
14
14
  */
15
15
  const toJsType = (type) =>
@@ -23,8 +23,8 @@ const toJsType = (type) =>
23
23
  }[type] || type);
24
24
 
25
25
  /**
26
- * @param {*} type
27
- * @param {object[]} fields
26
+ * @param {*} type
27
+ * @param {object[]} fields
28
28
  * @returns {string[]}
29
29
  */
30
30
  const intExamples = (type, fields) => {
@@ -43,8 +43,8 @@ const intExamples = (type, fields) => {
43
43
  };
44
44
 
45
45
  /**
46
- * @param {*} type
47
- * @param {object[]} fields
46
+ * @param {*} type
47
+ * @param {object[]} fields
48
48
  * @returns {string[]}
49
49
  */
50
50
  const colorExamples = (type, fields) => {
@@ -58,8 +58,8 @@ const colorExamples = (type, fields) => {
58
58
  };
59
59
 
60
60
  /**
61
- * @param {*} type
62
- * @param {object[]} fields
61
+ * @param {*} type
62
+ * @param {object[]} fields
63
63
  * @returns {string[]}
64
64
  */
65
65
  const stringExamples = (type, fields) => {
@@ -78,8 +78,8 @@ const stringExamples = (type, fields) => {
78
78
  };
79
79
 
80
80
  /**
81
- * @param {*} type
82
- * @param {object[]} fields
81
+ * @param {*} type
82
+ * @param {object[]} fields
83
83
  * @returns {string[]}
84
84
  */
85
85
  const floatExamples = (type, fields) => {
@@ -107,8 +107,8 @@ const floatExamples = (type, fields) => {
107
107
  };
108
108
 
109
109
  /**
110
- * @param {*} type
111
- * @param {object[]} fields
110
+ * @param {*} type
111
+ * @param {object[]} fields
112
112
  * @returns {string[]}
113
113
  */
114
114
  const boolExamples = (type, fields) => {
@@ -139,10 +139,10 @@ const boolExamples = (type, fields) => {
139
139
  };
140
140
 
141
141
  /**
142
- * @param {string} type
143
- * @param {*} stored
144
- * @param {object[]} allFields
145
- * @param {object} req
142
+ * @param {string} type
143
+ * @param {*} stored
144
+ * @param {object[]} allFields
145
+ * @param {object} req
146
146
  * @returns {p[]}
147
147
  */
148
148
  const expressionBlurb = (type, stored, allFields, req) => {
package/markup/forms.js CHANGED
@@ -50,33 +50,32 @@ const editRoleForm = ({ url, current_role, roles, req }) =>
50
50
  * @param folder
51
51
  * @returns {Form}
52
52
  */
53
- const fileUploadForm = (req, folder
54
- ) => {
55
- const frm = form(
56
- {
57
- action: "/files/upload",
58
- method: "post",
59
- enctype: "multipart/form-data",
60
- },
61
- csrfField(req),
62
- label(req.__("Upload file(s)")),
63
- input({
64
- name: "file",
65
- class: "form-control ms-1 w-unset d-inline",
66
- type: "file",
67
- onchange: "form.submit()",
68
- multiple: true,
69
- }),
70
- folder && input({type: "hidden", name: "folder", value: folder})
71
- );
72
- return frm;
53
+ const fileUploadForm = (req, folder) => {
54
+ const frm = form(
55
+ {
56
+ action: "/files/upload",
57
+ method: "post",
58
+ enctype: "multipart/form-data",
59
+ },
60
+ csrfField(req),
61
+ label(req.__("Upload file(s)")),
62
+ input({
63
+ name: "file",
64
+ class: "form-control ms-1 w-unset d-inline",
65
+ type: "file",
66
+ onchange: "form.submit()",
67
+ multiple: true,
68
+ }),
69
+ folder && input({ type: "hidden", name: "folder", value: folder })
70
+ );
71
+ return frm;
73
72
  };
74
73
 
75
74
  /**
76
75
  * Get Wizard Card Title
77
76
  * @param {string} wizardTitle
78
- * @param {*} wf
79
- * @param {object} wfres
77
+ * @param {*} wf
78
+ * @param {object} wfres
80
79
  * @returns {string}
81
80
  */
82
81
  const wizardCardTitle = (wizardTitle, wf, wfres) =>