@saltcorn/server 0.7.4-beta.3 → 0.7.4
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 +26 -12
- package/auth/routes.js +41 -27
- package/locales/en.json +10 -1
- package/package.json +7 -7
- package/public/saltcorn-common.js +86 -21
- package/public/saltcorn.css +16 -10
- package/public/saltcorn.js +13 -7
- package/routes/actions.js +15 -5
- package/routes/admin.js +21 -10
- package/routes/diagram.js +436 -35
- package/routes/pageedit.js +9 -6
- package/routes/tags.js +26 -22
- package/routes/utils.js +60 -20
- package/routes/viewedit.js +40 -20
package/app.js
CHANGED
|
@@ -85,6 +85,8 @@ const getApp = async (opts = {}) => {
|
|
|
85
85
|
const development_mode = getState().getConfig("development_mode", false);
|
|
86
86
|
// switch on sql logging - but it was initiated before???
|
|
87
87
|
if (getState().getConfig("log_sql", false)) db.set_sql_logging();
|
|
88
|
+
// for multi-tenant with localhost, we need 1 instead of the default of 2
|
|
89
|
+
if (opts.subdomainOffset) app.set("subdomain offset", opts.subdomainOffset);
|
|
88
90
|
|
|
89
91
|
// https://www.npmjs.com/package/helmet
|
|
90
92
|
// helmet is secure app by adding HTTP headers
|
|
@@ -225,8 +227,9 @@ const getApp = async (opts = {}) => {
|
|
|
225
227
|
})
|
|
226
228
|
);
|
|
227
229
|
passport.use(
|
|
228
|
-
new JwtStrategy(jwtOpts, (jwt_payload, done) => {
|
|
229
|
-
|
|
230
|
+
new JwtStrategy(jwtOpts, async (jwt_payload, done) => {
|
|
231
|
+
const userCheck = async () => {
|
|
232
|
+
const u = await User.findOne({ email: jwt_payload.sub });
|
|
230
233
|
if (
|
|
231
234
|
u &&
|
|
232
235
|
u.last_mobile_login &&
|
|
@@ -242,7 +245,16 @@ const getApp = async (opts = {}) => {
|
|
|
242
245
|
} else {
|
|
243
246
|
return done(null, { role_id: 10 });
|
|
244
247
|
}
|
|
245
|
-
}
|
|
248
|
+
};
|
|
249
|
+
if (
|
|
250
|
+
db.is_it_multi_tenant() &&
|
|
251
|
+
jwt_payload.tenant?.length > 0 &&
|
|
252
|
+
jwt_payload.tenant !== db.connectObj.default_schema
|
|
253
|
+
) {
|
|
254
|
+
return await db.runWithTenant(jwt_payload.tenant, userCheck);
|
|
255
|
+
} else {
|
|
256
|
+
return await userCheck();
|
|
257
|
+
}
|
|
246
258
|
})
|
|
247
259
|
);
|
|
248
260
|
passport.use(
|
|
@@ -259,6 +271,12 @@ const getApp = async (opts = {}) => {
|
|
|
259
271
|
passport.deserializeUser(function (user, done) {
|
|
260
272
|
done(null, user);
|
|
261
273
|
});
|
|
274
|
+
app.use(function (req, res, next) {
|
|
275
|
+
if (req.headers["x-saltcorn-client"] === "mobile-app") {
|
|
276
|
+
req.smr = true; // saltcorn-mobile-request
|
|
277
|
+
}
|
|
278
|
+
return next();
|
|
279
|
+
});
|
|
262
280
|
app.use(setTenant);
|
|
263
281
|
|
|
264
282
|
// Change into s3storage compatible selector
|
|
@@ -267,12 +285,15 @@ const getApp = async (opts = {}) => {
|
|
|
267
285
|
app.use(s3storage.middlewareTransform);
|
|
268
286
|
|
|
269
287
|
app.use(wrapper(version_tag));
|
|
288
|
+
|
|
270
289
|
const csurf = csrf();
|
|
271
290
|
if (!opts.disableCsrf)
|
|
272
291
|
app.use(function (req, res, next) {
|
|
273
292
|
if (
|
|
274
|
-
req.
|
|
275
|
-
|
|
293
|
+
(req.smr &&
|
|
294
|
+
(req.url.startsWith("/api/") ||
|
|
295
|
+
req.url === "/auth/login-with/jwt" ||
|
|
296
|
+
req.url === "/auth/signup")) ||
|
|
276
297
|
jwt_extractor(req)
|
|
277
298
|
)
|
|
278
299
|
return disabledCsurf(req, res, next);
|
|
@@ -280,13 +301,6 @@ const getApp = async (opts = {}) => {
|
|
|
280
301
|
});
|
|
281
302
|
else app.use(disabledCsurf);
|
|
282
303
|
|
|
283
|
-
app.use(function (req, res, next) {
|
|
284
|
-
if (req.headers["x-saltcorn-client"] === "mobile-app") {
|
|
285
|
-
req.smr = true; // saltcorn-mobile-request
|
|
286
|
-
}
|
|
287
|
-
return next();
|
|
288
|
-
});
|
|
289
|
-
|
|
290
304
|
mountRoutes(app);
|
|
291
305
|
// set tenant homepage as / root
|
|
292
306
|
app.get("/", error_catcher(homepage));
|
package/auth/routes.js
CHANGED
|
@@ -199,33 +199,41 @@ const getAuthLinks = (current, noMethods) => {
|
|
|
199
199
|
return links;
|
|
200
200
|
};
|
|
201
201
|
|
|
202
|
-
const loginWithJwt = async (email, password, res) => {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
202
|
+
const loginWithJwt = async (email, password, saltcornApp, res) => {
|
|
203
|
+
const loginFn = async () => {
|
|
204
|
+
const user = await User.findOne({ email });
|
|
205
|
+
if (user && user.checkPassword(password)) {
|
|
206
|
+
const now = new Date();
|
|
207
|
+
const jwt_secret = db.connectObj.jwt_secret;
|
|
208
|
+
const token = jwt.sign(
|
|
209
|
+
{
|
|
210
|
+
sub: email,
|
|
211
|
+
user: {
|
|
212
|
+
id: user.id,
|
|
213
|
+
email: user.email,
|
|
214
|
+
role_id: user.role_id,
|
|
215
|
+
language: user.language ? user.language : "en",
|
|
216
|
+
disabled: user.disabled,
|
|
217
|
+
},
|
|
218
|
+
iss: "saltcorn@saltcorn",
|
|
219
|
+
aud: "saltcorn-mobile-app",
|
|
220
|
+
iat: now.valueOf(),
|
|
221
|
+
tenant: db.getTenantSchema(),
|
|
216
222
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
jwt_secret
|
|
224
|
+
);
|
|
225
|
+
if (!user.last_mobile_login) await user.updateLastMobileLogin(now);
|
|
226
|
+
res.json(token);
|
|
227
|
+
} else {
|
|
228
|
+
res.json({
|
|
229
|
+
alerts: [{ type: "danger", msg: "Incorrect user or password" }],
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
if (saltcornApp && saltcornApp !== db.connectObj.default_schema) {
|
|
234
|
+
await db.runWithTenant(saltcornApp, loginFn);
|
|
225
235
|
} else {
|
|
226
|
-
|
|
227
|
-
alerts: [{ type: "danger", msg: "Incorrect user or password" }],
|
|
228
|
-
});
|
|
236
|
+
await loginFn();
|
|
229
237
|
}
|
|
230
238
|
};
|
|
231
239
|
|
|
@@ -899,7 +907,13 @@ router.post(
|
|
|
899
907
|
} else {
|
|
900
908
|
const u = await User.create({ email, password });
|
|
901
909
|
await send_verification_email(u, req);
|
|
902
|
-
if (req.smr)
|
|
910
|
+
if (req.smr)
|
|
911
|
+
await loginWithJwt(
|
|
912
|
+
email,
|
|
913
|
+
password,
|
|
914
|
+
req.headers["x-saltcorn-app"],
|
|
915
|
+
res
|
|
916
|
+
);
|
|
903
917
|
else signup_login_with_user(u, req, res);
|
|
904
918
|
}
|
|
905
919
|
}
|
|
@@ -1008,7 +1022,7 @@ router.get(
|
|
|
1008
1022
|
const { method } = req.params;
|
|
1009
1023
|
if (method === "jwt") {
|
|
1010
1024
|
const { email, password } = req.query;
|
|
1011
|
-
await loginWithJwt(email, password, res);
|
|
1025
|
+
await loginWithJwt(email, password, req.headers["x-saltcorn-app"], res);
|
|
1012
1026
|
} else {
|
|
1013
1027
|
const auth = getState().auth_methods[method];
|
|
1014
1028
|
if (auth) {
|
package/locales/en.json
CHANGED
|
@@ -983,5 +983,14 @@
|
|
|
983
983
|
"Restore/download automated backups »": "Restore/download automated backups »",
|
|
984
984
|
"Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).": "Snapshots store your application structure and definition, without the table data. Individual views and pages can be restored from snapshots from the <a href='/viewedit'>view</a> or <a href='/pageedit'>pages</a> overviews (\"Restore\" from individual page or view dropdowns).",
|
|
985
985
|
"List/download snapshots »": "List/download snapshots »",
|
|
986
|
-
"Discover tables that are already in the Database, but not known to Saltcorn": "Discover tables that are already in the Database, but not known to Saltcorn"
|
|
986
|
+
"Discover tables that are already in the Database, but not known to Saltcorn": "Discover tables that are already in the Database, but not known to Saltcorn",
|
|
987
|
+
"Split paste": "Split paste",
|
|
988
|
+
"Separate paste content into separate inputs": "Separate paste content into separate inputs",
|
|
989
|
+
"Add entries to tag": "Add entries to tag",
|
|
990
|
+
"Add pages": "Add pages",
|
|
991
|
+
"Add triggers": "Add triggers",
|
|
992
|
+
"Formula value": "Formula value",
|
|
993
|
+
"The build was successfully": "The build was successfully",
|
|
994
|
+
"Unable to build the app:": "Unable to build the app:",
|
|
995
|
+
"Add tag": "Add tag"
|
|
987
996
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.7.4
|
|
3
|
+
"version": "0.7.4",
|
|
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.4
|
|
10
|
-
"@saltcorn/builder": "0.7.4
|
|
11
|
-
"@saltcorn/data": "0.7.4
|
|
12
|
-
"@saltcorn/admin-models": "0.7.4
|
|
13
|
-
"@saltcorn/markup": "0.7.4
|
|
14
|
-
"@saltcorn/sbadmin2": "0.7.4
|
|
9
|
+
"@saltcorn/base-plugin": "0.7.4",
|
|
10
|
+
"@saltcorn/builder": "0.7.4",
|
|
11
|
+
"@saltcorn/data": "0.7.4",
|
|
12
|
+
"@saltcorn/admin-models": "0.7.4",
|
|
13
|
+
"@saltcorn/markup": "0.7.4",
|
|
14
|
+
"@saltcorn/sbadmin2": "0.7.4",
|
|
15
15
|
"@socket.io/cluster-adapter": "^0.1.0",
|
|
16
16
|
"@socket.io/sticky": "^1.0.1",
|
|
17
17
|
"aws-sdk": "^2.1037.0",
|
|
@@ -34,7 +34,12 @@ function add_repeater(nm) {
|
|
|
34
34
|
});
|
|
35
35
|
newe.appendTo($("div.repeats-" + nm));
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
|
|
38
|
+
const _apply_showif_plugins = []
|
|
39
|
+
|
|
40
|
+
const add_apply_showif_plugin = p => {
|
|
41
|
+
_apply_showif_plugins.push(p)
|
|
42
|
+
}
|
|
38
43
|
function apply_showif() {
|
|
39
44
|
$("[data-show-if]").each(function (ix, element) {
|
|
40
45
|
var e = $(element);
|
|
@@ -110,26 +115,54 @@ function apply_showif() {
|
|
|
110
115
|
e.change(function (ec) {
|
|
111
116
|
e.attr("data-selected", ec.target.value);
|
|
112
117
|
});
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
|
|
119
|
+
const currentOptionsSet = e.prop('data-fetch-options-current-set')
|
|
120
|
+
if (currentOptionsSet === qs) return;
|
|
121
|
+
|
|
122
|
+
const activate = (success, qs) => {
|
|
123
|
+
e.empty();
|
|
124
|
+
e.prop('data-fetch-options-current-set', qs)
|
|
125
|
+
if (!dynwhere.required) e.append($(`<option></option>`));
|
|
126
|
+
let currentDataOption = undefined;
|
|
127
|
+
const dataOptions = []
|
|
128
|
+
success.forEach((r) => {
|
|
129
|
+
const label = dynwhere.label_formula
|
|
130
|
+
? new Function(
|
|
131
|
+
`{${Object.keys(r).join(",")}}`,
|
|
132
|
+
"return " + dynwhere.label_formula
|
|
133
|
+
)(r)
|
|
134
|
+
: r[dynwhere.summary_field]
|
|
135
|
+
const value = r[dynwhere.refname]
|
|
136
|
+
const selected = `${current}` === `${r[dynwhere.refname]}`
|
|
137
|
+
dataOptions.push({ text: label, value });
|
|
138
|
+
if (selected) currentDataOption = value;
|
|
139
|
+
const html = `<option ${selected ? "selected" : ""
|
|
140
|
+
} value="${value}">${label}</option>`
|
|
141
|
+
e.append(
|
|
142
|
+
$(html)
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
element.dispatchEvent(new Event('RefreshSelectOptions'))
|
|
146
|
+
if (e.hasClass("selectized") && $().selectize) {
|
|
147
|
+
e.selectize()[0].selectize.clearOptions();
|
|
148
|
+
e.selectize()[0].selectize.addOption(dataOptions);
|
|
149
|
+
if (typeof currentDataOption !== "undefined")
|
|
150
|
+
e.selectize()[0].selectize.setValue(currentDataOption);
|
|
151
|
+
|
|
131
152
|
}
|
|
132
|
-
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const cache = e.prop('data-fetch-options-cache') || {}
|
|
156
|
+
if (cache[qs]) {
|
|
157
|
+
activate(cache[qs], qs)
|
|
158
|
+
} else
|
|
159
|
+
$.ajax(`/api/${dynwhere.table}?${qs}`).then((resp) => {
|
|
160
|
+
if (resp.success) {
|
|
161
|
+
activate(resp.success, qs)
|
|
162
|
+
const cacheNow = e.prop('data-fetch-options-cache') || {}
|
|
163
|
+
e.prop('data-fetch-options-cache', { ...cacheNow, [qs]: resp.success })
|
|
164
|
+
}
|
|
165
|
+
});
|
|
133
166
|
});
|
|
134
167
|
|
|
135
168
|
$("[data-source-url]").each(function (ix, element) {
|
|
@@ -145,7 +178,17 @@ function apply_showif() {
|
|
|
145
178
|
},
|
|
146
179
|
});
|
|
147
180
|
});
|
|
181
|
+
_apply_showif_plugins.forEach(p => p())
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function splitTargetMatch(elemValue, target, keySpec) {
|
|
185
|
+
if (!elemValue) return false;
|
|
186
|
+
const [fld, keySpec1] = keySpec.split("|_")
|
|
187
|
+
const [sep, pos] = keySpec1.split("_")
|
|
188
|
+
const elemValueShort = elemValue.split(sep)[pos]
|
|
189
|
+
return elemValueShort === target;
|
|
148
190
|
}
|
|
191
|
+
|
|
149
192
|
function get_form_record(e, select_labels) {
|
|
150
193
|
const rec = {};
|
|
151
194
|
e.closest("form")
|
|
@@ -576,6 +619,8 @@ const columnSummary = (col) => {
|
|
|
576
619
|
return `Field ${col.field_name} ${col.fieldview || ""}`;
|
|
577
620
|
case "Link":
|
|
578
621
|
return `Link ${col.link_text}`;
|
|
622
|
+
case "FormulaValue":
|
|
623
|
+
return `Formula ${col.formula}`;
|
|
579
624
|
case "JoinField":
|
|
580
625
|
return `Join ${col.join_field}`;
|
|
581
626
|
case "ViewLink":
|
|
@@ -583,7 +628,7 @@ const columnSummary = (col) => {
|
|
|
583
628
|
case "Action":
|
|
584
629
|
return `Action ${col.action_label || col.action_name}`;
|
|
585
630
|
case "Aggregation":
|
|
586
|
-
return `${col.stat} ${col.agg_field} ${col.agg_relation}`;
|
|
631
|
+
return `${col.stat} ${col.agg_field.split("@")[0]} ${col.agg_relation}`;
|
|
587
632
|
default:
|
|
588
633
|
return "Unknown";
|
|
589
634
|
}
|
|
@@ -678,3 +723,23 @@ function cancel_form(form) {
|
|
|
678
723
|
$(form).append(`<input type="hidden" name="_cancel" value="on">`);
|
|
679
724
|
$(form).submit();
|
|
680
725
|
}
|
|
726
|
+
|
|
727
|
+
function split_paste_handler(e) {
|
|
728
|
+
e.preventDefault();
|
|
729
|
+
let clipboardData = e.clipboardData || window.clipboardData || e.originalEvent.clipboardData;
|
|
730
|
+
|
|
731
|
+
const lines = clipboardData.getData('text').split(/\r\n/g)
|
|
732
|
+
|
|
733
|
+
const form = $(e.target).closest('form')
|
|
734
|
+
|
|
735
|
+
let matched = false;
|
|
736
|
+
|
|
737
|
+
form.find('input:not(:disabled):not([readonly]):not(:hidden)').each(function (ix, element) {
|
|
738
|
+
if (!matched && element === e.target) matched = true;
|
|
739
|
+
if (matched && lines.length > 0) {
|
|
740
|
+
$(element).val(lines.shift())
|
|
741
|
+
}
|
|
742
|
+
})
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
}
|
package/public/saltcorn.css
CHANGED
|
@@ -299,7 +299,9 @@ section.range-slider input[type="range"]::-moz-focus-outer {
|
|
|
299
299
|
padding: 0.1rem 0.4rem !important;
|
|
300
300
|
}
|
|
301
301
|
|
|
302
|
-
table.table-inner-grid,
|
|
302
|
+
table.table-inner-grid,
|
|
303
|
+
table.table-inner-grid th,
|
|
304
|
+
table.table-inner-grid td {
|
|
303
305
|
border: 1px solid black;
|
|
304
306
|
border-collapse: collapse;
|
|
305
307
|
}
|
|
@@ -307,18 +309,18 @@ table.table-inner-grid, table.table-inner-grid th, table.table-inner-grid td {
|
|
|
307
309
|
/* https://codepen.io/pezmotion/pen/RQERdm */
|
|
308
310
|
|
|
309
311
|
.editStarRating {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
312
|
+
direction: rtl;
|
|
313
|
+
unicode-bidi: bidi-override;
|
|
314
|
+
color: #ddd;
|
|
313
315
|
}
|
|
314
316
|
.editStarRating input {
|
|
315
|
-
|
|
317
|
+
display: none;
|
|
316
318
|
}
|
|
317
319
|
.editStarRating label:hover,
|
|
318
320
|
.editStarRating label:hover ~ label,
|
|
319
321
|
.editStarRating input:checked + label,
|
|
320
322
|
.editStarRating input:checked + label ~ label {
|
|
321
|
-
|
|
323
|
+
color: #ffc107;
|
|
322
324
|
}
|
|
323
325
|
|
|
324
326
|
.CodeMirror {
|
|
@@ -326,12 +328,12 @@ table.table-inner-grid, table.table-inner-grid th, table.table-inner-grid td {
|
|
|
326
328
|
}
|
|
327
329
|
|
|
328
330
|
/* copied from bootstrap and adjusted to show the arrow on the left */
|
|
329
|
-
.card .card-header-left-collapse[data-bs-toggle=collapse] {
|
|
331
|
+
.card .card-header-left-collapse[data-bs-toggle="collapse"] {
|
|
330
332
|
text-decoration: none;
|
|
331
333
|
position: relative;
|
|
332
334
|
padding: 0.75rem 3.25rem 0.75rem 1.25rem;
|
|
333
335
|
}
|
|
334
|
-
.card .card-header-left-collapse[data-bs-toggle=collapse]::before {
|
|
336
|
+
.card .card-header-left-collapse[data-bs-toggle="collapse"]::before {
|
|
335
337
|
position: absolute;
|
|
336
338
|
left: 0;
|
|
337
339
|
top: 0;
|
|
@@ -341,9 +343,13 @@ table.table-inner-grid, table.table-inner-grid th, table.table-inner-grid td {
|
|
|
341
343
|
font-family: "Font Awesome 5 Free";
|
|
342
344
|
color: #d1d3e2;
|
|
343
345
|
}
|
|
344
|
-
.card .card-header-left-collapse[data-bs-toggle=collapse].collapsed {
|
|
346
|
+
.card .card-header-left-collapse[data-bs-toggle="collapse"].collapsed {
|
|
345
347
|
border-radius: 0.35rem;
|
|
346
348
|
}
|
|
347
|
-
.card .card-header-left-collapse[data-bs-toggle=collapse].collapsed::before {
|
|
349
|
+
.card .card-header-left-collapse[data-bs-toggle="collapse"].collapsed::before {
|
|
348
350
|
content: "\f105";
|
|
349
351
|
}
|
|
352
|
+
|
|
353
|
+
.d-inline-maybe {
|
|
354
|
+
display: inline;
|
|
355
|
+
}
|
package/public/saltcorn.js
CHANGED
|
@@ -317,10 +317,11 @@ function ajaxSubmitForm(e) {
|
|
|
317
317
|
data: new FormData(form[0]),
|
|
318
318
|
processData: false,
|
|
319
319
|
contentType: false,
|
|
320
|
-
success: function () {
|
|
320
|
+
success: function (res) {
|
|
321
321
|
var no_reload = $("#scmodal").hasClass("no-submit-reload");
|
|
322
322
|
$("#scmodal").modal("hide");
|
|
323
323
|
if (!no_reload) location.reload();
|
|
324
|
+
else common_done(res);
|
|
324
325
|
},
|
|
325
326
|
error: function (request) {
|
|
326
327
|
var title = request.getResponseHeader("Page-Title");
|
|
@@ -444,12 +445,17 @@ async function fill_formula_btn_click(btn, k) {
|
|
|
444
445
|
}
|
|
445
446
|
}
|
|
446
447
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
448
|
+
try {
|
|
449
|
+
const val = new Function(
|
|
450
|
+
`{${Object.keys(rec).join(",")}}`,
|
|
451
|
+
"return " + formula
|
|
452
|
+
)(rec);
|
|
453
|
+
$(btn).closest(".input-group").find("input").val(val);
|
|
454
|
+
if (k) k();
|
|
455
|
+
} catch (e) {
|
|
456
|
+
notifyAlert({ type: "danger", text: `Error evaluating fill formula: ${e.message}` })
|
|
457
|
+
console.error(e)
|
|
458
|
+
}
|
|
453
459
|
}
|
|
454
460
|
|
|
455
461
|
/*
|
package/routes/actions.js
CHANGED
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
* @subcategory routes
|
|
6
6
|
*/
|
|
7
7
|
const Router = require("express-promise-router");
|
|
8
|
-
const {
|
|
8
|
+
const {
|
|
9
|
+
isAdmin,
|
|
10
|
+
error_catcher,
|
|
11
|
+
get_base_url,
|
|
12
|
+
addOnDoneRedirect,
|
|
13
|
+
} = require("./utils.js");
|
|
9
14
|
const { getState } = require("@saltcorn/data/db/state");
|
|
10
15
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
11
16
|
const { getTriggerList } = require("./common_lists");
|
|
@@ -149,6 +154,7 @@ const triggerForm = async (req, trigger) => {
|
|
|
149
154
|
id = trigger.id;
|
|
150
155
|
form_action = `/actions/edit/${id}`;
|
|
151
156
|
} else form_action = "/actions/new";
|
|
157
|
+
form_action = addOnDoneRedirect(form_action, req);
|
|
152
158
|
const hasChannel = Object.entries(getState().eventTypes)
|
|
153
159
|
.filter(([k, v]) => v.hasChannel)
|
|
154
160
|
.map(([k, v]) => k);
|
|
@@ -323,7 +329,7 @@ router.post(
|
|
|
323
329
|
const tr = await Trigger.create(form.values);
|
|
324
330
|
id = tr.id;
|
|
325
331
|
}
|
|
326
|
-
res.redirect(`/actions/configure/${id}
|
|
332
|
+
res.redirect(addOnDoneRedirect(`/actions/configure/${id}`, req));
|
|
327
333
|
}
|
|
328
334
|
})
|
|
329
335
|
);
|
|
@@ -389,7 +395,7 @@ router.get(
|
|
|
389
395
|
} else if (trigger.action === "blocks") {
|
|
390
396
|
const locale = req.getLocale();
|
|
391
397
|
const form = new Form({
|
|
392
|
-
action: `/actions/configure/${id}`,
|
|
398
|
+
action: addOnDoneRedirect(`/actions/configure/${id}`, req),
|
|
393
399
|
fields: action.configFields,
|
|
394
400
|
noSubmitButton: true,
|
|
395
401
|
id: "blocklyForm",
|
|
@@ -464,7 +470,7 @@ router.get(
|
|
|
464
470
|
const cfgFields = await getActionConfigFields(action, table);
|
|
465
471
|
// create form
|
|
466
472
|
const form = new Form({
|
|
467
|
-
action: `/actions/configure/${id}`,
|
|
473
|
+
action: addOnDoneRedirect(`/actions/configure/${id}`, req),
|
|
468
474
|
fields: cfgFields,
|
|
469
475
|
});
|
|
470
476
|
// populate form values
|
|
@@ -522,7 +528,11 @@ router.post(
|
|
|
522
528
|
} else {
|
|
523
529
|
await Trigger.update(trigger.id, { configuration: form.values });
|
|
524
530
|
req.flash("success", "Action configuration saved");
|
|
525
|
-
res.redirect(
|
|
531
|
+
res.redirect(
|
|
532
|
+
req.query.on_done_redirect
|
|
533
|
+
? `/${req.query.on_done_redirect}`
|
|
534
|
+
: "/actions/"
|
|
535
|
+
);
|
|
526
536
|
}
|
|
527
537
|
})
|
|
528
538
|
);
|
package/routes/admin.js
CHANGED
|
@@ -476,10 +476,10 @@ router.get(
|
|
|
476
476
|
li(
|
|
477
477
|
a({ href: "/admin/clear-all" }, req.__("Clear this application")),
|
|
478
478
|
" ",
|
|
479
|
-
|
|
479
|
+
req.__("(tick all boxes)")
|
|
480
480
|
),
|
|
481
481
|
li(
|
|
482
|
-
|
|
482
|
+
req.__("When prompted to create the first user, click the link to restore a backup")
|
|
483
483
|
),
|
|
484
484
|
li(req.__("Select the downloaded backup file"))
|
|
485
485
|
)
|
|
@@ -980,7 +980,7 @@ router.post(
|
|
|
980
980
|
res.attachment(fileName);
|
|
981
981
|
const file = fs.createReadStream(fileName);
|
|
982
982
|
file.on("end", function () {
|
|
983
|
-
fs.unlink(fileName, function () {
|
|
983
|
+
fs.unlink(fileName, function () {});
|
|
984
984
|
});
|
|
985
985
|
file.pipe(res);
|
|
986
986
|
})
|
|
@@ -1003,7 +1003,7 @@ router.post(
|
|
|
1003
1003
|
);
|
|
1004
1004
|
if (err) req.flash("error", err);
|
|
1005
1005
|
else req.flash("success", req.__("Successfully restored backup"));
|
|
1006
|
-
fs.unlink(newPath, function () {
|
|
1006
|
+
fs.unlink(newPath, function () {});
|
|
1007
1007
|
res.redirect(`/admin`);
|
|
1008
1008
|
})
|
|
1009
1009
|
);
|
|
@@ -1198,12 +1198,17 @@ router.get(
|
|
|
1198
1198
|
"/configuration-check",
|
|
1199
1199
|
isAdmin,
|
|
1200
1200
|
error_catcher(async (req, res) => {
|
|
1201
|
-
const { passes, errors, pass } = await runConfigurationCheck(req);
|
|
1201
|
+
const { passes, errors, pass, warnings } = await runConfigurationCheck(req);
|
|
1202
1202
|
const mkError = (err) =>
|
|
1203
1203
|
div(
|
|
1204
1204
|
{ class: "alert alert-danger", role: "alert" },
|
|
1205
1205
|
pre({ class: "mb-0" }, code(err))
|
|
1206
1206
|
);
|
|
1207
|
+
const mkWarning = (err) =>
|
|
1208
|
+
div(
|
|
1209
|
+
{ class: "alert alert-warning", role: "alert" },
|
|
1210
|
+
pre({ class: "mb-0" }, code(err))
|
|
1211
|
+
);
|
|
1207
1212
|
res.sendWrap(req.__(`Admin`), {
|
|
1208
1213
|
above: [
|
|
1209
1214
|
{
|
|
@@ -1227,7 +1232,8 @@ router.get(
|
|
|
1227
1232
|
req.__("No errors detected during configuration check")
|
|
1228
1233
|
)
|
|
1229
1234
|
)
|
|
1230
|
-
: errors.map(mkError)
|
|
1235
|
+
: errors.map(mkError),
|
|
1236
|
+
(warnings || []).map(mkWarning)
|
|
1231
1237
|
),
|
|
1232
1238
|
},
|
|
1233
1239
|
{
|
|
@@ -1269,11 +1275,10 @@ const buildDialogScript = () => {
|
|
|
1269
1275
|
|
|
1270
1276
|
function handleMessages() {
|
|
1271
1277
|
notifyAlert("This is still under development and might run longer.")
|
|
1272
|
-
${
|
|
1273
|
-
getState().getConfig("apple_team_id") &&
|
|
1278
|
+
${getState().getConfig("apple_team_id") &&
|
|
1274
1279
|
getState().getConfig("apple_team_id") !== "null"
|
|
1275
|
-
|
|
1276
|
-
|
|
1280
|
+
? ""
|
|
1281
|
+
: `
|
|
1277
1282
|
if ($("#iOSCheckboxId")[0].checked) {
|
|
1278
1283
|
notifyAlert(
|
|
1279
1284
|
"No 'Apple Team ID' is configured, I will try to build a project for the iOS simulator."
|
|
@@ -1547,6 +1552,12 @@ router.post(
|
|
|
1547
1552
|
}
|
|
1548
1553
|
if (appFile) spawnParams.push("-a", appFile);
|
|
1549
1554
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
1555
|
+
if (
|
|
1556
|
+
db.is_it_multi_tenant() &&
|
|
1557
|
+
db.getTenantSchema() !== db.connectObj.default_schema
|
|
1558
|
+
) {
|
|
1559
|
+
spawnParams.push("--tenantAppName", db.getTenantSchema());
|
|
1560
|
+
}
|
|
1550
1561
|
const child = spawn("saltcorn", spawnParams, {
|
|
1551
1562
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1552
1563
|
cwd: ".",
|