@saltcorn/server 1.0.0-beta.1 → 1.0.0-beta.11
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 -0
- package/help/Android App Signing.tmd +1 -1
- package/load_plugins.js +51 -6
- package/locales/en.json +10 -1
- package/markup/blockly.js +4 -4
- package/package.json +14 -14
- package/public/saltcorn-common.js +101 -65
- package/public/saltcorn.js +40 -7
- package/routes/actions.js +7 -0
- package/routes/admin.js +35 -10
- package/routes/files.js +101 -15
- package/routes/notifications.js +13 -3
- package/routes/plugins.js +56 -18
- package/routes/search.js +6 -1
- package/routes/tables.js +2 -1
- package/routes/viewedit.js +10 -2
- package/serve.js +1 -1
- package/tests/edit.test.js +5 -5
- package/tests/files.test.js +87 -7
- package/tests/plugin_install.test.js +235 -0
- package/tests/plugins.test.js +140 -0
package/auth/admin.js
CHANGED
|
@@ -8,7 +8,7 @@ To build an Android app for the Play Store, you need to create an Android Applic
|
|
|
8
8
|
|
|
9
9
|
*Note: You need a [Play Console developer account](https://support.google.com/googleplay/android-developer/answer/6112435?hl=en&ref_topic=3450769&sjid=11090022771305927482-EU) to publish your app on the Play Store.*
|
|
10
10
|
|
|
11
|
-
### Create a
|
|
11
|
+
### Create a Keystore file
|
|
12
12
|
On any Unix-based system, you can use the `keytool` command to create the keystore file. For example:
|
|
13
13
|
```sh
|
|
14
14
|
keytool -genkey -v -keystore my-app-key.jks
|
package/load_plugins.js
CHANGED
|
@@ -12,6 +12,37 @@ const { isRoot } = require("@saltcorn/data/utils");
|
|
|
12
12
|
const { eachTenant } = require("@saltcorn/admin-models/models/tenant");
|
|
13
13
|
|
|
14
14
|
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
15
|
+
const npmFetch = require("npm-registry-fetch");
|
|
16
|
+
const packagejson = require("./package.json");
|
|
17
|
+
const {
|
|
18
|
+
supportedVersion,
|
|
19
|
+
resolveLatest,
|
|
20
|
+
} = require("@saltcorn/plugins-loader/stable_versioning");
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* checks the saltcorn engine property and changes the plugin version if necessary
|
|
24
|
+
* @param plugin plugin to load
|
|
25
|
+
*/
|
|
26
|
+
const ensurePluginSupport = async (plugin) => {
|
|
27
|
+
const pkgInfo = await npmFetch.json(
|
|
28
|
+
`https://registry.npmjs.org/${plugin.location}`
|
|
29
|
+
);
|
|
30
|
+
const supported = supportedVersion(
|
|
31
|
+
plugin.version || "latest",
|
|
32
|
+
pkgInfo.versions,
|
|
33
|
+
packagejson.version
|
|
34
|
+
);
|
|
35
|
+
if (!supported)
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Unable to find a supported version for '${plugin.location}'`
|
|
38
|
+
);
|
|
39
|
+
else if (
|
|
40
|
+
supported !== plugin.version ||
|
|
41
|
+
(plugin.version === "latest" &&
|
|
42
|
+
supported !== resolveLatest(pkgInfo.versions))
|
|
43
|
+
)
|
|
44
|
+
plugin.version = supported;
|
|
45
|
+
};
|
|
15
46
|
|
|
16
47
|
/**
|
|
17
48
|
* Load one plugin
|
|
@@ -20,6 +51,15 @@ const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
|
20
51
|
* @param force - force flag
|
|
21
52
|
*/
|
|
22
53
|
const loadPlugin = async (plugin, force) => {
|
|
54
|
+
if (plugin.source === "npm") {
|
|
55
|
+
try {
|
|
56
|
+
await ensurePluginSupport(plugin);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
console.log(
|
|
59
|
+
`Warning: Unable to find a supported version for '${plugin.location}' Continuing with the installed version`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
23
63
|
// load plugin
|
|
24
64
|
const loader = new PluginInstaller(plugin);
|
|
25
65
|
const res = await loader.install(force);
|
|
@@ -151,11 +191,14 @@ const loadAndSaveNewPlugin = async (
|
|
|
151
191
|
return;
|
|
152
192
|
}
|
|
153
193
|
}
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const
|
|
194
|
+
if (plugin.source === "npm") await ensurePluginSupport(plugin);
|
|
195
|
+
const loadMsgs = [];
|
|
196
|
+
const loader = new PluginInstaller(plugin, {
|
|
197
|
+
scVersion: packagejson.version,
|
|
198
|
+
});
|
|
199
|
+
const { version, plugin_module, location, loadedWithReload, msgs } =
|
|
157
200
|
await loader.install(force);
|
|
158
|
-
|
|
201
|
+
if (msgs) loadMsgs.push(...msgs);
|
|
159
202
|
// install dependecies
|
|
160
203
|
for (const loc of plugin_module.dependencies || []) {
|
|
161
204
|
const existing = await Plugin.findOne({ location: loc });
|
|
@@ -201,7 +244,7 @@ const loadAndSaveNewPlugin = async (
|
|
|
201
244
|
}
|
|
202
245
|
}
|
|
203
246
|
if (loadedWithReload || registeredWithReload) {
|
|
204
|
-
|
|
247
|
+
loadMsgs.push(
|
|
205
248
|
__(
|
|
206
249
|
"The plugin was corrupted and had to be repaired. We recommend restarting your server.",
|
|
207
250
|
plugin.name
|
|
@@ -228,7 +271,7 @@ const loadAndSaveNewPlugin = async (
|
|
|
228
271
|
force: false, // okay ??
|
|
229
272
|
});
|
|
230
273
|
}
|
|
231
|
-
return
|
|
274
|
+
return loadMsgs;
|
|
232
275
|
};
|
|
233
276
|
|
|
234
277
|
module.exports = {
|
|
@@ -236,4 +279,6 @@ module.exports = {
|
|
|
236
279
|
loadAllPlugins,
|
|
237
280
|
loadPlugin,
|
|
238
281
|
requirePlugin,
|
|
282
|
+
supportedVersion,
|
|
283
|
+
ensurePluginSupport,
|
|
239
284
|
};
|
package/locales/en.json
CHANGED
|
@@ -1459,5 +1459,14 @@
|
|
|
1459
1459
|
"Hourly": "Hourly",
|
|
1460
1460
|
"Daily": "Daily",
|
|
1461
1461
|
"Weekly": "Weekly",
|
|
1462
|
-
"Code pages": "Code pages"
|
|
1462
|
+
"Code pages": "Code pages",
|
|
1463
|
+
"Please select a file": "Please select a file",
|
|
1464
|
+
"Zip compression level": "Zip compression level",
|
|
1465
|
+
"1=Fast, larger file, 9=Slow, smaller files": "1=Fast, larger file, 9=Slow, smaller files",
|
|
1466
|
+
"Use system zip": "Use system zip",
|
|
1467
|
+
"Recommended. Executable <code>zip</code> must be installed": "Recommended. Executable <code>zip</code> must be installed",
|
|
1468
|
+
"Time to run": "Time to run",
|
|
1469
|
+
"Mobile": "Mobile",
|
|
1470
|
+
"Plain password trigger row": "Plain password trigger row",
|
|
1471
|
+
"Send plaintext password changes to Users table triggers (Insert, Update and Validate).": "Send plaintext password changes to Users table triggers (Insert, Update and Validate)."
|
|
1463
1472
|
}
|
package/markup/blockly.js
CHANGED
|
@@ -22,16 +22,16 @@ const db = require("@saltcorn/data/db");
|
|
|
22
22
|
*/
|
|
23
23
|
const blocklyImportScripts = ({ locale }) =>
|
|
24
24
|
script({
|
|
25
|
-
src: "/plugins/pubdeps/base/blockly/
|
|
25
|
+
src: "/plugins/pubdeps/base/blockly/8.0.5/blockly_compressed.js",
|
|
26
26
|
}) +
|
|
27
27
|
script({
|
|
28
|
-
src: "/plugins/pubdeps/base/blockly/
|
|
28
|
+
src: "/plugins/pubdeps/base/blockly/8.0.5/blocks_compressed.js",
|
|
29
29
|
}) +
|
|
30
30
|
script({
|
|
31
|
-
src: `/plugins/pubdeps/base/blockly/
|
|
31
|
+
src: `/plugins/pubdeps/base/blockly/8.0.5/msg/${locale}.js`,
|
|
32
32
|
}) +
|
|
33
33
|
script({
|
|
34
|
-
src: "/plugins/pubdeps/base/blockly/
|
|
34
|
+
src: "/plugins/pubdeps/base/blockly/8.0.5/javascript_compressed.js",
|
|
35
35
|
}) +
|
|
36
36
|
script({
|
|
37
37
|
src: `/static_assets/${db.connectObj.version_tag}/blockly.js`,
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.11",
|
|
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": "1.0.0-beta.
|
|
11
|
-
"@saltcorn/builder": "1.0.0-beta.
|
|
12
|
-
"@saltcorn/data": "1.0.0-beta.
|
|
13
|
-
"@saltcorn/admin-models": "1.0.0-beta.
|
|
14
|
-
"@saltcorn/filemanager": "1.0.0-beta.
|
|
15
|
-
"@saltcorn/markup": "1.0.0-beta.
|
|
16
|
-
"@saltcorn/plugins-loader": "1.0.0-beta.
|
|
17
|
-
"@saltcorn/sbadmin2": "1.0.0-beta.
|
|
10
|
+
"@saltcorn/base-plugin": "1.0.0-beta.11",
|
|
11
|
+
"@saltcorn/builder": "1.0.0-beta.11",
|
|
12
|
+
"@saltcorn/data": "1.0.0-beta.11",
|
|
13
|
+
"@saltcorn/admin-models": "1.0.0-beta.11",
|
|
14
|
+
"@saltcorn/filemanager": "1.0.0-beta.11",
|
|
15
|
+
"@saltcorn/markup": "1.0.0-beta.11",
|
|
16
|
+
"@saltcorn/plugins-loader": "1.0.0-beta.11",
|
|
17
|
+
"@saltcorn/sbadmin2": "1.0.0-beta.11",
|
|
18
18
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
19
19
|
"@socket.io/sticky": "^1.0.1",
|
|
20
20
|
"adm-zip": "0.5.10",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"node-fetch": "2.6.9",
|
|
47
47
|
"node-watch": "^0.7.2",
|
|
48
48
|
"notp": "2.0.3",
|
|
49
|
-
"npm-registry-fetch": "
|
|
49
|
+
"npm-registry-fetch": "17.1.0",
|
|
50
50
|
"passport": "^0.6.0",
|
|
51
51
|
"passport-custom": "^1.1.1",
|
|
52
52
|
"passport-http-bearer": "^1.0.1",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"tmp-promise": "^3.0.2",
|
|
64
64
|
"ua-parser-js": "^1.0.37",
|
|
65
65
|
"underscore": "1.13.6",
|
|
66
|
-
"uuid": "^
|
|
66
|
+
"uuid": "^10.0.0"
|
|
67
67
|
},
|
|
68
68
|
"optionalDependencies": {
|
|
69
69
|
"connect-sqlite3": "^0.9.11",
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
},
|
|
72
72
|
"repository": "github:saltcorn/saltcorn",
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"jest": "^
|
|
75
|
-
"jest-environment-jsdom": "
|
|
76
|
-
"supertest": "
|
|
74
|
+
"jest": "^29.7.0",
|
|
75
|
+
"jest-environment-jsdom": "29.7.0",
|
|
76
|
+
"supertest": "7.0.0"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"dev": "nodemon index.js",
|
|
@@ -303,25 +303,27 @@ function apply_showif() {
|
|
|
303
303
|
...cache,
|
|
304
304
|
[qs]: "fetching",
|
|
305
305
|
});
|
|
306
|
-
$.ajax(`/api/${dynwhere.table}?${qs}`)
|
|
307
|
-
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
306
|
+
$.ajax(`/api/${dynwhere.table}?${qs}`)
|
|
307
|
+
.then((resp) => {
|
|
308
|
+
if (resp.success) {
|
|
309
|
+
if (window._sc_loglevel > 4)
|
|
310
|
+
console.log("dynwhere fetch", qs, resp.success);
|
|
311
|
+
|
|
312
|
+
activate(resp.success, qs);
|
|
313
|
+
const cacheNow = e.prop("data-fetch-options-cache") || {};
|
|
314
|
+
e.prop("data-fetch-options-cache", {
|
|
315
|
+
...cacheNow,
|
|
316
|
+
[qs]: resp.success,
|
|
317
|
+
});
|
|
318
|
+
} else {
|
|
319
|
+
const cacheNow = e.prop("data-fetch-options-cache") || {};
|
|
320
|
+
e.prop("data-fetch-options-cache", {
|
|
321
|
+
...cacheNow,
|
|
322
|
+
[qs]: undefined,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
.fail(checkNetworkError);
|
|
325
327
|
}
|
|
326
328
|
});
|
|
327
329
|
$("[data-filter-table]").each(function (ix, element) {
|
|
@@ -421,7 +423,14 @@ function apply_showif() {
|
|
|
421
423
|
navigator.systemLanguage ||
|
|
422
424
|
"en";
|
|
423
425
|
window.detected_locale = locale;
|
|
424
|
-
const parse = (s) =>
|
|
426
|
+
const parse = (s, def = {}) => {
|
|
427
|
+
try {
|
|
428
|
+
return JSON.parse(decodeURIComponent(s));
|
|
429
|
+
} catch (e) {
|
|
430
|
+
console.error("failed to parse time format", e);
|
|
431
|
+
return def;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
425
434
|
$("time[locale-time-options]").each(function () {
|
|
426
435
|
var el = $(this);
|
|
427
436
|
var date = new Date(el.attr("datetime"));
|
|
@@ -443,8 +452,9 @@ function apply_showif() {
|
|
|
443
452
|
$("time[locale-date-format]").each(function () {
|
|
444
453
|
var el = $(this);
|
|
445
454
|
var date = el.attr("datetime");
|
|
446
|
-
const format = parse(el.attr("locale-date-format"));
|
|
447
|
-
el.text(dayjs(date).format(format));
|
|
455
|
+
const format = parse(el.attr("locale-date-format"), "");
|
|
456
|
+
if (format) el.text(dayjs(date).format(format));
|
|
457
|
+
else el.text(dayjs(date));
|
|
448
458
|
});
|
|
449
459
|
|
|
450
460
|
_apply_showif_plugins.forEach((p) => p());
|
|
@@ -522,6 +532,7 @@ function get_form_record(e_in, select_labels) {
|
|
|
522
532
|
$(e_in).prop("data-join-values", jvs);
|
|
523
533
|
apply_showif();
|
|
524
534
|
},
|
|
535
|
+
error: checkNetworkError,
|
|
525
536
|
});
|
|
526
537
|
}
|
|
527
538
|
$(e_in).prop("data-join-key-values", keyVals);
|
|
@@ -886,31 +897,33 @@ function initialize_page() {
|
|
|
886
897
|
})
|
|
887
898
|
);
|
|
888
899
|
const doAjaxOptionsFetch = (tblName, target) => {
|
|
889
|
-
$.ajax(`/api/${tblName}`)
|
|
890
|
-
|
|
891
|
-
resp.success
|
|
892
|
-
a
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
(
|
|
897
|
-
|
|
898
|
-
r.id
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
900
|
+
$.ajax(`/api/${tblName}`)
|
|
901
|
+
.then((resp) => {
|
|
902
|
+
if (resp.success) {
|
|
903
|
+
resp.success.sort((a, b) =>
|
|
904
|
+
a[target]?.toLowerCase?.() > b[target]?.toLowerCase?.() ? 1 : -1
|
|
905
|
+
);
|
|
906
|
+
|
|
907
|
+
const selopts = resp.success.map(
|
|
908
|
+
(r) =>
|
|
909
|
+
`<option ${current == r.id ? `selected ` : ``}value="${
|
|
910
|
+
r.id
|
|
911
|
+
}">${escapeHtml(r[target])}</option>`
|
|
912
|
+
);
|
|
913
|
+
$(this).replaceWith(
|
|
914
|
+
`<form method="post" action="${url}" ${
|
|
915
|
+
ajax ? `onsubmit="inline_ajax_submit(event, '${opts}')"` : ""
|
|
916
|
+
}>
|
|
905
917
|
<input type="hidden" name="_csrf" value="${_sc_globalCsrf}">
|
|
906
918
|
<select name="${key}" value="${current}">${selopts}
|
|
907
919
|
</select>
|
|
908
920
|
<button type="submit" class="btn btn-sm btn-primary">OK</button>
|
|
909
921
|
<button onclick="cancel_inline_edit(event, '${opts}')" type="button" class="btn btn-sm btn-danger"><i class="fas fa-times"></i></button>
|
|
910
922
|
</form>`
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
})
|
|
926
|
+
.fail(checkNetworkError);
|
|
914
927
|
};
|
|
915
928
|
if (type === "JSON" && schema && schema.type.startsWith("Key to ")) {
|
|
916
929
|
const tblName = schema.type.replace("Key to ", "");
|
|
@@ -1089,7 +1102,8 @@ function initialize_page() {
|
|
|
1089
1102
|
initialize_page();
|
|
1090
1103
|
},
|
|
1091
1104
|
error: function (res) {
|
|
1092
|
-
|
|
1105
|
+
if (!checkNetworkError(res))
|
|
1106
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
1093
1107
|
if ($e.html() === "Loading...") $e.html("");
|
|
1094
1108
|
},
|
|
1095
1109
|
});
|
|
@@ -1164,9 +1178,10 @@ function inline_ajax_submit(e, opts1) {
|
|
|
1164
1178
|
inline_submit_success(e, form, opts);
|
|
1165
1179
|
},
|
|
1166
1180
|
error: function (e) {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1181
|
+
if (!checkNetworkError(e))
|
|
1182
|
+
ajax_done(
|
|
1183
|
+
e.responseJSON || { error: "Unknown error: " + e.responseText }
|
|
1184
|
+
);
|
|
1170
1185
|
},
|
|
1171
1186
|
});
|
|
1172
1187
|
}
|
|
@@ -1209,6 +1224,7 @@ function enable_codemirror(f) {
|
|
|
1209
1224
|
dataType: "script",
|
|
1210
1225
|
cache: true,
|
|
1211
1226
|
success: f,
|
|
1227
|
+
error: checkNetworkError,
|
|
1212
1228
|
});
|
|
1213
1229
|
}
|
|
1214
1230
|
function tristateClick(e, required) {
|
|
@@ -1516,7 +1532,8 @@ function reloadEmbeddedEditOwnViews(form, id) {
|
|
|
1516
1532
|
initialize_page();
|
|
1517
1533
|
},
|
|
1518
1534
|
error: function (res) {
|
|
1519
|
-
|
|
1535
|
+
if (!checkNetworkError(res))
|
|
1536
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
1520
1537
|
},
|
|
1521
1538
|
});
|
|
1522
1539
|
});
|
|
@@ -1749,24 +1766,26 @@ function is_paging_param(key) {
|
|
|
1749
1766
|
return key.endsWith("_page") || key.endsWith("_pagesize");
|
|
1750
1767
|
}
|
|
1751
1768
|
function check_saltcorn_notifications() {
|
|
1752
|
-
$.ajax(`/notifications/count-unread`)
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
window.update_theme_notification_count
|
|
1768
|
-
|
|
1769
|
-
|
|
1769
|
+
$.ajax(`/notifications/count-unread`)
|
|
1770
|
+
.then((resp) => {
|
|
1771
|
+
if (resp.success) {
|
|
1772
|
+
const n = resp.success;
|
|
1773
|
+
const menu_item = $(`a.notify-menu-item`);
|
|
1774
|
+
|
|
1775
|
+
menu_item.html(
|
|
1776
|
+
`<i class="fa-fw mr-05 fas fa-bell"></i>Notifications (${n})`
|
|
1777
|
+
);
|
|
1778
|
+
$(".user-nav-section").html(
|
|
1779
|
+
`<i class="fa-fw mr-05 fas fa-user"></i>User (${n})`
|
|
1780
|
+
);
|
|
1781
|
+
$(".user-nav-section-with-span").html(
|
|
1782
|
+
`<i class="fa-fw mr-05 fas fa-user"></i><span>User (${n})</span>`
|
|
1783
|
+
);
|
|
1784
|
+
window.update_theme_notification_count &&
|
|
1785
|
+
window.update_theme_notification_count(n);
|
|
1786
|
+
}
|
|
1787
|
+
})
|
|
1788
|
+
.fail(checkNetworkError);
|
|
1770
1789
|
}
|
|
1771
1790
|
|
|
1772
1791
|
function disable_inactive_tab_inputs(id) {
|
|
@@ -1862,7 +1881,8 @@ function reload_embedded_view(viewname, new_query_string) {
|
|
|
1862
1881
|
updater($e, res);
|
|
1863
1882
|
},
|
|
1864
1883
|
error: function (res) {
|
|
1865
|
-
|
|
1884
|
+
if (!checkNetworkError(res))
|
|
1885
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
1866
1886
|
},
|
|
1867
1887
|
});
|
|
1868
1888
|
} else {
|
|
@@ -1872,3 +1892,19 @@ function reload_embedded_view(viewname, new_query_string) {
|
|
|
1872
1892
|
}
|
|
1873
1893
|
});
|
|
1874
1894
|
}
|
|
1895
|
+
|
|
1896
|
+
function update_time_of_week(nm) {
|
|
1897
|
+
return function () {
|
|
1898
|
+
const day = $(`#input${nm}__day`).val();
|
|
1899
|
+
const flat = document.querySelector(`#input${nm}__time`)._flatpickr;
|
|
1900
|
+
|
|
1901
|
+
const time = flat.selectedDates?.[0];
|
|
1902
|
+
let s;
|
|
1903
|
+
if (time) {
|
|
1904
|
+
const m = time.getMinutes();
|
|
1905
|
+
|
|
1906
|
+
s = `${day} ${time.getHours()} ${m < 10 ? `0${m}` : m}`;
|
|
1907
|
+
} else s = day;
|
|
1908
|
+
$(`#inputh${nm}`).val(s).trigger("change");
|
|
1909
|
+
};
|
|
1910
|
+
}
|
package/public/saltcorn.js
CHANGED
|
@@ -190,7 +190,8 @@ function pjax_to(href, e) {
|
|
|
190
190
|
initialize_page();
|
|
191
191
|
},
|
|
192
192
|
error: function (res) {
|
|
193
|
-
|
|
193
|
+
if (!checkNetworkError(res))
|
|
194
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
194
195
|
},
|
|
195
196
|
});
|
|
196
197
|
}
|
|
@@ -264,7 +265,8 @@ function view_post(viewnameOrElem, route, data, onDone, sendState) {
|
|
|
264
265
|
reset_spinners();
|
|
265
266
|
})
|
|
266
267
|
.fail(function (res) {
|
|
267
|
-
|
|
268
|
+
if (!checkNetworkError(res))
|
|
269
|
+
notifyAlert({ type: "danger", text: res.responseText });
|
|
268
270
|
reset_spinners();
|
|
269
271
|
});
|
|
270
272
|
}
|
|
@@ -388,9 +390,12 @@ function ajax_modal(url, opts = {}) {
|
|
|
388
390
|
? {
|
|
389
391
|
error: opts.onError,
|
|
390
392
|
}
|
|
391
|
-
: {}),
|
|
393
|
+
: { error: checkNetworkError }),
|
|
392
394
|
});
|
|
393
395
|
}
|
|
396
|
+
function closeModal() {
|
|
397
|
+
$("#scmodal").modal("toggle");
|
|
398
|
+
}
|
|
394
399
|
|
|
395
400
|
function selectVersionError(res, btnId) {
|
|
396
401
|
notifyAlert({
|
|
@@ -448,7 +453,8 @@ function saveAndContinue(e, k, event) {
|
|
|
448
453
|
},
|
|
449
454
|
error: function (request) {
|
|
450
455
|
var ct = request.getResponseHeader("content-type") || "";
|
|
451
|
-
if (
|
|
456
|
+
if (checkNetworkError(request)) {
|
|
457
|
+
} else if (ct.startsWith && ct.startsWith("application/json")) {
|
|
452
458
|
notifyAlert({ type: "danger", text: request.responseJSON.error });
|
|
453
459
|
} else {
|
|
454
460
|
$("#page-inner-content").html(request.responseText);
|
|
@@ -497,6 +503,7 @@ function applyViewConfig(e, url, k, event) {
|
|
|
497
503
|
},
|
|
498
504
|
data: JSON.stringify(cfg),
|
|
499
505
|
error: function (request) {
|
|
506
|
+
checkNetworkError(request);
|
|
500
507
|
window.savingViewConfig = false;
|
|
501
508
|
ajax_indicate_error(e, request);
|
|
502
509
|
},
|
|
@@ -575,6 +582,7 @@ function ajaxSubmitForm(e, force_no_reload) {
|
|
|
575
582
|
else common_done(res, form.attr("data-viewname"));
|
|
576
583
|
},
|
|
577
584
|
error: function (request) {
|
|
585
|
+
checkNetworkError(request);
|
|
578
586
|
var title = request.getResponseHeader("Page-Title");
|
|
579
587
|
if (title) $("#scmodal .modal-title").html(decodeURIComponent(title));
|
|
580
588
|
var body = request.responseText;
|
|
@@ -591,6 +599,9 @@ function ajax_post_json(url, data, args = {}) {
|
|
|
591
599
|
...args,
|
|
592
600
|
});
|
|
593
601
|
}
|
|
602
|
+
|
|
603
|
+
let scNetworkErrorSignaled = false;
|
|
604
|
+
|
|
594
605
|
function ajax_post(url, args) {
|
|
595
606
|
$.ajax(url, {
|
|
596
607
|
type: "POST",
|
|
@@ -600,10 +611,30 @@ function ajax_post(url, args) {
|
|
|
600
611
|
...(args || {}),
|
|
601
612
|
})
|
|
602
613
|
.done(ajax_done)
|
|
603
|
-
.fail((e) =>
|
|
604
|
-
|
|
605
|
-
|
|
614
|
+
.fail((e, ...more) => {
|
|
615
|
+
if (!checkNetworkError(e))
|
|
616
|
+
return ajax_done(
|
|
617
|
+
e.responseJSON || { error: "Unknown error: " + e.responseText }
|
|
618
|
+
);
|
|
619
|
+
});
|
|
606
620
|
}
|
|
621
|
+
|
|
622
|
+
function checkNetworkError(e) {
|
|
623
|
+
if (e.readyState == 0 && !e.responseText && !e.responseJSON) {
|
|
624
|
+
//network error
|
|
625
|
+
if (scNetworkErrorSignaled) return true;
|
|
626
|
+
scNetworkErrorSignaled = true;
|
|
627
|
+
setTimeout(() => {
|
|
628
|
+
scNetworkErrorSignaled = false;
|
|
629
|
+
}, 1000);
|
|
630
|
+
notifyAlert({
|
|
631
|
+
type: "danger",
|
|
632
|
+
text: "Network connection error",
|
|
633
|
+
});
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
607
638
|
function ajax_post_btn(e, reload_on_done, reload_delay) {
|
|
608
639
|
var form = $(e).closest("form");
|
|
609
640
|
var url = form.attr("action");
|
|
@@ -617,6 +648,7 @@ function ajax_post_btn(e, reload_on_done, reload_delay) {
|
|
|
617
648
|
success: function () {
|
|
618
649
|
if (reload_on_done) location.reload();
|
|
619
650
|
},
|
|
651
|
+
error: checkNetworkError,
|
|
620
652
|
complete: function () {
|
|
621
653
|
if (reload_delay)
|
|
622
654
|
setTimeout(function () {
|
|
@@ -638,6 +670,7 @@ function api_action_call(name, body) {
|
|
|
638
670
|
success: function (res) {
|
|
639
671
|
common_done(res.data);
|
|
640
672
|
},
|
|
673
|
+
error: checkNetworkError,
|
|
641
674
|
});
|
|
642
675
|
}
|
|
643
676
|
|
package/routes/actions.js
CHANGED
|
@@ -237,6 +237,13 @@ const triggerForm = async (req, trigger) => {
|
|
|
237
237
|
showIf: { when_trigger: "Daily" },
|
|
238
238
|
sublabel: req.__("UTC timezone"),
|
|
239
239
|
},
|
|
240
|
+
{
|
|
241
|
+
name: "channel",
|
|
242
|
+
label: req.__("Time to run"),
|
|
243
|
+
input_type: "time_of_week",
|
|
244
|
+
showIf: { when_trigger: "Weekly" },
|
|
245
|
+
sublabel: req.__("UTC timezone"),
|
|
246
|
+
},
|
|
240
247
|
{
|
|
241
248
|
name: "channel",
|
|
242
249
|
label: req.__("Channel"),
|
package/routes/admin.js
CHANGED
|
@@ -309,9 +309,15 @@ router.get(
|
|
|
309
309
|
backupForm.values.auto_backup_expire_days = getState().getConfig(
|
|
310
310
|
"auto_backup_expire_days"
|
|
311
311
|
);
|
|
312
|
-
|
|
312
|
+
aBackupFilePrefixForm.values.backup_with_event_log = getState().getConfig(
|
|
313
313
|
"backup_with_event_log"
|
|
314
314
|
);
|
|
315
|
+
aBackupFilePrefixForm.values.backup_with_system_zip = getState().getConfig(
|
|
316
|
+
"backup_with_system_zip"
|
|
317
|
+
);
|
|
318
|
+
aBackupFilePrefixForm.values.backup_system_zip_level = getState().getConfig(
|
|
319
|
+
"backup_system_zip_level"
|
|
320
|
+
);
|
|
315
321
|
//
|
|
316
322
|
const aSnapshotForm = snapshotForm(req);
|
|
317
323
|
aSnapshotForm.values.snapshots_enabled =
|
|
@@ -702,6 +708,33 @@ const backupFilePrefixForm = (req) =>
|
|
|
702
708
|
sublabel: req.__("Include table history in backup"),
|
|
703
709
|
default: true,
|
|
704
710
|
},
|
|
711
|
+
{
|
|
712
|
+
type: "Bool",
|
|
713
|
+
label: req.__("Include Event Logs"),
|
|
714
|
+
sublabel: req.__("Backup with event logs"),
|
|
715
|
+
name: "backup_with_event_log",
|
|
716
|
+
},
|
|
717
|
+
{
|
|
718
|
+
type: "Bool",
|
|
719
|
+
label: req.__("Use system zip"),
|
|
720
|
+
sublabel: req.__(
|
|
721
|
+
"Recommended. Executable <code>zip</code> must be installed"
|
|
722
|
+
),
|
|
723
|
+
name: "backup_with_system_zip",
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
type: "Integer",
|
|
727
|
+
label: req.__("Zip compression level"),
|
|
728
|
+
sublabel: req.__("1=Fast, larger file, 9=Slow, smaller files"),
|
|
729
|
+
name: "backup_system_zip_level",
|
|
730
|
+
attributes: {
|
|
731
|
+
min: 1,
|
|
732
|
+
max: 9,
|
|
733
|
+
},
|
|
734
|
+
showIf: {
|
|
735
|
+
backup_with_system_zip: true,
|
|
736
|
+
},
|
|
737
|
+
},
|
|
705
738
|
],
|
|
706
739
|
});
|
|
707
740
|
|
|
@@ -829,15 +862,6 @@ const autoBackupForm = (req) => {
|
|
|
829
862
|
},
|
|
830
863
|
]
|
|
831
864
|
: []),
|
|
832
|
-
{
|
|
833
|
-
type: "Bool",
|
|
834
|
-
label: req.__("Include Event Logs"),
|
|
835
|
-
sublabel: req.__("Backup with event logs"),
|
|
836
|
-
name: "backup_with_event_log",
|
|
837
|
-
showIf: {
|
|
838
|
-
auto_backup_frequency: ["Daily", "Weekly"],
|
|
839
|
-
},
|
|
840
|
-
},
|
|
841
865
|
],
|
|
842
866
|
});
|
|
843
867
|
};
|
|
@@ -3514,6 +3538,7 @@ router.get(
|
|
|
3514
3538
|
send_admin_page({
|
|
3515
3539
|
res,
|
|
3516
3540
|
req,
|
|
3541
|
+
page_title: req.__(`%s code page`, name),
|
|
3517
3542
|
active_sub: "Development",
|
|
3518
3543
|
sub2_page: req.__(`%s code page`, name),
|
|
3519
3544
|
contents: {
|