@saltcorn/server 0.9.5-beta.2 → 0.9.5-beta.20
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/errors.js +1 -0
- package/help/Cordova Builder.tmd +13 -0
- package/load_plugins.js +89 -144
- package/locales/en.json +30 -1
- package/locales/ru.json +1134 -1101
- package/package.json +16 -13
- package/public/log_viewer_utils.js +17 -1
- package/public/saltcorn-common.js +154 -24
- package/public/saltcorn.css +4 -0
- package/public/saltcorn.js +67 -46
- package/restart_watcher.js +2 -0
- package/routes/actions.js +17 -1
- package/routes/admin.js +389 -20
- package/routes/api.js +4 -1
- package/routes/common_lists.js +1 -1
- package/routes/fields.js +13 -8
- package/routes/homepage.js +6 -3
- package/routes/menu.js +12 -3
- package/routes/pageedit.js +1 -1
- package/routes/plugins.js +265 -29
- package/routes/search.js +28 -2
- package/routes/tables.js +4 -0
- package/serve.js +1 -1
- package/tests/page.test.js +11 -1
package/routes/pageedit.js
CHANGED
|
@@ -196,7 +196,7 @@ const pageBuilderData = async (req, context) => {
|
|
|
196
196
|
f.required = false;
|
|
197
197
|
if (f.type && f.type.name === "Bool") f.fieldview = "tristate";
|
|
198
198
|
|
|
199
|
-
await f.fill_fkey_options(true);
|
|
199
|
+
//await f.fill_fkey_options(true);
|
|
200
200
|
fixed_state_fields[view.name].push(f);
|
|
201
201
|
if (table.name === "users" && f.primary_key)
|
|
202
202
|
fixed_state_fields[view.name].push(
|
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,
|
|
@@ -57,6 +58,7 @@ const { flash_restart } = require("../markup/admin.js");
|
|
|
57
58
|
const { sleep, removeNonWordChars } = require("@saltcorn/data/utils");
|
|
58
59
|
const { loadAllPlugins } = require("../load_plugins");
|
|
59
60
|
const npmFetch = require("npm-registry-fetch");
|
|
61
|
+
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
64
|
* @type {object}
|
|
@@ -545,6 +547,16 @@ const plugin_store_html = (items, req) => {
|
|
|
545
547
|
};
|
|
546
548
|
};
|
|
547
549
|
|
|
550
|
+
const flash_relogin = (req, exposedConfigs) => {
|
|
551
|
+
req.flash(
|
|
552
|
+
"warning",
|
|
553
|
+
req.__(
|
|
554
|
+
"To see changes for '%s' in show-if-formulas, users need to relogin",
|
|
555
|
+
exposedConfigs.join(", ")
|
|
556
|
+
)
|
|
557
|
+
);
|
|
558
|
+
};
|
|
559
|
+
|
|
548
560
|
/**
|
|
549
561
|
* @name get
|
|
550
562
|
* @function
|
|
@@ -696,7 +708,12 @@ router.get(
|
|
|
696
708
|
label: "Reload page to see changes",
|
|
697
709
|
id: "btnReloadNow",
|
|
698
710
|
class: "btn btn-outline-secondary",
|
|
699
|
-
onclick:
|
|
711
|
+
onclick: `if (window.savingViewConfig)
|
|
712
|
+
notifyAlert({
|
|
713
|
+
type: 'danger',
|
|
714
|
+
text: 'Still saving, please wait',
|
|
715
|
+
});
|
|
716
|
+
else location.reload();`,
|
|
700
717
|
},
|
|
701
718
|
];
|
|
702
719
|
wfres.renderForm.onChange = `${
|
|
@@ -704,25 +721,31 @@ router.get(
|
|
|
704
721
|
};$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
705
722
|
}
|
|
706
723
|
|
|
707
|
-
res.sendWrap(
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
724
|
+
res.sendWrap(
|
|
725
|
+
{
|
|
726
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
727
|
+
headers: wfres.renderForm?.additionalHeaders || [],
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
above: [
|
|
731
|
+
{
|
|
732
|
+
type: "breadcrumbs",
|
|
733
|
+
crumbs: [
|
|
734
|
+
{ text: req.__("Settings"), href: "/settings" },
|
|
735
|
+
{ text: req.__("Module store"), href: "/plugins" },
|
|
736
|
+
{ text: plugin.name },
|
|
737
|
+
],
|
|
738
|
+
},
|
|
739
|
+
{
|
|
740
|
+
type: "card",
|
|
741
|
+
class: "mt-0",
|
|
742
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
743
|
+
titleAjaxIndicator: true,
|
|
744
|
+
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
745
|
+
},
|
|
746
|
+
],
|
|
747
|
+
}
|
|
748
|
+
);
|
|
726
749
|
})
|
|
727
750
|
);
|
|
728
751
|
|
|
@@ -769,17 +792,21 @@ router.post(
|
|
|
769
792
|
contents: renderForm(wfres.renderForm, req.csrfToken()),
|
|
770
793
|
});
|
|
771
794
|
} else {
|
|
772
|
-
|
|
795
|
+
const newCfg = wfres.cleanup ? wfres.context : wfres;
|
|
796
|
+
plugin.configuration = newCfg;
|
|
773
797
|
await plugin.upsert();
|
|
774
798
|
await load_plugins.loadPlugin(plugin);
|
|
775
799
|
const instore = await Plugin.store_plugins_available();
|
|
776
800
|
const store_plugin = instore.find((p) => p.name === plugin.name);
|
|
777
801
|
if (store_plugin && store_plugin.has_auth) flash_restart(req);
|
|
802
|
+
if (module.exposed_configs?.length > 0)
|
|
803
|
+
flash_relogin(req, module.exposed_configs);
|
|
778
804
|
getState().processSend({
|
|
779
805
|
refresh_plugin_cfg: plugin.name,
|
|
780
806
|
tenant: db.getTenantSchema(),
|
|
781
807
|
});
|
|
782
808
|
if (module.layout) await sleep(500); // Allow other workers to reload this plugin
|
|
809
|
+
if (wfres.cleanup) await wfres.cleanup();
|
|
783
810
|
res.redirect("/plugins");
|
|
784
811
|
}
|
|
785
812
|
})
|
|
@@ -798,22 +825,223 @@ router.post(
|
|
|
798
825
|
const flow = module.configuration_workflow();
|
|
799
826
|
const step = await flow.singleStepForm(req.body, req);
|
|
800
827
|
if (step?.renderForm) {
|
|
801
|
-
if (
|
|
828
|
+
if (step.renderForm.hasErrors || step.savingErrors)
|
|
829
|
+
res.status(400).send(step.savingErrors || "Error");
|
|
830
|
+
else {
|
|
802
831
|
plugin.configuration = {
|
|
803
832
|
...plugin.configuration,
|
|
804
833
|
...step.renderForm.values,
|
|
805
834
|
};
|
|
806
835
|
await plugin.upsert();
|
|
807
836
|
await load_plugins.loadPlugin(plugin);
|
|
808
|
-
getState().processSend({
|
|
809
|
-
refresh_plugin_cfg: plugin.name,
|
|
810
|
-
tenant: db.getTenantSchema(),
|
|
811
|
-
});
|
|
812
|
-
res.json({ success: "ok" });
|
|
813
837
|
}
|
|
838
|
+
getState().processSend({
|
|
839
|
+
refresh_plugin_cfg: plugin.name,
|
|
840
|
+
tenant: db.getTenantSchema(),
|
|
841
|
+
});
|
|
842
|
+
res.json({ success: "ok" });
|
|
814
843
|
}
|
|
815
844
|
})
|
|
816
845
|
);
|
|
846
|
+
|
|
847
|
+
router.get(
|
|
848
|
+
"/user_configure/:name",
|
|
849
|
+
loggedIn,
|
|
850
|
+
error_catcher(async (req, res) => {
|
|
851
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
852
|
+
if (!user) {
|
|
853
|
+
req.flash("error", req.__("Not authorized"));
|
|
854
|
+
return res.redirect("/");
|
|
855
|
+
}
|
|
856
|
+
const { name } = req.params;
|
|
857
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
858
|
+
if (!plugin) {
|
|
859
|
+
req.flash("warning", req.__("Module not found"));
|
|
860
|
+
return res.redirect("/auth/settings");
|
|
861
|
+
}
|
|
862
|
+
let module = getState().plugins[plugin.name];
|
|
863
|
+
if (!module) {
|
|
864
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
865
|
+
}
|
|
866
|
+
const form = await module.user_config_form({
|
|
867
|
+
...(plugin.configuration || {}),
|
|
868
|
+
...(user._attributes?.layout?.config || {}),
|
|
869
|
+
});
|
|
870
|
+
form.action = `/plugins/user_configure/${encodeURIComponent(plugin.name)}`;
|
|
871
|
+
form.onChange = `applyViewConfig(this, '/plugins/user_saveconfig/${encodeURIComponent(
|
|
872
|
+
name
|
|
873
|
+
)}', null, event);$('#btnReloadNow').removeClass('btn-outline-secondary').addClass('btn-secondary')`;
|
|
874
|
+
|
|
875
|
+
form.additionalButtons = [
|
|
876
|
+
{
|
|
877
|
+
label: "Reload page to see changes",
|
|
878
|
+
id: "btnReloadNow",
|
|
879
|
+
class: "btn btn-outline-secondary",
|
|
880
|
+
onclick: "location.reload();",
|
|
881
|
+
},
|
|
882
|
+
];
|
|
883
|
+
form.submitLabel = req.__("Finish") + " »";
|
|
884
|
+
res.sendWrap(
|
|
885
|
+
{
|
|
886
|
+
title: req.__(`Configure %s Plugin for %s`, plugin.name, user.email),
|
|
887
|
+
headers: form.additionalHeaders || [],
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
above: [
|
|
891
|
+
{
|
|
892
|
+
type: "breadcrumbs",
|
|
893
|
+
crumbs: [
|
|
894
|
+
{ text: req.__("Settings"), href: "/settings" },
|
|
895
|
+
{ text: req.__("Module store"), href: "/plugins" },
|
|
896
|
+
{ text: plugin.name },
|
|
897
|
+
],
|
|
898
|
+
},
|
|
899
|
+
{
|
|
900
|
+
type: "card",
|
|
901
|
+
class: "mt-0",
|
|
902
|
+
title: req.__(`Configure %s Plugin`, plugin.name),
|
|
903
|
+
titleAjaxIndicator: true,
|
|
904
|
+
contents: renderForm(form, req.csrfToken()),
|
|
905
|
+
},
|
|
906
|
+
],
|
|
907
|
+
}
|
|
908
|
+
);
|
|
909
|
+
})
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
router.post(
|
|
913
|
+
"/user_configure/:name",
|
|
914
|
+
loggedIn,
|
|
915
|
+
error_catcher(async (req, res) => {
|
|
916
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
917
|
+
if (!user) {
|
|
918
|
+
req.flash("error", req.__("Not authorized"));
|
|
919
|
+
return res.redirect("/");
|
|
920
|
+
}
|
|
921
|
+
const { name } = req.params;
|
|
922
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
923
|
+
let module = getState().plugins[plugin.name];
|
|
924
|
+
if (!module) {
|
|
925
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
926
|
+
}
|
|
927
|
+
const form = await module.user_config_form({
|
|
928
|
+
...(plugin.configuration || {}),
|
|
929
|
+
...(user._attributes?.layout?.config || {}),
|
|
930
|
+
});
|
|
931
|
+
const valResult = form.validate(req.body);
|
|
932
|
+
if (form.hasErrors) {
|
|
933
|
+
req.flash("warning", req.__("An error occurred"));
|
|
934
|
+
return res.sendWrap(
|
|
935
|
+
req.__(`Configure %s Plugin for %s`, plugin.name, user.email),
|
|
936
|
+
renderForm(form, req.csrfToken())
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
const values = valResult.success;
|
|
940
|
+
values.is_user_config = true;
|
|
941
|
+
const userAttrs = user._attributes ? { ...user._attributes } : {};
|
|
942
|
+
userAttrs.layout = {
|
|
943
|
+
plugin: plugin.name,
|
|
944
|
+
config: values,
|
|
945
|
+
};
|
|
946
|
+
await user.update({ _attributes: userAttrs });
|
|
947
|
+
getState().userLayouts[req.user.email] = module.layout({
|
|
948
|
+
...(plugin.configuration ? plugin.configuration : {}),
|
|
949
|
+
...values,
|
|
950
|
+
});
|
|
951
|
+
const sessionUser = req.session?.passport?.user;
|
|
952
|
+
if (sessionUser) {
|
|
953
|
+
const pluginName = module.plugin_name;
|
|
954
|
+
if (sessionUser.attributes) {
|
|
955
|
+
const oldAttrs = sessionUser.attributes[pluginName] || {};
|
|
956
|
+
sessionUser.attributes[pluginName] = { ...oldAttrs, ...values };
|
|
957
|
+
} else sessionUser.attributes = { [pluginName]: values };
|
|
958
|
+
}
|
|
959
|
+
getState().processSend({
|
|
960
|
+
refresh_plugin_cfg: plugin.name,
|
|
961
|
+
tenant: db.getTenantSchema(),
|
|
962
|
+
});
|
|
963
|
+
if (module.layout) await sleep(500); // Allow other workers to reload this plugin
|
|
964
|
+
res.redirect("/auth/settings");
|
|
965
|
+
})
|
|
966
|
+
);
|
|
967
|
+
|
|
968
|
+
router.post(
|
|
969
|
+
"/user_saveconfig/:name",
|
|
970
|
+
loggedIn,
|
|
971
|
+
error_catcher(async (req, res) => {
|
|
972
|
+
const user = await User.findOne({ id: req.user?.id });
|
|
973
|
+
if (!user) return res.status(401).json({ error: req.__("Not authorized") });
|
|
974
|
+
const { name } = req.params;
|
|
975
|
+
const plugin = await Plugin.findOne({ name: decodeURIComponent(name) });
|
|
976
|
+
let module = getState().plugins[plugin.name];
|
|
977
|
+
if (!module) {
|
|
978
|
+
module = getState().plugins[getState().plugin_module_names[plugin.name]];
|
|
979
|
+
}
|
|
980
|
+
const form = await module.user_config_form({
|
|
981
|
+
...(plugin.configuration || {}),
|
|
982
|
+
...(user._attributes?.layout?.config || {}),
|
|
983
|
+
});
|
|
984
|
+
const valResult = form.validate(req.body);
|
|
985
|
+
if (form.hasErrors) {
|
|
986
|
+
return res.status(400).json({ error: req.__("An error occured") });
|
|
987
|
+
}
|
|
988
|
+
const values = valResult.success;
|
|
989
|
+
values.is_user_config = true;
|
|
990
|
+
const userAttrs = user._attributes ? { ...user._attributes } : {};
|
|
991
|
+
userAttrs.layout = {
|
|
992
|
+
plugin: plugin.name,
|
|
993
|
+
config: values,
|
|
994
|
+
};
|
|
995
|
+
await user.update({ _attributes: userAttrs });
|
|
996
|
+
getState().userLayouts[req.user.email] = module.layout(
|
|
997
|
+
userAttrs.layout.config
|
|
998
|
+
);
|
|
999
|
+
const sessionUser = req.session?.passport?.user;
|
|
1000
|
+
if (sessionUser) {
|
|
1001
|
+
const pluginName = module.plugin_name;
|
|
1002
|
+
if (sessionUser.attributes) {
|
|
1003
|
+
const oldAttrs = sessionUser.attributes[pluginName] || {};
|
|
1004
|
+
sessionUser.attributes[pluginName] = { ...oldAttrs, ...values };
|
|
1005
|
+
} else sessionUser.attributes = { [pluginName]: values };
|
|
1006
|
+
}
|
|
1007
|
+
getState().processSend({
|
|
1008
|
+
refresh_plugin_cfg: plugin.name,
|
|
1009
|
+
tenant: db.getTenantSchema(),
|
|
1010
|
+
});
|
|
1011
|
+
res.json({ success: "ok" });
|
|
1012
|
+
})
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
router.post(
|
|
1016
|
+
"/remove_user_layout",
|
|
1017
|
+
loggedIn,
|
|
1018
|
+
error_catcher(async (req, res) => {
|
|
1019
|
+
const user = await User.findOne({ id: req.user.id });
|
|
1020
|
+
if (!user) {
|
|
1021
|
+
return res.status(401).json({ error: req.__("Not authorized") });
|
|
1022
|
+
} else if (user._attributes?.layout) {
|
|
1023
|
+
const userAttrs = { ...user._attributes };
|
|
1024
|
+
const plugin = userAttrs.layout.plugin;
|
|
1025
|
+
delete userAttrs.layout;
|
|
1026
|
+
await user.update({ _attributes: userAttrs });
|
|
1027
|
+
getState().userLayouts[req.user.email] = null;
|
|
1028
|
+
let module = getState().plugins[plugin];
|
|
1029
|
+
if (!module) {
|
|
1030
|
+
module = getState().plugins[getState().plugin_module_names[plugin]];
|
|
1031
|
+
}
|
|
1032
|
+
const pluginName = module.plugin_name;
|
|
1033
|
+
const sessionUser = req.session?.passport?.user;
|
|
1034
|
+
if (sessionUser?.attributes[pluginName])
|
|
1035
|
+
sessionUser.attributes[pluginName] = {};
|
|
1036
|
+
getState().processSend({
|
|
1037
|
+
refresh_plugin_cfg: plugin,
|
|
1038
|
+
tenant: db.getTenantSchema(),
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
res.json({ success: "ok", reload_page: true });
|
|
1042
|
+
})
|
|
1043
|
+
);
|
|
1044
|
+
|
|
817
1045
|
/**
|
|
818
1046
|
* @name get/new
|
|
819
1047
|
* @function
|
|
@@ -1178,6 +1406,7 @@ router.post(
|
|
|
1178
1406
|
getState().getConfig("development_mode", false)
|
|
1179
1407
|
) {
|
|
1180
1408
|
await plugin.delete();
|
|
1409
|
+
await new PluginInstaller(plugin).remove();
|
|
1181
1410
|
req.flash("success", req.__(`Module %s removed.`, plugin.name));
|
|
1182
1411
|
} else {
|
|
1183
1412
|
req.flash(
|
|
@@ -1241,7 +1470,12 @@ router.post(
|
|
|
1241
1470
|
res.redirect(`/plugins`);
|
|
1242
1471
|
return;
|
|
1243
1472
|
}
|
|
1244
|
-
await load_plugins.loadAndSaveNewPlugin(
|
|
1473
|
+
const msgs = await load_plugins.loadAndSaveNewPlugin(
|
|
1474
|
+
plugin,
|
|
1475
|
+
forceReInstall,
|
|
1476
|
+
undefined,
|
|
1477
|
+
req.__
|
|
1478
|
+
);
|
|
1245
1479
|
const plugin_module = getState().plugins[name];
|
|
1246
1480
|
await sleep(1000); // Allow other workers to load this plugin
|
|
1247
1481
|
await getState().refresh_views();
|
|
@@ -1255,9 +1489,11 @@ router.post(
|
|
|
1255
1489
|
plugin_db.name
|
|
1256
1490
|
)
|
|
1257
1491
|
);
|
|
1492
|
+
if (msgs?.length > 0) req.flash("warning", msgs.join("<br>"));
|
|
1258
1493
|
res.redirect(`/plugins/configure/${plugin_db.name}`);
|
|
1259
1494
|
} else {
|
|
1260
1495
|
req.flash("success", req.__(`Module %s installed`, plugin.name));
|
|
1496
|
+
if (msgs?.length > 0) req.flash("warning", msgs.join("<br>"));
|
|
1261
1497
|
res.redirect(`/plugins`);
|
|
1262
1498
|
}
|
|
1263
1499
|
})
|
package/routes/search.js
CHANGED
|
@@ -52,6 +52,12 @@ const searchConfigForm = (tables, views, req) => {
|
|
|
52
52
|
attributes: { options: ok_views.map((v) => v.name).join() },
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
+
fields.push({
|
|
56
|
+
name: "search_table_description",
|
|
57
|
+
label: req.__("Description header"),
|
|
58
|
+
sublabel: req.__("Use table description instead of name as header"),
|
|
59
|
+
type: "Bool",
|
|
60
|
+
});
|
|
55
61
|
const blurb1 = req.__(
|
|
56
62
|
`Choose views for <a href="/search">search results</a> for each table.<br/>Set to blank to omit table from global search.`
|
|
57
63
|
);
|
|
@@ -84,7 +90,11 @@ router.get(
|
|
|
84
90
|
const views = await View.find({}, { orderBy: "name" });
|
|
85
91
|
const tables = await Table.find();
|
|
86
92
|
const form = searchConfigForm(tables, views, req);
|
|
87
|
-
form.values = getState().getConfig("globalSearch");
|
|
93
|
+
form.values = getState().getConfig("globalSearch", {});
|
|
94
|
+
form.values.search_table_description = getState().getConfig(
|
|
95
|
+
"search_table_description",
|
|
96
|
+
false
|
|
97
|
+
);
|
|
88
98
|
send_infoarch_page({
|
|
89
99
|
res,
|
|
90
100
|
req,
|
|
@@ -116,6 +126,13 @@ router.post(
|
|
|
116
126
|
const result = form.validate(req.body);
|
|
117
127
|
|
|
118
128
|
if (result.success) {
|
|
129
|
+
const search_table_description =
|
|
130
|
+
!!result.success.search_table_description;
|
|
131
|
+
await getState().setConfig(
|
|
132
|
+
"search_table_description",
|
|
133
|
+
search_table_description
|
|
134
|
+
);
|
|
135
|
+
delete result.success.search_table_description;
|
|
119
136
|
await getState().setConfig("globalSearch", result.success);
|
|
120
137
|
if (!req.xhr) res.redirect("/search/config");
|
|
121
138
|
else res.json({ success: "ok" });
|
|
@@ -175,6 +192,10 @@ const runSearch = async ({ q, _page, table }, req, res) => {
|
|
|
175
192
|
res.redirect("/");
|
|
176
193
|
return;
|
|
177
194
|
}
|
|
195
|
+
const search_table_description = getState().getConfig(
|
|
196
|
+
"search_table_description",
|
|
197
|
+
false
|
|
198
|
+
);
|
|
178
199
|
const current_page = parseInt(_page) || 1;
|
|
179
200
|
const offset = (current_page - 1) * page_size;
|
|
180
201
|
let resp = [];
|
|
@@ -184,6 +205,11 @@ const runSearch = async ({ q, _page, table }, req, res) => {
|
|
|
184
205
|
if (!viewName || viewName === "") continue;
|
|
185
206
|
tablesConfigured += 1;
|
|
186
207
|
if (table && tableName !== table) continue;
|
|
208
|
+
let sectionHeader = tableName;
|
|
209
|
+
if (search_table_description) {
|
|
210
|
+
sectionHeader =
|
|
211
|
+
Table.findOne({ name: tableName })?.description || tableName;
|
|
212
|
+
}
|
|
187
213
|
const view = await View.findOne({ name: viewName });
|
|
188
214
|
if (!view)
|
|
189
215
|
throw new InvalidConfiguration(
|
|
@@ -209,7 +235,7 @@ const runSearch = async ({ q, _page, table }, req, res) => {
|
|
|
209
235
|
tablesWithResults.push(tableName);
|
|
210
236
|
resp.push({
|
|
211
237
|
type: "card",
|
|
212
|
-
title: span({ id: tableName },
|
|
238
|
+
title: span({ id: tableName }, sectionHeader),
|
|
213
239
|
contents: vresps.map((vr) => vr.html).join("<hr>") + paginate,
|
|
214
240
|
});
|
|
215
241
|
}
|
package/routes/tables.js
CHANGED
|
@@ -167,6 +167,10 @@ const tableForm = async (table, req) => {
|
|
|
167
167
|
label: req.__("Version history"),
|
|
168
168
|
sublabel: req.__("Track table data changes over time"),
|
|
169
169
|
name: "versioned",
|
|
170
|
+
attributes: {
|
|
171
|
+
onChange:
|
|
172
|
+
"if(!this.checked && !confirm('Are you sure? This will delete all history')) {this.checked = true; return false}",
|
|
173
|
+
},
|
|
170
174
|
type: "Bool",
|
|
171
175
|
},
|
|
172
176
|
...(table.name === "users"
|
package/serve.js
CHANGED
|
@@ -105,7 +105,7 @@ const initMaster = async ({ disableMigrate }, useClusterAdaptor = true) => {
|
|
|
105
105
|
// migrate database
|
|
106
106
|
if (!disableMigrate) await migrate(db.connectObj.default_schema, true);
|
|
107
107
|
// load all plugins
|
|
108
|
-
await loadAllPlugins();
|
|
108
|
+
await loadAllPlugins(true);
|
|
109
109
|
// switch on sql logging - but it was initiated before???
|
|
110
110
|
if (getState().getConfig("log_sql", false)) db.set_sql_logging();
|
|
111
111
|
if (db.is_it_multi_tenant()) {
|
package/tests/page.test.js
CHANGED
|
@@ -30,7 +30,17 @@ const prepHtmlFiles = async () => {
|
|
|
30
30
|
const html = `<html><head><title>Landing page</title></head><body><h1>${content}</h1></body></html>`;
|
|
31
31
|
if (!existsSync(scFolder)) await File.new_folder(folder);
|
|
32
32
|
if (!existsSync(join(scFolder, name))) {
|
|
33
|
-
|
|
33
|
+
const file = await File.from_contents(
|
|
34
|
+
name,
|
|
35
|
+
"text/html",
|
|
36
|
+
html,
|
|
37
|
+
1,
|
|
38
|
+
1,
|
|
39
|
+
folder
|
|
40
|
+
);
|
|
41
|
+
file.location = File.absPathToServePath(file.location);
|
|
42
|
+
|
|
43
|
+
return file;
|
|
34
44
|
} else {
|
|
35
45
|
const file = await File.from_file_on_disk(name, scFolder);
|
|
36
46
|
fs.writeFileSync(file.location, html);
|