@saltcorn/server 0.9.6-beta.7 → 0.9.6-beta.8

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/locales/en.json CHANGED
@@ -1425,5 +1425,6 @@
1425
1425
  "Keystore Alias": "Keystore Alias",
1426
1426
  "Keystore Password": "Keystore Password",
1427
1427
  "xcodebuild": "xcodebuild",
1428
- "Provisioning Profile": "Provisioning Profile"
1428
+ "Provisioning Profile": "Provisioning Profile",
1429
+ "Registry editor": "Registry editor"
1429
1430
  }
package/markup/admin.js CHANGED
@@ -241,6 +241,7 @@ const send_infoarch_page = (args) => {
241
241
  { text: "Pagegroups", href: "/page_group/settings" },
242
242
  { text: "Tags", href: "/tag" },
243
243
  { text: "Diagram", href: "/diagram" },
244
+ { text: "Registry editor", href: "/registry-editor" },
244
245
  ],
245
246
  ...args,
246
247
  });
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@saltcorn/server",
3
- "version": "0.9.6-beta.7",
3
+ "version": "0.9.6-beta.8",
4
4
  "description": "Server app for Saltcorn, open-source no-code platform",
5
5
  "homepage": "https://saltcorn.com",
6
6
  "main": "index.js",
7
7
  "license": "MIT",
8
8
  "dependencies": {
9
9
  "@aws-sdk/client-s3": "^3.451.0",
10
- "@saltcorn/base-plugin": "0.9.6-beta.7",
11
- "@saltcorn/builder": "0.9.6-beta.7",
12
- "@saltcorn/data": "0.9.6-beta.7",
13
- "@saltcorn/admin-models": "0.9.6-beta.7",
14
- "@saltcorn/filemanager": "0.9.6-beta.7",
15
- "@saltcorn/markup": "0.9.6-beta.7",
16
- "@saltcorn/plugins-loader": "0.9.6-beta.7",
17
- "@saltcorn/sbadmin2": "0.9.6-beta.7",
10
+ "@saltcorn/base-plugin": "0.9.6-beta.8",
11
+ "@saltcorn/builder": "0.9.6-beta.8",
12
+ "@saltcorn/data": "0.9.6-beta.8",
13
+ "@saltcorn/admin-models": "0.9.6-beta.8",
14
+ "@saltcorn/filemanager": "0.9.6-beta.8",
15
+ "@saltcorn/markup": "0.9.6-beta.8",
16
+ "@saltcorn/plugins-loader": "0.9.6-beta.8",
17
+ "@saltcorn/sbadmin2": "0.9.6-beta.8",
18
18
  "@socket.io/cluster-adapter": "^0.2.1",
19
19
  "@socket.io/sticky": "^1.0.1",
20
20
  "adm-zip": "0.5.10",
@@ -179,7 +179,11 @@ function apply_showif() {
179
179
  is_or ? "&_or_field=" + k : ""
180
180
  }`;
181
181
  };
182
- const qss = Object.entries(dynwhere.whereParsed).map(kvToQs);
182
+ const qss = Object.entries(dynwhere.whereParsed).map((kv) => kvToQs(kv));
183
+ if (dynwhere.existingValue) {
184
+ qss.push(`id=${dynwhere.existingValue}`);
185
+ qss.push(`_or_field=id`);
186
+ }
183
187
  if (dynwhere.dereference) {
184
188
  if (Array.isArray(dynwhere.dereference))
185
189
  qss.push(...dynwhere.dereference.map((d) => `dereference=${d}`));
@@ -506,3 +506,11 @@ tr[onclick] {
506
506
  .modal-header {
507
507
  justify-content: space-between;
508
508
  }
509
+
510
+ ul.katetree {
511
+ list-style-type: none;
512
+ }
513
+
514
+ ul.katetree details ul {
515
+ list-style-type: none;
516
+ }
package/routes/index.js CHANGED
@@ -36,6 +36,7 @@ const roleadmin = require("../auth/roleadmin");
36
36
  const tags = require("./tags");
37
37
  const tagentries = require("./tag_entries");
38
38
  const diagram = require("./diagram");
39
+ const registry = require("./registry");
39
40
  const sync = require("./sync");
40
41
 
41
42
  module.exports =
@@ -78,5 +79,6 @@ module.exports =
78
79
  app.use("/tag", tags);
79
80
  app.use("/tag-entries", tagentries);
80
81
  app.use("/diagram", diagram);
82
+ app.use("/registry-editor", registry);
81
83
  app.use("/sync", sync);
82
84
  };
@@ -0,0 +1,289 @@
1
+ const Router = require("express-promise-router");
2
+
3
+ const db = require("@saltcorn/data/db");
4
+ const { mkTable, link, post_btn, renderForm } = require("@saltcorn/markup");
5
+ const {
6
+ script,
7
+ domReady,
8
+ a,
9
+ div,
10
+ i,
11
+ text,
12
+ button,
13
+ input,
14
+ label,
15
+ form,
16
+ ul,
17
+ li,
18
+ details,
19
+ summary,
20
+ } = require("@saltcorn/markup/tags");
21
+ const Table = require("@saltcorn/data/models/table");
22
+ const { isAdmin, error_catcher } = require("./utils");
23
+ const { send_infoarch_page } = require("../markup/admin.js");
24
+ const View = require("@saltcorn/data/models/view");
25
+ const Page = require("@saltcorn/data/models/page");
26
+ const Form = require("@saltcorn/data/models/form");
27
+ const {
28
+ table_pack,
29
+ view_pack,
30
+ plugin_pack,
31
+ page_pack,
32
+ page_group_pack,
33
+ role_pack,
34
+ library_pack,
35
+ trigger_pack,
36
+ tag_pack,
37
+ model_pack,
38
+ model_instance_pack,
39
+ event_log_pack,
40
+ install_pack,
41
+ } = require("@saltcorn/admin-models/models/pack");
42
+ const Trigger = require("@saltcorn/data/models/trigger");
43
+ /**
44
+ * @type {object}
45
+ * @const
46
+ * @namespace listRouter
47
+ * @category server
48
+ * @subcategory routes
49
+ */
50
+ const router = new Router();
51
+
52
+ // export our router to be mounted by the parent application
53
+ module.exports = router;
54
+
55
+ async function asyncFilter(arr, cb) {
56
+ const filtered = [];
57
+
58
+ for (const element of arr) {
59
+ const needAdd = await cb(element);
60
+
61
+ if (needAdd) {
62
+ filtered.push(element);
63
+ }
64
+ }
65
+
66
+ return filtered;
67
+ }
68
+ router.get(
69
+ "/",
70
+ isAdmin,
71
+ error_catcher(async (req, res) => {
72
+ const { etype, ename, q } = req.query;
73
+ const qlink = q ? `&q=${encodeURIComponent(q)}` : "";
74
+ let edContents = "Choose an entity to edit";
75
+ const all_tables = await Table.find({}, { orderBy: "name", nocase: true });
76
+ const all_views = await View.find({}, { orderBy: "name", nocase: true });
77
+ const all_pages = await Page.find({}, { orderBy: "name", nocase: true });
78
+ const all_triggers = await Trigger.find(
79
+ {},
80
+ { orderBy: "name", nocase: true }
81
+ );
82
+ let tables, views, pages, triggers;
83
+ if (q) {
84
+ const qlower = q.toLowerCase();
85
+ const includesQ = (s) => s.toLowerCase().includes(qlower);
86
+
87
+ tables = await asyncFilter(all_tables, async (t) => {
88
+ const pack = await table_pack(t);
89
+ return includesQ(JSON.stringify(pack));
90
+ });
91
+ views = await asyncFilter(all_views, async (t) => {
92
+ const pack = await view_pack(t);
93
+ return includesQ(JSON.stringify(pack));
94
+ });
95
+ pages = await asyncFilter(all_pages, async (t) => {
96
+ const pack = await page_pack(t);
97
+ return includesQ(JSON.stringify(pack));
98
+ });
99
+ triggers = await asyncFilter(all_triggers, async (t) => {
100
+ const pack = await trigger_pack(t);
101
+ return includesQ(JSON.stringify(pack));
102
+ });
103
+ } else {
104
+ tables = all_tables;
105
+ views = all_views;
106
+ pages = all_pages;
107
+ triggers = all_triggers;
108
+ }
109
+ const li_link = (etype1, ename1) =>
110
+ li(
111
+ a(
112
+ {
113
+ href: `/registry-editor?etype=${etype1}&ename=${encodeURIComponent(
114
+ ename1
115
+ )}${qlink}`,
116
+ class: etype1 === etype && ename1 === ename ? "fw-bold" : undefined,
117
+ },
118
+ ename1
119
+ )
120
+ );
121
+ const mkForm = (jsonVal) =>
122
+ new Form({
123
+ labelCols: 0,
124
+ action: `/registry-editor?etype=${etype}&ename=${encodeURIComponent(
125
+ ename
126
+ )}${qlink}`,
127
+
128
+ values: { regval: JSON.stringify(jsonVal, null, 2) },
129
+ fields: [
130
+ {
131
+ name: "regval",
132
+ label: "",
133
+ input_type: "code",
134
+ attributes: { mode: "application/json" },
135
+ },
136
+ ],
137
+ });
138
+ switch (etype) {
139
+ case "table":
140
+ const tpack = await table_pack(
141
+ all_tables.find((t) => t.name === ename)
142
+ );
143
+ edContents = renderForm(mkForm(tpack), req.csrfToken());
144
+ break;
145
+ case "view":
146
+ const vpack = await view_pack(all_views.find((v) => v.name === ename));
147
+ edContents = renderForm(mkForm(vpack), req.csrfToken());
148
+ break;
149
+ case "page":
150
+ const ppack = await page_pack(all_pages.find((v) => v.name === ename));
151
+ edContents = renderForm(mkForm(ppack), req.csrfToken());
152
+ break;
153
+ case "trigger":
154
+ const trpack = await trigger_pack(
155
+ all_triggers.find((t) => t.name === ename)
156
+ );
157
+ edContents = renderForm(mkForm(trpack), req.csrfToken());
158
+ break;
159
+ }
160
+ send_infoarch_page({
161
+ res,
162
+ req,
163
+ active_sub: "Registry editor",
164
+ contents: {
165
+ widths: [3, 9],
166
+ besides: [
167
+ {
168
+ type: "card",
169
+ bodyClass: "p-1",
170
+ title: "Entities",
171
+ contents: div(
172
+ form(
173
+ { method: "GET", action: `/registry-editor` },
174
+ div(
175
+ { class: "input-group search-bar mb-2" },
176
+ etype &&
177
+ input({ type: "hidden", name: "etype", value: etype }),
178
+ ename &&
179
+ input({ type: "hidden", name: "ename", value: ename }),
180
+ input({
181
+ type: "search",
182
+ class: "form-control search-bar ps-2 hasbl",
183
+ placeholder: "Search",
184
+ name: "q",
185
+ value: q,
186
+ "aria-label": "Search",
187
+ "aria-describedby": "button-search-submit",
188
+ }),
189
+ button(
190
+ {
191
+ class: "btn btn-outline-secondary search-bar",
192
+ type: "submit",
193
+ },
194
+ i({ class: "fas fa-search" })
195
+ )
196
+ )
197
+ ),
198
+ // following https://iamkate.com/code/tree-views/
199
+ ul(
200
+ { class: "katetree ps-2" },
201
+ li(
202
+ details(
203
+ { open: q || etype === "table" },
204
+ summary("Tables"),
205
+ ul(
206
+ { class: "ps-3" },
207
+ tables.map((t) => li_link("table", t.name))
208
+ )
209
+ )
210
+ ),
211
+ li(
212
+ details(
213
+ { open: q || etype === "view" },
214
+ summary("Views"),
215
+ ul(
216
+ { class: "ps-3" },
217
+ views.map((v) => li_link("view", v.name))
218
+ )
219
+ )
220
+ ),
221
+ li(
222
+ details(
223
+ { open: q || etype === "page" }, //
224
+ summary("Pages"),
225
+ ul(
226
+ { class: "ps-3" },
227
+ pages.map((p) => li_link("page", p.name))
228
+ )
229
+ )
230
+ ),
231
+ li(
232
+ details(
233
+ { open: q || etype === "trigger" }, //
234
+ summary("Triggers"),
235
+ ul(
236
+ { class: "ps-3" },
237
+ triggers.map((t) => li_link("trigger", t.name))
238
+ )
239
+ )
240
+ )
241
+ )
242
+ ),
243
+ },
244
+ {
245
+ type: "card",
246
+ title:
247
+ ename && etype
248
+ ? `Registry editor: ${ename} ${etype}`
249
+ : "Registry editor",
250
+ contents: edContents,
251
+ },
252
+ ],
253
+ },
254
+ });
255
+ })
256
+ );
257
+
258
+ router.post(
259
+ "/",
260
+ isAdmin,
261
+ error_catcher(async (req, res) => {
262
+ const { etype, ename, q } = req.query;
263
+ const qlink = q ? `&q=${encodeURIComponent(q)}` : "";
264
+
265
+ const entVal = JSON.parse(req.body.regval);
266
+ let pack = { plugins: [], tables: [], views: [], pages: [], triggers: [] };
267
+
268
+ switch (etype) {
269
+ case "table":
270
+ pack.tables = [entVal];
271
+ break;
272
+ case "view":
273
+ pack.views = [entVal];
274
+ break;
275
+ case "page":
276
+ pack.pages = [entVal];
277
+ break;
278
+ case "trigger":
279
+ pack.triggers = [entVal];
280
+ break;
281
+ }
282
+ await install_pack(pack);
283
+ res.redirect(
284
+ `/registry-editor?etype=${etype}&ename=${encodeURIComponent(
285
+ ename
286
+ )}${qlink}`
287
+ );
288
+ })
289
+ );