@saltcorn/server 1.1.1-beta.1 → 1.1.1-beta.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/CHANGELOG.md +83 -5
- package/auth/testhelp.js +6 -0
- package/help/Event types.tmd +2 -0
- package/locales/en.json +7 -1
- package/package.json +9 -9
- package/public/saltcorn-common.js +30 -0
- package/public/saltcorn.css +141 -0
- package/public/saltcorn.js +87 -17
- package/routes/actions.js +86 -11
- package/routes/admin.js +281 -48
- package/routes/api.js +19 -8
- package/routes/fields.js +9 -0
- package/routes/menu.js +1 -0
- package/routes/notifications.js +20 -12
- package/routes/page.js +2 -2
- package/routes/pageedit.js +35 -1
- package/routes/tables.js +1 -0
- package/routes/utils.js +4 -0
- package/routes/view.js +14 -12
- package/routes/viewedit.js +26 -0
- package/tests/clientjs.test.js +22 -0
package/routes/admin.js
CHANGED
|
@@ -19,6 +19,7 @@ const Plugin = require("@saltcorn/data/models/plugin");
|
|
|
19
19
|
const File = require("@saltcorn/data/models/file");
|
|
20
20
|
const { spawn, exec } = require("child_process");
|
|
21
21
|
const User = require("@saltcorn/data/models/user");
|
|
22
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
22
23
|
const path = require("path");
|
|
23
24
|
const { X509Certificate } = require("crypto");
|
|
24
25
|
const { getAllTenants } = require("@saltcorn/admin-models/models/tenant");
|
|
@@ -114,6 +115,8 @@ const { get_help_markup } = require("../help/index.js");
|
|
|
114
115
|
const Docker = require("dockerode");
|
|
115
116
|
const npmFetch = require("npm-registry-fetch");
|
|
116
117
|
const Tag = require("@saltcorn/data/models/tag");
|
|
118
|
+
const MarkdownIt = require("markdown-it"),
|
|
119
|
+
md = new MarkdownIt();
|
|
117
120
|
|
|
118
121
|
const router = new Router();
|
|
119
122
|
module.exports = router;
|
|
@@ -146,6 +149,24 @@ const app_files_table = (files, buildDirName, req) =>
|
|
|
146
149
|
],
|
|
147
150
|
files
|
|
148
151
|
);
|
|
152
|
+
const intermediate_build_result = (outDirName, buildDir, req) => {
|
|
153
|
+
return div(
|
|
154
|
+
h3("Intermediate build result"),
|
|
155
|
+
div(
|
|
156
|
+
button(
|
|
157
|
+
{
|
|
158
|
+
id: "finishMobileAppBtnId",
|
|
159
|
+
type: "button",
|
|
160
|
+
onClick: `finish_mobile_app(this, '${outDirName}', '${buildDir}');`,
|
|
161
|
+
class: "btn btn-warning",
|
|
162
|
+
},
|
|
163
|
+
i({ class: "fas fa-hammer pe-2" }),
|
|
164
|
+
|
|
165
|
+
req.__("Finish the build")
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
);
|
|
169
|
+
};
|
|
149
170
|
|
|
150
171
|
admin_config_route({
|
|
151
172
|
router,
|
|
@@ -268,6 +289,18 @@ router.get(
|
|
|
268
289
|
})
|
|
269
290
|
);
|
|
270
291
|
|
|
292
|
+
router.get(
|
|
293
|
+
"/whatsnew",
|
|
294
|
+
isAdmin,
|
|
295
|
+
error_catcher(async (req, res) => {
|
|
296
|
+
const fp = path.join(__dirname, "..", "CHANGELOG.md");
|
|
297
|
+
const fileBuf = await fs.promises.readFile(fp);
|
|
298
|
+
const mdContents = fileBuf.toString().replace("# Notable changes\n","");
|
|
299
|
+
const markup = md.render(mdContents);
|
|
300
|
+
res.sendWrap(`What's new in Saltcorn`, { above: [markup] });
|
|
301
|
+
})
|
|
302
|
+
);
|
|
303
|
+
|
|
271
304
|
/**
|
|
272
305
|
* @name get/backup
|
|
273
306
|
* @function
|
|
@@ -1118,7 +1151,7 @@ router.get(
|
|
|
1118
1151
|
table(
|
|
1119
1152
|
tbody(
|
|
1120
1153
|
tr(
|
|
1121
|
-
th(req.__("Saltcorn version")),
|
|
1154
|
+
th({ valign: "top" }, req.__("Saltcorn version")),
|
|
1122
1155
|
td(
|
|
1123
1156
|
packagejson.version,
|
|
1124
1157
|
isRoot && can_update
|
|
@@ -1158,7 +1191,15 @@ router.get(
|
|
|
1158
1191
|
` onError: (res) => { selectVersionError(res, '${rndid}') } });`,
|
|
1159
1192
|
},
|
|
1160
1193
|
req.__("Choose version")
|
|
1161
|
-
)
|
|
1194
|
+
),
|
|
1195
|
+
"<br>",
|
|
1196
|
+
a(
|
|
1197
|
+
{
|
|
1198
|
+
onclick: "ajax_modal('/admin/whatsnew')",
|
|
1199
|
+
href: `javascript:void(0)`,
|
|
1200
|
+
},
|
|
1201
|
+
"What's new?"
|
|
1202
|
+
)
|
|
1162
1203
|
)
|
|
1163
1204
|
),
|
|
1164
1205
|
git_commit &&
|
|
@@ -1993,9 +2034,6 @@ const buildDialogScript = (capacitorBuilderAvailable, isSbadmin2) =>
|
|
|
1993
2034
|
$("#entryPointTypeID").attr("value", type);
|
|
1994
2035
|
}
|
|
1995
2036
|
|
|
1996
|
-
function handleMessages() {
|
|
1997
|
-
notifyAlert("Building the app, please wait.", true)
|
|
1998
|
-
}
|
|
1999
2037
|
const versionPattern = /^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/;
|
|
2000
2038
|
${domReady(`
|
|
2001
2039
|
const versionInput = document.getElementById('appVersionInputId');
|
|
@@ -3077,6 +3115,48 @@ router.get(
|
|
|
3077
3115
|
)
|
|
3078
3116
|
)
|
|
3079
3117
|
)
|
|
3118
|
+
// Share Extension provisioning profile
|
|
3119
|
+
// disabled for now
|
|
3120
|
+
// div(
|
|
3121
|
+
// { class: "row pb-3" },
|
|
3122
|
+
// div(
|
|
3123
|
+
// { class: "col-sm-8" },
|
|
3124
|
+
// label(
|
|
3125
|
+
// {
|
|
3126
|
+
// for: "shareProvisioningProfileInputId",
|
|
3127
|
+
// class: "form-label fw-bold",
|
|
3128
|
+
// },
|
|
3129
|
+
// req.__("Share Extension Provisioning Profile"),
|
|
3130
|
+
// a(
|
|
3131
|
+
// {
|
|
3132
|
+
// href: "javascript:ajax_modal('/admin/help/Provisioning Profile?')",
|
|
3133
|
+
// },
|
|
3134
|
+
// i({ class: "fas fa-question-circle ps-1" })
|
|
3135
|
+
// )
|
|
3136
|
+
// ),
|
|
3137
|
+
// select(
|
|
3138
|
+
// {
|
|
3139
|
+
// class: "form-select",
|
|
3140
|
+
// name: "shareProvisioningProfile",
|
|
3141
|
+
// id: "shareProvisioningProfileInputId",
|
|
3142
|
+
// },
|
|
3143
|
+
// [
|
|
3144
|
+
// option({ value: "" }, ""),
|
|
3145
|
+
// ...provisioningFiles.map((file) =>
|
|
3146
|
+
// option(
|
|
3147
|
+
// {
|
|
3148
|
+
// value: file.location,
|
|
3149
|
+
// selected:
|
|
3150
|
+
// builderSettings.shareProvisioningProfile ===
|
|
3151
|
+
// file.location,
|
|
3152
|
+
// },
|
|
3153
|
+
// file.filename
|
|
3154
|
+
// )
|
|
3155
|
+
// ),
|
|
3156
|
+
// ].join("")
|
|
3157
|
+
// )
|
|
3158
|
+
// )
|
|
3159
|
+
// )
|
|
3080
3160
|
)
|
|
3081
3161
|
)
|
|
3082
3162
|
),
|
|
@@ -3100,15 +3180,13 @@ router.get(
|
|
|
3100
3180
|
})
|
|
3101
3181
|
);
|
|
3102
3182
|
|
|
3103
|
-
const checkFiles = async (
|
|
3183
|
+
const checkFiles = async (outDirName, fileNames) => {
|
|
3104
3184
|
const rootFolder = await File.rootFolder();
|
|
3105
|
-
const
|
|
3185
|
+
const outDir = path.join(rootFolder.location, "mobile_app", outDirName);
|
|
3106
3186
|
const unsafeFiles = await Promise.all(
|
|
3107
3187
|
fs
|
|
3108
|
-
.readdirSync(
|
|
3109
|
-
.map(
|
|
3110
|
-
async (outFile) => await File.from_file_on_disk(outFile, mobile_app_dir)
|
|
3111
|
-
)
|
|
3188
|
+
.readdirSync(outDir)
|
|
3189
|
+
.map(async (outFile) => await File.from_file_on_disk(outFile, outDir))
|
|
3112
3190
|
);
|
|
3113
3191
|
const entries = unsafeFiles
|
|
3114
3192
|
.filter(
|
|
@@ -3127,9 +3205,18 @@ router.get(
|
|
|
3127
3205
|
"/build-mobile-app/finished",
|
|
3128
3206
|
isAdmin,
|
|
3129
3207
|
error_catcher(async (req, res) => {
|
|
3130
|
-
const {
|
|
3208
|
+
const { out_dir_name, mode } = req.query;
|
|
3209
|
+
const stepDesc =
|
|
3210
|
+
mode === "prepare"
|
|
3211
|
+
? "_prepare_step"
|
|
3212
|
+
: mode === "finish"
|
|
3213
|
+
? "_finish_step"
|
|
3214
|
+
: "";
|
|
3131
3215
|
res.json({
|
|
3132
|
-
finished: await checkFiles(
|
|
3216
|
+
finished: await checkFiles(out_dir_name, [
|
|
3217
|
+
`logs${stepDesc}.txt`,
|
|
3218
|
+
`error_logs${stepDesc}.txt`,
|
|
3219
|
+
]),
|
|
3133
3220
|
});
|
|
3134
3221
|
})
|
|
3135
3222
|
);
|
|
@@ -3164,8 +3251,8 @@ router.get(
|
|
|
3164
3251
|
"/build-mobile-app/result",
|
|
3165
3252
|
isAdmin,
|
|
3166
3253
|
error_catcher(async (req, res) => {
|
|
3167
|
-
const {
|
|
3168
|
-
if (!validateBuildDirName(
|
|
3254
|
+
const { out_dir_name, build_dir, mode } = req.query;
|
|
3255
|
+
if (!validateBuildDirName(out_dir_name)) {
|
|
3169
3256
|
return res.sendWrap(req.__(`Admin`), {
|
|
3170
3257
|
above: [
|
|
3171
3258
|
{
|
|
@@ -3177,11 +3264,7 @@ router.get(
|
|
|
3177
3264
|
});
|
|
3178
3265
|
}
|
|
3179
3266
|
const rootFolder = await File.rootFolder();
|
|
3180
|
-
const buildDir = path.join(
|
|
3181
|
-
rootFolder.location,
|
|
3182
|
-
"mobile_app",
|
|
3183
|
-
build_dir_name
|
|
3184
|
-
);
|
|
3267
|
+
const buildDir = path.join(rootFolder.location, "mobile_app", out_dir_name);
|
|
3185
3268
|
if (!validateBuildDir(buildDir, rootFolder.location)) {
|
|
3186
3269
|
return res.sendWrap(req.__(`Admin`), {
|
|
3187
3270
|
above: [
|
|
@@ -3199,7 +3282,15 @@ router.get(
|
|
|
3199
3282
|
.readdirSync(buildDir)
|
|
3200
3283
|
.map(async (outFile) => await File.from_file_on_disk(outFile, buildDir))
|
|
3201
3284
|
);
|
|
3202
|
-
const
|
|
3285
|
+
const stepDesc =
|
|
3286
|
+
mode === "prepare"
|
|
3287
|
+
? "_prepare_step"
|
|
3288
|
+
: mode === "finish"
|
|
3289
|
+
? "_finish_step"
|
|
3290
|
+
: "";
|
|
3291
|
+
const resultMsg = files.find(
|
|
3292
|
+
(file) => file.filename === `logs${stepDesc}.txt`
|
|
3293
|
+
)
|
|
3203
3294
|
? req.__("The build was successfully")
|
|
3204
3295
|
: req.__("Unable to build the app");
|
|
3205
3296
|
res.sendWrap(req.__(`Admin`), {
|
|
@@ -3209,11 +3300,98 @@ router.get(
|
|
|
3209
3300
|
title: req.__("Build Result"),
|
|
3210
3301
|
contents: div(resultMsg),
|
|
3211
3302
|
},
|
|
3212
|
-
files.length > 0 ? app_files_table(files,
|
|
3303
|
+
files.length > 0 ? app_files_table(files, out_dir_name, req) : "",
|
|
3304
|
+
mode === "prepare"
|
|
3305
|
+
? intermediate_build_result(out_dir_name, build_dir, req)
|
|
3306
|
+
: "",
|
|
3213
3307
|
],
|
|
3214
3308
|
});
|
|
3215
3309
|
})
|
|
3216
3310
|
);
|
|
3311
|
+
|
|
3312
|
+
router.post(
|
|
3313
|
+
"/build-mobile-app/finish",
|
|
3314
|
+
isAdmin,
|
|
3315
|
+
error_catcher(async (req, res) => {
|
|
3316
|
+
const { out_dir_name, build_dir } = req.body;
|
|
3317
|
+
const content = await fs.promises.readFile(
|
|
3318
|
+
path.join(build_dir, "spawnParams.json")
|
|
3319
|
+
);
|
|
3320
|
+
const spawnParams = JSON.parse(content);
|
|
3321
|
+
const rootFolder = await File.rootFolder();
|
|
3322
|
+
const outDirFullPath = path.join(
|
|
3323
|
+
rootFolder.location,
|
|
3324
|
+
"mobile_app",
|
|
3325
|
+
out_dir_name
|
|
3326
|
+
);
|
|
3327
|
+
res.json({
|
|
3328
|
+
success: true,
|
|
3329
|
+
});
|
|
3330
|
+
const child = spawn(
|
|
3331
|
+
getSafeSaltcornCmd(),
|
|
3332
|
+
[...spawnParams, "-m", "finish"],
|
|
3333
|
+
{
|
|
3334
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3335
|
+
cwd: ".",
|
|
3336
|
+
}
|
|
3337
|
+
);
|
|
3338
|
+
const childOutputs = [];
|
|
3339
|
+
child.stdout.on("data", (data) => {
|
|
3340
|
+
const outMsg = data.toString();
|
|
3341
|
+
getState().log(5, outMsg);
|
|
3342
|
+
if (data) childOutputs.push(outMsg);
|
|
3343
|
+
});
|
|
3344
|
+
child.stderr.on("data", (data) => {
|
|
3345
|
+
const errMsg = data ? data.toString() : req.__("An error occurred");
|
|
3346
|
+
getState().log(5, errMsg);
|
|
3347
|
+
childOutputs.push(errMsg);
|
|
3348
|
+
});
|
|
3349
|
+
child.on("exit", async (exitCode, signal) => {
|
|
3350
|
+
const logFile =
|
|
3351
|
+
exitCode === 0 ? "logs_finish_step.txt" : "error_logs_finish_step.txt";
|
|
3352
|
+
try {
|
|
3353
|
+
const exitMsg = childOutputs.join("\n");
|
|
3354
|
+
await fs.promises.writeFile(
|
|
3355
|
+
path.join(outDirFullPath, logFile),
|
|
3356
|
+
exitMsg
|
|
3357
|
+
);
|
|
3358
|
+
await File.set_xattr_of_existing_file(
|
|
3359
|
+
logFile,
|
|
3360
|
+
outDirFullPath,
|
|
3361
|
+
req.user
|
|
3362
|
+
);
|
|
3363
|
+
} catch (error) {
|
|
3364
|
+
console.log(`unable to write '${logFile}' to '${outDirFullPath}'`);
|
|
3365
|
+
console.log(error);
|
|
3366
|
+
}
|
|
3367
|
+
});
|
|
3368
|
+
child.on("error", (msg) => {
|
|
3369
|
+
const message = msg.message ? msg.message : msg.code;
|
|
3370
|
+
const stack = msg.stack ? msg.stack : "";
|
|
3371
|
+
const logFile = "error_logs.txt";
|
|
3372
|
+
const errMsg = [message, stack].join("\n");
|
|
3373
|
+
getState().log(5, msg);
|
|
3374
|
+
fs.writeFile(
|
|
3375
|
+
path.join(outDirFullPath, "error_logs.txt"),
|
|
3376
|
+
errMsg,
|
|
3377
|
+
async (error) => {
|
|
3378
|
+
if (error) {
|
|
3379
|
+
console.log(`unable to write logFile to '${outDirFullPath}'`);
|
|
3380
|
+
console.log(error);
|
|
3381
|
+
} else {
|
|
3382
|
+
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
3383
|
+
await File.set_xattr_of_existing_file(
|
|
3384
|
+
logFile,
|
|
3385
|
+
outDirFullPath,
|
|
3386
|
+
req.user
|
|
3387
|
+
);
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
);
|
|
3391
|
+
});
|
|
3392
|
+
})
|
|
3393
|
+
);
|
|
3394
|
+
|
|
3217
3395
|
/**
|
|
3218
3396
|
* Do Build Mobile App
|
|
3219
3397
|
*/
|
|
@@ -3222,6 +3400,8 @@ router.post(
|
|
|
3222
3400
|
isAdmin,
|
|
3223
3401
|
error_catcher(async (req, res) => {
|
|
3224
3402
|
getState().log(2, `starting mobile build: ${JSON.stringify(req.body)}`);
|
|
3403
|
+
const msgs = [];
|
|
3404
|
+
let mode = "full";
|
|
3225
3405
|
let {
|
|
3226
3406
|
entryPoint,
|
|
3227
3407
|
entryPointType,
|
|
@@ -3239,11 +3419,27 @@ router.post(
|
|
|
3239
3419
|
synchedTables,
|
|
3240
3420
|
includedPlugins,
|
|
3241
3421
|
provisioningProfile,
|
|
3422
|
+
shareProvisioningProfile,
|
|
3242
3423
|
buildType,
|
|
3243
3424
|
keystoreFile,
|
|
3244
3425
|
keystoreAlias,
|
|
3245
3426
|
keystorePassword,
|
|
3246
3427
|
} = req.body;
|
|
3428
|
+
// const receiveShareTriggers = Trigger.find({
|
|
3429
|
+
// when_trigger: "ReceiveMobileShareData",
|
|
3430
|
+
// });
|
|
3431
|
+
// disabeling share to support for now
|
|
3432
|
+
let allowShareTo = false; // receiveShareTriggers.length > 0;
|
|
3433
|
+
if (allowShareTo && iOSPlatform && !shareProvisioningProfile) {
|
|
3434
|
+
allowShareTo = false;
|
|
3435
|
+
msgs.push({
|
|
3436
|
+
type: "warning",
|
|
3437
|
+
text: req.__(
|
|
3438
|
+
"A ReceiveMobileShareData trigger exists, but no Share Extension Provisioning Profile is provided. " +
|
|
3439
|
+
"Building without share to support."
|
|
3440
|
+
),
|
|
3441
|
+
});
|
|
3442
|
+
}
|
|
3247
3443
|
if (!includedPlugins) includedPlugins = [];
|
|
3248
3444
|
if (!synchedTables) synchedTables = [];
|
|
3249
3445
|
if (!entryPoint) {
|
|
@@ -3279,14 +3475,26 @@ router.post(
|
|
|
3279
3475
|
),
|
|
3280
3476
|
});
|
|
3281
3477
|
}
|
|
3282
|
-
if (iOSPlatform
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3478
|
+
if (iOSPlatform) {
|
|
3479
|
+
if (!provisioningProfile)
|
|
3480
|
+
return res.json({
|
|
3481
|
+
error: req.__(
|
|
3482
|
+
"Please provide a Provisioning Profile for the iOS build."
|
|
3483
|
+
),
|
|
3484
|
+
});
|
|
3485
|
+
}
|
|
3486
|
+
if (buildType === "debug" && keystoreFile) {
|
|
3487
|
+
msgs.push({
|
|
3488
|
+
type: "warning",
|
|
3489
|
+
text: req.__("Keystore file is not applied for debug builds."),
|
|
3287
3490
|
});
|
|
3288
3491
|
}
|
|
3289
|
-
|
|
3492
|
+
|
|
3493
|
+
if (
|
|
3494
|
+
buildType === "release" &&
|
|
3495
|
+
keystoreFile &&
|
|
3496
|
+
(!keystoreAlias || !keystorePassword)
|
|
3497
|
+
) {
|
|
3290
3498
|
return res.json({
|
|
3291
3499
|
error: req.__(
|
|
3292
3500
|
"Please provide the keystore alias and password for the android build."
|
|
@@ -3294,8 +3502,9 @@ router.post(
|
|
|
3294
3502
|
});
|
|
3295
3503
|
}
|
|
3296
3504
|
const outDirName = `build_${new Date().valueOf()}`;
|
|
3505
|
+
const buildDir = `${os.userInfo().homedir}/mobile_app_build`;
|
|
3297
3506
|
const rootFolder = await File.rootFolder();
|
|
3298
|
-
const
|
|
3507
|
+
const outDir = path.join(rootFolder.location, "mobile_app", outDirName);
|
|
3299
3508
|
await File.new_folder(outDirName, "/mobile_app");
|
|
3300
3509
|
const spawnParams = [
|
|
3301
3510
|
"build-app",
|
|
@@ -3304,9 +3513,9 @@ router.post(
|
|
|
3304
3513
|
"-t",
|
|
3305
3514
|
entryPointType === "pagegroup" ? "page" : entryPointType,
|
|
3306
3515
|
"-c",
|
|
3307
|
-
|
|
3516
|
+
outDir,
|
|
3308
3517
|
"-b",
|
|
3309
|
-
|
|
3518
|
+
buildDir,
|
|
3310
3519
|
"-u",
|
|
3311
3520
|
req.user.email, // ensured by isAdmin
|
|
3312
3521
|
];
|
|
@@ -3319,6 +3528,13 @@ router.post(
|
|
|
3319
3528
|
"--provisioningProfile",
|
|
3320
3529
|
provisioningProfile
|
|
3321
3530
|
);
|
|
3531
|
+
if (allowShareTo) {
|
|
3532
|
+
mode = "prepare";
|
|
3533
|
+
spawnParams.push(
|
|
3534
|
+
"--shareExtensionProvisioningProfile",
|
|
3535
|
+
shareProvisioningProfile
|
|
3536
|
+
);
|
|
3537
|
+
}
|
|
3322
3538
|
}
|
|
3323
3539
|
if (appName) spawnParams.push("--appName", appName);
|
|
3324
3540
|
if (appId) spawnParams.push("--appId", appId);
|
|
@@ -3327,6 +3543,7 @@ router.post(
|
|
|
3327
3543
|
if (serverURL) spawnParams.push("-s", serverURL);
|
|
3328
3544
|
if (splashPage) spawnParams.push("--splashPage", splashPage);
|
|
3329
3545
|
if (allowOfflineMode) spawnParams.push("--allowOfflineMode");
|
|
3546
|
+
if (allowShareTo) spawnParams.push("--allowShareTo");
|
|
3330
3547
|
if (autoPublicLogin) spawnParams.push("--autoPublicLogin");
|
|
3331
3548
|
if (synchedTables.length > 0)
|
|
3332
3549
|
spawnParams.push("--synchedTables", ...synchedTables.map((tbl) => tbl));
|
|
@@ -3348,10 +3565,15 @@ router.post(
|
|
|
3348
3565
|
spawnParams.push("--androidKeyStoreAlias", keystoreAlias);
|
|
3349
3566
|
if (keystorePassword)
|
|
3350
3567
|
spawnParams.push("--androidKeystorePassword", keystorePassword);
|
|
3351
|
-
// end http call, return the out directory name
|
|
3568
|
+
// end http call, return the out directory name, the build directory path and the mode
|
|
3352
3569
|
// the gui polls for results
|
|
3353
|
-
res.json({
|
|
3354
|
-
|
|
3570
|
+
res.json({
|
|
3571
|
+
out_dir_name: outDirName,
|
|
3572
|
+
build_dir: buildDir,
|
|
3573
|
+
mode: mode,
|
|
3574
|
+
msgs,
|
|
3575
|
+
});
|
|
3576
|
+
const child = spawn(getSafeSaltcornCmd(), [...spawnParams, "-m", mode], {
|
|
3355
3577
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3356
3578
|
cwd: ".",
|
|
3357
3579
|
});
|
|
@@ -3366,18 +3588,29 @@ router.post(
|
|
|
3366
3588
|
getState().log(5, errMsg);
|
|
3367
3589
|
childOutputs.push(errMsg);
|
|
3368
3590
|
});
|
|
3369
|
-
child.on("exit", (exitCode, signal) => {
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3591
|
+
child.on("exit", async (exitCode, signal) => {
|
|
3592
|
+
if (mode === "prepare" && exitCode === 0) {
|
|
3593
|
+
try {
|
|
3594
|
+
fs.promises.writeFile(
|
|
3595
|
+
path.join(buildDir, "spawnParams.json"),
|
|
3596
|
+
JSON.stringify(spawnParams)
|
|
3597
|
+
);
|
|
3598
|
+
} catch (error) {
|
|
3599
|
+
console.log(`unable to write spawnParams to '${buildDir}'`);
|
|
3375
3600
|
console.log(error);
|
|
3376
|
-
} else {
|
|
3377
|
-
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
3378
|
-
await File.set_xattr_of_existing_file(logFile, buildDir, req.user);
|
|
3379
3601
|
}
|
|
3380
|
-
}
|
|
3602
|
+
}
|
|
3603
|
+
const stepDesc = mode === "prepare" ? "_prepare_step" : "";
|
|
3604
|
+
const logFile =
|
|
3605
|
+
exitCode === 0 ? `logs${stepDesc}.txt` : `error_logs${stepDesc}.txt`;
|
|
3606
|
+
try {
|
|
3607
|
+
const exitMsg = childOutputs.join("\n");
|
|
3608
|
+
await fs.promises.writeFile(path.join(outDir, logFile), exitMsg);
|
|
3609
|
+
await File.set_xattr_of_existing_file(logFile, outDir, req.user);
|
|
3610
|
+
} catch (error) {
|
|
3611
|
+
console.log(`unable to write '${logFile}' to '${outDir}'`);
|
|
3612
|
+
console.log(error);
|
|
3613
|
+
}
|
|
3381
3614
|
});
|
|
3382
3615
|
child.on("error", (msg) => {
|
|
3383
3616
|
const message = msg.message ? msg.message : msg.code;
|
|
@@ -3386,15 +3619,15 @@ router.post(
|
|
|
3386
3619
|
const errMsg = [message, stack].join("\n");
|
|
3387
3620
|
getState().log(5, msg);
|
|
3388
3621
|
fs.writeFile(
|
|
3389
|
-
path.join(
|
|
3622
|
+
path.join(outDir, "error_logs.txt"),
|
|
3390
3623
|
errMsg,
|
|
3391
3624
|
async (error) => {
|
|
3392
3625
|
if (error) {
|
|
3393
|
-
console.log(`unable to write logFile to '${
|
|
3626
|
+
console.log(`unable to write logFile to '${outDir}'`);
|
|
3394
3627
|
console.log(error);
|
|
3395
3628
|
} else {
|
|
3396
3629
|
// no transaction, '/build-mobile-app/finished' filters for valid attributes
|
|
3397
|
-
await File.set_xattr_of_existing_file(logFile,
|
|
3630
|
+
await File.set_xattr_of_existing_file(logFile, outDir, req.user);
|
|
3398
3631
|
}
|
|
3399
3632
|
}
|
|
3400
3633
|
);
|
|
@@ -3462,7 +3695,7 @@ router.post(
|
|
|
3462
3695
|
.filter(
|
|
3463
3696
|
(plugin) =>
|
|
3464
3697
|
["base", "sbadmin2"].indexOf(plugin.name) < 0 &&
|
|
3465
|
-
newCfg.includedPlugins.indexOf(plugin.name) < 0
|
|
3698
|
+
(newCfg.includedPlugins || []).indexOf(plugin.name) < 0
|
|
3466
3699
|
)
|
|
3467
3700
|
.map((plugin) => plugin.name);
|
|
3468
3701
|
newCfg.excludedPlugins = excludedPlugins;
|
package/routes/api.js
CHANGED
|
@@ -398,15 +398,26 @@ router.all(
|
|
|
398
398
|
async function (err, user, info) {
|
|
399
399
|
if (accessAllowed(req, user, trigger)) {
|
|
400
400
|
try {
|
|
401
|
-
|
|
401
|
+
let resp;
|
|
402
402
|
const row = req.method === "GET" ? req.query : req.body;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
403
|
+
if (trigger.action === "Workflow") {
|
|
404
|
+
resp = await trigger.runWithoutRow({
|
|
405
|
+
req,
|
|
406
|
+
interactive: true,
|
|
407
|
+
row,
|
|
408
|
+
user: user || req.user,
|
|
409
|
+
});
|
|
410
|
+
delete resp.__wf_run_id;
|
|
411
|
+
} else {
|
|
412
|
+
const action = getState().actions[trigger.action];
|
|
413
|
+
resp = await action.run({
|
|
414
|
+
configuration: trigger.configuration,
|
|
415
|
+
body: req.body,
|
|
416
|
+
row,
|
|
417
|
+
req,
|
|
418
|
+
user: user || req.user,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
410
421
|
if (
|
|
411
422
|
(row._process_result || req.headers?.scprocessresults) &&
|
|
412
423
|
resp?.goto
|
package/routes/fields.js
CHANGED
|
@@ -12,6 +12,7 @@ const { getState } = require("@saltcorn/data/db/state");
|
|
|
12
12
|
const { renderForm } = require("@saltcorn/markup");
|
|
13
13
|
const Field = require("@saltcorn/data/models/field");
|
|
14
14
|
const Table = require("@saltcorn/data/models/table");
|
|
15
|
+
const Trigger = require("@saltcorn/data/models/trigger");
|
|
15
16
|
const Form = require("@saltcorn/data/models/form");
|
|
16
17
|
const Workflow = require("@saltcorn/data/models/workflow");
|
|
17
18
|
const User = require("@saltcorn/data/models/user");
|
|
@@ -306,6 +307,10 @@ const fieldFlow = (req) =>
|
|
|
306
307
|
}
|
|
307
308
|
|
|
308
309
|
await field.update(fldRow);
|
|
310
|
+
Trigger.emitEvent("AppChange", `Field ${fldRow.name}`, req.user, {
|
|
311
|
+
entity_type: "Field",
|
|
312
|
+
entity_name: fldRow.name || fldRow.label,
|
|
313
|
+
});
|
|
309
314
|
} catch (e) {
|
|
310
315
|
return {
|
|
311
316
|
redirect: `/table/${context.table_id}`,
|
|
@@ -315,6 +320,10 @@ const fieldFlow = (req) =>
|
|
|
315
320
|
} else {
|
|
316
321
|
try {
|
|
317
322
|
await Field.create(fldRow);
|
|
323
|
+
Trigger.emitEvent("AppChange", `Field ${fldRow.name}`, req.user, {
|
|
324
|
+
entity_type: "Field",
|
|
325
|
+
entity_name: fldRow.name || fldRow.label,
|
|
326
|
+
});
|
|
318
327
|
} catch (e) {
|
|
319
328
|
return {
|
|
320
329
|
redirect: `/table/${context.table_id}`,
|
package/routes/menu.js
CHANGED
package/routes/notifications.js
CHANGED
|
@@ -199,23 +199,31 @@ router.post(
|
|
|
199
199
|
error_catcher(async (req, res) => {
|
|
200
200
|
const role = req.user?.role_id || 100;
|
|
201
201
|
if (role === 100) {
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
const msg = req.__("You must be logged in to share");
|
|
203
|
+
if (!req.smr) {
|
|
204
|
+
req.flash("error", msg);
|
|
205
|
+
res.redirect("/auth/login");
|
|
206
|
+
} else res.json({ error: msg });
|
|
204
207
|
} else if (!getState().getConfig("pwa_share_to_enabled", false)) {
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
const msg = req.__("Sharing not enabled");
|
|
209
|
+
if (!req.smr) {
|
|
210
|
+
req.flash("error", msg);
|
|
211
|
+
res.redirect("/");
|
|
212
|
+
} else res.json({ error: msg });
|
|
207
213
|
} else {
|
|
208
214
|
Trigger.emitEvent("ReceiveMobileShareData", null, req.user, {
|
|
209
215
|
row: req.body,
|
|
210
216
|
});
|
|
211
|
-
req.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
217
|
+
if (!req.smr) {
|
|
218
|
+
req.flash(
|
|
219
|
+
"success",
|
|
220
|
+
req.__(
|
|
221
|
+
"Shared: %s",
|
|
222
|
+
req.body.title || req.body.text || req.body.url || ""
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
res.status(303).redirect("/");
|
|
226
|
+
} else res.json({ success: "ok" });
|
|
219
227
|
}
|
|
220
228
|
})
|
|
221
229
|
);
|
package/routes/page.js
CHANGED
|
@@ -68,13 +68,13 @@ const runPage = async (page, req, res, tic) => {
|
|
|
68
68
|
no_menu: page.attributes?.no_menu,
|
|
69
69
|
requestFluidLayout: page.attributes?.request_fluid_layout,
|
|
70
70
|
} || `${page.name} page`,
|
|
71
|
-
add_edit_bar({
|
|
71
|
+
req.smr ? contents : add_edit_bar({
|
|
72
72
|
role,
|
|
73
73
|
title: page.name,
|
|
74
74
|
what: req.__("Page"),
|
|
75
75
|
url: `/pageedit/edit/${encodeURIComponent(page.name)}`,
|
|
76
76
|
contents,
|
|
77
|
-
})
|
|
77
|
+
}),
|
|
78
78
|
);
|
|
79
79
|
} else {
|
|
80
80
|
getState().log(2, `Page ${page.name} not authorized`);
|