@saltcorn/server 0.7.3-beta.3 → 0.7.3
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/app.js +5 -1
- package/auth/admin.js +7 -4
- package/auth/routes.js +37 -10
- package/locales/en.json +6 -1
- package/locales/zh.json +188 -188
- package/markup/admin.js +5 -3
- package/package.json +8 -8
- package/public/gridedit.js +6 -0
- package/public/jquery-menu-editor.min.js +15 -2
- package/public/saltcorn-common.js +37 -4
- package/public/saltcorn.css +17 -0
- package/public/saltcorn.js +24 -3
- package/routes/admin.js +142 -22
- package/routes/eventlog.js +24 -22
- package/routes/fields.js +3 -0
- package/routes/files.js +10 -8
- package/routes/homepage.js +7 -7
- package/routes/infoarch.js +6 -3
- package/routes/pageedit.js +1 -0
- package/routes/plugins.js +78 -11
- package/routes/search.js +4 -2
- package/routes/tables.js +11 -9
- package/routes/tenant.js +4 -2
- package/routes/viewedit.js +13 -2
- package/tests/tenant.test.js +6 -0
package/markup/admin.js
CHANGED
|
@@ -124,6 +124,7 @@ const send_settings_page = ({
|
|
|
124
124
|
: [
|
|
125
125
|
{
|
|
126
126
|
type: "card",
|
|
127
|
+
class: "mt-0",
|
|
127
128
|
contents: div(
|
|
128
129
|
{ class: "d-flex" },
|
|
129
130
|
ul(
|
|
@@ -344,7 +345,7 @@ const flash_restart = (req) => {
|
|
|
344
345
|
* @param {*} opts.formArgs
|
|
345
346
|
* @returns {Promise<Form>}
|
|
346
347
|
*/
|
|
347
|
-
const config_fields_form = async ({ field_names, req, ...formArgs }) => {
|
|
348
|
+
const config_fields_form = async ({ field_names, req, action, ...formArgs }) => {
|
|
348
349
|
const values = {};
|
|
349
350
|
const state = getState();
|
|
350
351
|
const fields = [];
|
|
@@ -395,8 +396,9 @@ const config_fields_form = async ({ field_names, req, ...formArgs }) => {
|
|
|
395
396
|
const form = new Form({
|
|
396
397
|
fields,
|
|
397
398
|
values,
|
|
398
|
-
|
|
399
|
-
|
|
399
|
+
action,
|
|
400
|
+
noSubmitButton: true,
|
|
401
|
+
onChange: `saveAndContinue(this)`,
|
|
400
402
|
...formArgs,
|
|
401
403
|
});
|
|
402
404
|
await form.fill_fkey_options();
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.7.3
|
|
3
|
+
"version": "0.7.3",
|
|
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
|
-
"@saltcorn/base-plugin": "0.7.3
|
|
10
|
-
"@saltcorn/builder": "0.7.3
|
|
11
|
-
"@saltcorn/data": "0.7.3
|
|
12
|
-
"@saltcorn/admin-models": "0.7.3
|
|
13
|
-
"@saltcorn/markup": "0.7.3
|
|
14
|
-
"@saltcorn/sbadmin2": "0.7.3
|
|
9
|
+
"@saltcorn/base-plugin": "0.7.3",
|
|
10
|
+
"@saltcorn/builder": "0.7.3",
|
|
11
|
+
"@saltcorn/data": "0.7.3",
|
|
12
|
+
"@saltcorn/admin-models": "0.7.3",
|
|
13
|
+
"@saltcorn/markup": "0.7.3",
|
|
14
|
+
"@saltcorn/sbadmin2": "0.7.3",
|
|
15
15
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
16
16
|
"@socket.io/sticky": "^1.0.1",
|
|
17
17
|
"aws-sdk": "^2.1037.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"pg": "^8.2.1",
|
|
49
49
|
"pluralize": "^8.0.0",
|
|
50
50
|
"qrcode": "1.5.0",
|
|
51
|
-
"resize-with-sharp-or-jimp": "0.1.
|
|
51
|
+
"resize-with-sharp-or-jimp": "0.1.5",
|
|
52
52
|
"socket.io": "4.2.0",
|
|
53
53
|
"thirty-two": "1.0.2",
|
|
54
54
|
"tmp-promise": "^3.0.2",
|
package/public/gridedit.js
CHANGED
|
@@ -76,6 +76,8 @@ function isoDateTimeFormatter(cell, formatterParams, onRendered) {
|
|
|
76
76
|
function isoDateFormatter(cell, formatterParams, onRendered) {
|
|
77
77
|
const val = cell.getValue();
|
|
78
78
|
if (!val) return "";
|
|
79
|
+
if (formatterParams && formatterParams.format)
|
|
80
|
+
return moment(val).format(formatterParams.format);
|
|
79
81
|
|
|
80
82
|
return new Date(val).toLocaleDateString(window.detected_locale || "en");
|
|
81
83
|
}
|
|
@@ -144,6 +146,10 @@ function add_tabulator_row() {
|
|
|
144
146
|
}
|
|
145
147
|
|
|
146
148
|
function delete_tabulator_row(e, cell) {
|
|
149
|
+
const def = cell.getColumn().getDefinition();
|
|
150
|
+
if (def && def.formatterParams && def.formatterParams.confirm) {
|
|
151
|
+
if (!confirm("Are you sure you want to delete this row?")) return;
|
|
152
|
+
}
|
|
147
153
|
const row = cell.getRow().getData();
|
|
148
154
|
if (!row.id) {
|
|
149
155
|
cell.getRow().delete();
|
|
@@ -117,7 +117,7 @@ function MenuEditor(e, t) {
|
|
|
117
117
|
(n.prev("div").children(".sortableListsOpener").first().remove(),
|
|
118
118
|
n.remove()),
|
|
119
119
|
MenuEditor.updateButtons(s);
|
|
120
|
-
|
|
120
|
+
l.onUpdate();
|
|
121
121
|
}
|
|
122
122
|
}),
|
|
123
123
|
$(document).on("click", ".btnEdit", function (e) {
|
|
@@ -132,6 +132,9 @@ function MenuEditor(e, t) {
|
|
|
132
132
|
"checked",
|
|
133
133
|
true
|
|
134
134
|
);
|
|
135
|
+
} else if (el.prop("tagName") == "SELECT") {
|
|
136
|
+
el.val(t);
|
|
137
|
+
el.attr("data-selected", t);
|
|
135
138
|
} else el.val(t);
|
|
136
139
|
}),
|
|
137
140
|
i.find(".item-menu").first().focus(),
|
|
@@ -139,6 +142,16 @@ function MenuEditor(e, t) {
|
|
|
139
142
|
? c.iconpicker("setIcon", t.icon)
|
|
140
143
|
: c.iconpicker("setIcon", "empty");
|
|
141
144
|
r.removeAttr("disabled");
|
|
145
|
+
i.find("[name]").each(function () {
|
|
146
|
+
try {
|
|
147
|
+
const el = $(this);
|
|
148
|
+
if (typeof t[el.attr("name")] === "undefined") {
|
|
149
|
+
el.val("");
|
|
150
|
+
}
|
|
151
|
+
} catch (e) {
|
|
152
|
+
console.warn(e);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
142
155
|
})((n = $(this).closest("li")));
|
|
143
156
|
l.onUpdate();
|
|
144
157
|
}),
|
|
@@ -164,7 +177,7 @@ function MenuEditor(e, t) {
|
|
|
164
177
|
t.remove()),
|
|
165
178
|
MenuEditor.updateButtons(s),
|
|
166
179
|
s.updateLevels();
|
|
167
|
-
|
|
180
|
+
l.onUpdate();
|
|
168
181
|
}),
|
|
169
182
|
s.on("click", ".btnIn", function (e) {
|
|
170
183
|
e.preventDefault();
|
|
@@ -63,8 +63,8 @@ function apply_showif() {
|
|
|
63
63
|
.val();
|
|
64
64
|
|
|
65
65
|
var options = data[1][val];
|
|
66
|
-
var current = e.attr("data-selected");
|
|
67
|
-
//console.log({
|
|
66
|
+
var current = e.attr("data-selected") || e.val();
|
|
67
|
+
//console.log({ field: e.attr("name"), target: data[0], val, current });
|
|
68
68
|
e.empty();
|
|
69
69
|
(options || []).forEach((o) => {
|
|
70
70
|
if (
|
|
@@ -87,6 +87,39 @@ function apply_showif() {
|
|
|
87
87
|
e.attr("data-selected", ec.target.value);
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
|
+
$("[data-fetch-options]").each(function (ix, element) {
|
|
91
|
+
const e = $(element);
|
|
92
|
+
const rec = get_form_record(e);
|
|
93
|
+
const dynwhere = JSON.parse(
|
|
94
|
+
decodeURIComponent(e.attr("data-fetch-options"))
|
|
95
|
+
);
|
|
96
|
+
//console.log(dynwhere);
|
|
97
|
+
const qs = Object.entries(dynwhere.whereParsed)
|
|
98
|
+
.map(([k, v]) => `${k}=${v[0] === "$" ? rec[v.substring(1)] : v}`)
|
|
99
|
+
.join("&");
|
|
100
|
+
var current = e.attr("data-selected");
|
|
101
|
+
e.change(function (ec) {
|
|
102
|
+
e.attr("data-selected", ec.target.value);
|
|
103
|
+
});
|
|
104
|
+
$.ajax(`/api/${dynwhere.table}?${qs}`).then((resp) => {
|
|
105
|
+
if (resp.success) {
|
|
106
|
+
e.empty();
|
|
107
|
+
if (!dynwhere.required) e.append($(`<option></option>`));
|
|
108
|
+
resp.success.forEach((r) => {
|
|
109
|
+
e.append(
|
|
110
|
+
$(
|
|
111
|
+
`<option ${
|
|
112
|
+
`${current}` === `${r[dynwhere.refname]}` ? "selected" : ""
|
|
113
|
+
} value="${r[dynwhere.refname]}">${
|
|
114
|
+
r[dynwhere.summary_field]
|
|
115
|
+
}</option>`
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
90
123
|
$("[data-source-url]").each(function (ix, element) {
|
|
91
124
|
const e = $(element);
|
|
92
125
|
const rec = get_form_record(e);
|
|
@@ -469,7 +502,7 @@ function common_done(res, isWeb = true) {
|
|
|
469
502
|
}
|
|
470
503
|
}
|
|
471
504
|
|
|
472
|
-
const repeaterCopyValuesToForm = (form, editor) => {
|
|
505
|
+
const repeaterCopyValuesToForm = (form, editor, noTriggerChange) => {
|
|
473
506
|
const vs = JSON.parse(editor.getString());
|
|
474
507
|
|
|
475
508
|
const setVal = (k, ix, v) => {
|
|
@@ -500,6 +533,7 @@ const repeaterCopyValuesToForm = (form, editor) => {
|
|
|
500
533
|
if (typeof ix !== "number" || isNaN(ix)) return;
|
|
501
534
|
if (ix >= vs.length) $(this).remove();
|
|
502
535
|
});
|
|
536
|
+
!noTriggerChange && form.trigger("change");
|
|
503
537
|
};
|
|
504
538
|
function align_dropdown(id) {
|
|
505
539
|
setTimeout(() => {
|
|
@@ -631,4 +665,3 @@ function cancel_form(form) {
|
|
|
631
665
|
$(form).append(`<input type="hidden" name="_cancel" value="on">`);
|
|
632
666
|
$(form).submit();
|
|
633
667
|
}
|
|
634
|
-
|
package/public/saltcorn.css
CHANGED
|
@@ -302,4 +302,21 @@ section.range-slider input[type="range"]::-moz-focus-outer {
|
|
|
302
302
|
table.table-inner-grid, table.table-inner-grid th, table.table-inner-grid td {
|
|
303
303
|
border: 1px solid black;
|
|
304
304
|
border-collapse: collapse;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* https://codepen.io/pezmotion/pen/RQERdm */
|
|
308
|
+
|
|
309
|
+
.editStarRating {
|
|
310
|
+
direction: rtl;
|
|
311
|
+
unicode-bidi: bidi-override;
|
|
312
|
+
color: #ddd;
|
|
313
|
+
}
|
|
314
|
+
.editStarRating input {
|
|
315
|
+
display: none;
|
|
316
|
+
}
|
|
317
|
+
.editStarRating label:hover,
|
|
318
|
+
.editStarRating label:hover ~ label,
|
|
319
|
+
.editStarRating input:checked + label,
|
|
320
|
+
.editStarRating input:checked + label ~ label {
|
|
321
|
+
color: #ffc107;
|
|
305
322
|
}
|
package/public/saltcorn.js
CHANGED
|
@@ -169,7 +169,7 @@ function close_saltcorn_modal() {
|
|
|
169
169
|
if (modal) modal.dispose();
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
function
|
|
172
|
+
function ensure_modal_exists_and_closed() {
|
|
173
173
|
if ($("#scmodal").length === 0) {
|
|
174
174
|
$("body").append(`<div id="scmodal", class="modal">
|
|
175
175
|
<div class="modal-dialog">
|
|
@@ -188,6 +188,19 @@ function ajax_modal(url, opts = {}) {
|
|
|
188
188
|
} else if ($("#scmodal").hasClass("show")) {
|
|
189
189
|
close_saltcorn_modal();
|
|
190
190
|
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function expand_thumbnail(img_id, filename) {
|
|
194
|
+
ensure_modal_exists_and_closed();
|
|
195
|
+
$("#scmodal .modal-body").html(
|
|
196
|
+
`<img src="/files/serve/${img_id}" style="width: 100%">`
|
|
197
|
+
);
|
|
198
|
+
$("#scmodal .modal-title").html(decodeURIComponent(filename));
|
|
199
|
+
new bootstrap.Modal($("#scmodal")).show();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function ajax_modal(url, opts = {}) {
|
|
203
|
+
ensure_modal_exists_and_closed();
|
|
191
204
|
if (opts.submitReload === false) $("#scmodal").addClass("no-submit-reload");
|
|
192
205
|
else $("#scmodal").removeClass("no-submit-reload");
|
|
193
206
|
$.ajax(url, {
|
|
@@ -200,6 +213,7 @@ function ajax_modal(url, opts = {}) {
|
|
|
200
213
|
(opts.onOpen || function () {})(res);
|
|
201
214
|
$("#scmodal").on("hidden.bs.modal", function (e) {
|
|
202
215
|
(opts.onClose || function () {})(res);
|
|
216
|
+
$("body").css("overflow", "");
|
|
203
217
|
});
|
|
204
218
|
},
|
|
205
219
|
});
|
|
@@ -235,7 +249,7 @@ function saveAndContinue(e, k) {
|
|
|
235
249
|
return false;
|
|
236
250
|
}
|
|
237
251
|
|
|
238
|
-
function applyViewConfig(e, url) {
|
|
252
|
+
function applyViewConfig(e, url, k) {
|
|
239
253
|
var form = $(e).closest("form");
|
|
240
254
|
var form_data = form.serializeArray();
|
|
241
255
|
const cfg = {};
|
|
@@ -251,6 +265,9 @@ function applyViewConfig(e, url) {
|
|
|
251
265
|
},
|
|
252
266
|
data: JSON.stringify(cfg),
|
|
253
267
|
error: function (request) {},
|
|
268
|
+
success: function (res) {
|
|
269
|
+
k && k(res);
|
|
270
|
+
},
|
|
254
271
|
});
|
|
255
272
|
|
|
256
273
|
return false;
|
|
@@ -296,7 +313,11 @@ function ajax_post(url, args) {
|
|
|
296
313
|
"CSRF-Token": _sc_globalCsrf,
|
|
297
314
|
},
|
|
298
315
|
...(args || {}),
|
|
299
|
-
})
|
|
316
|
+
})
|
|
317
|
+
.done(ajax_done)
|
|
318
|
+
.fail((e) =>
|
|
319
|
+
ajax_done(e.responseJSON || { error: "Unknown error: " + e.responseText })
|
|
320
|
+
);
|
|
300
321
|
}
|
|
301
322
|
function ajax_post_btn(e, reload_on_done, reload_delay) {
|
|
302
323
|
var form = $(e).closest("form");
|
package/routes/admin.js
CHANGED
|
@@ -43,6 +43,11 @@ const {
|
|
|
43
43
|
option,
|
|
44
44
|
fieldset,
|
|
45
45
|
legend,
|
|
46
|
+
ul,
|
|
47
|
+
li,
|
|
48
|
+
ol,
|
|
49
|
+
script,
|
|
50
|
+
domReady,
|
|
46
51
|
} = require("@saltcorn/markup/tags");
|
|
47
52
|
const db = require("@saltcorn/data/db");
|
|
48
53
|
const {
|
|
@@ -83,6 +88,7 @@ const {
|
|
|
83
88
|
const moment = require("moment");
|
|
84
89
|
const View = require("@saltcorn/data/models/view");
|
|
85
90
|
const { getConfigFile } = require("@saltcorn/data/db/connect");
|
|
91
|
+
const os = require("os");
|
|
86
92
|
|
|
87
93
|
/**
|
|
88
94
|
* @type {object}
|
|
@@ -137,10 +143,6 @@ const email_form = async (req) => {
|
|
|
137
143
|
],
|
|
138
144
|
action: "/admin/email",
|
|
139
145
|
});
|
|
140
|
-
form.submitButtonClass = "btn-outline-primary";
|
|
141
|
-
form.submitLabel = req.__("Save");
|
|
142
|
-
form.onChange =
|
|
143
|
-
"remove_outline(this);$('#testemail').attr('href','#').removeClass('btn-primary').addClass('btn-outline-primary')";
|
|
144
146
|
return form;
|
|
145
147
|
};
|
|
146
148
|
|
|
@@ -211,8 +213,10 @@ router.post(
|
|
|
211
213
|
flash_restart_if_required(form, req);
|
|
212
214
|
await save_config_from_form(form);
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
+
if (!req.xhr) {
|
|
217
|
+
req.flash("success", req.__("Site identity settings updated"));
|
|
218
|
+
res.redirect("/admin");
|
|
219
|
+
} else res.json({ success: "ok" });
|
|
216
220
|
}
|
|
217
221
|
})
|
|
218
222
|
);
|
|
@@ -305,7 +309,8 @@ router.post(
|
|
|
305
309
|
} else {
|
|
306
310
|
await save_config_from_form(form);
|
|
307
311
|
req.flash("success", req.__("Email settings updated"));
|
|
308
|
-
res.redirect("/admin/email");
|
|
312
|
+
if (!req.xhr) res.redirect("/admin/email");
|
|
313
|
+
else res.json({ success: "ok" });
|
|
309
314
|
}
|
|
310
315
|
})
|
|
311
316
|
);
|
|
@@ -370,7 +375,18 @@ router.get(
|
|
|
370
375
|
? {
|
|
371
376
|
type: "card",
|
|
372
377
|
title: req.__("Automated backup"),
|
|
373
|
-
contents: div(
|
|
378
|
+
contents: div(
|
|
379
|
+
renderForm(backupForm, req.csrfToken()),
|
|
380
|
+
a(
|
|
381
|
+
{ href: "/admin/auto-backup-list" },
|
|
382
|
+
"Restore/download automated backups »"
|
|
383
|
+
),
|
|
384
|
+
script(
|
|
385
|
+
domReady(
|
|
386
|
+
`$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
),
|
|
374
390
|
}
|
|
375
391
|
: { type: "blank", contents: "" },
|
|
376
392
|
],
|
|
@@ -379,6 +395,93 @@ router.get(
|
|
|
379
395
|
})
|
|
380
396
|
);
|
|
381
397
|
|
|
398
|
+
/**
|
|
399
|
+
* @name get/backup
|
|
400
|
+
* @function
|
|
401
|
+
* @memberof module:routes/admin~routes/adminRouter
|
|
402
|
+
*/
|
|
403
|
+
router.get(
|
|
404
|
+
"/auto-backup-list",
|
|
405
|
+
isAdmin,
|
|
406
|
+
error_catcher(async (req, res) => {
|
|
407
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
408
|
+
if (!isRoot) {
|
|
409
|
+
res.redirect("/admin/backup");
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const auto_backup_directory = getState().getConfig("auto_backup_directory");
|
|
413
|
+
const fileNms = await fs.promises.readdir(auto_backup_directory);
|
|
414
|
+
const backupFiles = fileNms.filter(
|
|
415
|
+
(fnm) => fnm.startsWith("sc-backup") && fnm.endsWith(".zip")
|
|
416
|
+
);
|
|
417
|
+
send_admin_page({
|
|
418
|
+
res,
|
|
419
|
+
req,
|
|
420
|
+
active_sub: "Backup",
|
|
421
|
+
contents: {
|
|
422
|
+
above: [
|
|
423
|
+
{
|
|
424
|
+
type: "card",
|
|
425
|
+
title: req.__("Download automated backup"),
|
|
426
|
+
contents: div(
|
|
427
|
+
ul(
|
|
428
|
+
backupFiles.map((fnm) =>
|
|
429
|
+
li(
|
|
430
|
+
a(
|
|
431
|
+
{
|
|
432
|
+
href: `/admin/auto-backup-download/${encodeURIComponent(
|
|
433
|
+
fnm
|
|
434
|
+
)}`,
|
|
435
|
+
},
|
|
436
|
+
fnm
|
|
437
|
+
)
|
|
438
|
+
)
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
),
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
type: "card",
|
|
445
|
+
title: req.__("Restoring automated backup"),
|
|
446
|
+
contents: div(
|
|
447
|
+
ol(
|
|
448
|
+
li("Download one of the backups above"),
|
|
449
|
+
li(
|
|
450
|
+
a({ href: "/admin/clear-all" }, "Clear this application"),
|
|
451
|
+
" ",
|
|
452
|
+
"(tick all boxes)"
|
|
453
|
+
),
|
|
454
|
+
li(
|
|
455
|
+
"When prompted to create the first user, click the link to restore a backup"
|
|
456
|
+
),
|
|
457
|
+
li("Select the downloaded backup file")
|
|
458
|
+
)
|
|
459
|
+
),
|
|
460
|
+
},
|
|
461
|
+
],
|
|
462
|
+
},
|
|
463
|
+
});
|
|
464
|
+
})
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
router.get(
|
|
468
|
+
"/auto-backup-download/:filename",
|
|
469
|
+
isAdmin,
|
|
470
|
+
error_catcher(async (req, res) => {
|
|
471
|
+
const { filename } = req.params;
|
|
472
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
473
|
+
if (
|
|
474
|
+
!isRoot ||
|
|
475
|
+
!(filename.startsWith("sc-backup") && filename.endsWith(".zip"))
|
|
476
|
+
) {
|
|
477
|
+
res.redirect("/admin/backup");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const auto_backup_directory = getState().getConfig("auto_backup_directory");
|
|
481
|
+
res.download(path.join(auto_backup_directory, filename), filename);
|
|
482
|
+
})
|
|
483
|
+
);
|
|
484
|
+
|
|
382
485
|
/**
|
|
383
486
|
* Auto backup Form
|
|
384
487
|
* @param {object} req
|
|
@@ -387,9 +490,8 @@ router.get(
|
|
|
387
490
|
const autoBackupForm = (req) =>
|
|
388
491
|
new Form({
|
|
389
492
|
action: "/admin/set-auto-backup",
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
submitLabel: "Save settings",
|
|
493
|
+
onChange: `saveAndContinue(this);$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`,
|
|
494
|
+
noSubmitButton: true,
|
|
393
495
|
additionalButtons: [
|
|
394
496
|
{
|
|
395
497
|
label: "Backup now",
|
|
@@ -458,7 +560,8 @@ router.post(
|
|
|
458
560
|
} else {
|
|
459
561
|
await save_config_from_form(form);
|
|
460
562
|
req.flash("success", req.__("Backup settings updated"));
|
|
461
|
-
res.redirect("/admin/backup");
|
|
563
|
+
if (!req.xhr) res.redirect("/admin/backup");
|
|
564
|
+
else res.json({ success: "ok" });
|
|
462
565
|
}
|
|
463
566
|
})
|
|
464
567
|
);
|
|
@@ -1141,22 +1244,30 @@ router.post(
|
|
|
1141
1244
|
"error",
|
|
1142
1245
|
req.__("Please select at least one platform (android or iOS).")
|
|
1143
1246
|
);
|
|
1144
|
-
return res.redirect("/admin/
|
|
1247
|
+
return res.redirect("/admin/build-mobile-app");
|
|
1145
1248
|
}
|
|
1146
1249
|
if (!androidPlatform && useDocker) {
|
|
1147
1250
|
req.flash("error", req.__("Only the android build supports docker."));
|
|
1148
|
-
return res.redirect("/admin/
|
|
1251
|
+
return res.redirect("/admin/build-mobile-app");
|
|
1149
1252
|
}
|
|
1150
1253
|
if (appFile && !appFile.endsWith(".apk")) appFile = `${appFile}.apk`;
|
|
1151
1254
|
const appOut = path.join(__dirname, "..", "mobile-app-out");
|
|
1152
|
-
const spawnParams = [
|
|
1255
|
+
const spawnParams = [
|
|
1256
|
+
"build-app",
|
|
1257
|
+
"-v",
|
|
1258
|
+
entryView,
|
|
1259
|
+
"-c",
|
|
1260
|
+
appOut,
|
|
1261
|
+
"-b",
|
|
1262
|
+
`${os.userInfo().homedir}/mobile_app_build`,
|
|
1263
|
+
];
|
|
1153
1264
|
if (useDocker) spawnParams.push("-d");
|
|
1154
1265
|
if (androidPlatform) spawnParams.push("-p", "android");
|
|
1155
1266
|
if (iOSPlatform) spawnParams.push("-p", "ios");
|
|
1156
1267
|
if (appFile) spawnParams.push("-a", appFile);
|
|
1157
1268
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1158
1269
|
const child = spawn("saltcorn", spawnParams, {
|
|
1159
|
-
stdio: ["ignore", "pipe",
|
|
1270
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1160
1271
|
cwd: ".",
|
|
1161
1272
|
});
|
|
1162
1273
|
const childOutputs = [];
|
|
@@ -1164,8 +1275,12 @@ router.post(
|
|
|
1164
1275
|
// console.log(data.toString());
|
|
1165
1276
|
childOutputs.push(data.toString());
|
|
1166
1277
|
});
|
|
1167
|
-
child.on("
|
|
1168
|
-
|
|
1278
|
+
child.stderr.on("data", (data) => {
|
|
1279
|
+
// console.log(data.toString());
|
|
1280
|
+
childOutputs.push(data.toString());
|
|
1281
|
+
});
|
|
1282
|
+
child.on("exit", async function (exitCode, signal) {
|
|
1283
|
+
if (exitCode === 0) {
|
|
1169
1284
|
const file = await File.from_existing_file(
|
|
1170
1285
|
appOut,
|
|
1171
1286
|
appFile ? appFile : "app-debug.apk",
|
|
@@ -1187,9 +1302,11 @@ router.post(
|
|
|
1187
1302
|
{
|
|
1188
1303
|
type: "card",
|
|
1189
1304
|
title: req.__("Build Result"),
|
|
1190
|
-
contents: div(
|
|
1305
|
+
contents: div(
|
|
1306
|
+
"Unable to build the app:",
|
|
1307
|
+
pre(code(childOutputs.join("<br/>")))
|
|
1308
|
+
),
|
|
1191
1309
|
},
|
|
1192
|
-
childOutputs.join("<br/>"),
|
|
1193
1310
|
],
|
|
1194
1311
|
});
|
|
1195
1312
|
});
|
|
@@ -1201,9 +1318,12 @@ router.post(
|
|
|
1201
1318
|
{
|
|
1202
1319
|
type: "card",
|
|
1203
1320
|
title: req.__("Build Result"),
|
|
1204
|
-
contents: div(
|
|
1321
|
+
contents: div(
|
|
1322
|
+
p("Unable to build the app:"),
|
|
1323
|
+
pre(code(message)),
|
|
1324
|
+
pre(code(stack))
|
|
1325
|
+
),
|
|
1205
1326
|
},
|
|
1206
|
-
`${message} <br/> ${stack}`,
|
|
1207
1327
|
],
|
|
1208
1328
|
});
|
|
1209
1329
|
});
|
package/routes/eventlog.js
CHANGED
|
@@ -73,8 +73,9 @@ const logSettingsForm = async (req) => {
|
|
|
73
73
|
fields.push({
|
|
74
74
|
name: w + "_channel",
|
|
75
75
|
label: w + " channel",
|
|
76
|
-
sublabel:
|
|
77
|
-
|
|
76
|
+
sublabel: req.__(
|
|
77
|
+
"Channels to create events for. Separate by comma; leave blank for all"
|
|
78
|
+
),
|
|
78
79
|
type: "String",
|
|
79
80
|
showIf: { [w]: true },
|
|
80
81
|
});
|
|
@@ -82,8 +83,8 @@ const logSettingsForm = async (req) => {
|
|
|
82
83
|
return new Form({
|
|
83
84
|
action: "/eventlog/settings",
|
|
84
85
|
blurb: req.__("Which events should be logged?"),
|
|
85
|
-
|
|
86
|
-
onChange: "
|
|
86
|
+
noSubmitButton: true,
|
|
87
|
+
onChange: "saveAndContinue(this)",
|
|
87
88
|
fields,
|
|
88
89
|
});
|
|
89
90
|
};
|
|
@@ -169,23 +170,23 @@ router.get(
|
|
|
169
170
|
* @returns {Form}
|
|
170
171
|
*/
|
|
171
172
|
const customEventForm = async (req) => {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
173
|
+
return new Form({
|
|
174
|
+
action: "/eventlog/custom/new",
|
|
175
|
+
submitButtonClass: "btn-outline-primary",
|
|
176
|
+
onChange: "remove_outline(this)",
|
|
177
|
+
fields: [
|
|
178
|
+
{
|
|
179
|
+
name: "name",
|
|
180
|
+
label: req.__("Event Name"),
|
|
181
|
+
type: "String",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "hasChannel",
|
|
185
|
+
label: req.__("Has channels?"),
|
|
186
|
+
type: "Bool",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
});
|
|
189
190
|
};
|
|
190
191
|
/**
|
|
191
192
|
* @name get/custom/new
|
|
@@ -297,7 +298,8 @@ router.post(
|
|
|
297
298
|
} else {
|
|
298
299
|
await getState().setConfig("event_log_settings", form.values);
|
|
299
300
|
|
|
300
|
-
res.redirect(`/eventlog/settings`);
|
|
301
|
+
if (!req.xhr) res.redirect(`/eventlog/settings`);
|
|
302
|
+
else res.json({ success: "ok" });
|
|
301
303
|
}
|
|
302
304
|
})
|
|
303
305
|
);
|
package/routes/fields.js
CHANGED
|
@@ -488,6 +488,7 @@ router.get(
|
|
|
488
488
|
},
|
|
489
489
|
{
|
|
490
490
|
type: "card",
|
|
491
|
+
class: "mt-0",
|
|
491
492
|
title: wizardCardTitle(field.label, wf, wfres),
|
|
492
493
|
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
493
494
|
},
|
|
@@ -524,6 +525,7 @@ router.get(
|
|
|
524
525
|
},
|
|
525
526
|
{
|
|
526
527
|
type: "card",
|
|
528
|
+
class: "mt-0",
|
|
527
529
|
title: wizardCardTitle(req.__(`New field`), wf, wfres),
|
|
528
530
|
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
529
531
|
},
|
|
@@ -589,6 +591,7 @@ router.post(
|
|
|
589
591
|
},
|
|
590
592
|
{
|
|
591
593
|
type: "card",
|
|
594
|
+
class: "mt-0",
|
|
592
595
|
title: wizardCardTitle(
|
|
593
596
|
wfres.context.label || req.__("New field"),
|
|
594
597
|
wf,
|