@saltcorn/server 0.7.4-beta.3 → 0.8.0-beta.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/routes/files.js CHANGED
@@ -11,6 +11,7 @@ const User = require("@saltcorn/data/models/user");
11
11
  const { getState } = require("@saltcorn/data/db/state");
12
12
  const s3storage = require("../s3storage");
13
13
  const resizer = require("resize-with-sharp-or-jimp");
14
+ const db = require("@saltcorn/data/db");
14
15
 
15
16
  const {
16
17
  mkTable,
@@ -20,7 +21,7 @@ const {
20
21
  post_delete_btn,
21
22
  } = require("@saltcorn/markup");
22
23
  const { isAdmin, error_catcher, setTenant } = require("./utils.js");
23
- const { h1, div, text } = require("@saltcorn/markup/tags");
24
+ const { h1, div, text, button, i, a } = require("@saltcorn/markup/tags");
24
25
  // const { csrfField } = require("./utils");
25
26
  const { editRoleForm, fileUploadForm } = require("../markup/forms.js");
26
27
  const { strictParseInt } = require("@saltcorn/data/plugin-helper");
@@ -31,6 +32,7 @@ const {
31
32
  } = require("../markup/admin");
32
33
  // const fsp = require("fs").promises;
33
34
  const fs = require("fs");
35
+ const path = require("path");
34
36
 
35
37
  /**
36
38
  * @type {object}
@@ -51,7 +53,7 @@ module.exports = router;
51
53
  */
52
54
  const editFileRoleForm = (file, roles, req) =>
53
55
  editRoleForm({
54
- url: `/files/setrole/${file.id}`,
56
+ url: `/files/setrole/${file.path_to_serve}`,
55
57
  current_role: file.min_role_read,
56
58
  roles,
57
59
  req,
@@ -68,49 +70,51 @@ router.get(
68
70
  isAdmin,
69
71
  error_catcher(async (req, res) => {
70
72
  // todo limit select from file by 10 or 20
71
- const rows = await File.find({}, { orderBy: "filename" });
73
+ const { dir } = req.query
74
+ const safeDir = File.normalise(dir || "/")
75
+ const rows = await File.find({ folder: dir }, { orderBy: "filename" });
72
76
  const roles = await User.get_roles();
77
+ //console.log(rows);
78
+ if (safeDir && safeDir !== "/" && safeDir !== ".") {
79
+ let dirname = path.dirname(safeDir)
80
+ if (dirname === ".") dirname = "/"
81
+ rows.unshift(new File({
82
+ filename: "..",
83
+ location: dirname,
84
+ isDirectory: true,
85
+ mime_super: "",
86
+ mime_sub: "",
87
+ }))
88
+ }
89
+ if (req.xhr) {
90
+ for (const file of rows) {
91
+ file.location = file.path_to_serve
92
+ }
93
+ const directories = await File.allDirectories()
94
+ for (const file of directories) {
95
+ file.location = file.path_to_serve
96
+ }
97
+ res.json({ files: rows, roles, directories })
98
+ return
99
+ }
73
100
  send_files_page({
74
101
  res,
75
102
  req,
103
+ headers: [
104
+ {
105
+ script: `/static_assets/${db.connectObj.version_tag}/bundle.js`,
106
+ defer: true
107
+ },
108
+ {
109
+ css: `/static_assets/${db.connectObj.version_tag}/bundle.css`,
110
+ },
111
+ ],
76
112
  active_sub: "Files",
77
113
  contents: {
78
114
  type: "card",
79
115
  contents: [
80
- mkTable(
81
- [
82
- {
83
- label: req.__("Filename"),
84
- key: (r) =>
85
- div(
86
- { "data-inline-edit-dest-url": `/files/setname/${r.id}` },
87
- r.filename
88
- ),
89
- },
90
- { label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
91
- { label: req.__("Media type"), key: (r) => r.mimetype },
92
- {
93
- label: req.__("Role to access"),
94
- key: (r) => editFileRoleForm(r, roles, req),
95
- },
96
- {
97
- label: req.__("Link"),
98
- key: (r) => link(`/files/serve/${r.id}`, req.__("Link")),
99
- },
100
- {
101
- label: req.__("Download"),
102
- key: (r) => link(`/files/download/${r.id}`, req.__("Download")),
103
- },
104
- {
105
- label: req.__("Delete"),
106
- key: (r) =>
107
- post_delete_btn(`/files/delete/${r.id}`, req, r.filename),
108
- },
109
- ],
110
- rows,
111
- { hover: true }
112
- ),
113
- fileUploadForm(req),
116
+ div({ id: "saltcorn-file-manager" }),
117
+ fileUploadForm(req, safeDir),
114
118
  ],
115
119
  },
116
120
  });
@@ -124,19 +128,21 @@ router.get(
124
128
  * @function
125
129
  */
126
130
  router.get(
127
- "/download/:id",
131
+ "/download/*",
128
132
  error_catcher(async (req, res) => {
129
133
  const role = req.user && req.user.id ? req.user.role_id : 10;
130
134
  const user_id = req.user && req.user.id;
131
- const { id } = req.params;
132
- const file = await File.findOne({ id });
133
- if (role <= file.min_role_read || (user_id && user_id === file.user_id)) {
135
+ const serve_path = req.params[0];
136
+ const file = await File.findOne(serve_path);
137
+
138
+ if (file && (role <= file.min_role_read || (user_id && user_id === file.user_id))) {
134
139
  res.type(file.mimetype);
135
140
  if (file.s3_store) s3storage.serveObject(file, res, true);
136
141
  else res.download(file.location, file.filename);
137
142
  } else {
138
- req.flash("warning", req.__("Not authorized"));
139
- res.redirect("/");
143
+ res
144
+ .status(404)
145
+ .sendWrap(req.__("Not found"), h1(req.__("File not found")));
140
146
  }
141
147
  })
142
148
  );
@@ -148,31 +154,25 @@ router.get(
148
154
  * @function
149
155
  */
150
156
  router.get(
151
- "/serve/:id",
157
+ "/serve/*",
152
158
  error_catcher(async (req, res) => {
153
159
  const role = req.user && req.user.id ? req.user.role_id : 10;
154
160
  const user_id = req.user && req.user.id;
155
- const { id } = req.params;
156
- let file;
157
- if (typeof strictParseInt(id) !== "undefined")
158
- file = await File.findOne({ id });
159
- else file = await File.findOne({ filename: id });
161
+ const serve_path = req.params[0];
162
+ //let file;
163
+ //if (typeof strictParseInt(id) !== "undefined")
164
+ const file = await File.findOne(serve_path);
160
165
 
161
- if (!file) {
162
- res
163
- .status(404)
164
- .sendWrap(req.__("Not found"), h1(req.__("File not found")));
165
- return;
166
- }
167
- if (role <= file.min_role_read || (user_id && user_id === file.user_id)) {
166
+ if (file && (role <= file.min_role_read || (user_id && user_id === file.user_id))) {
168
167
  res.type(file.mimetype);
169
168
  const cacheability = file.min_role_read === 10 ? "public" : "private";
170
169
  res.set("Cache-Control", `${cacheability}, max-age=86400`);
171
170
  if (file.s3_store) s3storage.serveObject(file, res, false);
172
171
  else res.sendFile(file.location);
173
172
  } else {
174
- req.flash("warning", req.__("Not authorized"));
175
- res.redirect("/");
173
+ res
174
+ .status(404)
175
+ .sendWrap(req.__("Not found"), h1(req.__("File not found")));
176
176
  }
177
177
  })
178
178
  );
@@ -184,23 +184,17 @@ router.get(
184
184
  * @function
185
185
  */
186
186
  router.get(
187
- "/resize/:id/:width_str/:height_str?",
187
+ "/resize/:width_str/:height_str/*",
188
188
  error_catcher(async (req, res) => {
189
189
  const role = req.user && req.user.id ? req.user.role_id : 10;
190
190
  const user_id = req.user && req.user.id;
191
- const { id, width_str, height_str } = req.params;
192
- let file;
193
- if (typeof strictParseInt(id) !== "undefined")
194
- file = await File.findOne({ id });
195
- else file = await File.findOne({ filename: id });
191
+ const { width_str, height_str } = req.params;
192
+ const serve_path = req.params[0];
196
193
 
197
- if (!file) {
198
- res
199
- .status(404)
200
- .sendWrap(req.__("Not found"), h1(req.__("File not found")));
201
- return;
202
- }
203
- if (role <= file.min_role_read || (user_id && user_id === file.user_id)) {
194
+
195
+ const file = await File.findOne(serve_path);
196
+
197
+ if (file && (role <= file.min_role_read || (user_id && user_id === file.user_id))) {
204
198
  res.type(file.mimetype);
205
199
  const cacheability = file.min_role_read === 10 ? "public" : "private";
206
200
  res.set("Cache-Control", `${cacheability}, max-age=86400`);
@@ -208,12 +202,14 @@ router.get(
208
202
  if (file.s3_store) s3storage.serveObject(file, res, false);
209
203
  else {
210
204
  const width = strictParseInt(width_str);
211
- const height = height_str ? strictParseInt(height_str) : null;
205
+ const height = height_str && height_str !== "0"
206
+ ? strictParseInt(height_str) : null;
212
207
  if (!width) {
213
208
  res.sendFile(file.location);
214
209
  return;
215
210
  }
216
- const fnm = `${file.location}_w${width}${height ? `_h${height}` : ""}`;
211
+ const basenm = path.join(path.dirname(file.location), '_resized_' + path.basename(file.location))
212
+ const fnm = `${basenm}_w${width}${height ? `_h${height}` : ""}`;
217
213
  if (!fs.existsSync(fnm)) {
218
214
  await resizer({
219
215
  fromFileName: file.location,
@@ -225,8 +221,9 @@ router.get(
225
221
  res.sendFile(fnm);
226
222
  }
227
223
  } else {
228
- req.flash("warning", req.__("Not authorized"));
229
- res.redirect("/");
224
+ res
225
+ .status(404)
226
+ .sendWrap(req.__("Not found"), h1(req.__("File not found")));
230
227
  }
231
228
  })
232
229
  );
@@ -238,23 +235,42 @@ router.get(
238
235
  * @function
239
236
  */
240
237
  router.post(
241
- "/setrole/:id",
238
+ "/setrole/*",
242
239
  isAdmin,
243
240
  error_catcher(async (req, res) => {
244
- const { id } = req.params;
241
+ const serve_path = req.params[0];
242
+ const file = await File.findOne(serve_path);
245
243
  const role = req.body.role;
246
- await File.update(+id, { min_role_read: role });
247
- const file = await File.findOne({ id });
248
244
  const roles = await User.get_roles();
249
245
  const roleRow = roles.find((r) => r.id === +role);
250
- if (roleRow && file)
251
- req.flash(
252
- "success",
253
- req.__(`Minimum role for %s updated to %s`, file.filename, roleRow.role)
254
- );
255
- else req.flash("success", req.__(`Minimum role updated`));
256
246
 
257
- res.redirect("/files");
247
+ if (roleRow && file) {
248
+ await file.set_role(role);
249
+
250
+ }
251
+
252
+
253
+ res.redirect(file ? `/files?dir=${encodeURIComponent(file.current_folder)}` : "/files");
254
+ })
255
+ );
256
+
257
+
258
+ router.post(
259
+ "/move/*",
260
+ isAdmin,
261
+ error_catcher(async (req, res) => {
262
+ const serve_path = req.params[0];
263
+ const file = await File.findOne(serve_path);
264
+ const new_path = req.body.new_path;
265
+
266
+ if (file) {
267
+ await file.move_to_dir(new_path);
268
+ }
269
+ if (req.xhr) {
270
+ res.json({ success: "ok" })
271
+ return
272
+ }
273
+ res.redirect(file ? `/files?dir=${encodeURIComponent(file.current_folder)}` : "/files");
258
274
  })
259
275
  );
260
276
 
@@ -265,14 +281,28 @@ router.post(
265
281
  * @function
266
282
  */
267
283
  router.post(
268
- "/setname/:id",
284
+ "/setname/*",
269
285
  isAdmin,
270
286
  error_catcher(async (req, res) => {
271
- const { id } = req.params;
287
+ const serve_path = req.params[0];
272
288
  const filename = req.body.value;
273
- await File.update(+id, { filename });
274
289
 
275
- res.redirect("/files");
290
+ const file = await File.findOne(serve_path);
291
+ await file.rename(filename);
292
+
293
+ res.redirect(`/files?dir=${encodeURIComponent(file.current_folder)}`);
294
+
295
+ })
296
+ );
297
+
298
+ router.post(
299
+ "/new-folder",
300
+ isAdmin,
301
+ error_catcher(async (req, res) => {
302
+ const { name, folder } = req.body
303
+ await File.new_folder(name, folder);
304
+
305
+ res.json({ success: "ok" });
276
306
  })
277
307
  );
278
308
 
@@ -286,9 +316,11 @@ router.post(
286
316
  "/upload",
287
317
  setTenant, // TODO why is this needed?????
288
318
  error_catcher(async (req, res) => {
319
+ let { folder } = req.body
289
320
  let jsonResp = {};
290
321
  const min_role_upload = getState().getConfig("min_role_upload", 1);
291
322
  const role = req.user && req.user.id ? req.user.role_id : 10;
323
+ let file_for_redirect
292
324
  if (role > +min_role_upload) {
293
325
  if (!req.xhr) req.flash("warning", req.__("Not authorized"));
294
326
  else jsonResp = { error: "Not authorized" };
@@ -300,9 +332,11 @@ router.post(
300
332
  const f = await File.from_req_files(
301
333
  req.files.file,
302
334
  req.user.id,
303
- +min_role_read
335
+ +min_role_read,
336
+ folder ? File.normalise(folder) : undefined
304
337
  );
305
338
  const many = Array.isArray(f);
339
+ file_for_redirect = many ? f[0] : f
306
340
  if (!req.xhr)
307
341
  req.flash(
308
342
  "success",
@@ -317,14 +351,18 @@ router.post(
317
351
  jsonResp = {
318
352
  success: {
319
353
  filename: many ? f.map((fl) => fl.filename) : f.filename,
320
- id: many ? f.map((fl) => fl.id) : f.id,
354
+ location: many ? f.map((fl) => fl.path_to_serve) : f.path_to_serve,
321
355
  url: many
322
- ? f.map((fl) => `/files/serve/${fl.id}`)
323
- : `/files/serve/${f.id}`,
356
+ ? f.map((fl) => `/files/serve/${fl.path_to_serve}`)
357
+ : `/files/serve/${f.path_to_serve}`,
324
358
  },
325
359
  };
326
360
  }
327
- if (!req.xhr) res.redirect("/files");
361
+ if (!req.xhr)
362
+ res.redirect(!file_for_redirect
363
+ ? '/files'
364
+ : `/files?dir=${encodeURIComponent(file_for_redirect.current_folder)}`);
365
+
328
366
  else res.json(jsonResp);
329
367
  })
330
368
  );
@@ -336,11 +374,11 @@ router.post(
336
374
  * @function
337
375
  */
338
376
  router.post(
339
- "/delete/:id",
377
+ "/delete/*",
340
378
  isAdmin,
341
379
  error_catcher(async (req, res) => {
342
- const { id } = req.params;
343
- const f = await File.findOne({ id });
380
+ const serve_path = req.params[0];
381
+ const f = await File.findOne(serve_path);
344
382
  if (!f) {
345
383
  req.flash("error", "File not found");
346
384
  res.redirect("/files");
@@ -351,10 +389,8 @@ router.post(
351
389
  );
352
390
  if (result && result.error) {
353
391
  req.flash("error", result.error);
354
- } else {
355
- req.flash("success", req.__(`File %s deleted`, text(f.filename)));
356
392
  }
357
- res.redirect(`/files`);
393
+ res.redirect(`/files?dir=${encodeURIComponent(f.current_folder)}`);
358
394
  })
359
395
  );
360
396
 
@@ -194,7 +194,7 @@ const filesTab = async (req) => {
194
194
  [
195
195
  {
196
196
  label: req.__("Filename"),
197
- key: (r) => link(`/files/serve/${r.id}`, r.filename),
197
+ key: (r) => r.isDirectory ? r.filename : link(`/files/serve/${r.path_to_serve}`, r.filename),
198
198
  },
199
199
  { label: req.__("Size (KiB)"), key: "size_kb", align: "right" },
200
200
  { label: req.__("Media type"), key: (r) => r.mimetype },
@@ -352,7 +352,7 @@ const helpCard = (req) =>
352
352
  const welcome_page = async (req) => {
353
353
  const packs_available = await fetch_available_packs();
354
354
  const packlist = [
355
- ...packs_available.slice(0, 5),
355
+ ...(packs_available || []).slice(0, 5),
356
356
  { name: req.__("More..."), description: "" },
357
357
  ];
358
358
  const tables = await Table.find({}, { cached: true });
@@ -60,7 +60,7 @@ const languageForm = (req) =>
60
60
  {
61
61
  name: "locale",
62
62
  label: req.__("Locale"),
63
- sublabel: "Locale identifier short code, e.g. en, zh, fr, ar etc. ",
63
+ sublabel: req.__("Locale identifier short code, e.g. en, zh, fr, ar etc. "),
64
64
  type: "String",
65
65
  required: true,
66
66
  },
@@ -68,7 +68,7 @@ const languageForm = (req) =>
68
68
  name: "is_default",
69
69
  label: req.__("Default language"),
70
70
  sublabel:
71
- "Is this the default language in which the application is built?",
71
+ req.__("Is this the default language in which the application is built?"),
72
72
  type: "Bool",
73
73
  },
74
74
  ],
package/routes/list.js CHANGED
@@ -217,7 +217,8 @@ jsGrid.fields.versions = VersionsField;
217
217
  const arrangeIdFirst = (flds) => {
218
218
  const noId = flds.filter((f) => f.name !== "id");
219
219
  const id = flds.find((f) => f.name === "id");
220
- return [id, ...noId];
220
+ if (id) return [id, ...noId];
221
+ else return flds
221
222
  };
222
223
 
223
224
  /**
@@ -369,9 +370,8 @@ router.get(
369
370
  })
370
371
  })
371
372
  window.tabulator_table = new Tabulator("#jsGrid", {
372
- ajaxURL:"/api/${table.name}${
373
- table.versioned ? "?versioncount=on" : ""
374
- }",
373
+ ajaxURL:"/api/${table.name}${table.versioned ? "?versioncount=on" : ""
374
+ }",
375
375
  layout:"fitColumns",
376
376
  columns,
377
377
  height:"100%",
package/routes/page.js CHANGED
@@ -7,10 +7,8 @@
7
7
  const Router = require("express-promise-router");
8
8
 
9
9
  const Page = require("@saltcorn/data/models/page");
10
- const { div, a, i } = require("@saltcorn/markup/tags");
11
- const { renderForm } = require("@saltcorn/markup");
12
10
  const { getState } = require("@saltcorn/data/db/state");
13
- const { error_catcher, scan_for_page_title } = require("../routes/utils.js");
11
+ const { error_catcher, scan_for_page_title, isAdmin } = require("../routes/utils.js");
14
12
  const { add_edit_bar } = require("../markup/admin.js");
15
13
  const { traverseSync } = require("@saltcorn/data/models/layout");
16
14
  const { run_action_column } = require("@saltcorn/data/plugin-helper");
@@ -67,6 +65,21 @@ router.get(
67
65
  })
68
66
  );
69
67
 
68
+ router.post(
69
+ "/:pagename/preview",
70
+ isAdmin,
71
+ error_catcher(async (req, res) => {
72
+ const { pagename } = req.params;
73
+ const page = await Page.findOne({ name: pagename });
74
+ if (!page) {
75
+ res.send("");
76
+ return;
77
+ }
78
+ const contents = await page.run(req.query, { res, req });
79
+ res.sendWrap({}, contents);
80
+ })
81
+ );
82
+
70
83
  /**
71
84
  * @name post/:pagename/action/:rndid
72
85
  * @function
@@ -21,7 +21,7 @@ const { add_to_menu } = require("@saltcorn/admin-models/models/pack");
21
21
  const db = require("@saltcorn/data/db");
22
22
  const { getPageList } = require("./common_lists");
23
23
 
24
- const { isAdmin, error_catcher } = require("./utils.js");
24
+ const { isAdmin, error_catcher, addOnDoneRedirect } = require("./utils.js");
25
25
  const {
26
26
  mkTable,
27
27
  renderForm,
@@ -54,7 +54,7 @@ const pagePropertiesForm = async (req) => {
54
54
  const roles = await User.get_roles();
55
55
 
56
56
  const form = new Form({
57
- action: "/pageedit/edit-properties",
57
+ action: addOnDoneRedirect("/pageedit/edit-properties", req),
58
58
  fields: [
59
59
  new Field({
60
60
  label: req.__("Name"),
@@ -100,6 +100,7 @@ const pageBuilderData = async (req, context) => {
100
100
  const views = await View.find();
101
101
  const pages = await Page.find();
102
102
  const images = await File.find({ mime_super: "image" });
103
+ images.forEach(im => im.location = im.path_to_serve)
103
104
  const roles = await User.get_roles();
104
105
  const stateActions = getState().actions;
105
106
  const actions = [
@@ -130,6 +131,9 @@ const pageBuilderData = async (req, context) => {
130
131
  const fs = await view.get_state_fields();
131
132
  for (const frec of fs) {
132
133
  const f = new Field(frec);
134
+ if (f.input_type === "hidden") continue;
135
+ if (f.name === "_fts") continue;
136
+
133
137
  f.required = false;
134
138
  if (f.type && f.type.name === "Bool") f.fieldview = "tristate";
135
139
 
@@ -156,6 +160,7 @@ const pageBuilderData = async (req, context) => {
156
160
  }
157
161
  }
158
162
  }
163
+ //console.log(fixed_state_fields.ListTasks);
159
164
  return {
160
165
  views,
161
166
  images,
@@ -357,7 +362,7 @@ router.post(
357
362
  if (!pageRow.fixed_states) pageRow.fixed_states = {};
358
363
  if (!pageRow.layout) pageRow.layout = {};
359
364
  await Page.create(pageRow);
360
- res.redirect(`/pageedit/edit/${pageRow.name}`);
365
+ res.redirect(addOnDoneRedirect(`/pageedit/edit/${pageRow.name}`, req));
361
366
  }
362
367
  }
363
368
  })
@@ -416,20 +421,23 @@ router.post(
416
421
  error_catcher(async (req, res) => {
417
422
  const { pagename } = req.params;
418
423
 
424
+ let redirectTarget = req.query.on_done_redirect
425
+ ? `/${req.query.on_done_redirect}`
426
+ : "/pageedit";
419
427
  const page = await Page.findOne({ name: pagename });
420
428
  if (!page) {
421
429
  req.flash("error", req.__(`Page %s not found`, pagename));
422
- res.redirect(`/pageedit`);
430
+ res.redirect(redirectTarget);
423
431
  } else if (req.body.layout) {
424
432
  await Page.update(page.id, {
425
433
  layout: decodeURIComponent(req.body.layout),
426
434
  });
427
435
 
428
436
  req.flash("success", req.__(`Page %s saved`, pagename));
429
- res.redirect(`/pageedit`);
437
+ res.redirect(redirectTarget);
430
438
  } else {
431
439
  req.flash("error", req.__(`Error processing page`));
432
- res.redirect(`/pageedit`);
440
+ res.redirect(redirectTarget);
433
441
  }
434
442
  })
435
443
  );
@@ -566,13 +574,13 @@ router.post(
566
574
  const page = await Page.findOne({ id });
567
575
  const roles = await User.get_roles();
568
576
  const roleRow = roles.find((r) => r.id === +role);
569
- if (roleRow && page)
570
- req.flash(
571
- "success",
572
- req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
573
- );
574
- else req.flash("success", req.__(`Minimum role updated`));
575
-
576
- res.redirect("/pageedit");
577
+ const message =
578
+ roleRow && page
579
+ ? req.__(`Minimum role for %s updated to %s`, page.name, roleRow.role)
580
+ : req.__(`Minimum role updated`);
581
+ if (!req.xhr) {
582
+ req.flash("success", message);
583
+ res.redirect("/pageedit");
584
+ } else res.json({ okay: true, responseText: message });
577
585
  })
578
586
  );
package/routes/scapi.js CHANGED
@@ -185,7 +185,7 @@ router.get(
185
185
  { session: false },
186
186
  async function (err, user, info) {
187
187
  if (accessAllowedRead(req, user)) {
188
- const triggers = await Trigger.find({});
188
+ const triggers = Trigger.find({});
189
189
 
190
190
  res.json({ success: triggers });
191
191
  } else {
package/routes/search.js CHANGED
@@ -188,7 +188,7 @@ const runSearch = async ({ q, _page, table }, req, res) => {
188
188
  pages: current_page + (vresps.length === 20 ? 1 : 0),
189
189
  trailing_ellipsis: vresps.length === 20,
190
190
  get_page_link: (n) =>
191
- `javascript:gopage(${n}, 20, {table:'${tableName}'})`,
191
+ `javascript:gopage(${n}, 20, undefined, {table:'${tableName}'})`,
192
192
  });
193
193
  }
194
194
 
package/routes/tables.js CHANGED
@@ -135,6 +135,7 @@ const tableForm = async (table, req) => {
135
135
  name: "min_role_read",
136
136
  input_type: "select",
137
137
  options: roleOptions,
138
+ attributes: { asideNext: !table.external }
138
139
  },
139
140
  ...(table.external
140
141
  ? []
@@ -885,15 +886,13 @@ router.post(
885
886
  const table = await Table.findOne({ id: parseInt(id) });
886
887
  const old_versioned = table.versioned;
887
888
  let hasError = false;
889
+ let notify = ""
888
890
  if (!rest.versioned) rest.versioned = false;
889
891
  if (rest.ownership_field_id === "_formula") {
890
892
  rest.ownership_field_id = null;
891
893
  const fmlValidRes = expressionValidator(rest.ownership_formula);
892
894
  if (typeof fmlValidRes === "string") {
893
- req.flash(
894
- "error",
895
- req.__(`Invalid ownership formula: %s`, fmlValidRes)
896
- );
895
+ notify = req.__(`Invalid ownership formula: %s`, fmlValidRes)
897
896
  hasError = true;
898
897
  }
899
898
  } else rest.ownership_formula = null;
@@ -911,7 +910,7 @@ router.post(
911
910
  else if (!hasError) req.flash("success", req.__("Table saved"));
912
911
 
913
912
  if (!req.xhr) res.redirect(`/table/${id}`);
914
- else res.json({ success: "ok" });
913
+ else res.json({ success: "ok", notify });
915
914
  }
916
915
  })
917
916
  );