@saltcorn/server 1.1.2-beta.15 → 1.1.2-beta.17
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/package.json +9 -9
- package/public/saltcorn-common.js +45 -13
- package/public/saltcorn.js +1 -29
- package/routes/actions.js +3 -40
- package/routes/admin.js +6 -2
- package/routes/tables.js +5 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "1.1.2-beta.
|
|
3
|
+
"version": "1.1.2-beta.17",
|
|
4
4
|
"description": "Server app for Saltcorn, open-source no-code platform",
|
|
5
5
|
"homepage": "https://saltcorn.com",
|
|
6
6
|
"main": "index.js",
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@aws-sdk/client-s3": "^3.735.0",
|
|
10
10
|
"@dr.pogodin/csurf": "^1.14.1",
|
|
11
|
-
"@saltcorn/base-plugin": "1.1.2-beta.
|
|
12
|
-
"@saltcorn/builder": "1.1.2-beta.
|
|
13
|
-
"@saltcorn/data": "1.1.2-beta.
|
|
14
|
-
"@saltcorn/admin-models": "1.1.2-beta.
|
|
15
|
-
"@saltcorn/filemanager": "1.1.2-beta.
|
|
16
|
-
"@saltcorn/markup": "1.1.2-beta.
|
|
17
|
-
"@saltcorn/plugins-loader": "1.1.2-beta.
|
|
18
|
-
"@saltcorn/sbadmin2": "1.1.2-beta.
|
|
11
|
+
"@saltcorn/base-plugin": "1.1.2-beta.17",
|
|
12
|
+
"@saltcorn/builder": "1.1.2-beta.17",
|
|
13
|
+
"@saltcorn/data": "1.1.2-beta.17",
|
|
14
|
+
"@saltcorn/admin-models": "1.1.2-beta.17",
|
|
15
|
+
"@saltcorn/filemanager": "1.1.2-beta.17",
|
|
16
|
+
"@saltcorn/markup": "1.1.2-beta.17",
|
|
17
|
+
"@saltcorn/plugins-loader": "1.1.2-beta.17",
|
|
18
|
+
"@saltcorn/sbadmin2": "1.1.2-beta.17",
|
|
19
19
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
20
20
|
"@socket.io/sticky": "^1.0.1",
|
|
21
21
|
"adm-zip": "0.5.16",
|
|
@@ -318,11 +318,11 @@ function apply_showif() {
|
|
|
318
318
|
a.label === dynwhere.neutral_label
|
|
319
319
|
? -1
|
|
320
320
|
: b.label === dynwhere.neutral_label
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
321
|
+
? 1
|
|
322
|
+
: (a.label?.toLowerCase?.() || a.label) >
|
|
323
|
+
(b.label?.toLowerCase?.() || b.label)
|
|
324
|
+
? 1
|
|
325
|
+
: -1
|
|
326
326
|
);
|
|
327
327
|
if (!dynwhere.required)
|
|
328
328
|
toAppend.unshift({ label: dynwhere.neutral_label || "", value: "" });
|
|
@@ -1120,8 +1120,8 @@ function initialize_page() {
|
|
|
1120
1120
|
type === "Integer" || type === "Float"
|
|
1121
1121
|
? "number"
|
|
1122
1122
|
: type === "Bool"
|
|
1123
|
-
|
|
1124
|
-
|
|
1123
|
+
? "checkbox"
|
|
1124
|
+
: "text"
|
|
1125
1125
|
}" ${
|
|
1126
1126
|
type === "Float"
|
|
1127
1127
|
? `step="${
|
|
@@ -1490,10 +1490,10 @@ function buildToast(txt, type, spin) {
|
|
|
1490
1490
|
realtype === "success"
|
|
1491
1491
|
? "fa-check-circle"
|
|
1492
1492
|
: realtype === "danger"
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1493
|
+
? "fa-times-circle"
|
|
1494
|
+
: realtype === "warning"
|
|
1495
|
+
? "fa-exclamation-triangle"
|
|
1496
|
+
: "";
|
|
1497
1497
|
const isNode = getIsNode();
|
|
1498
1498
|
const rndid = `tab${Math.floor(Math.random() * 16777215).toString(16)}`;
|
|
1499
1499
|
return {
|
|
@@ -1587,7 +1587,11 @@ function press_store_button(clicked, keepOld, disable) {
|
|
|
1587
1587
|
$(btn).data("old-text", oldText);
|
|
1588
1588
|
}
|
|
1589
1589
|
const width = $(btn).width();
|
|
1590
|
-
$(btn).
|
|
1590
|
+
const height = $(btn).height();
|
|
1591
|
+
$(btn)
|
|
1592
|
+
.html('<i class="fas fa-spinner fa-spin"></i>')
|
|
1593
|
+
.width(width)
|
|
1594
|
+
.height(height);
|
|
1591
1595
|
setTimeout(() => {
|
|
1592
1596
|
$(btn).prop("disabled", true);
|
|
1593
1597
|
}, 50);
|
|
@@ -1597,7 +1601,7 @@ function restore_old_button(btnId) {
|
|
|
1597
1601
|
const btn = btnId instanceof jQuery ? btnId : $(`#${btnId}`);
|
|
1598
1602
|
const oldText = $(btn).data("old-text");
|
|
1599
1603
|
btn.html(oldText);
|
|
1600
|
-
btn.css({ width: "" }).prop("disabled", false);
|
|
1604
|
+
btn.css({ width: "", height: "" }).prop("disabled", false);
|
|
1601
1605
|
btn.removeData("old-text");
|
|
1602
1606
|
}
|
|
1603
1607
|
|
|
@@ -2174,6 +2178,34 @@ function restrict_options(selector, restriction) {
|
|
|
2174
2178
|
});
|
|
2175
2179
|
}
|
|
2176
2180
|
|
|
2181
|
+
function handle_identical_fields(event) {
|
|
2182
|
+
let form = null;
|
|
2183
|
+
if (event.currentTarget.tagName === "FORM") form = event.currentTarget;
|
|
2184
|
+
else form = $(event.currentTarget).closest("form")[0];
|
|
2185
|
+
if (!form) {
|
|
2186
|
+
console.warn("No form found");
|
|
2187
|
+
} else {
|
|
2188
|
+
const name = event.target.name;
|
|
2189
|
+
const newValue = event.target.value;
|
|
2190
|
+
const tagName = event.target.tagName;
|
|
2191
|
+
const isRadio = event.target.type === "radio";
|
|
2192
|
+
if (tagName === "SELECT" || isRadio) {
|
|
2193
|
+
form.querySelectorAll(`select[name="${name}"]`).forEach((select) => {
|
|
2194
|
+
$(select).val(newValue); //.trigger("change");
|
|
2195
|
+
});
|
|
2196
|
+
form
|
|
2197
|
+
.querySelectorAll(`input[type="radio"][name="${name}"]`)
|
|
2198
|
+
.forEach((input) => {
|
|
2199
|
+
input.checked = input.value === newValue;
|
|
2200
|
+
});
|
|
2201
|
+
} else if (tagName === "INPUT") {
|
|
2202
|
+
form.querySelectorAll(`input[name="${name}"]`).forEach((input) => {
|
|
2203
|
+
input.value = newValue;
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2177
2209
|
const observer = new IntersectionObserver(
|
|
2178
2210
|
(entries, observer) => {
|
|
2179
2211
|
entries.forEach((entry) => {
|
package/public/saltcorn.js
CHANGED
|
@@ -693,7 +693,7 @@ function ajax_post(url, args) {
|
|
|
693
693
|
},
|
|
694
694
|
...(args || {}),
|
|
695
695
|
})
|
|
696
|
-
.done(ajax_done)
|
|
696
|
+
.done((res) => ajax_done(res))
|
|
697
697
|
.fail((e, ...more) => {
|
|
698
698
|
if (!checkNetworkError(e))
|
|
699
699
|
return ajax_done(
|
|
@@ -1322,34 +1322,6 @@ function check_delete_unsaved(tablename, script_tag) {
|
|
|
1322
1322
|
}
|
|
1323
1323
|
}
|
|
1324
1324
|
|
|
1325
|
-
function handle_identical_fields(event) {
|
|
1326
|
-
let form = null;
|
|
1327
|
-
if (event.currentTarget.tagName === "FORM") form = event.currentTarget;
|
|
1328
|
-
else form = $(event.currentTarget).closest("form")[0];
|
|
1329
|
-
if (!form) {
|
|
1330
|
-
console.warn("No form found");
|
|
1331
|
-
} else {
|
|
1332
|
-
const name = event.target.name;
|
|
1333
|
-
const newValue = event.target.value;
|
|
1334
|
-
const tagName = event.target.tagName;
|
|
1335
|
-
const isRadio = event.target.type === "radio";
|
|
1336
|
-
if (tagName === "SELECT" || isRadio) {
|
|
1337
|
-
form.querySelectorAll(`select[name="${name}"]`).forEach((select) => {
|
|
1338
|
-
$(select).val(newValue); //.trigger("change");
|
|
1339
|
-
});
|
|
1340
|
-
form
|
|
1341
|
-
.querySelectorAll(`input[type="radio"][name="${name}"]`)
|
|
1342
|
-
.forEach((input) => {
|
|
1343
|
-
input.checked = input.value === newValue;
|
|
1344
|
-
});
|
|
1345
|
-
} else if (tagName === "INPUT") {
|
|
1346
|
-
form.querySelectorAll(`input[name="${name}"]`).forEach((input) => {
|
|
1347
|
-
input.value = newValue;
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
1325
|
(() => {
|
|
1354
1326
|
const e = document.querySelector("[data-sidebar-toggler]");
|
|
1355
1327
|
let closed = localStorage.getItem("sidebarClosed") === "true";
|
package/routes/actions.js
CHANGED
|
@@ -15,10 +15,6 @@ const {
|
|
|
15
15
|
const { ppVal, jsIdentifierValidator } = require("@saltcorn/data/utils");
|
|
16
16
|
const { getState } = require("@saltcorn/data/db/state");
|
|
17
17
|
const Trigger = require("@saltcorn/data/models/trigger");
|
|
18
|
-
const View = require("@saltcorn/data/models/view");
|
|
19
|
-
const {
|
|
20
|
-
getForm,
|
|
21
|
-
} = require("@saltcorn/data/base-plugin/viewtemplates/viewable_fields");
|
|
22
18
|
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
23
19
|
const { getTriggerList } = require("./common_lists");
|
|
24
20
|
const TagEntry = require("@saltcorn/data/models/tag_entry");
|
|
@@ -29,6 +25,9 @@ const Tag = require("@saltcorn/data/models/tag");
|
|
|
29
25
|
const db = require("@saltcorn/data/db");
|
|
30
26
|
const MarkdownIt = require("markdown-it"),
|
|
31
27
|
md = new MarkdownIt();
|
|
28
|
+
const {
|
|
29
|
+
getWorkflowStepUserForm,
|
|
30
|
+
} = require("@saltcorn/data/web-mobile-commons");
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
33
|
* @type {object}
|
|
@@ -1690,42 +1689,6 @@ router.post(
|
|
|
1690
1689
|
})
|
|
1691
1690
|
);
|
|
1692
1691
|
|
|
1693
|
-
const getWorkflowStepUserForm = async (run, trigger, step, req) => {
|
|
1694
|
-
if (step.action_name === "EditViewForm") {
|
|
1695
|
-
const view = View.findOne({ name: step.configuration.edit_view });
|
|
1696
|
-
const table = Table.findOne({ id: view.table_id });
|
|
1697
|
-
const form = await getForm(
|
|
1698
|
-
table,
|
|
1699
|
-
view.name,
|
|
1700
|
-
view.configuration.columns,
|
|
1701
|
-
view.configuration.layout,
|
|
1702
|
-
null,
|
|
1703
|
-
req
|
|
1704
|
-
);
|
|
1705
|
-
await form.fill_fkey_options(false, undefined, req?.user);
|
|
1706
|
-
form.action = `/actions/fill-workflow-form/${run.id}`;
|
|
1707
|
-
if (run.context[step.configuration.response_variable])
|
|
1708
|
-
Object.assign(
|
|
1709
|
-
form.values,
|
|
1710
|
-
run.context[step.configuration.response_variable]
|
|
1711
|
-
);
|
|
1712
|
-
|
|
1713
|
-
return form;
|
|
1714
|
-
}
|
|
1715
|
-
|
|
1716
|
-
let blurb = run.wait_info.output || step.configuration?.form_header || "";
|
|
1717
|
-
if (run.wait_info.markdown && run.wait_info.output) blurb = md.render(blurb);
|
|
1718
|
-
const form = new Form({
|
|
1719
|
-
action: `/actions/fill-workflow-form/${run.id}`,
|
|
1720
|
-
submitLabel: run.wait_info.output ? req.__("OK") : req.__("Submit"),
|
|
1721
|
-
onSubmit: "press_store_button(this)",
|
|
1722
|
-
blurb,
|
|
1723
|
-
formStyle: run.wait_info.output || req.xhr ? "vert" : undefined,
|
|
1724
|
-
fields: await run.userFormFields(step),
|
|
1725
|
-
});
|
|
1726
|
-
return form;
|
|
1727
|
-
};
|
|
1728
|
-
|
|
1729
1692
|
router.get(
|
|
1730
1693
|
"/fill-workflow-form/:id",
|
|
1731
1694
|
error_catcher(async (req, res) => {
|
package/routes/admin.js
CHANGED
|
@@ -236,6 +236,7 @@ admin_config_route({
|
|
|
236
236
|
id: "testemail",
|
|
237
237
|
href: "/admin/send-test-email",
|
|
238
238
|
class: "btn btn-primary",
|
|
239
|
+
onclick: "spin_action_link(this)",
|
|
239
240
|
},
|
|
240
241
|
req.__("Send test email")
|
|
241
242
|
),
|
|
@@ -262,12 +263,14 @@ router.get(
|
|
|
262
263
|
html: req.__("Hello from Saltcorn"),
|
|
263
264
|
};
|
|
264
265
|
try {
|
|
265
|
-
await getMailTransport().sendMail(email);
|
|
266
|
+
const sendres = await getMailTransport().sendMail(email);
|
|
267
|
+
getState().log(6, sendres);
|
|
266
268
|
req.flash(
|
|
267
269
|
"success",
|
|
268
270
|
req.__("Email sent to %s with no errors", req.user.email)
|
|
269
271
|
);
|
|
270
272
|
} catch (e) {
|
|
273
|
+
console.error(e);
|
|
271
274
|
req.flash("error", e.message);
|
|
272
275
|
}
|
|
273
276
|
|
|
@@ -384,6 +387,7 @@ router.get(
|
|
|
384
387
|
req.csrfToken(),
|
|
385
388
|
{
|
|
386
389
|
btnClass: "btn-outline-primary",
|
|
390
|
+
spinner: true,
|
|
387
391
|
}
|
|
388
392
|
)
|
|
389
393
|
),
|
|
@@ -837,7 +841,7 @@ const autoBackupForm = (req) => {
|
|
|
837
841
|
label: req.__("Backup now"),
|
|
838
842
|
id: "btnBackupNow",
|
|
839
843
|
class: "btn btn-outline-secondary",
|
|
840
|
-
onclick: "ajax_post('/admin/auto-backup-now')",
|
|
844
|
+
onclick: "ajax_post('/admin/auto-backup-now');press_store_button(this);",
|
|
841
845
|
},
|
|
842
846
|
],
|
|
843
847
|
fields: [
|
package/routes/tables.js
CHANGED
|
@@ -800,7 +800,9 @@ router.get(
|
|
|
800
800
|
let fieldCard;
|
|
801
801
|
const primaryKeys = fields.filter((f) => f.primary_key);
|
|
802
802
|
const nPrimaryKeys = primaryKeys.length;
|
|
803
|
-
const nonSerialPKS = primaryKeys.some(
|
|
803
|
+
const nonSerialPKS = primaryKeys.some(
|
|
804
|
+
(f) => f.attributes?.NonSerial && f.type?.name === "Integer"
|
|
805
|
+
);
|
|
804
806
|
|
|
805
807
|
if (fields.length === 0) {
|
|
806
808
|
fieldCard = [
|
|
@@ -864,11 +866,11 @@ router.get(
|
|
|
864
866
|
{ hover: true }
|
|
865
867
|
);
|
|
866
868
|
fieldCard = [
|
|
867
|
-
(nPrimaryKeys
|
|
869
|
+
(nPrimaryKeys !== 1 || nonSerialPKS) &&
|
|
868
870
|
div(
|
|
869
871
|
{ class: "alert alert-danger", role: "alert" },
|
|
870
872
|
i({ class: "fas fa-exclamation-triangle" }),
|
|
871
|
-
"This table has composite
|
|
873
|
+
"This table has composite, non-defaulted integer, or no primary keys, which are not supported in Saltcorn. A procedure to introduce a single autoincrementing primary key is available.",
|
|
872
874
|
post_btn(
|
|
873
875
|
`/table/repair-composite-primary/${table.id}`,
|
|
874
876
|
"Add autoincrementing primary key",
|