@saltcorn/server 0.6.1-beta.1 → 0.6.2-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.
@@ -21,8 +21,8 @@ const Trigger = require("@saltcorn/data/models/trigger");
21
21
  const { fileUploadForm } = require("../markup/forms");
22
22
 
23
23
  /**
24
- * @param {*} tables
25
- * @param {object} req
24
+ * @param {*} tables
25
+ * @param {object} req
26
26
  * @returns {Table}
27
27
  */
28
28
  const tableTable = (tables, req) =>
@@ -38,8 +38,8 @@ const tableTable = (tables, req) =>
38
38
  );
39
39
 
40
40
  /**
41
- * @param {*} tables
42
- * @param {object} req
41
+ * @param {*} tables
42
+ * @param {object} req
43
43
  * @returns {object}
44
44
  */
45
45
  const tableCard = (tables, req) => ({
@@ -67,8 +67,8 @@ const tableCard = (tables, req) => ({
67
67
  });
68
68
 
69
69
  /**
70
- * @param {*} views
71
- * @param {object} req
70
+ * @param {*} views
71
+ * @param {object} req
72
72
  * @returns {Table}
73
73
  */
74
74
  const viewTable = (views, req) =>
@@ -88,8 +88,8 @@ const viewTable = (views, req) =>
88
88
  );
89
89
 
90
90
  /**
91
- * @param {*} views
92
- * @param {object} req
91
+ * @param {*} views
92
+ * @param {object} req
93
93
  * @returns {object}
94
94
  */
95
95
  const viewCard = (views, req) => ({
@@ -119,8 +119,8 @@ const viewCard = (views, req) => ({
119
119
  });
120
120
 
121
121
  /**
122
- * @param {*} pages
123
- * @param {object} req
122
+ * @param {*} pages
123
+ * @param {object} req
124
124
  * @returns {Table}
125
125
  */
126
126
  const pageTable = (pages, req) =>
@@ -140,8 +140,8 @@ const pageTable = (pages, req) =>
140
140
  );
141
141
 
142
142
  /**
143
- * @param {*} pages
144
- * @param {object} req
143
+ * @param {*} pages
144
+ * @param {object} req
145
145
  * @returns {object}
146
146
  */
147
147
  const pageCard = (pages, req) => ({
@@ -172,11 +172,11 @@ const pageCard = (pages, req) => ({
172
172
  });
173
173
 
174
174
  /**
175
- * @param {object} req
175
+ * @param {object} req
176
176
  * @returns {Promise<div>}
177
177
  */
178
178
  const filesTab = async (req) => {
179
- const files = await File.find({}, { orderBy: "filename" });
179
+ const files = await File.find({}, { orderBy: "filename", cached: true });
180
180
  return div(
181
181
  files.length == 0
182
182
  ? p(req.__("No files"))
@@ -196,16 +196,10 @@ const filesTab = async (req) => {
196
196
  };
197
197
 
198
198
  /**
199
- * @param {object} req
199
+ * @param {object} req
200
200
  * @returns {Promise<div>}
201
201
  */
202
- const usersTab = async (req) => {
203
- const users = await User.find({}, { orderBy: "id" });
204
- const roles = await User.get_roles();
205
- var roleMap = {};
206
- roles.forEach((r) => {
207
- roleMap[r.id] = r.role;
208
- });
202
+ const usersTab = async (req, users, roleMap) => {
209
203
  return div(
210
204
  mkTable(
211
205
  [
@@ -219,20 +213,19 @@ const usersTab = async (req) => {
219
213
  users
220
214
  ),
221
215
  a(
222
- { href: `/useradmin/new`, class: "btn btn-secondary" },
216
+ { href: `/useradmin/new`, class: "btn btn-secondary my-3" },
223
217
  req.__("Create user")
224
218
  )
225
219
  );
226
220
  };
227
221
 
228
222
  /**
229
- * @param {object} req
223
+ * @param {object} req
230
224
  * @returns {Promise<div>}
231
225
  */
232
- const actionsTab = async (req) => {
233
- const triggers = await Trigger.findAllWithTableName();
234
-
226
+ const actionsTab = async (req, triggers) => {
235
227
  return div(
228
+ { class: "pb-3" },
236
229
  triggers.length <= 1 &&
237
230
  p(
238
231
  { class: "mt-2 pr-2" },
@@ -259,14 +252,73 @@ const actionsTab = async (req) => {
259
252
  triggers
260
253
  ),
261
254
  a(
262
- { href: "/actions/new", class: "btn btn-secondary btn-smj" },
255
+ { href: "/actions/new", class: "btn btn-secondary my-3" },
263
256
  req.__("Add trigger")
264
257
  )
265
258
  );
266
259
  };
260
+ const packTab = (req, packlist) =>
261
+ div(
262
+ { class: "pb-3 pt-2 pr-4" },
263
+ p(req.__("Instead of building, get up and running in no time with packs")),
264
+ p(
265
+ { class: "font-italic" },
266
+ req.__(
267
+ "Packs are collections of tables, views and plugins that give you a full application which you can then edit to suit your needs."
268
+ )
269
+ ),
270
+ mkTable(
271
+ [
272
+ { label: req.__("Name"), key: "name" },
273
+ {
274
+ label: req.__("Description"),
275
+ key: "description",
276
+ },
277
+ ],
278
+ packlist,
279
+ { noHeader: true }
280
+ ),
281
+ a(
282
+ { href: `/plugins?set=packs`, class: "btn btn-primary" },
283
+ req.__("Go to pack store »")
284
+ )
285
+ );
286
+
287
+ const helpCard = (req) =>
288
+ div(
289
+ { class: "pb-3 pt-2 pr-4" },
290
+ p(req.__("Confused?")),
291
+ p(
292
+ req.__(
293
+ "The Wiki contains the documentation and tutorials on installing and using Saltcorn"
294
+ )
295
+ ),
296
+ a(
297
+ {
298
+ href: `https://wiki.saltcorn.com/`,
299
+ class: "btn btn-primary",
300
+ },
301
+ req.__("Go to Wiki »")
302
+ ),
303
+ p(req.__("The YouTube channel has some video tutorials")),
304
+ a(
305
+ {
306
+ href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
307
+ class: "btn btn-secondary",
308
+ },
309
+ req.__("Go to YouTube »")
310
+ ),
311
+ div(
312
+ { class: "mt-3" },
313
+ a(
314
+ { href: `https://blog.saltcorn.com/` },
315
+ req.__("What's new? Read the blog »")
316
+ )
317
+ )
318
+ );
267
319
 
268
320
  /**
269
- * @param {object} req
321
+ * @param {object} req
270
322
  * @returns {Promise<object>}
271
323
  */
272
324
  const welcome_page = async (req) => {
@@ -275,10 +327,16 @@ const welcome_page = async (req) => {
275
327
  ...packs_available.slice(0, 5),
276
328
  { name: req.__("More..."), description: "" },
277
329
  ];
278
- const tables = await Table.find({}, { orderBy: "name" });
279
- const views = await View.find({});
280
- const pages = await Page.find({});
281
-
330
+ const tables = await Table.find({}, { cached: true });
331
+ const views = await View.find({}, { cached: true });
332
+ const pages = await Page.find({}, { cached: true });
333
+ const triggers = await Trigger.findAllWithTableName();
334
+ const users = await User.find({}, { orderBy: "id" });
335
+ const roles = await User.get_roles();
336
+ let roleMap = {};
337
+ roles.forEach((r) => {
338
+ roleMap[r.id] = r.role;
339
+ });
282
340
  return {
283
341
  above: [
284
342
  {
@@ -293,75 +351,37 @@ const welcome_page = async (req) => {
293
351
  {
294
352
  type: "card",
295
353
  //title: req.__("Install pack"),
296
- tabContents: {
297
- Packs: div(
298
- p(
299
- req.__(
300
- "Instead of building, get up and running in no time with packs"
301
- )
302
- ),
303
- p(
304
- { class: "font-italic" },
305
- req.__(
306
- "Packs are collections of tables, views and plugins that give you a full application which you can then edit to suit your needs."
307
- )
308
- ),
309
- mkTable(
310
- [
311
- { label: req.__("Name"), key: "name" },
312
- {
313
- label: req.__("Description"),
314
- key: "description",
315
- },
316
- ],
317
- packlist,
318
- { noHeader: true }
319
- ),
320
- a(
321
- { href: `/plugins?set=packs`, class: "btn btn-primary" },
322
- req.__("Go to pack store »")
323
- )
324
- ),
325
- Triggers: await actionsTab(req),
326
- Files: await filesTab(req),
327
- },
354
+ bodyClass: "py-0 pr-0",
355
+ class: "welcome-page-entity-list",
356
+
357
+ tabContents:
358
+ triggers.length > 0
359
+ ? {
360
+ Triggers: await actionsTab(req, triggers),
361
+ Files: await filesTab(req),
362
+ Packs: packTab(req, packlist),
363
+ }
364
+ : {
365
+ Packs: packTab(req, packlist),
366
+ Triggers: await actionsTab(req, triggers),
367
+ Files: await filesTab(req),
368
+ },
328
369
  },
329
370
  {
330
371
  type: "card",
331
372
  //title: req.__("Learn"),
332
- tabContents: {
333
- Help: div(
334
- p(req.__("Confused?")),
335
- p(
336
- req.__(
337
- "The Wiki contains the documentation and tutorials on installing and using Saltcorn"
338
- )
339
- ),
340
- a(
341
- {
342
- href: `https://wiki.saltcorn.com/`,
343
- class: "btn btn-primary",
373
+ bodyClass: "py-0 pr-0",
374
+ class: "welcome-page-entity-list",
375
+ tabContents:
376
+ users.length > 4
377
+ ? {
378
+ Users: await usersTab(req, users, roleMap),
379
+ Help: helpCard(req),
380
+ }
381
+ : {
382
+ Help: helpCard(req),
383
+ Users: await usersTab(req, users, roleMap),
344
384
  },
345
- req.__("Go to Wiki »")
346
- ),
347
- p(req.__("The YouTube channel has some video tutorials")),
348
- a(
349
- {
350
- href: `https://www.youtube.com/channel/UCBOpAcH8ep7ESbuocxcq0KQ`,
351
- class: "btn btn-secondary",
352
- },
353
- req.__("Go to YouTube »")
354
- ),
355
- div(
356
- { class: "mt-3" },
357
- a(
358
- { href: `https://blog.saltcorn.com/` },
359
- req.__("What's new? Read the blog »")
360
- )
361
- )
362
- ),
363
- Users: await usersTab(req),
364
- },
365
385
  },
366
386
  ],
367
387
  },
@@ -370,8 +390,8 @@ const welcome_page = async (req) => {
370
390
  };
371
391
 
372
392
  /**
373
- * @param {object} req
374
- * @param {object} res
393
+ * @param {object} req
394
+ * @param {object} res
375
395
  * @returns {Promise<void>}
376
396
  */
377
397
  const no_views_logged_in = async (req, res) => {
@@ -400,9 +420,9 @@ const no_views_logged_in = async (req, res) => {
400
420
  };
401
421
 
402
422
  /**
403
- * @param {number} role_id
404
- * @param {object} res
405
- * @param {object} req
423
+ * @param {number} role_id
424
+ * @param {object} res
425
+ * @param {object} req
406
426
  * @returns {Promise<boolean>}
407
427
  */
408
428
  const get_config_response = async (role_id, res, req) => {
@@ -431,8 +451,8 @@ const get_config_response = async (role_id, res, req) => {
431
451
 
432
452
  /**
433
453
  * Function assigned to 'module.exports'.
434
- * @param {object} req
435
- * @param {object} res
454
+ * @param {object} req
455
+ * @param {object} res
436
456
  * @returns {Promise<void>}
437
457
  */
438
458
  module.exports = async (req, res) => {
package/routes/index.js CHANGED
@@ -4,6 +4,43 @@
4
4
  * @module routes/index
5
5
  * @subcategory routes
6
6
  */
7
+
8
+ /**
9
+ * All files in the routes module.
10
+ * @namespace routes_overview
11
+ * @property {module:routes/actions} actions
12
+ * @property {module:routes/admin} admin
13
+ * @property {module:routes/api} api
14
+ * @property {module:routes/config} config
15
+ * @property {module:routes/crashlog} crashlog
16
+ * @property {module:routes/delete} delete
17
+ * @property {module:routes/edit} edit
18
+ * @property {module:routes/eventlog} eventlog
19
+ * @property {module:routes/events} events
20
+ * @property {module:routes/fields} fields
21
+ * @property {module:routes/files} files
22
+ * @property {module:routes/homepage} homepage
23
+ * @property {module:routes/infoarch} infoarch
24
+ * @property {module:routes/library} library
25
+ * @property {module:routes/list} list
26
+ * @property {module:routes/menu} menu
27
+ * @property {module:routes/packs} packs
28
+ * @property {module:routes/page} page
29
+ * @property {module:routes/pageedit} pageedit
30
+ * @property {module:routes/plugins} plugins
31
+ * @property {module:routes/scapi} scapi
32
+ * @property {module:routes/search} search
33
+ * @property {module:routes/settings} settings
34
+ * @property {module:routes/tables} tables
35
+ * @property {module:routes/tenant} tenant
36
+ * @property {module:routes/utils} utils
37
+ * @property {module:routes/view} view
38
+ * @property {module:routes/viewedit} viewedit
39
+ *
40
+ * @category server
41
+ * @subcategory routes
42
+ */
43
+
7
44
  const table = require("./tables");
8
45
  const field = require("./fields");
9
46
  const list = require("./list");
package/routes/tables.js CHANGED
@@ -347,8 +347,8 @@ router.get(
347
347
  })
348
348
  );
349
349
 
350
- /**
351
- * Create Table from CSV file (post)
350
+ /**
351
+ * Create Table from CSV file (post)
352
352
  * @name post/create-from-csv
353
353
  * @function
354
354
  * @memberof module:routes/tables~tablesRouter
@@ -396,7 +396,7 @@ router.post(
396
396
  })
397
397
  );
398
398
 
399
- /**
399
+ /**
400
400
  * Show Relational Diagram (get)
401
401
  * @name get/relationship-diagram
402
402
  * @function
@@ -489,16 +489,16 @@ router.get(
489
489
  );
490
490
 
491
491
  /**
492
- * @param {string} col
493
- * @param {string} lbl
492
+ * @param {string} col
493
+ * @param {string} lbl
494
494
  * @returns {string}
495
495
  */
496
496
  const badge = (col, lbl) =>
497
497
  `<span class="badge badge-${col}">${lbl}</span>&nbsp;`;
498
498
 
499
499
  /**
500
- * @param {object} f
501
- * @param {object} req
500
+ * @param {object} f
501
+ * @param {object} req
502
502
  * @returns {string}
503
503
  */
504
504
  const typeBadges = (f, req) => {
@@ -512,7 +512,7 @@ const typeBadges = (f, req) => {
512
512
  };
513
513
 
514
514
  /**
515
- * @param {object} f
515
+ * @param {object} f
516
516
  * @returns {string}
517
517
  */
518
518
  const attribBadges = (f) => {
@@ -526,7 +526,7 @@ const attribBadges = (f) => {
526
526
  return s;
527
527
  };
528
528
 
529
- /**
529
+ /**
530
530
  * Table Constructor (GET Handler)
531
531
  * @name get/:idorname
532
532
  * @function
@@ -762,6 +762,7 @@ router.get(
762
762
  ),
763
763
  // rename table doesnt supported for sqlite
764
764
  !db.isSQLite &&
765
+ table.name !== "users" &&
765
766
  a(
766
767
  {
767
768
  class: "dropdown-item",
@@ -782,12 +783,13 @@ router.get(
782
783
  req,
783
784
  true
784
785
  ),
785
- post_dropdown_item(
786
- `/table/forget-table/${table.id}`,
787
- '<i class="fas fa-recycle"></i>&nbsp;' + req.__("Forget table"),
788
- req,
789
- true
790
- ),
786
+ table.name !== "users" &&
787
+ post_dropdown_item(
788
+ `/table/forget-table/${table.id}`,
789
+ '<i class="fas fa-recycle"></i>&nbsp;' + req.__("Forget table"),
790
+ req,
791
+ true
792
+ ),
791
793
  ])
792
794
  )
793
795
  );
@@ -833,7 +835,7 @@ router.get(
833
835
  })
834
836
  );
835
837
 
836
- /**
838
+ /**
837
839
  * @name post
838
840
  * @function
839
841
  * @memberof module:routes/tables~tablesRouter
@@ -906,7 +908,7 @@ router.post(
906
908
  })
907
909
  );
908
910
 
909
- /**
911
+ /**
910
912
  * Delete Table Route Handler definition
911
913
  * /delete:/id, where id is table id in _sc_tables
912
914
  * @name post/delete/:id
@@ -1084,7 +1086,7 @@ router.get(
1084
1086
  })
1085
1087
  );
1086
1088
 
1087
- /**
1089
+ /**
1088
1090
  * Download CSV file
1089
1091
  * @name get/download/:name
1090
1092
  * @function
@@ -1190,7 +1192,7 @@ const constraintForm = (req, table_id, fields) =>
1190
1192
  type: "Bool",
1191
1193
  })),
1192
1194
  });
1193
-
1195
+
1194
1196
  /**
1195
1197
  * Add constraint GET handler
1196
1198
  * ${base_url}/table/add-constraint/:id
package/routes/tenant.js CHANGED
@@ -34,7 +34,7 @@ const {
34
34
  } = require("@saltcorn/markup/tags");
35
35
  const db = require("@saltcorn/data/db");
36
36
  const url = require("url");
37
- const { loadAllPlugins } = require("../load_plugins");
37
+ const { loadAllPlugins, loadAndSaveNewPlugin } = require("../load_plugins");
38
38
  const { setTenant, isAdmin, error_catcher } = require("./utils.js");
39
39
  const User = require("@saltcorn/data/models/user");
40
40
  const File = require("@saltcorn/data/models/file");
@@ -139,31 +139,37 @@ router.get(
139
139
  "You are trying to create a tenant while connecting via an IP address rather than a domain. This will probably not work."
140
140
  )
141
141
  );
142
+ let create_tenant_warning = "";
142
143
  if (getState().getConfig("create_tenant_warning"))
143
- req.flash(
144
- "warning",
145
- h4(req.__("Warning")) +
146
- p(
144
+ create_tenant_warning = div(
145
+ {
146
+ class: "alert alert-warning alert-dismissible fade show mt-5",
147
+ role: "alert",
148
+ },
149
+ h4(req.__("Warning")),
150
+ p(
151
+ req.__(
152
+ "Hosting on this site is provided for free and with no guarantee of availability or security of your application. "
153
+ ) +
154
+ " " +
147
155
  req.__(
148
- "Hosting on this site is provided for free and with no guarantee of availability or security of your application. "
156
+ "This facility is intended solely for you to evaluate the suitability of Saltcorn. "
149
157
  ) +
150
- " " +
151
- req.__(
152
- "This facility is intended solely for you to evaluate the suitability of Saltcorn. "
153
- ) +
154
- " " +
155
- req.__(
156
- "If you would like to store private information that needs to be secure, please use self-hosted Saltcorn. "
157
- ) +
158
- " " +
159
- req.__(
160
- 'See <a href="https://github.com/saltcorn/saltcorn">GitHub repository</a> for instructions<p>'
161
- )
162
- )
158
+ " " +
159
+ req.__(
160
+ "If you would like to store private information that needs to be secure, please use self-hosted Saltcorn. "
161
+ ) +
162
+ " " +
163
+ req.__(
164
+ 'See <a href="https://github.com/saltcorn/saltcorn">GitHub repository</a> for instructions<p>'
165
+ )
166
+ )
163
167
  );
168
+
164
169
  res.sendWrap(
165
170
  req.__("Create application"),
166
- renderForm(tenant_form(req), req.csrfToken()) +
171
+ create_tenant_warning +
172
+ renderForm(tenant_form(req), req.csrfToken()) +
167
173
  p(
168
174
  { class: "mt-2" },
169
175
  req.__("To login to a previously created application, go to: "),
@@ -245,7 +251,19 @@ router.post(
245
251
  );
246
252
  } else {
247
253
  const newurl = getNewURL(req, subdomain);
248
- await create_tenant(subdomain, loadAllPlugins, newurl);
254
+ await create_tenant(
255
+ subdomain,
256
+ loadAllPlugins,
257
+ newurl,
258
+ false,
259
+ loadAndSaveNewPlugin
260
+ );
261
+ let new_url_create = newurl;
262
+ const hasTemplate = getState().getConfig("tenant_template");
263
+ if (hasTemplate) {
264
+ new_url_create += "auth/create_first_user";
265
+ }
266
+
249
267
  res.sendWrap(
250
268
  req.__("Create application"),
251
269
  div(
@@ -253,14 +271,25 @@ router.post(
253
271
 
254
272
  div(
255
273
  { class: "my-3", style: "font-size: 22px" },
256
- a({ href: newurl, class: "new-tenant-link" }, newurl)
274
+ a(
275
+ { href: new_url_create, class: "new-tenant-link" },
276
+ new_url_create
277
+ )
257
278
  ),
258
279
  p(
259
280
  req.__(
260
281
  "Please click the above link now to create the first user."
261
282
  ) +
262
283
  " " +
263
- req.__("Use this link to revisit your application at any time.")
284
+ hasTemplate
285
+ ? req.__(
286
+ 'Use this link: <a href="%s">%s</a> to revisit your application at any time.',
287
+ newurl,
288
+ newurl
289
+ )
290
+ : req.__(
291
+ "Use this link to revisit your application at any time."
292
+ )
264
293
  )
265
294
  )
266
295
  );
@@ -339,13 +368,17 @@ router.get(
339
368
  );
340
369
 
341
370
  /**
342
- * @param {object} req
371
+ * @param {object} req
343
372
  * @returns {Form}
344
373
  */
345
374
  const tenant_settings_form = (req) =>
346
375
  config_fields_form({
347
376
  req,
348
- field_names: ["role_to_create_tenant", "create_tenant_warning"],
377
+ field_names: [
378
+ "role_to_create_tenant",
379
+ "create_tenant_warning",
380
+ "tenant_template",
381
+ ],
349
382
  action: "/tenant/settings",
350
383
  submitLabel: req.__("Save"),
351
384
  });
package/routes/utils.js CHANGED
@@ -4,8 +4,8 @@
4
4
  * @subcategory routes
5
5
  */
6
6
 
7
- const { sqlsanitize } = require("@saltcorn/data/db/internal.js");
8
7
  const db = require("@saltcorn/data/db");
8
+ const { sqlsanitize } = db;
9
9
  const {
10
10
  getState,
11
11
  getTenant,
package/routes/view.js CHANGED
@@ -68,7 +68,7 @@ router.get(
68
68
  what: req.__("View"),
69
69
  url: `/viewedit/edit/${encodeURIComponent(view.name)}`,
70
70
  contents,
71
- req
71
+ req,
72
72
  })
73
73
  );
74
74
  })
@@ -92,18 +92,18 @@ router.post(
92
92
  res.send("");
93
93
  return;
94
94
  }
95
- let query = {};
95
+ let query = req.body || {};
96
96
  let row;
97
97
  let table;
98
98
  const sfs = await view.get_state_fields();
99
99
  for (const sf of sfs) {
100
- if (sf.required) {
100
+ if (sf.required && !query[sf.name]) {
101
101
  if (!row) {
102
102
  if (!table)
103
103
  table = await Table.findOne(view.table_id || view.exttable_name);
104
104
  row = await table.getRow({});
105
105
  }
106
- if(row) query[sf.name] = row[sf.name];
106
+ if (row) query[sf.name] = row[sf.name];
107
107
  }
108
108
  }
109
109
  const contents = await view.run(query, { req, res });