@saltcorn/server 1.1.0-beta.8 → 1.1.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/auth/admin.js +1 -1
- package/auth/roleadmin.js +10 -2
- package/help/{Cordova Builder.tmd → Capacitor Builder.tmd } +1 -1
- package/help/Configuration keys.tmd +5 -0
- package/help/index.js +2 -0
- package/load_plugins.js +12 -5
- package/locales/en.json +29 -1
- package/locales/pl.json +19 -2
- package/markup/admin.js +7 -3
- package/package.json +9 -9
- package/public/codemirror.css +33 -0
- package/public/flatpickr-dark.css +795 -0
- package/public/gridedit.js +1 -1
- package/public/mermaid.min.js +1077 -792
- package/public/saltcorn-common.js +93 -44
- package/public/saltcorn.css +11 -4
- package/public/saltcorn.js +60 -22
- package/routes/actions.js +936 -4
- package/routes/admin.js +98 -89
- package/routes/api.js +51 -0
- package/routes/config.js +39 -25
- package/routes/eventlog.js +41 -1
- package/routes/fields.js +8 -2
- package/routes/homepage.js +13 -3
- package/routes/list.js +17 -1
- package/routes/plugins.js +7 -2
- package/routes/registry.js +45 -3
- package/routes/tables.js +58 -20
- package/routes/tenant.js +53 -3
- package/routes/viewedit.js +8 -8
- package/tests/plugin_install.test.js +10 -10
- package/tests/plugins.test.js +6 -6
- package/wrapper.js +3 -1
package/routes/list.js
CHANGED
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
const Table = require("@saltcorn/data/models/table");
|
|
27
27
|
const { isAdmin, error_catcher } = require("./utils");
|
|
28
28
|
const moment = require("moment");
|
|
29
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* @type {object}
|
|
@@ -267,9 +268,11 @@ router.get(
|
|
|
267
268
|
clipboard: false,
|
|
268
269
|
cellClick: "__delete_tabulator_row",
|
|
269
270
|
});
|
|
271
|
+
const isDark = getState().getLightDarkMode(req.user) === "dark";
|
|
270
272
|
res.sendWrap(
|
|
271
273
|
{
|
|
272
274
|
title: req.__(`%s data table`, table.name),
|
|
275
|
+
requestFluidLayout: true,
|
|
273
276
|
headers: [
|
|
274
277
|
//jsgrid - grid editor external component
|
|
275
278
|
{
|
|
@@ -295,6 +298,13 @@ router.get(
|
|
|
295
298
|
{
|
|
296
299
|
css: `/static_assets/${db.connectObj.version_tag}/flatpickr.min.css`,
|
|
297
300
|
},
|
|
301
|
+
...(isDark
|
|
302
|
+
? [
|
|
303
|
+
{
|
|
304
|
+
css: `/static_assets/${db.connectObj.version_tag}/flatpickr-dark.css`,
|
|
305
|
+
},
|
|
306
|
+
]
|
|
307
|
+
: []),
|
|
298
308
|
],
|
|
299
309
|
},
|
|
300
310
|
{
|
|
@@ -426,7 +436,13 @@ router.get(
|
|
|
426
436
|
),
|
|
427
437
|
div({ id: "jsGridNotify" }),
|
|
428
438
|
|
|
429
|
-
div({
|
|
439
|
+
div({
|
|
440
|
+
id: "jsGrid",
|
|
441
|
+
class:
|
|
442
|
+
getState().getLightDarkMode(req.user) === "dark"
|
|
443
|
+
? "table-dark"
|
|
444
|
+
: undefined,
|
|
445
|
+
})
|
|
430
446
|
),
|
|
431
447
|
},
|
|
432
448
|
],
|
package/routes/plugins.js
CHANGED
|
@@ -57,7 +57,11 @@ const fs = require("fs");
|
|
|
57
57
|
const path = require("path");
|
|
58
58
|
const { get_latest_npm_version } = require("@saltcorn/data/models/config");
|
|
59
59
|
const { flash_restart } = require("../markup/admin.js");
|
|
60
|
-
const {
|
|
60
|
+
const {
|
|
61
|
+
sleep,
|
|
62
|
+
removeNonWordChars,
|
|
63
|
+
getFetchProxyOptions,
|
|
64
|
+
} = require("@saltcorn/data/utils");
|
|
61
65
|
const { loadAllPlugins } = require("../load_plugins");
|
|
62
66
|
const npmFetch = require("npm-registry-fetch");
|
|
63
67
|
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
@@ -609,7 +613,8 @@ router.get(
|
|
|
609
613
|
} else {
|
|
610
614
|
try {
|
|
611
615
|
const pkgInfo = await npmFetch.json(
|
|
612
|
-
`https://registry.npmjs.org/${plugin.location}
|
|
616
|
+
`https://registry.npmjs.org/${plugin.location}`,
|
|
617
|
+
getFetchProxyOptions()
|
|
613
618
|
);
|
|
614
619
|
if (!pkgInfo?.versions)
|
|
615
620
|
throw new Error(req.__("Unable to fetch versions"));
|
package/routes/registry.js
CHANGED
|
@@ -7,6 +7,7 @@ const {
|
|
|
7
7
|
domReady,
|
|
8
8
|
a,
|
|
9
9
|
div,
|
|
10
|
+
h4,
|
|
10
11
|
i,
|
|
11
12
|
text,
|
|
12
13
|
button,
|
|
@@ -40,6 +41,7 @@ const {
|
|
|
40
41
|
install_pack,
|
|
41
42
|
} = require("@saltcorn/admin-models/models/pack");
|
|
42
43
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
44
|
+
const { getState } = require("@saltcorn/data/db/state");
|
|
43
45
|
/**
|
|
44
46
|
* @type {object}
|
|
45
47
|
* @const
|
|
@@ -79,7 +81,17 @@ router.get(
|
|
|
79
81
|
{},
|
|
80
82
|
{ orderBy: "name", nocase: true }
|
|
81
83
|
);
|
|
82
|
-
|
|
84
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
85
|
+
|
|
86
|
+
const all_configs_obj = await getState().getAllConfigOrDefaults();
|
|
87
|
+
const all_configs = Object.entries(all_configs_obj)
|
|
88
|
+
.map(([name, v]) => ({
|
|
89
|
+
...v,
|
|
90
|
+
name,
|
|
91
|
+
}))
|
|
92
|
+
.filter((c) => isRoot || !c.root_only);
|
|
93
|
+
|
|
94
|
+
let tables, views, pages, triggers, configs;
|
|
83
95
|
if (q) {
|
|
84
96
|
const qlower = q.toLowerCase();
|
|
85
97
|
const includesQ = (s) => s.toLowerCase().includes(qlower);
|
|
@@ -100,11 +112,13 @@ router.get(
|
|
|
100
112
|
const pack = await trigger_pack(t);
|
|
101
113
|
return includesQ(JSON.stringify(pack));
|
|
102
114
|
});
|
|
115
|
+
configs = all_configs.filter((c) => includesQ(JSON.stringify(c)));
|
|
103
116
|
} else {
|
|
104
117
|
tables = all_tables;
|
|
105
118
|
views = all_views;
|
|
106
119
|
pages = all_pages;
|
|
107
120
|
triggers = all_triggers;
|
|
121
|
+
configs = all_configs;
|
|
108
122
|
}
|
|
109
123
|
const li_link = (etype1, ename1) =>
|
|
110
124
|
li(
|
|
@@ -124,7 +138,7 @@ router.get(
|
|
|
124
138
|
action: `/registry-editor?etype=${etype}&ename=${encodeURIComponent(
|
|
125
139
|
ename
|
|
126
140
|
)}${qlink}`,
|
|
127
|
-
|
|
141
|
+
formStyle: "vert",
|
|
128
142
|
values: { regval: JSON.stringify(jsonVal, null, 2) },
|
|
129
143
|
fields: [
|
|
130
144
|
{
|
|
@@ -177,6 +191,14 @@ router.get(
|
|
|
177
191
|
const ppack = await page_pack(all_pages.find((v) => v.name === ename));
|
|
178
192
|
edContents = renderForm(mkForm(ppack), req.csrfToken());
|
|
179
193
|
break;
|
|
194
|
+
case "config":
|
|
195
|
+
const config = all_configs.find((t) => t.name === ename);
|
|
196
|
+
edContents =
|
|
197
|
+
h4(config.label) +
|
|
198
|
+
(config.blurb || "") +
|
|
199
|
+
(config.sublabel || "") +
|
|
200
|
+
renderForm(mkForm(config.value), req.csrfToken());
|
|
201
|
+
break;
|
|
180
202
|
case "trigger":
|
|
181
203
|
const trigger = all_triggers.find((t) => t.name === ename);
|
|
182
204
|
const trpack = await trigger_pack(trigger);
|
|
@@ -282,6 +304,16 @@ router.get(
|
|
|
282
304
|
triggers.map((t) => li_link("trigger", t.name))
|
|
283
305
|
)
|
|
284
306
|
)
|
|
307
|
+
),
|
|
308
|
+
li(
|
|
309
|
+
details(
|
|
310
|
+
{ open: q || etype === "CONFIG" }, //
|
|
311
|
+
summary("Configuration"),
|
|
312
|
+
ul(
|
|
313
|
+
{ class: "ps-3" },
|
|
314
|
+
configs.map((t) => li_link("config", t.name))
|
|
315
|
+
)
|
|
316
|
+
)
|
|
285
317
|
)
|
|
286
318
|
)
|
|
287
319
|
),
|
|
@@ -309,7 +341,14 @@ router.post(
|
|
|
309
341
|
const qlink = q ? `&q=${encodeURIComponent(q)}` : "";
|
|
310
342
|
|
|
311
343
|
const entVal = JSON.parse(req.body.regval);
|
|
312
|
-
let pack = {
|
|
344
|
+
let pack = {
|
|
345
|
+
plugins: [],
|
|
346
|
+
tables: [],
|
|
347
|
+
views: [],
|
|
348
|
+
pages: [],
|
|
349
|
+
triggers: [],
|
|
350
|
+
config: {},
|
|
351
|
+
};
|
|
313
352
|
|
|
314
353
|
switch (etype) {
|
|
315
354
|
case "table":
|
|
@@ -324,6 +363,9 @@ router.post(
|
|
|
324
363
|
case "trigger":
|
|
325
364
|
pack.triggers = [entVal];
|
|
326
365
|
break;
|
|
366
|
+
case "config":
|
|
367
|
+
pack.config[ename] = entVal;
|
|
368
|
+
break;
|
|
327
369
|
}
|
|
328
370
|
await install_pack(pack);
|
|
329
371
|
res.redirect(
|
package/routes/tables.js
CHANGED
|
@@ -56,7 +56,7 @@ const {
|
|
|
56
56
|
} = require("@saltcorn/data/models/discovery");
|
|
57
57
|
const { getState } = require("@saltcorn/data/db/state");
|
|
58
58
|
const { cardHeaderTabs } = require("@saltcorn/markup/layout_utils");
|
|
59
|
-
const { tablesList, viewsList } = require("./common_lists");
|
|
59
|
+
const { tablesList, viewsList, getTriggerList } = require("./common_lists");
|
|
60
60
|
const {
|
|
61
61
|
InvalidConfiguration,
|
|
62
62
|
removeAllWhiteSpace,
|
|
@@ -757,6 +757,8 @@ router.get(
|
|
|
757
757
|
const triggers = table.id ? Trigger.find({ table_id: table.id }) : [];
|
|
758
758
|
triggers.sort(comparingCaseInsensitive("name"));
|
|
759
759
|
let fieldCard;
|
|
760
|
+
const nPrimaryKeys = fields.filter((f) => f.primary_key).length;
|
|
761
|
+
|
|
760
762
|
if (fields.length === 0) {
|
|
761
763
|
fieldCard = [
|
|
762
764
|
h4(req.__(`No fields defined in %s table`, table.name)),
|
|
@@ -818,28 +820,25 @@ router.get(
|
|
|
818
820
|
{ hover: true }
|
|
819
821
|
);
|
|
820
822
|
fieldCard = [
|
|
823
|
+
nPrimaryKeys > 1 &&
|
|
824
|
+
div(
|
|
825
|
+
{ class: "alert alert-danger", role: "alert" },
|
|
826
|
+
i({ class: "fas fa-exclamation-triangle" }),
|
|
827
|
+
"This table has composite primary keys which is not supported in Saltcorn. A procedure to introduce a single autoincrementing primary key is available.",
|
|
828
|
+
post_btn(
|
|
829
|
+
`/table/repair-composite-primary/${table.id}`,
|
|
830
|
+
"Add autoincrementing primary key",
|
|
831
|
+
req.csrfToken(),
|
|
832
|
+
{ btnClass: "btn-danger" }
|
|
833
|
+
)
|
|
834
|
+
),
|
|
835
|
+
|
|
821
836
|
tableHtml,
|
|
822
837
|
inbound_refs.length > 0
|
|
823
838
|
? req.__("Inbound keys: ") +
|
|
824
839
|
inbound_refs.map((tnm) => link(`/table/${tnm}`, tnm)).join(", ") +
|
|
825
840
|
"<br>"
|
|
826
841
|
: "",
|
|
827
|
-
triggers.length
|
|
828
|
-
? req.__("Table triggers: ") +
|
|
829
|
-
triggers
|
|
830
|
-
.map((t) =>
|
|
831
|
-
link(
|
|
832
|
-
`/actions/configure/${
|
|
833
|
-
t.id
|
|
834
|
-
}?on_done_redirect=${encodeURIComponent(
|
|
835
|
-
`table/${table.name}`
|
|
836
|
-
)}`,
|
|
837
|
-
t.name
|
|
838
|
-
)
|
|
839
|
-
)
|
|
840
|
-
.join(", ") +
|
|
841
|
-
"<br>"
|
|
842
|
-
: "",
|
|
843
842
|
!table.external &&
|
|
844
843
|
!table.provider_name &&
|
|
845
844
|
a(
|
|
@@ -851,7 +850,8 @@ router.get(
|
|
|
851
850
|
),
|
|
852
851
|
];
|
|
853
852
|
}
|
|
854
|
-
|
|
853
|
+
let viewCard;
|
|
854
|
+
let triggerCard = "";
|
|
855
855
|
if (fields.length > 0) {
|
|
856
856
|
const views = await View.find(
|
|
857
857
|
table.id ? { table_id: table.id } : { exttable_name: table.name }
|
|
@@ -884,6 +884,25 @@ router.get(
|
|
|
884
884
|
req.__("Create view")
|
|
885
885
|
),
|
|
886
886
|
};
|
|
887
|
+
|
|
888
|
+
triggerCard = {
|
|
889
|
+
type: "card",
|
|
890
|
+
id: "table-triggers",
|
|
891
|
+
title: req.__("Triggers on table"),
|
|
892
|
+
contents:
|
|
893
|
+
(triggers.length
|
|
894
|
+
? await getTriggerList(triggers, req)
|
|
895
|
+
: p("Triggers run actions in response to events on this table")) +
|
|
896
|
+
a(
|
|
897
|
+
{
|
|
898
|
+
href: `/actions/new?table=${encodeURIComponent(
|
|
899
|
+
table.name
|
|
900
|
+
)}&on_done_redirect=${encodeURIComponent(`table/${table.name}`)}`,
|
|
901
|
+
class: "btn btn-primary",
|
|
902
|
+
},
|
|
903
|
+
req.__("Create trigger")
|
|
904
|
+
),
|
|
905
|
+
};
|
|
887
906
|
}
|
|
888
907
|
const models = await Model.find({ table_id: table.id });
|
|
889
908
|
const modelCard = div(
|
|
@@ -1078,6 +1097,7 @@ router.get(
|
|
|
1078
1097
|
]
|
|
1079
1098
|
: []),
|
|
1080
1099
|
...(viewCard ? [viewCard] : []),
|
|
1100
|
+
...(triggerCard ? [triggerCard] : []),
|
|
1081
1101
|
{
|
|
1082
1102
|
type: "card",
|
|
1083
1103
|
title: req.__("Edit table properties"),
|
|
@@ -1111,7 +1131,7 @@ router.post(
|
|
|
1111
1131
|
const v = req.body;
|
|
1112
1132
|
if (typeof v.id === "undefined" && typeof v.external === "undefined") {
|
|
1113
1133
|
// insert
|
|
1114
|
-
v.name = v.name.trim()
|
|
1134
|
+
v.name = v.name.trim();
|
|
1115
1135
|
const { name, ...rest } = v;
|
|
1116
1136
|
const alltables = await Table.find({});
|
|
1117
1137
|
const existing_tables = [
|
|
@@ -1162,6 +1182,7 @@ router.post(
|
|
|
1162
1182
|
let notify = "";
|
|
1163
1183
|
if (!rest.versioned) rest.versioned = false;
|
|
1164
1184
|
if (!rest.has_sync_info) rest.has_sync_info = false;
|
|
1185
|
+
rest.is_user_group = !!rest.is_user_group;
|
|
1165
1186
|
if (rest.ownership_field_id === "_formula") {
|
|
1166
1187
|
rest.ownership_field_id = null;
|
|
1167
1188
|
const fmlValidRes = expressionValidator(rest.ownership_formula);
|
|
@@ -1922,7 +1943,7 @@ router.post(
|
|
|
1922
1943
|
const table = Table.findOne({ name });
|
|
1923
1944
|
|
|
1924
1945
|
try {
|
|
1925
|
-
await table.deleteRows({}, req.user);
|
|
1946
|
+
await table.deleteRows({}, req.user, true);
|
|
1926
1947
|
req.flash("success", req.__("Deleted all rows"));
|
|
1927
1948
|
} catch (e) {
|
|
1928
1949
|
req.flash("error", e.message);
|
|
@@ -2074,3 +2095,20 @@ router.post(
|
|
|
2074
2095
|
respondWorkflow(table, workflow, wfres, req, res);
|
|
2075
2096
|
})
|
|
2076
2097
|
);
|
|
2098
|
+
|
|
2099
|
+
router.post(
|
|
2100
|
+
"/repair-composite-primary/:id",
|
|
2101
|
+
isAdmin,
|
|
2102
|
+
error_catcher(async (req, res) => {
|
|
2103
|
+
const { id } = req.params;
|
|
2104
|
+
|
|
2105
|
+
const table = Table.findOne({ id });
|
|
2106
|
+
if (!table) {
|
|
2107
|
+
req.flash("error", `Table not found`);
|
|
2108
|
+
res.redirect(`/table`);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
await table.repairCompositePrimary();
|
|
2112
|
+
res.redirect(`/table/${table.id}`);
|
|
2113
|
+
})
|
|
2114
|
+
);
|
package/routes/tenant.js
CHANGED
|
@@ -58,6 +58,7 @@ const {
|
|
|
58
58
|
save_config_from_form,
|
|
59
59
|
} = require("../markup/admin.js");
|
|
60
60
|
const { getConfig } = require("@saltcorn/data/models/config");
|
|
61
|
+
const path = require("path");
|
|
61
62
|
//const {quote} = require("@saltcorn/db-common");
|
|
62
63
|
// todo add button backup / restore for particular tenant (available in admin tenants screens)
|
|
63
64
|
//const {
|
|
@@ -318,7 +319,53 @@ router.post(
|
|
|
318
319
|
if (hasTemplate) {
|
|
319
320
|
new_url_create += "auth/create_first_user";
|
|
320
321
|
}
|
|
321
|
-
|
|
322
|
+
const letsencrypt = getState().getConfig("letsencrypt", false);
|
|
323
|
+
if (letsencrypt) {
|
|
324
|
+
let altname = await tenant_letsencrypt_name(subdomain);
|
|
325
|
+
const tenant_letsencrypt_sites = getState().getConfig(
|
|
326
|
+
"tenant_letsencrypt_sites",
|
|
327
|
+
[]
|
|
328
|
+
);
|
|
329
|
+
const has_cert = tenant_letsencrypt_sites.includes(altname);
|
|
330
|
+
if (!has_cert) {
|
|
331
|
+
const file_store = db.connectObj.file_store;
|
|
332
|
+
const admin_users = await User.find(
|
|
333
|
+
{ role_id: 1 },
|
|
334
|
+
{ orderBy: "id" }
|
|
335
|
+
);
|
|
336
|
+
// greenlock logic
|
|
337
|
+
const Greenlock = require("greenlock");
|
|
338
|
+
const greenlock = Greenlock.create({
|
|
339
|
+
packageRoot: path.resolve(__dirname, ".."),
|
|
340
|
+
configDir: path.join(file_store, "greenlock.d"),
|
|
341
|
+
maintainerEmail: admin_users[0].email,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
await greenlock.sites.add({
|
|
345
|
+
subject: altname,
|
|
346
|
+
altnames: [altname],
|
|
347
|
+
});
|
|
348
|
+
// letsencrypt
|
|
349
|
+
const tenant_letsencrypt_sites = getState().getConfig(
|
|
350
|
+
"tenant_letsencrypt_sites",
|
|
351
|
+
[]
|
|
352
|
+
);
|
|
353
|
+
await getState().setConfig("tenant_letsencrypt_sites", [
|
|
354
|
+
altname,
|
|
355
|
+
...tenant_letsencrypt_sites,
|
|
356
|
+
]);
|
|
357
|
+
if (req.user?.role_id === 1) {
|
|
358
|
+
req.flash(
|
|
359
|
+
"success",
|
|
360
|
+
req.__(
|
|
361
|
+
"Tenant created. Certificate will be acquired on first visit."
|
|
362
|
+
)
|
|
363
|
+
);
|
|
364
|
+
res.redirect("/tenant/list");
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
322
369
|
res.sendWrap(
|
|
323
370
|
req.__("Create application"),
|
|
324
371
|
div(
|
|
@@ -374,6 +421,7 @@ router.get(
|
|
|
374
421
|
return;
|
|
375
422
|
}
|
|
376
423
|
const tens = await db.select("_sc_tenants");
|
|
424
|
+
const locale = getState().getConfig("default_locale", "en");
|
|
377
425
|
send_infoarch_page({
|
|
378
426
|
res,
|
|
379
427
|
req,
|
|
@@ -400,7 +448,8 @@ router.get(
|
|
|
400
448
|
},
|
|
401
449
|
{
|
|
402
450
|
label: req.__("Created"),
|
|
403
|
-
key: (r) =>
|
|
451
|
+
key: (r) =>
|
|
452
|
+
r.created ? localeDateTime(r.created, {}, locale) : "",
|
|
404
453
|
},
|
|
405
454
|
{
|
|
406
455
|
label: req.__("Information"),
|
|
@@ -444,6 +493,7 @@ const tenant_settings_form = (req) =>
|
|
|
444
493
|
"tenant_template",
|
|
445
494
|
"tenant_baseurl",
|
|
446
495
|
"tenant_create_unauth_redirect",
|
|
496
|
+
"tenant_inherit_cfgs",
|
|
447
497
|
{ section_header: req.__("Tenant application capabilities") },
|
|
448
498
|
"tenants_install_git",
|
|
449
499
|
"tenants_set_npm_modules",
|
|
@@ -628,7 +678,7 @@ router.get(
|
|
|
628
678
|
[]
|
|
629
679
|
);
|
|
630
680
|
const has_cert = tenant_letsencrypt_sites.includes(altname);
|
|
631
|
-
|
|
681
|
+
|
|
632
682
|
// get list of files
|
|
633
683
|
let files;
|
|
634
684
|
await db.runWithTenant(subdomain, async () => {
|
package/routes/viewedit.js
CHANGED
|
@@ -151,14 +151,6 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
151
151
|
"The view name is part of the URL when it is shown alone."
|
|
152
152
|
),
|
|
153
153
|
}),
|
|
154
|
-
new Field({
|
|
155
|
-
label: req.__("Description"),
|
|
156
|
-
name: "description",
|
|
157
|
-
type: "String",
|
|
158
|
-
sublabel: req.__(
|
|
159
|
-
"Description allows you to give more information about the view."
|
|
160
|
-
),
|
|
161
|
-
}),
|
|
162
154
|
new Field({
|
|
163
155
|
label: req.__("View pattern"),
|
|
164
156
|
name: "viewtemplate",
|
|
@@ -200,6 +192,14 @@ const viewForm = async (req, tableOptions, roles, pages, values) => {
|
|
|
200
192
|
required: true,
|
|
201
193
|
options: roles.map((r) => ({ value: r.id, label: r.role })),
|
|
202
194
|
}),
|
|
195
|
+
new Field({
|
|
196
|
+
label: req.__("Description"),
|
|
197
|
+
name: "description",
|
|
198
|
+
type: "String",
|
|
199
|
+
sublabel: req.__(
|
|
200
|
+
"Description allows you to give more information about the view."
|
|
201
|
+
),
|
|
202
|
+
}),
|
|
203
203
|
new Field({
|
|
204
204
|
name: "page_title",
|
|
205
205
|
label: req.__("Page title"),
|
|
@@ -161,10 +161,10 @@ describe("Stable versioning install", () => {
|
|
|
161
161
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
162
162
|
});
|
|
163
163
|
expect(dbPlugin).not.toBe(null);
|
|
164
|
-
expect(dbPlugin.version).toBe("0.0
|
|
164
|
+
expect(dbPlugin.version).toBe("0.1.0");
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
-
it("installs a fixed version", async () => {
|
|
167
|
+
it("installs and upgrades a fixed version", async () => {
|
|
168
168
|
const loadAndSaveNewPlugin = load_plugins.loadAndSaveNewPlugin;
|
|
169
169
|
await loadAndSaveNewPlugin(
|
|
170
170
|
new Plugin({
|
|
@@ -178,7 +178,7 @@ describe("Stable versioning install", () => {
|
|
|
178
178
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
179
179
|
});
|
|
180
180
|
expect(dbPlugin).not.toBe(null);
|
|
181
|
-
expect(dbPlugin.version).toBe("0.0
|
|
181
|
+
expect(dbPlugin.version).toBe("0.1.0");
|
|
182
182
|
});
|
|
183
183
|
|
|
184
184
|
it("installs and downgrades a fixed version", async () => {
|
|
@@ -188,7 +188,7 @@ describe("Stable versioning install", () => {
|
|
|
188
188
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
189
189
|
location: "@christianhugoch/empty_sc_test_plugin",
|
|
190
190
|
source: "npm",
|
|
191
|
-
version: "0.0
|
|
191
|
+
version: "0.2.0",
|
|
192
192
|
}),
|
|
193
193
|
true
|
|
194
194
|
);
|
|
@@ -196,7 +196,7 @@ describe("Stable versioning install", () => {
|
|
|
196
196
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
197
197
|
});
|
|
198
198
|
expect(dbPlugin).not.toBe(null);
|
|
199
|
-
expect(dbPlugin.version).toBe("0.0
|
|
199
|
+
expect(dbPlugin.version).toBe("0.1.0");
|
|
200
200
|
});
|
|
201
201
|
});
|
|
202
202
|
|
|
@@ -245,7 +245,7 @@ describe("Stable versioning upgrade", () => {
|
|
|
245
245
|
expect(newPlugin.version).toBe("0.0.3");
|
|
246
246
|
});
|
|
247
247
|
|
|
248
|
-
it("upgrades to latest with downgrade", async () => {
|
|
248
|
+
it("upgrades to latest with downgrade to supported", async () => {
|
|
249
249
|
const loadAndSaveNewPlugin = load_plugins.loadAndSaveNewPlugin;
|
|
250
250
|
await loadAndSaveNewPlugin(
|
|
251
251
|
new Plugin({
|
|
@@ -276,7 +276,7 @@ describe("Stable versioning upgrade", () => {
|
|
|
276
276
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
277
277
|
});
|
|
278
278
|
expect(newPlugin).not.toBe(null);
|
|
279
|
-
expect(newPlugin.version).toBe("0.0
|
|
279
|
+
expect(newPlugin.version).toBe("0.1.0");
|
|
280
280
|
});
|
|
281
281
|
|
|
282
282
|
it("upgrades to fixed version", async () => {
|
|
@@ -313,7 +313,7 @@ describe("Stable versioning upgrade", () => {
|
|
|
313
313
|
expect(newPlugin.version).toBe("0.0.3");
|
|
314
314
|
});
|
|
315
315
|
|
|
316
|
-
it("upgrades to fixed version with downgrade", async () => {
|
|
316
|
+
it("upgrades to fixed version with downgrade to supported", async () => {
|
|
317
317
|
const loadAndSaveNewPlugin = load_plugins.loadAndSaveNewPlugin;
|
|
318
318
|
await loadAndSaveNewPlugin(
|
|
319
319
|
new Plugin({
|
|
@@ -336,7 +336,7 @@ describe("Stable versioning upgrade", () => {
|
|
|
336
336
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
337
337
|
location: "@christianhugoch/empty_sc_test_plugin",
|
|
338
338
|
source: "npm",
|
|
339
|
-
version: "0.0
|
|
339
|
+
version: "0.2.0",
|
|
340
340
|
}),
|
|
341
341
|
true
|
|
342
342
|
);
|
|
@@ -344,6 +344,6 @@ describe("Stable versioning upgrade", () => {
|
|
|
344
344
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
345
345
|
});
|
|
346
346
|
expect(newPlugin).not.toBe(null);
|
|
347
|
-
expect(newPlugin.version).toBe("0.0
|
|
347
|
+
expect(newPlugin.version).toBe("0.1.0");
|
|
348
348
|
});
|
|
349
349
|
});
|
package/tests/plugins.test.js
CHANGED
|
@@ -365,7 +365,7 @@ describe("Upgrade plugin to supported version", () => {
|
|
|
365
365
|
expect(upgradedPlugin.version).toBe("0.0.3");
|
|
366
366
|
});
|
|
367
367
|
|
|
368
|
-
it("upgrades to
|
|
368
|
+
it("upgrades to latest as fixed version", async () => {
|
|
369
369
|
const oldPlugin = await setupPluginVersion(
|
|
370
370
|
"@christianhugoch/empty_sc_test_plugin_two",
|
|
371
371
|
"0.0.1"
|
|
@@ -389,7 +389,7 @@ describe("Upgrade plugin to supported version", () => {
|
|
|
389
389
|
expect(upgradedPlugin.version).toBe("0.0.3");
|
|
390
390
|
});
|
|
391
391
|
|
|
392
|
-
it("upgrades with a downgrade of
|
|
392
|
+
it("upgrades with a downgrade of latest", async () => {
|
|
393
393
|
const oldPlugin = await setupPluginVersion(
|
|
394
394
|
"@christianhugoch/empty_sc_test_plugin",
|
|
395
395
|
"0.0.1"
|
|
@@ -404,10 +404,10 @@ describe("Upgrade plugin to supported version", () => {
|
|
|
404
404
|
const upgradedPlugin = await Plugin.findOne({
|
|
405
405
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
406
406
|
});
|
|
407
|
-
expect(upgradedPlugin.version).toBe("0.0
|
|
407
|
+
expect(upgradedPlugin.version).toBe("0.1.0");
|
|
408
408
|
});
|
|
409
409
|
|
|
410
|
-
it("upgrades with a downgrade of
|
|
410
|
+
it("upgrades with a downgrade of latest as fixed version", async () => {
|
|
411
411
|
const oldPlugin = await setupPluginVersion(
|
|
412
412
|
"@christianhugoch/empty_sc_test_plugin",
|
|
413
413
|
"0.0.1"
|
|
@@ -421,13 +421,13 @@ describe("Upgrade plugin to supported version", () => {
|
|
|
421
421
|
"@christianhugoch/empty_sc_test_plugin"
|
|
422
422
|
)}`
|
|
423
423
|
)
|
|
424
|
-
.send("version=0.
|
|
424
|
+
.send("version=0.2.0")
|
|
425
425
|
.set("Cookie", loginCookie)
|
|
426
426
|
.expect(toRedirect("/plugins"));
|
|
427
427
|
const upgradedPlugin = await Plugin.findOne({
|
|
428
428
|
name: "@christianhugoch/empty_sc_test_plugin",
|
|
429
429
|
});
|
|
430
|
-
expect(upgradedPlugin.version).toBe("0.0
|
|
430
|
+
expect(upgradedPlugin.version).toBe("0.1.0");
|
|
431
431
|
});
|
|
432
432
|
});
|
|
433
433
|
|
package/wrapper.js
CHANGED
|
@@ -193,7 +193,9 @@ const get_headers = (req, version_tag, description, extras = []) => {
|
|
|
193
193
|
state.logLevel
|
|
194
194
|
}, _sc_globalCsrf = "${req.csrfToken()}", _sc_version_tag = "${version_tag}"${
|
|
195
195
|
locale ? `, _sc_locale = "${locale}"` : ""
|
|
196
|
-
}
|
|
196
|
+
}, _sc_lightmode = ${JSON.stringify(
|
|
197
|
+
state.getLightDarkMode?.(req.user) || "light"
|
|
198
|
+
)};</script>`,
|
|
197
199
|
},
|
|
198
200
|
{ css: `/static_assets/${version_tag}/saltcorn.css` },
|
|
199
201
|
{ script: `/static_assets/${version_tag}/saltcorn-common.js` },
|