@saltcorn/server 0.9.5-beta.9 → 0.9.5
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/routes.js +54 -7
- package/help/Android App Signing.tmd +22 -0
- package/help/Provisioning Profile.tmd +60 -0
- package/help/xcodebuild.tmd +1 -0
- package/load_plugins.js +6 -0
- package/locales/en.json +27 -2
- package/locales/ru.json +1134 -1101
- package/package.json +9 -9
- package/public/saltcorn-common.js +174 -21
- package/public/saltcorn.css +4 -0
- package/public/saltcorn.js +81 -61
- package/restart_watcher.js +2 -0
- package/routes/actions.js +17 -1
- package/routes/admin.js +657 -77
- package/routes/api.js +4 -1
- package/routes/fields.js +107 -9
- package/routes/menu.js +12 -3
- package/routes/page.js +8 -2
- package/routes/pageedit.js +1 -1
- package/routes/plugins.js +266 -30
- package/routes/search.js +28 -2
- package/routes/tag_entries.js +3 -3
- package/routes/tenant.js +1 -0
- package/routes/utils.js +18 -2
- package/routes/view.js +15 -3
- package/s3storage.js +1 -0
- package/systemd.js +1 -1
- package/tests/page.test.js +11 -1
package/routes/admin.js
CHANGED
|
@@ -16,7 +16,7 @@ const {
|
|
|
16
16
|
const Table = require("@saltcorn/data/models/table");
|
|
17
17
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
18
18
|
const File = require("@saltcorn/data/models/file");
|
|
19
|
-
const { spawn } = require("child_process");
|
|
19
|
+
const { spawn, exec } = require("child_process");
|
|
20
20
|
const User = require("@saltcorn/data/models/user");
|
|
21
21
|
const path = require("path");
|
|
22
22
|
const { X509Certificate } = require("crypto");
|
|
@@ -284,9 +284,26 @@ router.get(
|
|
|
284
284
|
backupForm.values.auto_backup_destination = getState().getConfig(
|
|
285
285
|
"auto_backup_destination"
|
|
286
286
|
);
|
|
287
|
+
backupForm.values.auto_backup_tenants = getState().getConfig(
|
|
288
|
+
"auto_backup_tenants"
|
|
289
|
+
);
|
|
287
290
|
backupForm.values.auto_backup_directory = getState().getConfig(
|
|
288
291
|
"auto_backup_directory"
|
|
289
292
|
);
|
|
293
|
+
backupForm.values.auto_backup_retain_local_directory = getState().getConfig(
|
|
294
|
+
"auto_backup_retain_local_directory"
|
|
295
|
+
);
|
|
296
|
+
backupForm.values.auto_backup_username = getState().getConfig(
|
|
297
|
+
"auto_backup_username"
|
|
298
|
+
);
|
|
299
|
+
backupForm.values.auto_backup_server =
|
|
300
|
+
getState().getConfig("auto_backup_server");
|
|
301
|
+
backupForm.values.auto_backup_password = getState().getConfig(
|
|
302
|
+
"auto_backup_password"
|
|
303
|
+
);
|
|
304
|
+
backupForm.values.auto_backup_port =
|
|
305
|
+
getState().getConfig("auto_backup_port");
|
|
306
|
+
|
|
290
307
|
backupForm.values.auto_backup_expire_days = getState().getConfig(
|
|
291
308
|
"auto_backup_expire_days"
|
|
292
309
|
);
|
|
@@ -691,8 +708,10 @@ const backupFilePrefixForm = (req) =>
|
|
|
691
708
|
* @param {object} req
|
|
692
709
|
* @returns {Form} form
|
|
693
710
|
*/
|
|
694
|
-
const autoBackupForm = (req) =>
|
|
695
|
-
|
|
711
|
+
const autoBackupForm = (req) => {
|
|
712
|
+
const isRoot = db.getTenantSchema() === db.connectObj.default_schema;
|
|
713
|
+
|
|
714
|
+
return new Form({
|
|
696
715
|
action: "/admin/set-auto-backup",
|
|
697
716
|
onChange: `saveAndContinue(this);$('#btnBackupNow').prop('disabled', $('#inputauto_backup_frequency').val()==='Never');`,
|
|
698
717
|
noSubmitButton: true,
|
|
@@ -718,7 +737,47 @@ const autoBackupForm = (req) =>
|
|
|
718
737
|
name: "auto_backup_destination",
|
|
719
738
|
required: true,
|
|
720
739
|
showIf: { auto_backup_frequency: ["Daily", "Weekly"] },
|
|
721
|
-
attributes: {
|
|
740
|
+
attributes: {
|
|
741
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
742
|
+
options: ["Saltcorn files", "Local directory", "SFTP server"],
|
|
743
|
+
},
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
type: "String",
|
|
747
|
+
label: req.__("Server host"),
|
|
748
|
+
name: "auto_backup_server",
|
|
749
|
+
showIf: {
|
|
750
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
751
|
+
auto_backup_destination: "SFTP server",
|
|
752
|
+
},
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
type: "String",
|
|
756
|
+
label: req.__("Username"),
|
|
757
|
+
name: "auto_backup_username",
|
|
758
|
+
showIf: {
|
|
759
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
760
|
+
auto_backup_destination: "SFTP server",
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
type: "String",
|
|
765
|
+
label: req.__("Password"),
|
|
766
|
+
fieldview: "password",
|
|
767
|
+
name: "auto_backup_password",
|
|
768
|
+
showIf: {
|
|
769
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
770
|
+
auto_backup_destination: "SFTP server",
|
|
771
|
+
},
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
type: "Integer",
|
|
775
|
+
label: req.__("Port"),
|
|
776
|
+
name: "auto_backup_port",
|
|
777
|
+
showIf: {
|
|
778
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
779
|
+
auto_backup_destination: "SFTP server",
|
|
780
|
+
},
|
|
722
781
|
},
|
|
723
782
|
{
|
|
724
783
|
type: "String",
|
|
@@ -730,6 +789,19 @@ const autoBackupForm = (req) =>
|
|
|
730
789
|
//auto_backup_destination: "Local directory",
|
|
731
790
|
},
|
|
732
791
|
},
|
|
792
|
+
{
|
|
793
|
+
type: "String",
|
|
794
|
+
label: req.__("Retain local directory"),
|
|
795
|
+
name: "auto_backup_retain_local_directory",
|
|
796
|
+
sublabel: req.__(
|
|
797
|
+
"Retain a local backup copy in this directory (optional)"
|
|
798
|
+
),
|
|
799
|
+
showIf: {
|
|
800
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
801
|
+
auto_backup_destination: "SFTP server",
|
|
802
|
+
//auto_backup_destination: "Local directory",
|
|
803
|
+
},
|
|
804
|
+
},
|
|
733
805
|
{
|
|
734
806
|
type: "Integer",
|
|
735
807
|
label: req.__("Expiration in days"),
|
|
@@ -742,6 +814,19 @@ const autoBackupForm = (req) =>
|
|
|
742
814
|
auto_backup_destination: "Local directory",
|
|
743
815
|
},
|
|
744
816
|
},
|
|
817
|
+
...(isRoot
|
|
818
|
+
? [
|
|
819
|
+
{
|
|
820
|
+
type: "Bool",
|
|
821
|
+
label: req.__("All tenants"),
|
|
822
|
+
sublabel: req.__("Also backup all tenants"),
|
|
823
|
+
name: "auto_backup_tenants",
|
|
824
|
+
showIf: {
|
|
825
|
+
auto_backup_frequency: ["Daily", "Weekly"],
|
|
826
|
+
},
|
|
827
|
+
},
|
|
828
|
+
]
|
|
829
|
+
: []),
|
|
745
830
|
{
|
|
746
831
|
type: "Bool",
|
|
747
832
|
label: req.__("Include Event Logs"),
|
|
@@ -753,6 +838,7 @@ const autoBackupForm = (req) =>
|
|
|
753
838
|
},
|
|
754
839
|
],
|
|
755
840
|
});
|
|
841
|
+
};
|
|
756
842
|
|
|
757
843
|
/**
|
|
758
844
|
* Snapshot Form
|
|
@@ -1545,17 +1631,6 @@ const buildDialogScript = (cordovaBuilderAvailable) => {
|
|
|
1545
1631
|
|
|
1546
1632
|
function handleMessages() {
|
|
1547
1633
|
notifyAlert("Building the app, please wait.", true)
|
|
1548
|
-
${
|
|
1549
|
-
getState().getConfig("apple_team_id") &&
|
|
1550
|
-
getState().getConfig("apple_team_id") !== "null"
|
|
1551
|
-
? ""
|
|
1552
|
-
: `
|
|
1553
|
-
if ($("#iOSCheckboxId")[0].checked) {
|
|
1554
|
-
notifyAlert(
|
|
1555
|
-
"No 'Apple Team ID' is configured, I will try to build a project for the iOS simulator."
|
|
1556
|
-
);
|
|
1557
|
-
}`
|
|
1558
|
-
}
|
|
1559
1634
|
}
|
|
1560
1635
|
</script>`;
|
|
1561
1636
|
};
|
|
@@ -1570,6 +1645,33 @@ const imageAvailable = async () => {
|
|
|
1570
1645
|
}
|
|
1571
1646
|
};
|
|
1572
1647
|
|
|
1648
|
+
const checkXcodebuild = () => {
|
|
1649
|
+
return new Promise((resolve) => {
|
|
1650
|
+
exec("xcodebuild -version", (error, stdout, stderr) => {
|
|
1651
|
+
if (error) {
|
|
1652
|
+
resolve({ installed: false });
|
|
1653
|
+
} else {
|
|
1654
|
+
const tokens = stdout.split(" ");
|
|
1655
|
+
resolve({
|
|
1656
|
+
installed: true,
|
|
1657
|
+
version: tokens.length > 1 ? tokens[1] : undefined,
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
});
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
const versionMarker = (version) => {
|
|
1665
|
+
const tokens = version.split(".");
|
|
1666
|
+
const majVers = parseInt(tokens[0]);
|
|
1667
|
+
return i({
|
|
1668
|
+
id: "versionMarkerId",
|
|
1669
|
+
class: `fas ${
|
|
1670
|
+
majVers >= 11 ? "fa-check text-success" : "fa-times text-danger"
|
|
1671
|
+
}`,
|
|
1672
|
+
});
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1573
1675
|
/**
|
|
1574
1676
|
* Build mobile app
|
|
1575
1677
|
*/
|
|
@@ -1583,6 +1685,8 @@ router.get(
|
|
|
1583
1685
|
const images = (await File.find({ mime_super: "image" })).filter((image) =>
|
|
1584
1686
|
image.filename?.endsWith(".png")
|
|
1585
1687
|
);
|
|
1688
|
+
const keystoreFiles = await File.find({ folder: "keystore_files" });
|
|
1689
|
+
const provisioningFiles = await File.find({ folder: "provisioning_files" });
|
|
1586
1690
|
const withSyncInfo = await Table.find({ has_sync_info: true });
|
|
1587
1691
|
const plugins = (await Plugin.find()).filter(
|
|
1588
1692
|
(plugin) => ["base", "sbadmin2"].indexOf(plugin.name) < 0
|
|
@@ -1590,6 +1694,9 @@ router.get(
|
|
|
1590
1694
|
const builderSettings =
|
|
1591
1695
|
getState().getConfig("mobile_builder_settings") || {};
|
|
1592
1696
|
const dockerAvailable = await imageAvailable();
|
|
1697
|
+
const xcodeCheckRes = await checkXcodebuild();
|
|
1698
|
+
const xcodebuildAvailable = xcodeCheckRes.installed;
|
|
1699
|
+
const xcodebuildVersion = xcodeCheckRes.version;
|
|
1593
1700
|
send_admin_page({
|
|
1594
1701
|
res,
|
|
1595
1702
|
req,
|
|
@@ -1858,6 +1965,28 @@ router.get(
|
|
|
1858
1965
|
})
|
|
1859
1966
|
)
|
|
1860
1967
|
),
|
|
1968
|
+
// app id
|
|
1969
|
+
div(
|
|
1970
|
+
{ class: "row pb-2" },
|
|
1971
|
+
div(
|
|
1972
|
+
{ class: "col-sm-8" },
|
|
1973
|
+
label(
|
|
1974
|
+
{
|
|
1975
|
+
for: "appIdInputId",
|
|
1976
|
+
class: "form-label fw-bold",
|
|
1977
|
+
},
|
|
1978
|
+
req.__("App ID")
|
|
1979
|
+
),
|
|
1980
|
+
input({
|
|
1981
|
+
type: "text",
|
|
1982
|
+
class: "form-control",
|
|
1983
|
+
name: "appId",
|
|
1984
|
+
id: "appIdInputId",
|
|
1985
|
+
placeholder: "com.saltcorn.app",
|
|
1986
|
+
value: builderSettings.appId || "",
|
|
1987
|
+
})
|
|
1988
|
+
)
|
|
1989
|
+
),
|
|
1861
1990
|
// app version
|
|
1862
1991
|
div(
|
|
1863
1992
|
{ class: "row pb-2" },
|
|
@@ -2150,19 +2279,20 @@ router.get(
|
|
|
2150
2279
|
class: "form-control form-select",
|
|
2151
2280
|
multiple: true,
|
|
2152
2281
|
},
|
|
2153
|
-
plugins
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
value: plugin.name,
|
|
2157
|
-
label: plugin.name,
|
|
2158
|
-
hidden:
|
|
2282
|
+
plugins
|
|
2283
|
+
.filter(
|
|
2284
|
+
(plugin) =>
|
|
2159
2285
|
builderSettings.excludedPlugins?.indexOf(
|
|
2160
2286
|
plugin.name
|
|
2161
2287
|
) >= 0
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2288
|
+
)
|
|
2289
|
+
.map((plugin) =>
|
|
2290
|
+
option({
|
|
2291
|
+
id: `${plugin.name}_excluded_opt`,
|
|
2292
|
+
value: plugin.name,
|
|
2293
|
+
label: plugin.name,
|
|
2294
|
+
})
|
|
2295
|
+
)
|
|
2166
2296
|
)
|
|
2167
2297
|
),
|
|
2168
2298
|
div(
|
|
@@ -2200,71 +2330,350 @@ router.get(
|
|
|
2200
2330
|
class: "form-control form-select",
|
|
2201
2331
|
multiple: true,
|
|
2202
2332
|
},
|
|
2203
|
-
plugins
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
hidden:
|
|
2209
|
-
builderSettings.excludedPlugins?.indexOf(
|
|
2333
|
+
plugins
|
|
2334
|
+
.filter(
|
|
2335
|
+
(plugin) =>
|
|
2336
|
+
!builderSettings.excludedPlugins ||
|
|
2337
|
+
builderSettings.excludedPlugins.indexOf(
|
|
2210
2338
|
plugin.name
|
|
2211
|
-
)
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2339
|
+
) < 0
|
|
2340
|
+
)
|
|
2341
|
+
.map((plugin) =>
|
|
2342
|
+
option({
|
|
2343
|
+
id: `${plugin.name}_included_opt`,
|
|
2344
|
+
value: plugin.name,
|
|
2345
|
+
label: plugin.name,
|
|
2346
|
+
})
|
|
2347
|
+
)
|
|
2216
2348
|
)
|
|
2217
2349
|
)
|
|
2218
2350
|
)
|
|
2219
2351
|
)
|
|
2220
2352
|
),
|
|
2353
|
+
// build type
|
|
2221
2354
|
div(
|
|
2222
|
-
{ class: "row pb-3 pt-
|
|
2355
|
+
{ class: "row pb-3 pt-2" },
|
|
2223
2356
|
div(
|
|
2357
|
+
{ class: "col-sm-8" },
|
|
2224
2358
|
label(
|
|
2225
|
-
{
|
|
2226
|
-
|
|
2227
|
-
|
|
2359
|
+
{
|
|
2360
|
+
for: "splashPageInputId",
|
|
2361
|
+
class: "form-label fw-bold",
|
|
2362
|
+
},
|
|
2363
|
+
req.__("Build type")
|
|
2364
|
+
),
|
|
2365
|
+
|
|
2366
|
+
div(
|
|
2367
|
+
{ class: "form-check" },
|
|
2368
|
+
input({
|
|
2369
|
+
type: "radio",
|
|
2370
|
+
id: "debugBuildTypeId",
|
|
2371
|
+
class: "form-check-input me-2",
|
|
2372
|
+
name: "buildType",
|
|
2373
|
+
value: "debug",
|
|
2374
|
+
checked: builderSettings.buildType === "debug",
|
|
2375
|
+
}),
|
|
2376
|
+
label(
|
|
2377
|
+
{
|
|
2378
|
+
for: "debugBuildTypeId",
|
|
2379
|
+
class: "form-label",
|
|
2380
|
+
},
|
|
2381
|
+
req.__("debug")
|
|
2382
|
+
)
|
|
2383
|
+
),
|
|
2384
|
+
div(
|
|
2385
|
+
{ class: "form-check" },
|
|
2386
|
+
input({
|
|
2387
|
+
type: "radio",
|
|
2388
|
+
id: "releaseBuildTypeId",
|
|
2389
|
+
class: "form-check-input me-2",
|
|
2390
|
+
name: "buildType",
|
|
2391
|
+
value: "release",
|
|
2392
|
+
checked:
|
|
2393
|
+
builderSettings.buildType === "release" ||
|
|
2394
|
+
!builderSettings.buildType,
|
|
2395
|
+
}),
|
|
2396
|
+
label(
|
|
2397
|
+
{
|
|
2398
|
+
for: "releaseBuildTypeId",
|
|
2399
|
+
class: "form-label",
|
|
2400
|
+
},
|
|
2401
|
+
req.__("release")
|
|
2402
|
+
)
|
|
2403
|
+
)
|
|
2404
|
+
)
|
|
2405
|
+
),
|
|
2406
|
+
div(
|
|
2407
|
+
{ class: "mt-3 mb-3" },
|
|
2408
|
+
p({ class: "h3 ps-3" }, "Android configuration"),
|
|
2409
|
+
div(
|
|
2410
|
+
{ class: "form-group border border-2 p-3 rounded" },
|
|
2411
|
+
|
|
2412
|
+
div(
|
|
2413
|
+
{ class: "row pb-3 pt-2" },
|
|
2414
|
+
div(
|
|
2415
|
+
label(
|
|
2416
|
+
{ class: "form-label fw-bold" },
|
|
2417
|
+
req.__("Cordova builder") +
|
|
2418
|
+
a(
|
|
2419
|
+
{
|
|
2420
|
+
href: "javascript:ajax_modal('/admin/help/Cordova Builder?')",
|
|
2421
|
+
},
|
|
2422
|
+
i({ class: "fas fa-question-circle ps-1" })
|
|
2423
|
+
)
|
|
2424
|
+
)
|
|
2425
|
+
),
|
|
2426
|
+
div(
|
|
2427
|
+
{ class: "col-sm-4" },
|
|
2428
|
+
div(
|
|
2429
|
+
{
|
|
2430
|
+
id: "dockerBuilderStatusId",
|
|
2431
|
+
class: "",
|
|
2432
|
+
},
|
|
2433
|
+
dockerAvailable
|
|
2434
|
+
? span(
|
|
2435
|
+
req.__("installed"),
|
|
2436
|
+
i({ class: "ps-2 fas fa-check text-success" })
|
|
2437
|
+
)
|
|
2438
|
+
: span(
|
|
2439
|
+
req.__("not available"),
|
|
2440
|
+
i({ class: "ps-2 fas fa-times text-danger" })
|
|
2441
|
+
)
|
|
2442
|
+
)
|
|
2443
|
+
),
|
|
2444
|
+
div(
|
|
2445
|
+
{ class: "col-sm-4" },
|
|
2446
|
+
button(
|
|
2228
2447
|
{
|
|
2229
|
-
|
|
2448
|
+
id: "pullCordovaBtnId",
|
|
2449
|
+
type: "button",
|
|
2450
|
+
onClick: `pull_cordova_builder(this);`,
|
|
2451
|
+
class: "btn btn-warning",
|
|
2230
2452
|
},
|
|
2231
|
-
|
|
2453
|
+
req.__("pull")
|
|
2454
|
+
),
|
|
2455
|
+
span(
|
|
2456
|
+
{
|
|
2457
|
+
role: "button",
|
|
2458
|
+
onClick: "check_cordova_builder()",
|
|
2459
|
+
},
|
|
2460
|
+
span({ class: "ps-3" }, req.__("refresh")),
|
|
2461
|
+
i({ class: "ps-2 fas fa-undo" })
|
|
2232
2462
|
)
|
|
2463
|
+
)
|
|
2464
|
+
),
|
|
2465
|
+
// keystore file
|
|
2466
|
+
div(
|
|
2467
|
+
{ class: "row pb-3" },
|
|
2468
|
+
div(
|
|
2469
|
+
{ class: "col-sm-8" },
|
|
2470
|
+
label(
|
|
2471
|
+
{
|
|
2472
|
+
for: "keystoreInputId",
|
|
2473
|
+
class: "form-label fw-bold",
|
|
2474
|
+
},
|
|
2475
|
+
req.__("Keystore File"),
|
|
2476
|
+
a(
|
|
2477
|
+
{
|
|
2478
|
+
href: "javascript:ajax_modal('/admin/help/Android App Signing?')",
|
|
2479
|
+
},
|
|
2480
|
+
i({ class: "fas fa-question-circle ps-1" })
|
|
2481
|
+
)
|
|
2482
|
+
),
|
|
2483
|
+
select(
|
|
2484
|
+
{
|
|
2485
|
+
class: "form-select",
|
|
2486
|
+
name: "keystoreFile",
|
|
2487
|
+
id: "keystoreInputId",
|
|
2488
|
+
},
|
|
2489
|
+
[
|
|
2490
|
+
option({ value: "" }, ""),
|
|
2491
|
+
...keystoreFiles.map((file) =>
|
|
2492
|
+
option(
|
|
2493
|
+
{
|
|
2494
|
+
value: file.location,
|
|
2495
|
+
selected:
|
|
2496
|
+
builderSettings.keystoreFile ===
|
|
2497
|
+
file.location,
|
|
2498
|
+
},
|
|
2499
|
+
file.filename
|
|
2500
|
+
)
|
|
2501
|
+
),
|
|
2502
|
+
].join("")
|
|
2503
|
+
)
|
|
2504
|
+
)
|
|
2505
|
+
),
|
|
2506
|
+
// keystore alias
|
|
2507
|
+
div(
|
|
2508
|
+
{ class: "row pb-2" },
|
|
2509
|
+
div(
|
|
2510
|
+
{ class: "col-sm-8" },
|
|
2511
|
+
label(
|
|
2512
|
+
{
|
|
2513
|
+
for: "keystoreAliasInputId",
|
|
2514
|
+
class: "form-label fw-bold",
|
|
2515
|
+
},
|
|
2516
|
+
req.__("Keystore Alias")
|
|
2517
|
+
),
|
|
2518
|
+
input({
|
|
2519
|
+
type: "text",
|
|
2520
|
+
class: "form-control",
|
|
2521
|
+
name: "keystoreAlias",
|
|
2522
|
+
id: "keystoreAliasInputId",
|
|
2523
|
+
value: builderSettings.keystoreAlias || "",
|
|
2524
|
+
placeholder: "",
|
|
2525
|
+
})
|
|
2526
|
+
)
|
|
2527
|
+
),
|
|
2528
|
+
// keystore password
|
|
2529
|
+
div(
|
|
2530
|
+
{ class: "row pb-2" },
|
|
2531
|
+
div(
|
|
2532
|
+
{ class: "col-sm-8" },
|
|
2533
|
+
label(
|
|
2534
|
+
{
|
|
2535
|
+
for: "keystorePasswordInputId",
|
|
2536
|
+
class: "form-label fw-bold",
|
|
2537
|
+
},
|
|
2538
|
+
req.__("Keystore Password")
|
|
2539
|
+
),
|
|
2540
|
+
input({
|
|
2541
|
+
type: "password",
|
|
2542
|
+
class: "form-control",
|
|
2543
|
+
name: "keystorePassword",
|
|
2544
|
+
id: "keystorePasswordInputId",
|
|
2545
|
+
value: "",
|
|
2546
|
+
placeholder: "",
|
|
2547
|
+
})
|
|
2548
|
+
)
|
|
2233
2549
|
)
|
|
2234
|
-
)
|
|
2550
|
+
)
|
|
2551
|
+
),
|
|
2552
|
+
div(
|
|
2553
|
+
{ class: "mt-3" },
|
|
2554
|
+
p({ class: "h3 ps-3 mt-3" }, "iOS Configuration"),
|
|
2235
2555
|
div(
|
|
2236
|
-
{ class: "
|
|
2556
|
+
{ class: "form-group border border-2 p-3 rounded" },
|
|
2237
2557
|
div(
|
|
2238
|
-
{
|
|
2239
|
-
|
|
2240
|
-
class: "",
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
req.__("
|
|
2245
|
-
|
|
2558
|
+
{ class: "mb-3" },
|
|
2559
|
+
div(
|
|
2560
|
+
{ class: "row pb-3 pt-2" },
|
|
2561
|
+
div(
|
|
2562
|
+
label(
|
|
2563
|
+
{ class: "form-label fw-bold" },
|
|
2564
|
+
req.__("xcodebuild") +
|
|
2565
|
+
a(
|
|
2566
|
+
{
|
|
2567
|
+
href: "javascript:ajax_modal('/admin/help/xcodebuild?')",
|
|
2568
|
+
},
|
|
2569
|
+
i({ class: "fas fa-question-circle ps-1" })
|
|
2570
|
+
)
|
|
2246
2571
|
)
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2572
|
+
),
|
|
2573
|
+
div(
|
|
2574
|
+
{ class: "col-sm-4" },
|
|
2575
|
+
div(
|
|
2576
|
+
{
|
|
2577
|
+
id: "xcodebuildStatusId",
|
|
2578
|
+
class: "",
|
|
2579
|
+
},
|
|
2580
|
+
xcodebuildAvailable
|
|
2581
|
+
? span(
|
|
2582
|
+
req.__("installed"),
|
|
2583
|
+
i({
|
|
2584
|
+
class: "ps-2 fas fa-check text-success",
|
|
2585
|
+
})
|
|
2586
|
+
)
|
|
2587
|
+
: span(
|
|
2588
|
+
req.__("not available"),
|
|
2589
|
+
i({
|
|
2590
|
+
class: "ps-2 fas fa-times text-danger",
|
|
2591
|
+
})
|
|
2592
|
+
)
|
|
2250
2593
|
)
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2594
|
+
),
|
|
2595
|
+
div(
|
|
2596
|
+
{ class: "col-sm-4" },
|
|
2597
|
+
// not sure if we should provide this
|
|
2598
|
+
// button(
|
|
2599
|
+
// {
|
|
2600
|
+
// id: "installXCodeBtnId",
|
|
2601
|
+
// type: "button",
|
|
2602
|
+
// onClick: `install_xcode(this);`,
|
|
2603
|
+
// class: "btn btn-warning",
|
|
2604
|
+
// },
|
|
2605
|
+
// req.__("install")
|
|
2606
|
+
// ),
|
|
2607
|
+
span(
|
|
2608
|
+
{
|
|
2609
|
+
role: "button",
|
|
2610
|
+
onClick: "check_xcodebuild()",
|
|
2611
|
+
},
|
|
2612
|
+
span({ class: "ps-3" }, req.__("refresh")),
|
|
2613
|
+
i({ class: "ps-2 fas fa-undo" })
|
|
2614
|
+
)
|
|
2615
|
+
)
|
|
2616
|
+
),
|
|
2617
|
+
div(
|
|
2618
|
+
{
|
|
2619
|
+
class: `row mb-3 pb-3 ${
|
|
2620
|
+
xcodebuildAvailable ? "" : "d-none"
|
|
2621
|
+
}`,
|
|
2622
|
+
id: "xcodebuildVersionBoxId",
|
|
2623
|
+
},
|
|
2624
|
+
div(
|
|
2625
|
+
{ class: "col-sm-4" },
|
|
2626
|
+
span(
|
|
2627
|
+
req.__("Version") +
|
|
2628
|
+
span(
|
|
2629
|
+
{ id: "xcodebuildVersionId", class: "pe-2" },
|
|
2630
|
+
`: ${xcodebuildVersion || "unknown"}`
|
|
2631
|
+
),
|
|
2632
|
+
versionMarker(xcodebuildVersion || "0")
|
|
2633
|
+
)
|
|
2634
|
+
)
|
|
2635
|
+
)
|
|
2263
2636
|
),
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2637
|
+
// provisioning profile file
|
|
2638
|
+
div(
|
|
2639
|
+
{ class: "row pb-3" },
|
|
2640
|
+
div(
|
|
2641
|
+
{ class: "col-sm-8" },
|
|
2642
|
+
label(
|
|
2643
|
+
{
|
|
2644
|
+
for: "provisioningProfileInputId",
|
|
2645
|
+
class: "form-label fw-bold",
|
|
2646
|
+
},
|
|
2647
|
+
req.__("Provisioning Profile"),
|
|
2648
|
+
a(
|
|
2649
|
+
{
|
|
2650
|
+
href: "javascript:ajax_modal('/admin/help/Provisioning Profile?')",
|
|
2651
|
+
},
|
|
2652
|
+
i({ class: "fas fa-question-circle ps-1" })
|
|
2653
|
+
)
|
|
2654
|
+
),
|
|
2655
|
+
select(
|
|
2656
|
+
{
|
|
2657
|
+
class: "form-select",
|
|
2658
|
+
name: "provisioningProfile",
|
|
2659
|
+
id: "provisioningProfileInputId",
|
|
2660
|
+
},
|
|
2661
|
+
[
|
|
2662
|
+
option({ value: "" }, ""),
|
|
2663
|
+
...provisioningFiles.map((file) =>
|
|
2664
|
+
option(
|
|
2665
|
+
{
|
|
2666
|
+
value: file.location,
|
|
2667
|
+
selected:
|
|
2668
|
+
builderSettings.provisioningProfile ===
|
|
2669
|
+
file.location,
|
|
2670
|
+
},
|
|
2671
|
+
file.filename
|
|
2672
|
+
)
|
|
2673
|
+
),
|
|
2674
|
+
].join("")
|
|
2675
|
+
)
|
|
2676
|
+
)
|
|
2268
2677
|
)
|
|
2269
2678
|
)
|
|
2270
2679
|
)
|
|
@@ -2369,6 +2778,7 @@ router.post(
|
|
|
2369
2778
|
iOSPlatform,
|
|
2370
2779
|
useDocker,
|
|
2371
2780
|
appName,
|
|
2781
|
+
appId,
|
|
2372
2782
|
appVersion,
|
|
2373
2783
|
appIcon,
|
|
2374
2784
|
serverURL,
|
|
@@ -2377,6 +2787,11 @@ router.post(
|
|
|
2377
2787
|
allowOfflineMode,
|
|
2378
2788
|
synchedTables,
|
|
2379
2789
|
includedPlugins,
|
|
2790
|
+
provisioningProfile,
|
|
2791
|
+
buildType,
|
|
2792
|
+
keystoreFile,
|
|
2793
|
+
keystoreAlias,
|
|
2794
|
+
keystorePassword,
|
|
2380
2795
|
} = req.body;
|
|
2381
2796
|
if (!includedPlugins) includedPlugins = [];
|
|
2382
2797
|
if (!synchedTables) synchedTables = [];
|
|
@@ -2398,6 +2813,20 @@ router.post(
|
|
|
2398
2813
|
error: req.__("Please enter a valid server URL."),
|
|
2399
2814
|
});
|
|
2400
2815
|
}
|
|
2816
|
+
if (iOSPlatform && !provisioningProfile) {
|
|
2817
|
+
return res.json({
|
|
2818
|
+
error: req.__(
|
|
2819
|
+
"Please provide a Provisioning Profile for the iOS build."
|
|
2820
|
+
),
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2823
|
+
if (keystoreFile && (!keystoreAlias || !keystorePassword)) {
|
|
2824
|
+
return res.json({
|
|
2825
|
+
error: req.__(
|
|
2826
|
+
"Please provide the keystore alias and password for the android build."
|
|
2827
|
+
),
|
|
2828
|
+
});
|
|
2829
|
+
}
|
|
2401
2830
|
const outDirName = `build_${new Date().valueOf()}`;
|
|
2402
2831
|
const rootFolder = await File.rootFolder();
|
|
2403
2832
|
const buildDir = path.join(rootFolder.location, "mobile_app", outDirName);
|
|
@@ -2418,13 +2847,15 @@ router.post(
|
|
|
2418
2847
|
if (useDocker) spawnParams.push("-d");
|
|
2419
2848
|
if (androidPlatform) spawnParams.push("-p", "android");
|
|
2420
2849
|
if (iOSPlatform) {
|
|
2421
|
-
spawnParams.push(
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2850
|
+
spawnParams.push(
|
|
2851
|
+
"-p",
|
|
2852
|
+
"ios",
|
|
2853
|
+
"--provisioningProfile",
|
|
2854
|
+
provisioningProfile
|
|
2855
|
+
);
|
|
2426
2856
|
}
|
|
2427
2857
|
if (appName) spawnParams.push("--appName", appName);
|
|
2858
|
+
if (appId) spawnParams.push("--appId", appId);
|
|
2428
2859
|
if (appVersion) spawnParams.push("--appVersion", appVersion);
|
|
2429
2860
|
if (appIcon) spawnParams.push("--appIcon", appIcon);
|
|
2430
2861
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
@@ -2451,6 +2882,13 @@ router.post(
|
|
|
2451
2882
|
includedPlugins.indexOf(plugin.name) < 0
|
|
2452
2883
|
)
|
|
2453
2884
|
.map((plugin) => plugin.name);
|
|
2885
|
+
|
|
2886
|
+
if (buildType) spawnParams.push("--buildType", buildType);
|
|
2887
|
+
if (keystoreFile) spawnParams.push("--androidKeystore", keystoreFile);
|
|
2888
|
+
if (keystoreAlias)
|
|
2889
|
+
spawnParams.push("--androidKeyStoreAlias", keystoreAlias);
|
|
2890
|
+
if (keystorePassword)
|
|
2891
|
+
spawnParams.push("--androidKeystorePassword", keystorePassword);
|
|
2454
2892
|
await getState().setConfig("mobile_builder_settings", {
|
|
2455
2893
|
entryPoint,
|
|
2456
2894
|
entryPointType,
|
|
@@ -2458,6 +2896,7 @@ router.post(
|
|
|
2458
2896
|
iOSPlatform,
|
|
2459
2897
|
useDocker,
|
|
2460
2898
|
appName,
|
|
2899
|
+
appId,
|
|
2461
2900
|
appVersion,
|
|
2462
2901
|
appIcon,
|
|
2463
2902
|
serverURL,
|
|
@@ -2467,6 +2906,10 @@ router.post(
|
|
|
2467
2906
|
synchedTables: synchedTables,
|
|
2468
2907
|
includedPlugins: includedPlugins,
|
|
2469
2908
|
excludedPlugins,
|
|
2909
|
+
provisioningProfile,
|
|
2910
|
+
keystoreFile,
|
|
2911
|
+
keystoreAlias,
|
|
2912
|
+
buildType,
|
|
2470
2913
|
});
|
|
2471
2914
|
// end http call, return the out directory name
|
|
2472
2915
|
// the gui polls for results
|
|
@@ -2563,6 +3006,14 @@ router.get(
|
|
|
2563
3006
|
})
|
|
2564
3007
|
);
|
|
2565
3008
|
|
|
3009
|
+
router.get(
|
|
3010
|
+
"/mobile-app/check-xcodebuild",
|
|
3011
|
+
isAdmin,
|
|
3012
|
+
error_catcher(async (req, res) => {
|
|
3013
|
+
res.json(await checkXcodebuild());
|
|
3014
|
+
})
|
|
3015
|
+
);
|
|
3016
|
+
|
|
2566
3017
|
/**
|
|
2567
3018
|
* Do Clear All
|
|
2568
3019
|
* @function
|
|
@@ -2765,6 +3216,7 @@ admin_config_route({
|
|
|
2765
3216
|
"development_mode",
|
|
2766
3217
|
"log_sql",
|
|
2767
3218
|
"log_client_errors",
|
|
3219
|
+
"log_ip_address",
|
|
2768
3220
|
"log_level",
|
|
2769
3221
|
...(isRoot || tenants_set_npm_modules ? ["npm_available_js_code"] : []),
|
|
2770
3222
|
],
|
|
@@ -2772,6 +3224,7 @@ admin_config_route({
|
|
|
2772
3224
|
});
|
|
2773
3225
|
},
|
|
2774
3226
|
response(form, req, res) {
|
|
3227
|
+
const code_pages = getState().getConfig("function_code_pages", {});
|
|
2775
3228
|
send_admin_page({
|
|
2776
3229
|
res,
|
|
2777
3230
|
req,
|
|
@@ -2798,11 +3251,138 @@ admin_config_route({
|
|
|
2798
3251
|
),
|
|
2799
3252
|
],
|
|
2800
3253
|
},
|
|
3254
|
+
{
|
|
3255
|
+
type: "card",
|
|
3256
|
+
title: req.__("Constants and function code"),
|
|
3257
|
+
contents: [
|
|
3258
|
+
div(
|
|
3259
|
+
Object.keys(code_pages)
|
|
3260
|
+
.map((k) =>
|
|
3261
|
+
a(
|
|
3262
|
+
{
|
|
3263
|
+
href: `/admin/edit-codepage/${encodeURIComponent(k)}`,
|
|
3264
|
+
class: "",
|
|
3265
|
+
},
|
|
3266
|
+
k
|
|
3267
|
+
)
|
|
3268
|
+
)
|
|
3269
|
+
.join(" | "),
|
|
3270
|
+
button(
|
|
3271
|
+
{
|
|
3272
|
+
class: "btn btn-secondary btn-sm d-block mt-2",
|
|
3273
|
+
onclick: `location.href='/admin/edit-codepage/'+prompt('Name of the new page')`,
|
|
3274
|
+
},
|
|
3275
|
+
i({ class: "fas fa-plus me-1" }),
|
|
3276
|
+
"Add page"
|
|
3277
|
+
)
|
|
3278
|
+
),
|
|
3279
|
+
],
|
|
3280
|
+
},
|
|
2801
3281
|
],
|
|
2802
3282
|
},
|
|
2803
3283
|
});
|
|
2804
3284
|
},
|
|
2805
3285
|
});
|
|
3286
|
+
|
|
3287
|
+
router.get(
|
|
3288
|
+
"/edit-codepage/:name",
|
|
3289
|
+
isAdmin,
|
|
3290
|
+
error_catcher(async (req, res) => {
|
|
3291
|
+
const { name } = req.params;
|
|
3292
|
+
const code_pages = getState().getConfig("function_code_pages", {});
|
|
3293
|
+
const existing = code_pages[name] || "";
|
|
3294
|
+
const form = new Form({
|
|
3295
|
+
action: `/admin/edit-codepage/${encodeURIComponent(name)}`,
|
|
3296
|
+
onChange: "saveAndContinue(this)",
|
|
3297
|
+
values: { code: existing },
|
|
3298
|
+
noSubmitButton: true,
|
|
3299
|
+
labelCols: 0,
|
|
3300
|
+
additionalButtons: [
|
|
3301
|
+
{
|
|
3302
|
+
label: req.__("Delete code page"),
|
|
3303
|
+
class: "btn btn-outline-danger btn-sm",
|
|
3304
|
+
onclick: `if(confirm('Are you sure you would like to delete this code page?'))ajax_post('/admin/delete-codepage/${encodeURIComponent(
|
|
3305
|
+
name
|
|
3306
|
+
)}')`,
|
|
3307
|
+
},
|
|
3308
|
+
],
|
|
3309
|
+
fields: [
|
|
3310
|
+
{
|
|
3311
|
+
name: "code",
|
|
3312
|
+
form_name: "code",
|
|
3313
|
+
label: "Code",
|
|
3314
|
+
sublabel:
|
|
3315
|
+
"Only functions declared as <code>function name(...) {...}</code> or <code>async function name(...) {...}</code> will be available in formulae and code actions. Declare a constant <code>k</code> as <code>globalThis.k = ...</code> In scope: " +
|
|
3316
|
+
a(
|
|
3317
|
+
{
|
|
3318
|
+
href: "https://saltcorn.github.io/saltcorn/classes/_saltcorn_data.models.Table-1.html",
|
|
3319
|
+
target: "_blank",
|
|
3320
|
+
},
|
|
3321
|
+
"Table"
|
|
3322
|
+
),
|
|
3323
|
+
input_type: "code",
|
|
3324
|
+
attributes: { mode: "text/javascript" },
|
|
3325
|
+
class: "validate-statements",
|
|
3326
|
+
validator(s) {
|
|
3327
|
+
try {
|
|
3328
|
+
let AsyncFunction = Object.getPrototypeOf(
|
|
3329
|
+
async function () {}
|
|
3330
|
+
).constructor;
|
|
3331
|
+
AsyncFunction(s);
|
|
3332
|
+
return true;
|
|
3333
|
+
} catch (e) {
|
|
3334
|
+
return e.message;
|
|
3335
|
+
}
|
|
3336
|
+
},
|
|
3337
|
+
},
|
|
3338
|
+
],
|
|
3339
|
+
});
|
|
3340
|
+
|
|
3341
|
+
send_admin_page({
|
|
3342
|
+
res,
|
|
3343
|
+
req,
|
|
3344
|
+
active_sub: "Development",
|
|
3345
|
+
sub2_page: req.__(`%s code page`, name),
|
|
3346
|
+
contents: {
|
|
3347
|
+
type: "card",
|
|
3348
|
+
title: req.__(`%s code page`, name),
|
|
3349
|
+
contents: [renderForm(form, req.csrfToken())],
|
|
3350
|
+
},
|
|
3351
|
+
});
|
|
3352
|
+
})
|
|
3353
|
+
);
|
|
3354
|
+
|
|
3355
|
+
router.post(
|
|
3356
|
+
"/edit-codepage/:name",
|
|
3357
|
+
isAdmin,
|
|
3358
|
+
error_catcher(async (req, res) => {
|
|
3359
|
+
const { name } = req.params;
|
|
3360
|
+
const code_pages = getState().getConfigCopy("function_code_pages", {});
|
|
3361
|
+
|
|
3362
|
+
const code = req.body.code;
|
|
3363
|
+
await getState().setConfig("function_code_pages", {
|
|
3364
|
+
...code_pages,
|
|
3365
|
+
[name]: code,
|
|
3366
|
+
});
|
|
3367
|
+
await getState().refresh_codepages();
|
|
3368
|
+
|
|
3369
|
+
res.json({ success: true });
|
|
3370
|
+
})
|
|
3371
|
+
);
|
|
3372
|
+
router.post(
|
|
3373
|
+
"/delete-codepage/:name",
|
|
3374
|
+
isAdmin,
|
|
3375
|
+
error_catcher(async (req, res) => {
|
|
3376
|
+
const { name } = req.params;
|
|
3377
|
+
const code_pages = getState().getConfigCopy("function_code_pages", {});
|
|
3378
|
+
delete code_pages[name];
|
|
3379
|
+
await getState().setConfig("function_code_pages", code_pages);
|
|
3380
|
+
await getState().refresh_codepages();
|
|
3381
|
+
|
|
3382
|
+
res.json({ goto: `/admin/dev` });
|
|
3383
|
+
})
|
|
3384
|
+
);
|
|
3385
|
+
|
|
2806
3386
|
/**
|
|
2807
3387
|
* Notifications
|
|
2808
3388
|
*/
|