@saltcorn/server 0.8.0-beta.4 → 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.
@@ -9,6 +9,8 @@ let tagFilterEnabled = false;
9
9
 
10
10
  let activePopper = null;
11
11
 
12
+ const maxPreviewHeight = "350px";
13
+
12
14
  function initMouseOver() {
13
15
  cy.on("mouseover", "node", (event) => {
14
16
  const node = event.target;
@@ -41,7 +43,7 @@ function initMouseOver() {
41
43
  function buildCard(node) {
42
44
  const { type, label } = node.data();
43
45
  const html = `
44
- <div class="card" style="width: 18rem;">
46
+ <div class="card" style="width: 20rem;">
45
47
  <div class="card-header">
46
48
  <h5 class="card-title">${type}</h5>
47
49
  <h6 class="card-subtitle text-muted">${label}</h6>
@@ -49,7 +51,9 @@ function buildCard(node) {
49
51
  <div class="card-body">
50
52
  ${buildTagBadges(node)}
51
53
  ${buildCardBody(node)}
52
- ${type === "page" || type === "view" ? buildPreviewDiv(node) : ""}
54
+ <div>
55
+ ${type === "page" || type === "view" ? buildPreviewDiv(node) : ""}
56
+ </div>
53
57
  ${type === "page" || type === "view" ? buildMinRoleSelect(node) : ""}
54
58
  </div>
55
59
  </div>
@@ -70,26 +74,35 @@ function buildPreview(node) {
70
74
  "CSRF-Token": _sc_globalCsrf,
71
75
  },
72
76
  success: (res) => {
73
- $(`#${previewId}`).html(`
77
+ $(`[id='${previewId}']`).html(`
74
78
  <div
75
79
  id="preview_wrapper"
76
- style="min-height: 70px;"
80
+ style="min-height: 70px; pointer-events: none;"
77
81
  >
78
82
  ${res}
79
83
  </div></div></div>`);
80
- const previewDiv = $(`#${previewId}`);
84
+ const previewDiv = $(`[id='${previewId}']`);
81
85
  const pos = previewDiv.position();
82
86
  const cssBase = `
87
+ max-height: ${maxPreviewHeight};
88
+ pointer-events: none;
83
89
  position: absolute; top: ${pos.top}px; left: ${pos.left}px;
84
90
  width: ${previewDiv.width()}px; height: ${previewDiv.height() + 12}px;`;
85
- $(`#${previewId}`).after(`
86
- <div
91
+ const previewLabelId = `${previewId}_label`;
92
+ const previewOverlayId = `${previewId}_overlay`;
93
+ $(`[id='${previewLabelId}']`).remove();
94
+ $(`[id='${previewOverlayId}']`).remove();
95
+ $(`[id='${previewId}']`).after(`
96
+ <div
97
+ id="${previewOverlayId}"
87
98
  style="${cssBase}
88
99
  background-color: black; opacity: 0.1;
89
100
  z-index: 10;"
90
101
  >
91
102
  </div>
92
- <div style="${cssBase} opacity: 0.5;">
103
+ <div
104
+ id="${previewLabelId}"
105
+ style="${cssBase} opacity: 0.5;">
93
106
  <h2 class="preview-text fw-bold text-danger">
94
107
  Preview
95
108
  </h2>
@@ -105,7 +118,7 @@ function buildPreview(node) {
105
118
  function buildPreviewDiv(node) {
106
119
  const previewId = `preview_${node.id()}`;
107
120
  return `
108
- <div class="my-2" id="${previewId}" style="min-height: 70px;">
121
+ <div class="my-2" id="${previewId}" style="min-height: 70px; max-height: ${maxPreviewHeight}; overflow: scroll;">
109
122
  <div style="opacity: 0.5;">
110
123
  <h2>
111
124
  <span class="fw-bold text-danger">Preview</span>
@@ -364,3 +364,9 @@ table.table-inner-grid td {
364
364
  margin-left: 30px;
365
365
  margin-top: -5px;
366
366
  }
367
+
368
+ .accordion-button:after {
369
+ order: -1;
370
+ margin-left: 0;
371
+ margin-right: 0.5em;
372
+ }
@@ -1,157 +1,157 @@
1
- /**
2
- * @category server
3
- * @module restart_watcher
4
- */
5
-
6
- const path = require("path");
7
- const { spawnSync } = require("child_process");
8
- const watch = require("node-watch");
9
- const Plugin = require("@saltcorn/data/models/plugin");
10
- const db = require("@saltcorn/data/db");
11
- const { eachTenant } = require("@saltcorn/admin-models/models/tenant");
12
-
13
- /**
14
- * packages that should trigger a server re-start
15
- */
16
- const relevantPackages = [
17
- "db-common",
18
- "postgres",
19
- "saltcorn-data",
20
- "saltcorn-builder",
21
- "saltcorn-admin-models",
22
- "saltcorn-markup",
23
- "saltcorn-sbadmin2",
24
- "server",
25
- "sqlite",
26
- "filemanager"
27
- ];
28
-
29
- /**
30
- * excluded directories or file name patterns
31
- */
32
- const excludePatterns = [
33
- /\/node_modules/,
34
- /\.git/,
35
- /\.docs/,
36
- /\.docs/,
37
- /\migrations/,
38
- /.*test.js/,
39
- ];
40
-
41
- /**
42
- * get the root directory of the saltcorn project
43
- * @returns {string} project root path
44
- */
45
- const getProjectRoot = () => {
46
- return path.normalize(`${__dirname}/../../`);
47
- };
48
-
49
- /**
50
- * get the packages directory of the saltcorn project
51
- * @returns {string} packages path
52
- */
53
- const getPackagesDirectory = () => {
54
- return `${getProjectRoot()}/packages`;
55
- };
56
-
57
- /**
58
- * get all package directories that should trigger a server re-start
59
- * @returns {string[]} list of paths to relevant directories
60
- */
61
- const getRelevantPackages = () => {
62
- const packagesDir = getPackagesDirectory();
63
- return relevantPackages.map((packageName) => `${packagesDir}/${packageName}`);
64
- };
65
-
66
- /**
67
- * get all plugin directories that should trigger a server re-start
68
- * @returns {string[]} list of paths to relevant directories
69
- */
70
- const getPluginDirectories = async () => {
71
- const getDirs = async () => {
72
- const local_plugins = await Plugin.find({ source: "local" });
73
- return local_plugins.map((p) => p.location);
74
- };
75
- const listOfDirs = [];
76
- await eachTenant(async () => {
77
- listOfDirs.push(await getDirs());
78
- });
79
- return [...new Set(listOfDirs.flat(1))];
80
- };
81
-
82
- const projectRoot = getProjectRoot();
83
-
84
- const watchCfg = {
85
- recursive: true,
86
- filter(file, skip) {
87
- for (const excludePattern of excludePatterns) {
88
- if (excludePattern.test(file)) return skip;
89
- }
90
- return /(\.js|\.ts)$/.test(file);
91
- },
92
- };
93
-
94
- let activeWatchers = [];
95
-
96
- /**
97
- * close all open file watchers
98
- */
99
- const closeWatchers = () => {
100
- for (const activeWatcher of activeWatchers) {
101
- if (!activeWatcher.isClosed()) {
102
- activeWatcher.close();
103
- }
104
- }
105
- };
106
-
107
- /**
108
- * register many file change listener and do re-starts on changes
109
- * The listener calls process.exit() and assumes
110
- * that pm2 does the actual re-start.
111
- * @param {string[]} projectDirs package paths that should trigger re-starts.
112
- * @param {string[]} pluginDirs plugin paths that should trigger re-starts.
113
- */
114
- const listenForChanges = (projectDirs, pluginDirs) => {
115
- // watch project dirs
116
- for (const projectDir of projectDirs) {
117
- activeWatchers.push(
118
- watch(
119
- projectDir,
120
- watchCfg,
121
- // event is either 'update' or 'remove'
122
- (event, file) => {
123
- console.log("'%s' changed \n re-starting now", file);
124
- closeWatchers();
125
- spawnSync("npm", ["run", "tsc"], {
126
- stdio: "inherit",
127
- });
128
- process.exit();
129
- }
130
- )
131
- );
132
- }
133
- // watch plugin dirs
134
- for (const pluginDir of pluginDirs) {
135
- activeWatchers.push(
136
- watch(
137
- pluginDir,
138
- watchCfg,
139
- // event is either 'update' or 'remove'
140
- (event, file) => {
141
- console.log("'%s' changed \n re-starting now", file);
142
- closeWatchers();
143
- process.exit();
144
- }
145
- )
146
- );
147
- }
148
- };
149
-
150
- module.exports = {
151
- listenForChanges,
152
- getProjectRoot,
153
- getPackagesDirectory,
154
- getRelevantPackages,
155
- getPluginDirectories,
156
- closeWatchers,
157
- };
1
+ /**
2
+ * @category server
3
+ * @module restart_watcher
4
+ */
5
+
6
+ const path = require("path");
7
+ const { spawnSync } = require("child_process");
8
+ const watch = require("node-watch");
9
+ const Plugin = require("@saltcorn/data/models/plugin");
10
+ const db = require("@saltcorn/data/db");
11
+ const { eachTenant } = require("@saltcorn/admin-models/models/tenant");
12
+
13
+ /**
14
+ * packages that should trigger a server re-start
15
+ */
16
+ const relevantPackages = [
17
+ "db-common",
18
+ "postgres",
19
+ "saltcorn-data",
20
+ "saltcorn-builder",
21
+ "saltcorn-admin-models",
22
+ "saltcorn-markup",
23
+ "saltcorn-sbadmin2",
24
+ "server",
25
+ "sqlite",
26
+ "filemanager",
27
+ ];
28
+
29
+ /**
30
+ * excluded directories or file name patterns
31
+ */
32
+ const excludePatterns = [
33
+ /\/node_modules/,
34
+ /\.git/,
35
+ /\.docs/,
36
+ /\.docs/,
37
+ /migrations/,
38
+ /.*test.js/,
39
+ ];
40
+
41
+ /**
42
+ * get the root directory of the saltcorn project
43
+ * @returns {string} project root path
44
+ */
45
+ const getProjectRoot = () => {
46
+ return path.normalize(`${__dirname}/../../`);
47
+ };
48
+
49
+ /**
50
+ * get the packages directory of the saltcorn project
51
+ * @returns {string} packages path
52
+ */
53
+ const getPackagesDirectory = () => {
54
+ return `${getProjectRoot()}/packages`;
55
+ };
56
+
57
+ /**
58
+ * get all package directories that should trigger a server re-start
59
+ * @returns {string[]} list of paths to relevant directories
60
+ */
61
+ const getRelevantPackages = () => {
62
+ const packagesDir = getPackagesDirectory();
63
+ return relevantPackages.map((packageName) => `${packagesDir}/${packageName}`);
64
+ };
65
+
66
+ /**
67
+ * get all plugin directories that should trigger a server re-start
68
+ * @returns {string[]} list of paths to relevant directories
69
+ */
70
+ const getPluginDirectories = async () => {
71
+ const getDirs = async () => {
72
+ const local_plugins = await Plugin.find({ source: "local" });
73
+ return local_plugins.map((p) => p.location);
74
+ };
75
+ const listOfDirs = [];
76
+ await eachTenant(async () => {
77
+ listOfDirs.push(await getDirs());
78
+ });
79
+ return [...new Set(listOfDirs.flat(1))];
80
+ };
81
+
82
+ const projectRoot = getProjectRoot();
83
+
84
+ const watchCfg = {
85
+ recursive: true,
86
+ filter(file, skip) {
87
+ for (const excludePattern of excludePatterns) {
88
+ if (excludePattern.test(file)) return skip;
89
+ }
90
+ return /(\.js|\.ts)$/.test(file);
91
+ },
92
+ };
93
+
94
+ let activeWatchers = [];
95
+
96
+ /**
97
+ * close all open file watchers
98
+ */
99
+ const closeWatchers = () => {
100
+ for (const activeWatcher of activeWatchers) {
101
+ if (!activeWatcher.isClosed()) {
102
+ activeWatcher.close();
103
+ }
104
+ }
105
+ };
106
+
107
+ /**
108
+ * register many file change listener and do re-starts on changes
109
+ * The listener calls process.exit() and assumes
110
+ * that pm2 does the actual re-start.
111
+ * @param {string[]} projectDirs package paths that should trigger re-starts.
112
+ * @param {string[]} pluginDirs plugin paths that should trigger re-starts.
113
+ */
114
+ const listenForChanges = (projectDirs, pluginDirs) => {
115
+ // watch project dirs
116
+ for (const projectDir of projectDirs) {
117
+ activeWatchers.push(
118
+ watch(
119
+ projectDir,
120
+ watchCfg,
121
+ // event is either 'update' or 'remove'
122
+ (event, file) => {
123
+ console.log("'%s' changed \n re-starting now", file);
124
+ closeWatchers();
125
+ spawnSync("npm", ["run", "tsc"], {
126
+ stdio: "inherit",
127
+ });
128
+ process.exit();
129
+ }
130
+ )
131
+ );
132
+ }
133
+ // watch plugin dirs
134
+ for (const pluginDir of pluginDirs) {
135
+ activeWatchers.push(
136
+ watch(
137
+ pluginDir,
138
+ watchCfg,
139
+ // event is either 'update' or 'remove'
140
+ (event, file) => {
141
+ console.log("'%s' changed \n re-starting now", file);
142
+ closeWatchers();
143
+ process.exit();
144
+ }
145
+ )
146
+ );
147
+ }
148
+ };
149
+
150
+ module.exports = {
151
+ listenForChanges,
152
+ getProjectRoot,
153
+ getPackagesDirectory,
154
+ getRelevantPackages,
155
+ getPluginDirectories,
156
+ closeWatchers,
157
+ };
package/routes/actions.js CHANGED
@@ -5,11 +5,7 @@
5
5
  * @subcategory routes
6
6
  */
7
7
  const Router = require("express-promise-router");
8
- const {
9
- isAdmin,
10
- error_catcher,
11
- addOnDoneRedirect,
12
- } = require("./utils.js");
8
+ const { isAdmin, error_catcher, addOnDoneRedirect } = require("./utils.js");
13
9
  const { getState } = require("@saltcorn/data/db/state");
14
10
  const Trigger = require("@saltcorn/data/models/trigger");
15
11
  const { getTriggerList } = require("./common_lists");
@@ -23,10 +19,7 @@ const { getTriggerList } = require("./common_lists");
23
19
  */
24
20
  const router = new Router();
25
21
  module.exports = router;
26
- const {
27
- renderForm,
28
- link,
29
- } = require("@saltcorn/markup");
22
+ const { renderForm, link } = require("@saltcorn/markup");
30
23
  const Form = require("@saltcorn/data/models/form");
31
24
  const {
32
25
  div,
@@ -378,7 +371,7 @@ router.get(
378
371
  if (!trigger) {
379
372
  req.flash("warning", req.__("Action not found"));
380
373
  res.redirect(`/actions/`);
381
- return
374
+ return;
382
375
  }
383
376
  const action = getState().actions[trigger.action];
384
377
  if (!action) {
@@ -575,7 +568,7 @@ router.get(
575
568
  };
576
569
  let table, row;
577
570
  if (trigger.table_id) {
578
- table = await Table.findOne( { id: trigger.table_id } );
571
+ table = await Table.findOne({ id: trigger.table_id });
579
572
  row = await table.getRow({});
580
573
  }
581
574
  try {
package/routes/admin.js CHANGED
@@ -111,14 +111,17 @@ const site_id_form = (req) =>
111
111
  field_names: [
112
112
  "site_name",
113
113
  "timezone",
114
+ "base_url",
115
+ ...(getConfigFile() ? ["multitenancy_enabled"] : []),
116
+ { section_header: "Logo image" },
114
117
  "site_logo_id",
115
118
  "favicon_id",
116
- "base_url",
119
+ { section_header: "Custom code" },
117
120
  "page_custom_css",
118
121
  "page_custom_html",
122
+ { section_header: "Extension store" },
119
123
  "plugins_store_endpoint",
120
124
  "packs_store_endpoint",
121
- ...(getConfigFile() ? ["multitenancy_enabled"] : []),
122
125
  ],
123
126
  action: "/admin",
124
127
  submitLabel: req.__("Save"),
@@ -1654,11 +1657,11 @@ router.post(
1654
1657
  const childOutputs = [];
1655
1658
  child.stdout.on("data", (data) => {
1656
1659
  // console.log(data.toString());
1657
- childOutputs.push(data.toString());
1660
+ if (data) childOutputs.push(data.toString());
1658
1661
  });
1659
1662
  child.stderr.on("data", (data) => {
1660
1663
  // console.log(data.toString());
1661
- childOutputs.push(data.toString());
1664
+ childOutputs.push(data ? data.toString() : req.__("An error occurred"));
1662
1665
  });
1663
1666
  child.on("exit", async function (exitCode, signal) {
1664
1667
  const logFile = exitCode === 0 ? "logs.txt" : "error_logs.txt";
@@ -1681,7 +1684,7 @@ router.post(
1681
1684
  [message, stack].join("\n"),
1682
1685
  (error) => {
1683
1686
  if (error) {
1684
- console.log(`unable to write '${logFile}' to '${buildDir}'`);
1687
+ console.log(`unable to write logFile to '${buildDir}'`);
1685
1688
  console.log(error);
1686
1689
  }
1687
1690
  }
package/routes/api.js CHANGED
@@ -71,8 +71,8 @@ function accessAllowedRead(req, user, table) {
71
71
  req.user && req.user.id
72
72
  ? req.user.role_id
73
73
  : user && user.role_id
74
- ? user.role_id
75
- : 10;
74
+ ? user.role_id
75
+ : 10;
76
76
 
77
77
  return role <= table.min_role_read;
78
78
  }
@@ -89,8 +89,8 @@ function accessAllowedWrite(req, user, table) {
89
89
  req.user && req.user.id
90
90
  ? req.user.role_id
91
91
  : user && user.role_id
92
- ? user.role_id
93
- : 10;
92
+ ? user.role_id
93
+ : 10;
94
94
 
95
95
  return role <= table.min_role_write;
96
96
  }
@@ -106,8 +106,8 @@ function accessAllowed(req, user, trigger) {
106
106
  req.user && req.user.id
107
107
  ? req.user.role_id
108
108
  : user && user.role_id
109
- ? user.role_id
110
- : 10;
109
+ ? user.role_id
110
+ : 10;
111
111
 
112
112
  return role <= trigger.min_role;
113
113
  }
@@ -126,7 +126,7 @@ router.post(
126
126
  const view = await View.findOne({ name: viewName });
127
127
  const db = require("@saltcorn/data/db");
128
128
  if (!view) {
129
- res.status(404).json({
129
+ res.status(404).json({
130
130
  error: req.__("View %s not found", viewName),
131
131
  view: viewName,
132
132
  queryName: queryName,
@@ -152,7 +152,7 @@ router.post(
152
152
  const resp = await queries[queryName](...args, true);
153
153
  res.json({ success: resp, alerts: getFlashes(req) });
154
154
  } else {
155
- res.status(404).json({
155
+ res.status(404).json({
156
156
  error: req.__("Query %s not found", queryName),
157
157
  view: viewName,
158
158
  queryName: queryName,
@@ -264,7 +264,7 @@ router.get(
264
264
  fields: tbl_fields,
265
265
  approximate: !!approximate,
266
266
  state: req_query,
267
- table
267
+ table,
268
268
  });
269
269
  rows = await table.getRows(qstate);
270
270
  } else {