@saltcorn/server 0.7.3 → 0.7.4-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/auth/admin.js +2 -1
- package/auth/routes.js +80 -92
- package/errors.js +51 -48
- package/locales/en.json +47 -2
- package/locales/it.json +2 -1
- package/locales/ru.json +42 -6
- package/locales/zh.json +1 -1
- package/markup/admin.js +15 -1
- package/markup/plugin-store.js +5 -5
- package/package.json +7 -7
- package/public/jquery-menu-editor.min.js +1 -1
- package/public/saltcorn-builder.css +75 -0
- package/public/saltcorn-common.js +24 -9
- package/public/saltcorn.css +28 -1
- package/public/saltcorn.js +9 -7
- package/routes/actions.js +2 -39
- package/routes/admin.js +375 -90
- package/routes/api.js +9 -1
- package/routes/common_lists.js +419 -0
- package/routes/fields.js +34 -19
- package/routes/homepage.js +60 -60
- package/routes/index.js +4 -0
- package/routes/menu.js +65 -4
- package/routes/packs.js +4 -4
- package/routes/page.js +5 -1
- package/routes/pageedit.js +13 -98
- package/routes/plugins.js +116 -118
- package/routes/settings.js +3 -3
- package/routes/tables.js +158 -193
- package/routes/tag_entries.js +173 -0
- package/routes/tags.js +266 -0
- package/routes/tenant.js +27 -27
- package/routes/utils.js +4 -0
- package/routes/view.js +18 -1
- package/routes/viewedit.js +22 -132
- package/serve.js +54 -38
- package/tests/admin.test.js +1 -1
- package/tests/api.test.js +17 -0
- package/tests/clientjs.test.js +11 -1
- package/tests/plugins.test.js +1 -1
- package/tests/viewedit.test.js +1 -1
- package/wrapper.js +57 -55
package/auth/admin.js
CHANGED
|
@@ -693,7 +693,8 @@ router.post(
|
|
|
693
693
|
} = form.values;
|
|
694
694
|
if (id) {
|
|
695
695
|
try {
|
|
696
|
-
await
|
|
696
|
+
const u = await User.findOne({ id });
|
|
697
|
+
await u.update({ email, role_id, ...rest });
|
|
697
698
|
req.flash("success", req.__(`User %s saved`, email));
|
|
698
699
|
} catch (e) {
|
|
699
700
|
req.flash("error", req.__(`Error editing user: %s`, e.message));
|
package/auth/routes.js
CHANGED
|
@@ -199,8 +199,7 @@ const getAuthLinks = (current, noMethods) => {
|
|
|
199
199
|
return links;
|
|
200
200
|
};
|
|
201
201
|
|
|
202
|
-
const loginWithJwt = async (
|
|
203
|
-
const { email, password } = req.query;
|
|
202
|
+
const loginWithJwt = async (email, password, res) => {
|
|
204
203
|
const user = await User.findOne({ email });
|
|
205
204
|
if (user && user.checkPassword(password)) {
|
|
206
205
|
const now = new Date();
|
|
@@ -208,7 +207,13 @@ const loginWithJwt = async (req, res) => {
|
|
|
208
207
|
const token = jwt.sign(
|
|
209
208
|
{
|
|
210
209
|
sub: email,
|
|
211
|
-
|
|
210
|
+
user: {
|
|
211
|
+
id: user.id,
|
|
212
|
+
email: user.email,
|
|
213
|
+
role_id: user.role_id,
|
|
214
|
+
language: user.language ? user.language : "en",
|
|
215
|
+
disabled: user.disabled,
|
|
216
|
+
},
|
|
212
217
|
iss: "saltcorn@saltcorn",
|
|
213
218
|
aud: "saltcorn-mobile-app",
|
|
214
219
|
iat: now.valueOf(),
|
|
@@ -217,6 +222,10 @@ const loginWithJwt = async (req, res) => {
|
|
|
217
222
|
);
|
|
218
223
|
if (!user.last_mobile_login) await user.updateLastMobileLogin(now);
|
|
219
224
|
res.json(token);
|
|
225
|
+
} else {
|
|
226
|
+
res.json({
|
|
227
|
+
alerts: [{ type: "danger", msg: "Incorrect user or password" }],
|
|
228
|
+
});
|
|
220
229
|
}
|
|
221
230
|
};
|
|
222
231
|
|
|
@@ -326,7 +335,7 @@ router.get(
|
|
|
326
335
|
if (result.error) req.flash("danger", result.error);
|
|
327
336
|
else if (result) {
|
|
328
337
|
req.flash("success", req.__("Email verified"));
|
|
329
|
-
const u = await User.
|
|
338
|
+
const u = await User.findForSession({ email });
|
|
330
339
|
if (u) u.relogin(req);
|
|
331
340
|
}
|
|
332
341
|
res.redirect("/");
|
|
@@ -461,11 +470,11 @@ router.get(
|
|
|
461
470
|
form,
|
|
462
471
|
{},
|
|
463
472
|
restore +
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
)
|
|
473
|
+
script(
|
|
474
|
+
domReady(
|
|
475
|
+
`$('form.create-first-user button[type=submit]').click(function(){press_store_button(this)})`
|
|
468
476
|
)
|
|
477
|
+
)
|
|
469
478
|
);
|
|
470
479
|
} else {
|
|
471
480
|
req.flash("danger", req.__("Users already present"));
|
|
@@ -494,7 +503,7 @@ router.post(
|
|
|
494
503
|
);
|
|
495
504
|
if (err) req.flash("error", err);
|
|
496
505
|
else req.flash("success", req.__("Successfully restored backup"));
|
|
497
|
-
fs.unlink(newPath, function () {});
|
|
506
|
+
fs.unlink(newPath, function () { });
|
|
498
507
|
res.redirect(`/auth/login`);
|
|
499
508
|
} else {
|
|
500
509
|
req.flash("danger", req.__("Users already present"));
|
|
@@ -527,12 +536,7 @@ router.post(
|
|
|
527
536
|
const { email, password } = form.values;
|
|
528
537
|
const u = await User.create({ email, password, role_id: 1 });
|
|
529
538
|
req.login(
|
|
530
|
-
|
|
531
|
-
email: u.email,
|
|
532
|
-
id: u.id,
|
|
533
|
-
role_id: u.role_id,
|
|
534
|
-
tenant: db.getTenantSchema(),
|
|
535
|
-
},
|
|
539
|
+
u.session_object,
|
|
536
540
|
function (err) {
|
|
537
541
|
if (!err) {
|
|
538
542
|
Trigger.emitEvent("Login", null, u);
|
|
@@ -637,12 +641,7 @@ const getNewUserForm = async (new_user_view_name, req, askEmail) => {
|
|
|
637
641
|
*/
|
|
638
642
|
const signup_login_with_user = (u, req, res) =>
|
|
639
643
|
req.login(
|
|
640
|
-
|
|
641
|
-
email: u.email,
|
|
642
|
-
id: u.id,
|
|
643
|
-
role_id: u.role_id,
|
|
644
|
-
tenant: db.getTenantSchema(),
|
|
645
|
-
},
|
|
644
|
+
u.session_object,
|
|
646
645
|
function (err) {
|
|
647
646
|
if (!err) {
|
|
648
647
|
Trigger.emitEvent("Login", null, u);
|
|
@@ -900,8 +899,8 @@ router.post(
|
|
|
900
899
|
} else {
|
|
901
900
|
const u = await User.create({ email, password });
|
|
902
901
|
await send_verification_email(u, req);
|
|
903
|
-
|
|
904
|
-
signup_login_with_user(u, req, res);
|
|
902
|
+
if (req.smr) await loginWithJwt(email, password, res);
|
|
903
|
+
else signup_login_with_user(u, req, res);
|
|
905
904
|
}
|
|
906
905
|
}
|
|
907
906
|
})
|
|
@@ -921,7 +920,7 @@ function handler(req, res) {
|
|
|
921
920
|
req.flash(
|
|
922
921
|
"error",
|
|
923
922
|
"You've made too many failed attempts in a short period of time, please try again " +
|
|
924
|
-
|
|
923
|
+
moment(req.rateLimit.resetTime).fromNow()
|
|
925
924
|
);
|
|
926
925
|
res.redirect("/auth/login"); // brute force protection triggered, send them back to the login page
|
|
927
926
|
}
|
|
@@ -1008,7 +1007,8 @@ router.get(
|
|
|
1008
1007
|
error_catcher(async (req, res, next) => {
|
|
1009
1008
|
const { method } = req.params;
|
|
1010
1009
|
if (method === "jwt") {
|
|
1011
|
-
|
|
1010
|
+
const { email, password } = req.query;
|
|
1011
|
+
await loginWithJwt(email, password, res);
|
|
1012
1012
|
} else {
|
|
1013
1013
|
const auth = getState().auth_methods[method];
|
|
1014
1014
|
if (auth) {
|
|
@@ -1184,7 +1184,7 @@ const userSettings = async ({ req, res, pwform, user }) => {
|
|
|
1184
1184
|
div(
|
|
1185
1185
|
user.api_token
|
|
1186
1186
|
? span({ class: "me-1" }, req.__("API token for this user: ")) +
|
|
1187
|
-
|
|
1187
|
+
code(user.api_token)
|
|
1188
1188
|
: req.__("No API token issued")
|
|
1189
1189
|
),
|
|
1190
1190
|
// button for reset or generate api token
|
|
@@ -1198,16 +1198,16 @@ const userSettings = async ({ req, res, pwform, user }) => {
|
|
|
1198
1198
|
),
|
|
1199
1199
|
// button for remove api token
|
|
1200
1200
|
user.api_token &&
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1201
|
+
div(
|
|
1202
|
+
{ class: "mt-4 ms-2 d-inline-block" },
|
|
1203
|
+
post_btn(
|
|
1204
|
+
`/auth/remove-api-token`,
|
|
1205
|
+
// TBD localization
|
|
1206
|
+
user.api_token ? req.__("Remove") : req.__("Generate"),
|
|
1207
|
+
req.csrfToken(),
|
|
1208
|
+
{ req: req, confirm: true }
|
|
1209
|
+
)
|
|
1210
|
+
),
|
|
1211
1211
|
],
|
|
1212
1212
|
};
|
|
1213
1213
|
return {
|
|
@@ -1218,13 +1218,13 @@ const userSettings = async ({ req, res, pwform, user }) => {
|
|
|
1218
1218
|
},
|
|
1219
1219
|
...(usersets
|
|
1220
1220
|
? [
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1221
|
+
{
|
|
1222
|
+
type: "card",
|
|
1223
|
+
class: "mt-0",
|
|
1224
|
+
title: userSetsName,
|
|
1225
|
+
contents: usersets,
|
|
1226
|
+
},
|
|
1227
|
+
]
|
|
1228
1228
|
: []),
|
|
1229
1229
|
{
|
|
1230
1230
|
type: "card",
|
|
@@ -1247,35 +1247,35 @@ const userSettings = async ({ req, res, pwform, user }) => {
|
|
|
1247
1247
|
},
|
|
1248
1248
|
...(show2FAPolicy
|
|
1249
1249
|
? [
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1250
|
+
{
|
|
1251
|
+
type: "card",
|
|
1252
|
+
title: req.__("Two-factor authentication"),
|
|
1253
|
+
contents: [
|
|
1254
|
+
div(
|
|
1255
|
+
user._attributes.totp_enabled
|
|
1256
|
+
? req.__("Two-factor authentication is enabled")
|
|
1257
|
+
: req.__("Two-factor authentication is disabled")
|
|
1258
|
+
),
|
|
1259
|
+
div(
|
|
1260
|
+
user._attributes.totp_enabled
|
|
1261
|
+
? a(
|
|
1262
|
+
{
|
|
1263
|
+
href: "/auth/twofa/disable/totp",
|
|
1264
|
+
class: "btn btn-danger mt-2",
|
|
1265
|
+
},
|
|
1266
|
+
req.__("Disable TWA")
|
|
1267
|
+
)
|
|
1268
|
+
: a(
|
|
1269
|
+
{
|
|
1270
|
+
href: "/auth/twofa/setup/totp",
|
|
1271
|
+
class: "btn btn-primary mt-2",
|
|
1272
|
+
},
|
|
1273
|
+
req.__("Enable TWA")
|
|
1274
|
+
)
|
|
1275
|
+
),
|
|
1276
|
+
],
|
|
1277
|
+
},
|
|
1278
|
+
]
|
|
1279
1279
|
: []),
|
|
1280
1280
|
...(apikeycard ? [apikeycard] : []),
|
|
1281
1281
|
],
|
|
@@ -1328,18 +1328,12 @@ router.post(
|
|
|
1328
1328
|
"/setlanguage",
|
|
1329
1329
|
loggedIn,
|
|
1330
1330
|
error_catcher(async (req, res) => {
|
|
1331
|
-
const u = await User.
|
|
1331
|
+
const u = await User.findForSession({ id: req.user.id });
|
|
1332
1332
|
const newlang = available_languages[req.body.locale];
|
|
1333
1333
|
if (newlang && u) {
|
|
1334
1334
|
await u.set_language(req.body.locale);
|
|
1335
1335
|
req.login(
|
|
1336
|
-
|
|
1337
|
-
email: u.email,
|
|
1338
|
-
id: u.id,
|
|
1339
|
-
role_id: u.role_id,
|
|
1340
|
-
language: req.body.locale,
|
|
1341
|
-
tenant: db.getTenantSchema(),
|
|
1342
|
-
},
|
|
1336
|
+
u.session_object,
|
|
1343
1337
|
function (err) {
|
|
1344
1338
|
if (!err) {
|
|
1345
1339
|
req.flash("success", req.__("Language changed to %s", newlang));
|
|
@@ -1443,16 +1437,11 @@ router.post(
|
|
|
1443
1437
|
return;
|
|
1444
1438
|
}
|
|
1445
1439
|
|
|
1446
|
-
const u = await User.
|
|
1440
|
+
const u = await User.findForSession({ id: req.user.id });
|
|
1447
1441
|
await u.update({ email: form.values.email });
|
|
1448
1442
|
u.email = form.values.email;
|
|
1449
1443
|
req.login(
|
|
1450
|
-
|
|
1451
|
-
email: u.email,
|
|
1452
|
-
id: u.id,
|
|
1453
|
-
role_id: u.role_id,
|
|
1454
|
-
tenant: db.getTenantSchema(),
|
|
1455
|
-
},
|
|
1444
|
+
u.session_object,
|
|
1456
1445
|
function (err) {
|
|
1457
1446
|
if (!err) {
|
|
1458
1447
|
Trigger.emitEvent("Login", null, u);
|
|
@@ -1543,7 +1532,7 @@ router.all(
|
|
|
1543
1532
|
return;
|
|
1544
1533
|
}
|
|
1545
1534
|
if (wfres.verified === true) {
|
|
1546
|
-
const user = await User.
|
|
1535
|
+
const user = await User.findForSession({ id: req.user.id });
|
|
1547
1536
|
await user.set_to_verified();
|
|
1548
1537
|
req.flash("success", req.__("User verified"));
|
|
1549
1538
|
user.relogin(req);
|
|
@@ -1580,9 +1569,8 @@ router.get(
|
|
|
1580
1569
|
// generate QR code for scanning into Google Authenticator
|
|
1581
1570
|
// reference: https://code.google.com/p/google-authenticator/wiki/KeyUriFormat
|
|
1582
1571
|
const site_name = getState().getConfig("site_name");
|
|
1583
|
-
const otpUrl = `otpauth://totp/${
|
|
1584
|
-
|
|
1585
|
-
}?secret=${encodedKey}&period=30&issuer=${encodeURIComponent(site_name)}`;
|
|
1572
|
+
const otpUrl = `otpauth://totp/${user.email
|
|
1573
|
+
}?secret=${encodedKey}&period=30&issuer=${encodeURIComponent(site_name)}`;
|
|
1586
1574
|
const image = await qrcode.toDataURL(otpUrl);
|
|
1587
1575
|
res.sendWrap(req.__("Setup two-factor authentication"), {
|
|
1588
1576
|
type: "card",
|
|
@@ -1753,7 +1741,7 @@ router.post(
|
|
|
1753
1741
|
failureFlash: true,
|
|
1754
1742
|
}),
|
|
1755
1743
|
error_catcher(async (req, res) => {
|
|
1756
|
-
const user = await User.
|
|
1744
|
+
const user = await User.findForSession({ id: req.user.pending_user.id });
|
|
1757
1745
|
user.relogin(req);
|
|
1758
1746
|
Trigger.emitEvent("Login", null, user);
|
|
1759
1747
|
res.redirect("/");
|
package/errors.js
CHANGED
|
@@ -7,55 +7,58 @@ const { pre, p, text, h3 } = require("@saltcorn/markup/tags");
|
|
|
7
7
|
const Crash = require("@saltcorn/data/models/crash");
|
|
8
8
|
const { getState } = require("@saltcorn/data/db/state");
|
|
9
9
|
|
|
10
|
-
module.exports =
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async function (err, req, res, next) {
|
|
20
|
-
|
|
10
|
+
module.exports =
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param {object} err
|
|
14
|
+
* @param {object} req
|
|
15
|
+
* @param {object} res
|
|
16
|
+
* @param {*} next
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
async function (err, req, res, next) {
|
|
20
|
+
if (!req.__) req.__ = (s) => s;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const devmode = getState().getConfig("development_mode", false);
|
|
23
|
+
const log_sql = getState().getConfig("log_sql", false);
|
|
24
|
+
const role = (req.user || {}).role_id || 10;
|
|
25
|
+
if (err.message && err.message.includes("invalid csrf token")) {
|
|
26
|
+
console.error(err.message);
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
req.flash("error", req.__("Invalid form data, try again"));
|
|
29
|
+
if (req.url && req.url.includes("/auth/login"))
|
|
30
|
+
res.redirect("/auth/login");
|
|
31
|
+
else res.redirect("/");
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const code = err.httpCode || 500;
|
|
35
|
+
const headline = err.headline || "An error occurred";
|
|
36
|
+
const severity = err.severity || 2;
|
|
37
|
+
const createCrash = severity <= 3;
|
|
38
|
+
//console.error(err.stack);
|
|
39
|
+
if (!(devmode && log_sql) && createCrash) await Crash.create(err, req);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
?
|
|
55
|
-
|
|
56
|
-
|
|
41
|
+
if (req.xhr) {
|
|
42
|
+
res
|
|
43
|
+
.status(code)
|
|
44
|
+
.send(
|
|
45
|
+
devmode || role === 1
|
|
46
|
+
? text(err.message)
|
|
47
|
+
: req.__("An error occurred")
|
|
48
|
+
);
|
|
49
|
+
} else
|
|
50
|
+
res
|
|
51
|
+
.status(code)
|
|
52
|
+
.sendWrap(
|
|
53
|
+
req.__(headline),
|
|
54
|
+
devmode ? pre(text(err.stack)) : h3(req.__(headline)),
|
|
55
|
+
role === 1 && !devmode ? pre(text(err.message)) : "",
|
|
56
|
+
createCrash
|
|
57
|
+
? p(
|
|
58
|
+
req.__(
|
|
59
|
+
`A report has been logged and a team of bug-squashing squirrels has been dispatched to deal with the situation.`
|
|
60
|
+
)
|
|
57
61
|
)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
+
: ""
|
|
63
|
+
);
|
|
64
|
+
};
|
package/locales/en.json
CHANGED
|
@@ -921,5 +921,50 @@
|
|
|
921
921
|
"Restoring automated backup": "Restoring automated backup",
|
|
922
922
|
"No errors detected during configuration check": "No errors detected during configuration check",
|
|
923
923
|
"%s view - %s on %s": "%s view - %s on %s",
|
|
924
|
-
"
|
|
925
|
-
|
|
924
|
+
"Please select at least one platform (android or iOS).": "Please select at least one platform (android or iOS).",
|
|
925
|
+
"Back": "Back",
|
|
926
|
+
"Periodic snapshots enabled": "Periodic snapshots enabled",
|
|
927
|
+
"Snapshot will be made every hour if there are changes": "Snapshot will be made every hour if there are changes",
|
|
928
|
+
"Snapshots": "Snapshots",
|
|
929
|
+
"Snapshot settings updated": "Snapshot settings updated",
|
|
930
|
+
"Download snapshots": "Download snapshots",
|
|
931
|
+
"Snapshot successful": "Snapshot successful",
|
|
932
|
+
"System logging verbosity": "System logging verbosity",
|
|
933
|
+
"Destination URL Formula": "Destination URL Formula",
|
|
934
|
+
"Pattern": "Pattern",
|
|
935
|
+
"View pattern": "View pattern",
|
|
936
|
+
"The view pattern sets the foundation of how the view relates to the table and the behaviour of the view": "The view pattern sets the foundation of how the view relates to the table and the behaviour of the view",
|
|
937
|
+
"Views display data from tables. A view is a view pattern applied to a table, with configuration.": "Views display data from tables. A view is a view pattern applied to a table, with configuration.",
|
|
938
|
+
"Modules": "Modules",
|
|
939
|
+
"Module installation and control": "Module installation and control",
|
|
940
|
+
"Module store": "Module store",
|
|
941
|
+
"Module": "Module",
|
|
942
|
+
"View patterns": "View patterns",
|
|
943
|
+
"%s module information": "%s module information",
|
|
944
|
+
"Upgrade installed modules": "Upgrade installed modules",
|
|
945
|
+
"Add another module": "Add another module",
|
|
946
|
+
"Add module": "Add module",
|
|
947
|
+
"Module name": "Module name",
|
|
948
|
+
"Source of module for install. Few options:npm - download from npm repository,local - get from local file system,github - download from github,git - get from git": "Source of module for install. Few options:npm - download from npm repository,local - get from local file system,github - download from github,git - get from git",
|
|
949
|
+
"Version of module, latest is default value": "Version of module, latest is default value",
|
|
950
|
+
"For npm - name of npm package, e.g. @saltcorn/html or saltcorn-gantt, check at npmjs.com, for local - absolute path to module folder in file system, e.g. C:\\gitsrc\\any-bootstrap-theme\\, for github - name of github project.": "For npm - name of npm package, e.g. @saltcorn/html or saltcorn-gantt, check at npmjs.com, for local - absolute path to module folder in file system, e.g. C:\\gitsrc\\any-bootstrap-theme\\, for github - name of github project.",
|
|
951
|
+
"Modules up-to-date": "Modules up-to-date",
|
|
952
|
+
"User must have this role or higher to read rows from the table, unless they are the owner": "User must have this role or higher to read rows from the table, unless they are the owner",
|
|
953
|
+
"User must have this role or higher to edit or create new rows in the table, unless they are the owner": "User must have this role or higher to edit or create new rows in the table, unless they are the owner",
|
|
954
|
+
"Tag": "Tag",
|
|
955
|
+
"Tagname": "Tagname",
|
|
956
|
+
"Create tag": "Create tag",
|
|
957
|
+
"Tags": "Tags",
|
|
958
|
+
"New tag": "New tag",
|
|
959
|
+
"Tag name": "Tag name",
|
|
960
|
+
"%s Tag": "%s Tag",
|
|
961
|
+
"Remove From Tag": "Remove From Tag",
|
|
962
|
+
"Add tables": "Add tables",
|
|
963
|
+
"Add views": "Add views",
|
|
964
|
+
"Add tages": "Add tages",
|
|
965
|
+
"Trigger": "Trigger",
|
|
966
|
+
"Add %s to tag": "Add %s to tag",
|
|
967
|
+
"Tag %s deleted": "Tag %s deleted",
|
|
968
|
+
"Tag %s created": "Tag %s created"
|
|
969
|
+
|
|
970
|
+
}
|
package/locales/it.json
CHANGED
|
@@ -478,5 +478,6 @@
|
|
|
478
478
|
"Generate": "Generate",
|
|
479
479
|
"Two-factor authentication": "Two-factor authentication",
|
|
480
480
|
"Two-factor authentication is disabled": "Two-factor authentication is disabled",
|
|
481
|
-
"Enable TWA": "Enable TWA"
|
|
481
|
+
"Enable TWA": "Enable TWA",
|
|
482
|
+
"Modules": "Modules"
|
|
482
483
|
}
|
package/locales/ru.json
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"ID": "ID",
|
|
27
27
|
"Email": "Email",
|
|
28
28
|
"Role": "Роль",
|
|
29
|
-
"View": "
|
|
29
|
+
"View": "Представление",
|
|
30
30
|
"Delete": "Удалить",
|
|
31
31
|
"Add user": "Создать пользователя",
|
|
32
32
|
"New user": "Новый пользователь",
|
|
@@ -783,15 +783,51 @@
|
|
|
783
783
|
"Disable TWA": "Отключить TWA",
|
|
784
784
|
"Enable TWA": "Включить TWA",
|
|
785
785
|
"Deleted all rows": "Удалены все строки",
|
|
786
|
-
"Use this link: <a href=\"%s\">%s</a> to revisit your application at any time.": "В
|
|
786
|
+
"Use this link: <a href=\"%s\">%s</a> to revisit your application at any time.": "В дальнейшем используйте ссылку: <a href=\"%s\">%s</a> для входа в созданное приложение.",
|
|
787
787
|
"To login to a previously created application, go to: ": "Чтобы авторизоваться в ранее созданном приложении, перейдите по ссылке: ",
|
|
788
788
|
"Has channels?": "Связано с каналом?",
|
|
789
789
|
"Menu, search, languages and tenants": "Меню, поиск, языки и подсайты(тенанты)",
|
|
790
790
|
"Images and other files for download": "Изображения и другие файлы для загрузки",
|
|
791
791
|
"Channels to create events for. Separate by comma; leave blank for all": "Каналы, для которых создаются собятия. Разделяются ; Не заполняйте, чтобы использовать события для всех каналов",
|
|
792
792
|
"Event Name": "Имя события",
|
|
793
|
-
"Plugins (Extensions) Store endpoint": "
|
|
794
|
-
"Packs Store endpoint": "
|
|
795
|
-
"The endpoint of plugins store.": "
|
|
796
|
-
"The endpoint of packs store.": "
|
|
793
|
+
"Plugins (Extensions) Store endpoint": "Адрес (endpoint) магазина плагинов",
|
|
794
|
+
"Packs Store endpoint": "Адрес (endpoint) магазина паков",
|
|
795
|
+
"The endpoint of plugins store.": "Адрес (endpoint) магазина плагинов.",
|
|
796
|
+
"The endpoint of packs store.": "Адрес (endpoint) магазина паков.",
|
|
797
|
+
"Modules": "Модули",
|
|
798
|
+
"Frequency": "Регулярность",
|
|
799
|
+
"Destination": "Местоположение",
|
|
800
|
+
"Directory": "Папка",
|
|
801
|
+
"Expiration in days": "Срок хранения (в днях)",
|
|
802
|
+
"Delete old backup files in this directory after the set number of days": "Старые резервные копии в этой папке будут удалять после истечения срока хранения ",
|
|
803
|
+
"Backup settings updated": "Настройки резервного копирования обновлены",
|
|
804
|
+
"Periodic snapshots enabled": "Регулярные снимки (снэпшоты) активированы",
|
|
805
|
+
"Snapshot will be made every hour if there are changes": "Снимки (снэпшоты) будут выполняться каждый час",
|
|
806
|
+
"Manual backup": "Ручное резервное копирование",
|
|
807
|
+
"Automated backup": "Автоматическое резервное копирование",
|
|
808
|
+
"Snapshots": "Снимки (снэпшоты)",
|
|
809
|
+
"Mobile app": "Мобильное приложение",
|
|
810
|
+
"Module store": "Магазин модулей",
|
|
811
|
+
"Upgrade installed modules": "Обновить установленные модули",
|
|
812
|
+
"Add another module": "Добавить модуль",
|
|
813
|
+
"Module": "Модуль",
|
|
814
|
+
"Module installation and control": "Установка и управление модулями",
|
|
815
|
+
"System logging verbosity": "Уровень логирования системы",
|
|
816
|
+
"Source of module for install. Few options:npm - download from npm repository,local - get from local file system,github - download from github,git - get from git": "Источник для установки модуля:npm - загрузка из репозитория npm,local - установка из локальной файловой системы,github - загрузка из github,git - загрузка из git",
|
|
817
|
+
"Version of module, latest is default value": "Версия модуля, значение по-умолчанию latest",
|
|
818
|
+
"For npm - name of npm package, e.g. @saltcorn/html or saltcorn-gantt, check at npmjs.com, for local - absolute path to module folder in file system, e.g. C:\\gitsrc\\any-bootstrap-theme\\, for github - name of github project.": "для npm - имя npm пактеа, например, @saltcorn/html или saltcorn-gantt, см. npmjs.com, для local - абсолютный путь к папке модуля в локальной файловой системе, например, C:\\gitsrc\\any-bootstrap-theme\\, для github - имя проекта github.",
|
|
819
|
+
"Modules up-to-date": "Модули обновлены до актуальной версии",
|
|
820
|
+
"Add module": "Добавить модуль",
|
|
821
|
+
"Module name": "Имя модуля",
|
|
822
|
+
"Build mobile app": "Создать мобильное приложение",
|
|
823
|
+
"Please select at least one platform (android or iOS).": "Пожалуйста выберите как минимум одну платформу (android или iOS).",
|
|
824
|
+
"Please enter a valid server URL.": "Пожалуйста введите корректный URL сервера.",
|
|
825
|
+
"Only the android build supports docker.": "Только сборка для android поддерживает docker.",
|
|
826
|
+
"Entry point": "Точка входа в приложение",
|
|
827
|
+
"Platform": "Платформа",
|
|
828
|
+
"docker": "docker",
|
|
829
|
+
"App file": "Файл приложения",
|
|
830
|
+
"Server URL": "URL сервера",
|
|
831
|
+
"android": "android",
|
|
832
|
+
"iOS": "iOS"
|
|
797
833
|
}
|
package/locales/zh.json
CHANGED
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
"Pack %s installed": "已安装包 %s",
|
|
180
180
|
"Pack %s uninstalled": "包 %s 已卸载",
|
|
181
181
|
"Uninstall": "卸载",
|
|
182
|
-
"Result preview for ": "结果预览",
|
|
182
|
+
"Result preview for ": "结果预览",
|
|
183
183
|
"Choose views for <a href=\"/search\">search results</a> for each table.<br/>Set to blank to omit table from global search.": "为每个表选择<a href=\"/search\">搜索结果</a>的视图。<br/>为空则可从全局搜索中忽略表。",
|
|
184
184
|
"These tables lack suitable views: ": "这些表缺少合适的视图:",
|
|
185
185
|
"Search configuration": "搜索配置",
|
package/markup/admin.js
CHANGED
|
@@ -245,6 +245,13 @@ const send_files_page = (args) => {
|
|
|
245
245
|
});
|
|
246
246
|
};
|
|
247
247
|
|
|
248
|
+
const send_tags_page = (args) => {
|
|
249
|
+
return send_settings_page({
|
|
250
|
+
main_section: "Tags",
|
|
251
|
+
...args,
|
|
252
|
+
});
|
|
253
|
+
};
|
|
254
|
+
|
|
248
255
|
/**
|
|
249
256
|
* Send Events Page
|
|
250
257
|
* @param {object} args
|
|
@@ -281,6 +288,7 @@ const send_admin_page = (args) => {
|
|
|
281
288
|
{ text: "Backup", href: "/admin/backup" },
|
|
282
289
|
{ text: "Email", href: "/admin/email" },
|
|
283
290
|
{ text: "System", href: "/admin/system" },
|
|
291
|
+
...(isRoot ? [{ text: "Tag", href: "/tag" }] : []),
|
|
284
292
|
...(isRoot
|
|
285
293
|
? [{ text: "Mobile app", href: "/admin/build-mobile-app" }]
|
|
286
294
|
: []),
|
|
@@ -345,7 +353,12 @@ const flash_restart = (req) => {
|
|
|
345
353
|
* @param {*} opts.formArgs
|
|
346
354
|
* @returns {Promise<Form>}
|
|
347
355
|
*/
|
|
348
|
-
const config_fields_form = async ({
|
|
356
|
+
const config_fields_form = async ({
|
|
357
|
+
field_names,
|
|
358
|
+
req,
|
|
359
|
+
action,
|
|
360
|
+
...formArgs
|
|
361
|
+
}) => {
|
|
349
362
|
const values = {};
|
|
350
363
|
const state = getState();
|
|
351
364
|
const fields = [];
|
|
@@ -512,4 +525,5 @@ module.exports = {
|
|
|
512
525
|
save_config_from_form,
|
|
513
526
|
flash_restart_if_required,
|
|
514
527
|
flash_restart,
|
|
528
|
+
send_tags_page,
|
|
515
529
|
};
|
package/markup/plugin-store.js
CHANGED
|
@@ -87,7 +87,7 @@ const plugin_functions_info_card = (plugin, req) => ({
|
|
|
87
87
|
*/
|
|
88
88
|
const plugin_viewtemplates_info_card = (plugin, req) => ({
|
|
89
89
|
type: "card",
|
|
90
|
-
title: req.__("View
|
|
90
|
+
title: req.__("View patterns"),
|
|
91
91
|
contents: withCfg(plugin, "viewtemplates", [])
|
|
92
92
|
.map(({ name, description }) => div(h4(name), p(description)))
|
|
93
93
|
.join("<hr>"),
|
|
@@ -101,10 +101,10 @@ const showRepository = (repo) =>
|
|
|
101
101
|
!repo
|
|
102
102
|
? repo
|
|
103
103
|
: repo.url
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
? link(repo.url, repo.url)
|
|
105
|
+
: repo.startsWith && repo.startsWith("github:")
|
|
106
|
+
? link(repo.replace("github:", "https://github.com/"), repo)
|
|
107
|
+
: repo;
|
|
108
108
|
|
|
109
109
|
module.exports = {
|
|
110
110
|
plugin_types_info_card,
|