@saltcorn/server 0.9.3-beta.0 → 0.9.3-beta.2
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 +2 -1
- package/package.json +8 -8
- package/public/bootstrap-iconpicker.js +2 -1
- package/public/relation_helpers.js +8 -6
- package/routes/actions.js +23 -10
- package/routes/fields.js +12 -10
- package/routes/menu.js +63 -63
- package/routes/pageedit.js +1 -0
- package/routes/tables.js +5 -9
- package/routes/viewedit.js +2 -1
- package/tests/plugins.test.js +1 -1
package/locales/en.json
CHANGED
|
@@ -1286,5 +1286,6 @@
|
|
|
1286
1286
|
"Install more themes »": "Install more themes »",
|
|
1287
1287
|
"Configure action": "Configure action",
|
|
1288
1288
|
"No changes detected, snapshot skipped": "No changes detected, snapshot skipped",
|
|
1289
|
-
"Cannot remove module: views %s depend on it": "Cannot remove module: views %s depend on it"
|
|
1289
|
+
"Cannot remove module: views %s depend on it": "Cannot remove module: views %s depend on it",
|
|
1290
|
+
"The view name is part of the URL when it is shown alone.": "The view name is part of the URL when it is shown alone."
|
|
1290
1291
|
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.3-beta.
|
|
3
|
+
"version": "0.9.3-beta.2",
|
|
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.3-beta.
|
|
11
|
-
"@saltcorn/builder": "0.9.3-beta.
|
|
12
|
-
"@saltcorn/data": "0.9.3-beta.
|
|
13
|
-
"@saltcorn/admin-models": "0.9.3-beta.
|
|
14
|
-
"@saltcorn/filemanager": "0.9.3-beta.
|
|
15
|
-
"@saltcorn/markup": "0.9.3-beta.
|
|
16
|
-
"@saltcorn/sbadmin2": "0.9.3-beta.
|
|
10
|
+
"@saltcorn/base-plugin": "0.9.3-beta.2",
|
|
11
|
+
"@saltcorn/builder": "0.9.3-beta.2",
|
|
12
|
+
"@saltcorn/data": "0.9.3-beta.2",
|
|
13
|
+
"@saltcorn/admin-models": "0.9.3-beta.2",
|
|
14
|
+
"@saltcorn/filemanager": "0.9.3-beta.2",
|
|
15
|
+
"@saltcorn/markup": "0.9.3-beta.2",
|
|
16
|
+
"@saltcorn/sbadmin2": "0.9.3-beta.2",
|
|
17
17
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
18
18
|
"@socket.io/sticky": "^1.0.1",
|
|
19
19
|
"adm-zip": "0.5.10",
|
|
@@ -496,7 +496,8 @@
|
|
|
496
496
|
var el = $this.data('bs.popover');
|
|
497
497
|
var tip = ($.fn.bsVersion() === '3.x') ? el.tip()
|
|
498
498
|
: ($.fn.bsVersion() === '5.x')
|
|
499
|
-
? $(bootstrap.Popover.getInstance($this).getTipElement()
|
|
499
|
+
? $(bootstrap.Popover.getInstance($this).getTipElement?.() ||
|
|
500
|
+
bootstrap.Popover.getInstance($this).tip)
|
|
500
501
|
: $(el.getTipElement())
|
|
501
502
|
|
|
502
503
|
tip.addClass('iconpicker-popover');
|
|
@@ -169,6 +169,10 @@ var relationHelpers = (() => {
|
|
|
169
169
|
*/
|
|
170
170
|
const RelationsFinder = function (tablesCache, allViews, maxDepth) {
|
|
171
171
|
this.maxDepth = +maxDepth;
|
|
172
|
+
if (isNaN(this.maxDepth)) {
|
|
173
|
+
console.log(`maxDepth '${maxDepth}' is not a number, set to 6`);
|
|
174
|
+
this.maxDepth = 6;
|
|
175
|
+
}
|
|
172
176
|
this.allViews = allViews;
|
|
173
177
|
const { tableIdCache, tableNameCache, fieldCache } = tablesCache;
|
|
174
178
|
this.tableIdCache = tableIdCache;
|
|
@@ -303,8 +307,8 @@ var relationHelpers = (() => {
|
|
|
303
307
|
const searcher = (current, path, level, visited) => {
|
|
304
308
|
if (level > this.maxDepth) return;
|
|
305
309
|
const visitedFkCopy = new Set(visited);
|
|
306
|
-
const
|
|
307
|
-
|
|
310
|
+
for (const fk of current.foreign_keys) {
|
|
311
|
+
if (visitedFkCopy.has(fk.id)) continue;
|
|
308
312
|
visitedFkCopy.add(fk.id);
|
|
309
313
|
const target = this.tableNameCache[fk.reftable_name];
|
|
310
314
|
if (!target)
|
|
@@ -315,10 +319,8 @@ var relationHelpers = (() => {
|
|
|
315
319
|
}
|
|
316
320
|
|
|
317
321
|
const visitedInboundCopy = new Set(visited);
|
|
318
|
-
const
|
|
319
|
-
(
|
|
320
|
-
);
|
|
321
|
-
for (const inbound of inbounds) {
|
|
322
|
+
for (const inbound of this.fieldCache[current.name] || []) {
|
|
323
|
+
if (visitedInboundCopy.has(inbound.id)) continue;
|
|
322
324
|
visitedInboundCopy.add(inbound.id);
|
|
323
325
|
const target = this.tableIdCache[inbound.table_id];
|
|
324
326
|
if (!target)
|
package/routes/actions.js
CHANGED
|
@@ -162,6 +162,7 @@ const triggerForm = async (req, trigger) => {
|
|
|
162
162
|
label: req.__("Name"),
|
|
163
163
|
type: "String",
|
|
164
164
|
required: true,
|
|
165
|
+
attributes: { autofocus: true },
|
|
165
166
|
sublabel: req.__("Name of action"),
|
|
166
167
|
},
|
|
167
168
|
{
|
|
@@ -380,11 +381,15 @@ router.post(
|
|
|
380
381
|
* @memberof module:routes/actions~actionsRouter
|
|
381
382
|
*/
|
|
382
383
|
router.get(
|
|
383
|
-
"/configure/:
|
|
384
|
+
"/configure/:idorname",
|
|
384
385
|
isAdmin,
|
|
385
386
|
error_catcher(async (req, res) => {
|
|
386
|
-
const {
|
|
387
|
-
|
|
387
|
+
const { idorname } = req.params;
|
|
388
|
+
let trigger;
|
|
389
|
+
let id = parseInt(idorname);
|
|
390
|
+
if (id) trigger = await Trigger.findOne({ id });
|
|
391
|
+
else trigger = await Trigger.findOne({ name: idorname });
|
|
392
|
+
|
|
388
393
|
if (!trigger) {
|
|
389
394
|
req.flash("warning", req.__("Action not found"));
|
|
390
395
|
res.redirect(`/actions/`);
|
|
@@ -396,11 +401,16 @@ router.get(
|
|
|
396
401
|
? Table.findOne({ id: trigger.table_id })
|
|
397
402
|
: null;
|
|
398
403
|
|
|
399
|
-
const subtitle =
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
+
const subtitle =
|
|
405
|
+
span(
|
|
406
|
+
{ class: "ms-3" },
|
|
407
|
+
trigger.action,
|
|
408
|
+
table ? ` on ` + a({ href: `/table/${table.name}` }, table.name) : ""
|
|
409
|
+
) +
|
|
410
|
+
a(
|
|
411
|
+
{ href: `/actions/testrun/${id}`, class: "ms-2" },
|
|
412
|
+
req.__("Test run") + " »"
|
|
413
|
+
);
|
|
404
414
|
if (!action) {
|
|
405
415
|
req.flash("warning", req.__("Action not found"));
|
|
406
416
|
res.redirect(`/actions/`);
|
|
@@ -618,7 +628,7 @@ router.get(
|
|
|
618
628
|
let table, row;
|
|
619
629
|
if (trigger.table_id) {
|
|
620
630
|
table = Table.findOne({ id: trigger.table_id });
|
|
621
|
-
row = await table.getRow({}, {orderBy: "RANDOM()"});
|
|
631
|
+
row = await table.getRow({}, { orderBy: "RANDOM()" });
|
|
622
632
|
}
|
|
623
633
|
let runres;
|
|
624
634
|
|
|
@@ -666,7 +676,10 @@ router.get(
|
|
|
666
676
|
"« " + req.__("back to actions")
|
|
667
677
|
),
|
|
668
678
|
a(
|
|
669
|
-
{
|
|
679
|
+
{
|
|
680
|
+
href: `/actions/configure/${trigger.id}`,
|
|
681
|
+
class: "mt-4 btn btn-primary me-1",
|
|
682
|
+
},
|
|
670
683
|
req.__("Configure action")
|
|
671
684
|
),
|
|
672
685
|
a(
|
package/routes/fields.js
CHANGED
|
@@ -82,7 +82,8 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
|
|
|
82
82
|
label: req.__("Label"),
|
|
83
83
|
name: "label",
|
|
84
84
|
sublabel: req.__("Name of the field"),
|
|
85
|
-
|
|
85
|
+
type: "String",
|
|
86
|
+
attributes: { autofocus: true },
|
|
86
87
|
validator(s) {
|
|
87
88
|
if (!s || s === "") return req.__("Missing label");
|
|
88
89
|
if (!id && existing_names.includes(Field.labelToName(s)))
|
|
@@ -96,15 +97,6 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
|
|
|
96
97
|
}
|
|
97
98
|
},
|
|
98
99
|
}),
|
|
99
|
-
// description
|
|
100
|
-
new Field({
|
|
101
|
-
label: req.__("Description"),
|
|
102
|
-
name: "description",
|
|
103
|
-
sublabel: req.__(
|
|
104
|
-
"Description allows to give more information about field"
|
|
105
|
-
),
|
|
106
|
-
input_type: "text",
|
|
107
|
-
}),
|
|
108
100
|
new Field({
|
|
109
101
|
label: req.__("Type"),
|
|
110
102
|
name: "type",
|
|
@@ -120,6 +112,16 @@ const fieldForm = async (req, fkey_opts, existing_names, id, hasData) => {
|
|
|
120
112
|
!getState().getConfig("development_mode", false) &&
|
|
121
113
|
(hasData || db.isSQLite),
|
|
122
114
|
}),
|
|
115
|
+
// description
|
|
116
|
+
new Field({
|
|
117
|
+
label: req.__("Description"),
|
|
118
|
+
name: "description",
|
|
119
|
+
sublabel: req.__(
|
|
120
|
+
"Description allows to give more information about field"
|
|
121
|
+
),
|
|
122
|
+
input_type: "text",
|
|
123
|
+
}),
|
|
124
|
+
|
|
123
125
|
new Field({
|
|
124
126
|
label: req.__("Calculated"),
|
|
125
127
|
name: "calculated",
|
package/routes/menu.js
CHANGED
|
@@ -109,68 +109,6 @@ const menuForm = async (req) => {
|
|
|
109
109
|
"Action",
|
|
110
110
|
],
|
|
111
111
|
},
|
|
112
|
-
{
|
|
113
|
-
name: "text",
|
|
114
|
-
label: req.__("Text label"),
|
|
115
|
-
class: "item-menu",
|
|
116
|
-
input_type: "text",
|
|
117
|
-
required: true,
|
|
118
|
-
showIf: {
|
|
119
|
-
type: [
|
|
120
|
-
"View",
|
|
121
|
-
"Page",
|
|
122
|
-
"Link",
|
|
123
|
-
"Header",
|
|
124
|
-
"Dynamic",
|
|
125
|
-
"Search",
|
|
126
|
-
"Action",
|
|
127
|
-
],
|
|
128
|
-
},
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
name: "icon_btn",
|
|
132
|
-
label: req.__("Icon"),
|
|
133
|
-
input_type: "custom_html",
|
|
134
|
-
attributes: {
|
|
135
|
-
html: `<button type="button" id="myEditor_icon" class="btn btn-outline-secondary"></button>`,
|
|
136
|
-
},
|
|
137
|
-
showIf: { type: ["View", "Page", "Link", "Header", "Action"] },
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
name: "icon",
|
|
141
|
-
class: "item-menu",
|
|
142
|
-
input_type: "hidden",
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
name: "min_role",
|
|
146
|
-
label: req.__("Minimum role"),
|
|
147
|
-
class: "item-menu",
|
|
148
|
-
input_type: "select",
|
|
149
|
-
options: roles.map((r) => ({ label: r.role, value: r.id })),
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
name: "disable_on_mobile",
|
|
153
|
-
label: req.__("Disable on mobile"),
|
|
154
|
-
type: "Bool",
|
|
155
|
-
class: "item-menu",
|
|
156
|
-
required: false,
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
name: "target_blank",
|
|
160
|
-
label: req.__("Open in new tab"),
|
|
161
|
-
type: "Bool",
|
|
162
|
-
required: false,
|
|
163
|
-
class: "item-menu",
|
|
164
|
-
showIf: { type: ["View", "Page", "Link"] },
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
name: "in_modal",
|
|
168
|
-
label: req.__("Open in popup modal?"),
|
|
169
|
-
type: "Bool",
|
|
170
|
-
required: false,
|
|
171
|
-
class: "item-menu",
|
|
172
|
-
showIf: { type: ["View", "Page", "Link"] },
|
|
173
|
-
},
|
|
174
112
|
{
|
|
175
113
|
name: "url",
|
|
176
114
|
label: req.__("URL"),
|
|
@@ -196,7 +134,7 @@ const menuForm = async (req) => {
|
|
|
196
134
|
},
|
|
197
135
|
{
|
|
198
136
|
name: "viewname",
|
|
199
|
-
label: req.__("
|
|
137
|
+
label: req.__("View"),
|
|
200
138
|
type: "String",
|
|
201
139
|
class: "item-menu",
|
|
202
140
|
required: true,
|
|
@@ -275,6 +213,68 @@ const menuForm = async (req) => {
|
|
|
275
213
|
required: true,
|
|
276
214
|
showIf: { type: "Dynamic" },
|
|
277
215
|
},
|
|
216
|
+
{
|
|
217
|
+
name: "text",
|
|
218
|
+
label: req.__("Text label"),
|
|
219
|
+
class: "item-menu",
|
|
220
|
+
input_type: "text",
|
|
221
|
+
required: true,
|
|
222
|
+
showIf: {
|
|
223
|
+
type: [
|
|
224
|
+
"View",
|
|
225
|
+
"Page",
|
|
226
|
+
"Link",
|
|
227
|
+
"Header",
|
|
228
|
+
"Dynamic",
|
|
229
|
+
"Search",
|
|
230
|
+
"Action",
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "icon_btn",
|
|
236
|
+
label: req.__("Icon"),
|
|
237
|
+
input_type: "custom_html",
|
|
238
|
+
attributes: {
|
|
239
|
+
html: `<button type="button" id="myEditor_icon" class="btn btn-outline-secondary"></button>`,
|
|
240
|
+
},
|
|
241
|
+
showIf: { type: ["View", "Page", "Link", "Header", "Action"] },
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: "icon",
|
|
245
|
+
class: "item-menu",
|
|
246
|
+
input_type: "hidden",
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: "min_role",
|
|
250
|
+
label: req.__("Minimum role"),
|
|
251
|
+
class: "item-menu",
|
|
252
|
+
input_type: "select",
|
|
253
|
+
options: roles.map((r) => ({ label: r.role, value: r.id })),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "disable_on_mobile",
|
|
257
|
+
label: req.__("Disable on mobile"),
|
|
258
|
+
type: "Bool",
|
|
259
|
+
class: "item-menu",
|
|
260
|
+
required: false,
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: "target_blank",
|
|
264
|
+
label: req.__("Open in new tab"),
|
|
265
|
+
type: "Bool",
|
|
266
|
+
required: false,
|
|
267
|
+
class: "item-menu",
|
|
268
|
+
showIf: { type: ["View", "Page", "Link"] },
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: "in_modal",
|
|
272
|
+
label: req.__("Open in popup modal?"),
|
|
273
|
+
type: "Bool",
|
|
274
|
+
required: false,
|
|
275
|
+
class: "item-menu",
|
|
276
|
+
showIf: { type: ["View", "Page", "Link"] },
|
|
277
|
+
},
|
|
278
278
|
{
|
|
279
279
|
name: "style",
|
|
280
280
|
label: req.__("Style"),
|
package/routes/pageedit.js
CHANGED
package/routes/tables.js
CHANGED
|
@@ -162,9 +162,7 @@ const tableForm = async (table, req) => {
|
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
label: req.__("Version history"),
|
|
165
|
-
sublabel: req.__(
|
|
166
|
-
"Track table data changes over time"
|
|
167
|
-
),
|
|
165
|
+
sublabel: req.__("Track table data changes over time"),
|
|
168
166
|
name: "versioned",
|
|
169
167
|
type: "Bool",
|
|
170
168
|
},
|
|
@@ -225,8 +223,9 @@ router.get(
|
|
|
225
223
|
{
|
|
226
224
|
label: req.__("Table name"),
|
|
227
225
|
name: "name",
|
|
228
|
-
|
|
226
|
+
type: "String",
|
|
229
227
|
required: true,
|
|
228
|
+
attributes: { autofocus: true },
|
|
230
229
|
},
|
|
231
230
|
...(table_provider_names.length
|
|
232
231
|
? [
|
|
@@ -236,7 +235,7 @@ router.get(
|
|
|
236
235
|
input_type: "select",
|
|
237
236
|
options: [
|
|
238
237
|
// Due to packages/saltcorn-markup/helpers.ts#L45 (select_options replaces label if o.value === "")
|
|
239
|
-
{label:req.__("Database table"), value:
|
|
238
|
+
{ label: req.__("Database table"), value: "-" },
|
|
240
239
|
...table_provider_names,
|
|
241
240
|
],
|
|
242
241
|
required: true,
|
|
@@ -1065,10 +1064,7 @@ router.post(
|
|
|
1065
1064
|
} else if (db.sqlsanitize(name) === "") {
|
|
1066
1065
|
req.flash("error", req.__(`Invalid table name %s`, name));
|
|
1067
1066
|
res.redirect(`/table/new`);
|
|
1068
|
-
} else if (
|
|
1069
|
-
rest.provider_name &&
|
|
1070
|
-
rest.provider_name !== "-"
|
|
1071
|
-
) {
|
|
1067
|
+
} else if (rest.provider_name && rest.provider_name !== "-") {
|
|
1072
1068
|
const table = await Table.create(name, rest);
|
|
1073
1069
|
res.redirect(`/table/provider-cfg/${table.id}`);
|
|
1074
1070
|
} else {
|
package/routes/viewedit.js
CHANGED
|
@@ -131,8 +131,9 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
131
131
|
label: req.__("View name"),
|
|
132
132
|
name: "name",
|
|
133
133
|
type: "String",
|
|
134
|
+
attributes: { autofocus: true },
|
|
134
135
|
sublabel: req.__(
|
|
135
|
-
"The view name
|
|
136
|
+
"The view name is part of the URL when it is shown alone."
|
|
136
137
|
),
|
|
137
138
|
}),
|
|
138
139
|
new Field({
|
package/tests/plugins.test.js
CHANGED
|
@@ -233,7 +233,7 @@ describe("Pack Endpoints", () => {
|
|
|
233
233
|
.send(
|
|
234
234
|
"pack=les%22%3A+%5B%5D%2C+%22views%22%3A+%5B%5D%2C+%22plugins%22%3A+%5B%5D%2C+%22pages%22%3A+%5B%5D+%7D"
|
|
235
235
|
)
|
|
236
|
-
.expect(toInclude("
|
|
236
|
+
.expect(toInclude("Unexpected token l in JSON at position 0"));
|
|
237
237
|
});
|
|
238
238
|
it("should install named", async () => {
|
|
239
239
|
const loginCookie = await getAdminLoginCookie();
|