@saltcorn/server 0.9.5-beta.14 → 0.9.5-beta.15
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 +53 -0
- package/load_plugins.js +6 -0
- package/locales/en.json +6 -1
- package/package.json +9 -9
- package/public/saltcorn-common.js +5 -2
- package/public/saltcorn.js +3 -0
- package/restart_watcher.js +1 -0
- package/routes/plugins.js +219 -28
package/auth/routes.js
CHANGED
|
@@ -45,6 +45,7 @@ const {
|
|
|
45
45
|
p,
|
|
46
46
|
script,
|
|
47
47
|
domReady,
|
|
48
|
+
button,
|
|
48
49
|
} = require("@saltcorn/markup/tags");
|
|
49
50
|
const {
|
|
50
51
|
available_languages,
|
|
@@ -1362,6 +1363,57 @@ const userSettings = async ({ req, res, pwform, user }) => {
|
|
|
1362
1363
|
),
|
|
1363
1364
|
],
|
|
1364
1365
|
};
|
|
1366
|
+
let themeCfgCard;
|
|
1367
|
+
const layoutPlugin = getState().getLayoutPlugin(user);
|
|
1368
|
+
const modNames = getState().plugin_module_names;
|
|
1369
|
+
const pluginName = layoutPlugin.plugin_name;
|
|
1370
|
+
let safeName = pluginName;
|
|
1371
|
+
for (const [k, v] of Object.entries(modNames)) {
|
|
1372
|
+
if (v === pluginName) safeName = k;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
const hasUserConfigs =
|
|
1376
|
+
layoutPlugin.user_config_form &&
|
|
1377
|
+
(await layoutPlugin.user_config_form(
|
|
1378
|
+
getState().plugin_cfgs[pluginName] || {}
|
|
1379
|
+
)) !== null;
|
|
1380
|
+
themeCfgCard = {
|
|
1381
|
+
type: "card",
|
|
1382
|
+
title: req.__("Layout"),
|
|
1383
|
+
contents: [
|
|
1384
|
+
div(
|
|
1385
|
+
hasUserConfigs
|
|
1386
|
+
? req.__("Adjust the the theme for this user")
|
|
1387
|
+
: req.__("The current theme has no user specific settings")
|
|
1388
|
+
),
|
|
1389
|
+
hasUserConfigs
|
|
1390
|
+
? div(
|
|
1391
|
+
{
|
|
1392
|
+
class: "mt-4",
|
|
1393
|
+
},
|
|
1394
|
+
|
|
1395
|
+
a(
|
|
1396
|
+
{
|
|
1397
|
+
class: "btn btn-primary",
|
|
1398
|
+
role: "button",
|
|
1399
|
+
href: `/plugins/user_configure/${encodeURIComponent(safeName)}`,
|
|
1400
|
+
title: req.__("Configure theme"),
|
|
1401
|
+
},
|
|
1402
|
+
req.__("Configure")
|
|
1403
|
+
),
|
|
1404
|
+
button(
|
|
1405
|
+
{
|
|
1406
|
+
class: "btn btn-primary ms-2",
|
|
1407
|
+
onclick: "ajax_post('/plugins/remove_user_layout')",
|
|
1408
|
+
title: req.__("Remove all user specific theme settings"),
|
|
1409
|
+
},
|
|
1410
|
+
req.__("Reset")
|
|
1411
|
+
)
|
|
1412
|
+
)
|
|
1413
|
+
: "",
|
|
1414
|
+
],
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1365
1417
|
return {
|
|
1366
1418
|
above: [
|
|
1367
1419
|
{
|
|
@@ -1434,6 +1486,7 @@ const userSettings = async ({ req, res, pwform, user }) => {
|
|
|
1434
1486
|
]
|
|
1435
1487
|
: []),
|
|
1436
1488
|
...(apikeycard ? [apikeycard] : []),
|
|
1489
|
+
...(themeCfgCard ? [themeCfgCard] : []),
|
|
1437
1490
|
],
|
|
1438
1491
|
};
|
|
1439
1492
|
};
|
package/load_plugins.js
CHANGED
|
@@ -35,6 +35,10 @@ const loadPlugin = async (plugin, force) => {
|
|
|
35
35
|
res.name
|
|
36
36
|
);
|
|
37
37
|
} catch (error) {
|
|
38
|
+
getState().log(
|
|
39
|
+
3,
|
|
40
|
+
`Error loading plugin ${plugin.name}: ${error.message || error}`
|
|
41
|
+
);
|
|
38
42
|
if (force) {
|
|
39
43
|
// remove the install dir and try again
|
|
40
44
|
await loader.remove();
|
|
@@ -48,6 +52,7 @@ const loadPlugin = async (plugin, force) => {
|
|
|
48
52
|
);
|
|
49
53
|
}
|
|
50
54
|
}
|
|
55
|
+
if (res.plugin_module.user_config_form) getState().refreshUserLayouts();
|
|
51
56
|
if (res.plugin_module.onLoad) {
|
|
52
57
|
try {
|
|
53
58
|
await res.plugin_module.onLoad(plugin.configuration);
|
|
@@ -83,6 +88,7 @@ const loadAllPlugins = async (force) => {
|
|
|
83
88
|
console.error(e);
|
|
84
89
|
}
|
|
85
90
|
}
|
|
91
|
+
await getState().refreshUserLayouts();
|
|
86
92
|
await getState().refresh(true);
|
|
87
93
|
};
|
|
88
94
|
|
package/locales/en.json
CHANGED
|
@@ -1394,5 +1394,10 @@
|
|
|
1394
1394
|
"The plugin was corrupted and had to be repaired. We recommend restarting your server.": "The plugin was corrupted and had to be repaired. We recommend restarting your server.",
|
|
1395
1395
|
"%s code page": "%s code page",
|
|
1396
1396
|
"Constants and function code": "Constants and function code",
|
|
1397
|
-
"Delete code page": "Delete code page"
|
|
1397
|
+
"Delete code page": "Delete code page",
|
|
1398
|
+
"Adjust the the theme for this user": "Adjust the the theme for this user",
|
|
1399
|
+
"Configure theme": "Configure theme",
|
|
1400
|
+
"Remove all user specific theme settings": "Remove all user specific theme settings",
|
|
1401
|
+
"Configure %s Plugin for %s": "Configure %s Plugin for %s",
|
|
1402
|
+
"The current theme has no user specific settings": "The current theme has no user specific settings"
|
|
1398
1403
|
}
|
package/package.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.5-beta.
|
|
3
|
+
"version": "0.9.5-beta.15",
|
|
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
9
|
"@aws-sdk/client-s3": "^3.451.0",
|
|
10
|
-
"@saltcorn/base-plugin": "0.9.5-beta.
|
|
11
|
-
"@saltcorn/builder": "0.9.5-beta.
|
|
12
|
-
"@saltcorn/data": "0.9.5-beta.
|
|
13
|
-
"@saltcorn/admin-models": "0.9.5-beta.
|
|
14
|
-
"@saltcorn/filemanager": "0.9.5-beta.
|
|
15
|
-
"@saltcorn/markup": "0.9.5-beta.
|
|
16
|
-
"@saltcorn/plugins-loader": "0.9.5-beta.
|
|
17
|
-
"@saltcorn/sbadmin2": "0.9.5-beta.
|
|
10
|
+
"@saltcorn/base-plugin": "0.9.5-beta.15",
|
|
11
|
+
"@saltcorn/builder": "0.9.5-beta.15",
|
|
12
|
+
"@saltcorn/data": "0.9.5-beta.15",
|
|
13
|
+
"@saltcorn/admin-models": "0.9.5-beta.15",
|
|
14
|
+
"@saltcorn/filemanager": "0.9.5-beta.15",
|
|
15
|
+
"@saltcorn/markup": "0.9.5-beta.15",
|
|
16
|
+
"@saltcorn/plugins-loader": "0.9.5-beta.15",
|
|
17
|
+
"@saltcorn/sbadmin2": "0.9.5-beta.15",
|
|
18
18
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
19
19
|
"@socket.io/sticky": "^1.0.1",
|
|
20
20
|
"adm-zip": "0.5.10",
|
|
@@ -1184,8 +1184,10 @@ async function common_done(res, viewname, isWeb = true) {
|
|
|
1184
1184
|
await handle(res.notify_success, (text) =>
|
|
1185
1185
|
notifyAlert({ type: "success", text: text })
|
|
1186
1186
|
);
|
|
1187
|
-
if (res.set_fields && viewname) {
|
|
1188
|
-
const form = $(
|
|
1187
|
+
if (res.set_fields && (viewname || res.set_fields._viewname)) {
|
|
1188
|
+
const form = $(
|
|
1189
|
+
`form[data-viewname="${res.set_fields._viewname || viewname}"]`
|
|
1190
|
+
);
|
|
1189
1191
|
if (form.length === 0 && set_state_fields) {
|
|
1190
1192
|
// assume this is a filter
|
|
1191
1193
|
set_state_fields(
|
|
@@ -1195,6 +1197,7 @@ async function common_done(res, viewname, isWeb = true) {
|
|
|
1195
1197
|
);
|
|
1196
1198
|
} else {
|
|
1197
1199
|
Object.keys(res.set_fields).forEach((k) => {
|
|
1200
|
+
if (k === "_viewname") return;
|
|
1198
1201
|
const input = form.find(
|
|
1199
1202
|
`input[name=${k}], textarea[name=${k}], select[name=${k}]`
|
|
1200
1203
|
);
|
package/public/saltcorn.js
CHANGED
|
@@ -522,6 +522,7 @@ function applyViewConfig(e, url, k, event) {
|
|
|
522
522
|
cfg[item.name] = item.value;
|
|
523
523
|
});
|
|
524
524
|
ajax_indicator(true, e);
|
|
525
|
+
window.savingViewConfig = true;
|
|
525
526
|
$.ajax(url, {
|
|
526
527
|
type: "POST",
|
|
527
528
|
dataType: "json",
|
|
@@ -531,9 +532,11 @@ function applyViewConfig(e, url, k, event) {
|
|
|
531
532
|
},
|
|
532
533
|
data: JSON.stringify(cfg),
|
|
533
534
|
error: function (request) {
|
|
535
|
+
window.savingViewConfig = false;
|
|
534
536
|
ajax_indicate_error(e, request);
|
|
535
537
|
},
|
|
536
538
|
success: function (res) {
|
|
539
|
+
window.savingViewConfig = false;
|
|
537
540
|
ajax_indicator(false);
|
|
538
541
|
k && k(res);
|
|
539
542
|
!k && updateViewPreview();
|
package/restart_watcher.js
CHANGED
package/routes/plugins.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const Router = require("express-promise-router");
|
|
9
|
-
const { isAdmin, error_catcher } = require("./utils.js");
|
|
9
|
+
const { isAdmin, loggedIn, error_catcher } = require("./utils.js");
|
|
10
10
|
const { renderForm, link, post_btn } = require("@saltcorn/markup");
|
|
11
11
|
const {
|
|
12
12
|
getState,
|
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
const Form = require("@saltcorn/data/models/form");
|
|
17
17
|
const Field = require("@saltcorn/data/models/field");
|
|
18
18
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
19
|
+
const User = require("@saltcorn/data/models/user");
|
|
19
20
|
const { fetch_available_packs } = require("@saltcorn/admin-models/models/pack");
|
|
20
21
|
const {
|
|
21
22
|
upgrade_all_tenants_plugins,
|
|
@@ -697,7 +698,12 @@ router.get(
|
|
|
697
698
|
label: "Reload page to see changes",
|
|
698
699
|
id: "btnReloadNow",
|
|
699
700
|
class: "btn btn-outline-secondary",
|
|
700
|
-
onclick:
|
|
701
|
+
onclick: `if (window.savingViewConfig)
|
|
702
|
+
notifyAlert({
|
|
703
|
+
type: 'danger',
|
|
704
|
+
text: 'Still saving, please wait',
|
|
705
|
+
});
|
|
706
|
+
else location.reload();`,
|
|
701
707
|
},
|
|
702
708
|
];
|
|
703
709
|
wfres.renderForm.onChange = `${
|
|
@@ -705,25 +711,31 @@ router.get(
|
|
|
705
711
|
};$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
706
712
|
}
|
|
707
713
|
|
|
708
|
-
res.sendWrap(
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
714
|
+
res.sendWrap(
|
|
715
|
+
{
|
|
716
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
717
|
+
headers: wfres.renderForm?.additionalHeaders || [],
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
above: [
|
|
721
|
+
{
|
|
722
|
+
type: "breadcrumbs",
|
|
723
|
+
crumbs: [
|
|
724
|
+
{ text: req.__("Settings"), href: "/settings" },
|
|
725
|
+
{ text: req.__("Module store"), href: "/plugins" },
|
|
726
|
+
{ text: plugin.name },
|
|
727
|
+
],
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
type: "card",
|
|
731
|
+
class: "mt-0",
|
|
732
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
733
|
+
titleAjaxIndicator: true,
|
|
734
|
+
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
735
|
+
},
|
|
736
|
+
],
|
|
737
|
+
}
|
|
738
|
+
);
|
|
727
739
|
})
|
|
728
740
|
);
|
|
729
741
|
|
|
@@ -770,7 +782,8 @@ router.post(
|
|
|
770
782
|
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
771
783
|
});
|
|
772
784
|
} else {
|
|
773
|
-
|
|
785
|
+
const newCfg = wfres.cleanup ? wfres.context : wfres;
|
|
786
|
+
plugin.configuration = newCfg;
|
|
774
787
|
await plugin.upsert();
|
|
775
788
|
await load_plugins.loadPlugin(plugin);
|
|
776
789
|
const instore = await Plugin.store_plugins_available();
|
|
@@ -781,6 +794,7 @@ router.post(
|
|
|
781
794
|
tenant: db.getTenantSchema(),
|
|
782
795
|
});
|
|
783
796
|
if (module.layout) await sleep(500); // Allow other workers to reload this plugin
|
|
797
|
+
if (wfres.cleanup) await wfres.cleanup();
|
|
784
798
|
res.redirect("/plugins");
|
|
785
799
|
}
|
|
786
800
|
})
|
|
@@ -799,22 +813,199 @@ router.post(
|
|
|
799
813
|
const flow = module.configuration_workflow();
|
|
800
814
|
const step = await flow.singleStepForm(req.body, req);
|
|
801
815
|
if (step?.renderForm) {
|
|
802
|
-
if (
|
|
816
|
+
if (step.renderForm.hasErrors || step.savingErrors)
|
|
817
|
+
res.status(400).send(step.savingErrors || "Error");
|
|
818
|
+
else {
|
|
803
819
|
plugin.configuration = {
|
|
804
820
|
...plugin.configuration,
|
|
805
821
|
...step.renderForm.values,
|
|
806
822
|
};
|
|
807
823
|
await plugin.upsert();
|
|
808
824
|
await load_plugins.loadPlugin(plugin);
|
|
809
|
-
getState().processSend({
|
|
810
|
-
refresh_plugin_cfg: plugin.name,
|
|
811
|
-
tenant: db.getTenantSchema(),
|
|
812
|
-
});
|
|
813
|
-
res.json({ success: "ok" });
|
|
814
825
|
}
|
|
826
|
+
getState().processSend({
|
|
827
|
+
refresh_plugin_cfg: plugin.name,
|
|
828
|
+
tenant: db.getTenantSchema(),
|
|
829
|
+
});
|
|
830
|
+
res.json({ success: "ok" });
|
|
815
831
|
}
|
|
816
832
|
})
|
|
817
833
|
);
|
|
834
|
+
|
|
835
|
+
router.get(
|
|
836
|
+
"/user_configure/:name",
|
|
837
|
+
loggedIn,
|
|
838
|
+
error_catcher(async (req, res) => {
|
|
839
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
840
|
+
if (!user) {
|
|
841
|
+
req.flash("error", req.__("Not authorized"));
|
|
842
|
+
return res.redirect("/");
|
|
843
|
+
}
|
|
844
|
+
const { name } = req.params;
|
|
845
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
846
|
+
if (!plugin) {
|
|
847
|
+
req.flash("warning", req.__("Module not found"));
|
|
848
|
+
return res.redirect("/auth/settings");
|
|
849
|
+
}
|
|
850
|
+
let module = getState().plugins[plugin.name];
|
|
851
|
+
if (!module) {
|
|
852
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
853
|
+
}
|
|
854
|
+
const form = await module.user_config_form({
|
|
855
|
+
...(plugin.configuration || {}),
|
|
856
|
+
...(user._attributes?.layout?.config || {}),
|
|
857
|
+
});
|
|
858
|
+
form.action = `/plugins/user_configure/${encodeURIComponent(plugin.name)}`;
|
|
859
|
+
form.onChange = `applyViewConfig(this, '/plugins/user_saveconfig/${encodeURIComponent(
|
|
860
|
+
name
|
|
861
|
+
)}', null, event);$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
862
|
+
|
|
863
|
+
form.additionalButtons = [
|
|
864
|
+
{
|
|
865
|
+
label: "Reload page to see changes",
|
|
866
|
+
id: "btnReloadNow",
|
|
867
|
+
class: "btn btn-outline-secondary",
|
|
868
|
+
onclick: "location.reload();",
|
|
869
|
+
},
|
|
870
|
+
];
|
|
871
|
+
form.submitLabel = req.__("Finish") + " »";
|
|
872
|
+
res.sendWrap(
|
|
873
|
+
{
|
|
874
|
+
title: req.__(`Configure %s Plugin for %s`, plugin.name, user.email),
|
|
875
|
+
headers: form.additionalHeaders || [],
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
above: [
|
|
879
|
+
{
|
|
880
|
+
type: "breadcrumbs",
|
|
881
|
+
crumbs: [
|
|
882
|
+
{ text: req.__("Settings"), href: "/settings" },
|
|
883
|
+
{ text: req.__("Module store"), href: "/plugins" },
|
|
884
|
+
{ text: plugin.name },
|
|
885
|
+
],
|
|
886
|
+
},
|
|
887
|
+
{
|
|
888
|
+
type: "card",
|
|
889
|
+
class: "mt-0",
|
|
890
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
891
|
+
titleAjaxIndicator: true,
|
|
892
|
+
contents: renderForm(form, req.csrfToken()),
|
|
893
|
+
},
|
|
894
|
+
],
|
|
895
|
+
}
|
|
896
|
+
);
|
|
897
|
+
})
|
|
898
|
+
);
|
|
899
|
+
|
|
900
|
+
router.post(
|
|
901
|
+
"/user_configure/:name",
|
|
902
|
+
loggedIn,
|
|
903
|
+
error_catcher(async (req, res) => {
|
|
904
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
905
|
+
if (!user) {
|
|
906
|
+
req.flash("error", req.__("Not authorized"));
|
|
907
|
+
return res.redirect("/");
|
|
908
|
+
}
|
|
909
|
+
const { name } = req.params;
|
|
910
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
911
|
+
let module = getState().plugins[plugin.name];
|
|
912
|
+
if (!module) {
|
|
913
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
914
|
+
}
|
|
915
|
+
const form = await module.user_config_form({
|
|
916
|
+
...(plugin.configuration || {}),
|
|
917
|
+
...(user._attributes?.layout?.config || {}),
|
|
918
|
+
});
|
|
919
|
+
const valResult = form.validate(req.body);
|
|
920
|
+
if (form.hasErrors) {
|
|
921
|
+
req.flash("warning", req.__("An error occurred"));
|
|
922
|
+
return res.sendWrap(
|
|
923
|
+
req.__(`Configure %s Plugin for %s`, plugin.name, user.email),
|
|
924
|
+
renderForm(form, req.csrfToken())
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
const values = valResult.success;
|
|
928
|
+
values.is_user_config = true;
|
|
929
|
+
const userAttrs = user._attributes ? { ...user._attributes } : {};
|
|
930
|
+
userAttrs.layout = {
|
|
931
|
+
plugin: plugin.name,
|
|
932
|
+
config: values,
|
|
933
|
+
};
|
|
934
|
+
await user.update({ _attributes: userAttrs });
|
|
935
|
+
getState().userLayouts[req.user.email] = module.layout({
|
|
936
|
+
...(plugin.configuration ? plugin.configuration : {}),
|
|
937
|
+
...values,
|
|
938
|
+
});
|
|
939
|
+
getState().processSend({
|
|
940
|
+
refresh_plugin_cfg: plugin.name,
|
|
941
|
+
tenant: db.getTenantSchema(),
|
|
942
|
+
});
|
|
943
|
+
if (module.layout) await sleep(500); // Allow other workers to reload this plugin
|
|
944
|
+
res.redirect("/auth/settings");
|
|
945
|
+
})
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
router.post(
|
|
949
|
+
"/user_saveconfig/:name",
|
|
950
|
+
loggedIn,
|
|
951
|
+
error_catcher(async (req, res) => {
|
|
952
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
953
|
+
if (!user) return res.status(401).json({ error: req.__("Not authorized") });
|
|
954
|
+
const { name } = req.params;
|
|
955
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
956
|
+
let module = getState().plugins[plugin.name];
|
|
957
|
+
if (!module) {
|
|
958
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
959
|
+
}
|
|
960
|
+
const form = await module.user_config_form({
|
|
961
|
+
...(plugin.configuration || {}),
|
|
962
|
+
...(user._attributes?.layout?.config || {}),
|
|
963
|
+
});
|
|
964
|
+
const valResult = form.validate(req.body);
|
|
965
|
+
if (form.hasErrors) {
|
|
966
|
+
return res.status(400).json({ error: req.__("An error occured") });
|
|
967
|
+
}
|
|
968
|
+
const values = valResult.success;
|
|
969
|
+
values.is_user_config = true;
|
|
970
|
+
const userAttrs = user._attributes ? { ...user._attributes } : {};
|
|
971
|
+
userAttrs.layout = {
|
|
972
|
+
plugin: plugin.name,
|
|
973
|
+
config: values,
|
|
974
|
+
};
|
|
975
|
+
await user.update({ _attributes: userAttrs });
|
|
976
|
+
getState().userLayouts[req.user.email] = module.layout(
|
|
977
|
+
userAttrs.layout.config
|
|
978
|
+
);
|
|
979
|
+
getState().processSend({
|
|
980
|
+
refresh_plugin_cfg: plugin.name,
|
|
981
|
+
tenant: db.getTenantSchema(),
|
|
982
|
+
});
|
|
983
|
+
res.json({ success: "ok" });
|
|
984
|
+
})
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
router.post(
|
|
988
|
+
"/remove_user_layout",
|
|
989
|
+
loggedIn,
|
|
990
|
+
error_catcher(async (req, res) => {
|
|
991
|
+
const user = await User.findOne({ id: req.user.id });
|
|
992
|
+
if (!user) {
|
|
993
|
+
return res.status(401).json({ error: req.__("Not authorized") });
|
|
994
|
+
} else if (user._attributes?.layout) {
|
|
995
|
+
const userAttrs = { ...user._attributes };
|
|
996
|
+
const plugin = userAttrs.layout.plugin;
|
|
997
|
+
delete userAttrs.layout;
|
|
998
|
+
await user.update({ _attributes: userAttrs });
|
|
999
|
+
getState().userLayouts[req.user.email] = null;
|
|
1000
|
+
getState().processSend({
|
|
1001
|
+
refresh_plugin_cfg: plugin,
|
|
1002
|
+
tenant: db.getTenantSchema(),
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
res.json({ success: "ok", reload_page: true });
|
|
1006
|
+
})
|
|
1007
|
+
);
|
|
1008
|
+
|
|
818
1009
|
/**
|
|
819
1010
|
* @name get/new
|
|
820
1011
|
* @function
|