@saltcorn/server 0.9.5-beta.0 → 0.9.5-beta.10
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/admin.js +8 -0
- package/auth/routes.js +11 -3
- package/errors.js +1 -0
- package/help/Cordova Builder.tmd +13 -0
- package/load_plugins.js +83 -144
- package/locales/en.json +11 -2
- package/markup/admin.js +22 -0
- package/package.json +16 -13
- package/public/flatpickr.min.js +2 -2
- package/public/log_viewer_utils.js +17 -1
- package/public/saltcorn-common.js +33 -11
- package/public/saltcorn.js +51 -3
- package/public/serviceworker.js +4 -0
- package/restart_watcher.js +1 -0
- package/routes/admin.js +159 -7
- package/routes/common_lists.js +1 -1
- package/routes/fields.js +9 -7
- package/routes/homepage.js +6 -3
- package/routes/notifications.js +13 -2
- package/routes/plugins.js +10 -1
- package/routes/tables.js +4 -0
- package/serve.js +1 -1
- package/tests/page.test.js +11 -1
package/auth/admin.js
CHANGED
|
@@ -82,6 +82,13 @@ const getUserFields = async (req) => {
|
|
|
82
82
|
await iterForm("new_user_form");
|
|
83
83
|
//console.log(userFields);
|
|
84
84
|
for (const f of userFields) {
|
|
85
|
+
if (f.is_fkey && !f.fieldview) {
|
|
86
|
+
f.fieldviewObj = getState().keyFieldviews?.select;
|
|
87
|
+
if (f.fieldviewObj) {
|
|
88
|
+
f.input_type = "fromtype";
|
|
89
|
+
f.fieldview = "select";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
85
92
|
await f.fill_fkey_options();
|
|
86
93
|
if (f.name === "email") {
|
|
87
94
|
f.validator = (s) => {
|
|
@@ -357,6 +364,7 @@ const auth_settings_form = async (req) =>
|
|
|
357
364
|
"user_settings_form",
|
|
358
365
|
"verification_view",
|
|
359
366
|
"logout_url",
|
|
367
|
+
"signup_role",
|
|
360
368
|
"elevate_verified",
|
|
361
369
|
"email_mask",
|
|
362
370
|
],
|
package/auth/routes.js
CHANGED
|
@@ -514,9 +514,13 @@ router.get(
|
|
|
514
514
|
if (!signup_form) await defaultSignup();
|
|
515
515
|
else {
|
|
516
516
|
const resp = await signup_form.run_possibly_on_page({}, req, res);
|
|
517
|
-
if (signup_form.default_render_page)
|
|
518
|
-
|
|
519
|
-
|
|
517
|
+
if (signup_form.default_render_page) {
|
|
518
|
+
const page = Page.findOne({ name: signup_form.default_render_page });
|
|
519
|
+
res.sendWrap(
|
|
520
|
+
{ title: req.__(`Sign up`), no_menu: page?.attributes?.no_menu },
|
|
521
|
+
resp
|
|
522
|
+
);
|
|
523
|
+
} else res.sendAuthWrap(req.__(`Sign up`), resp, { methods: [] });
|
|
520
524
|
}
|
|
521
525
|
} else await defaultSignup();
|
|
522
526
|
})
|
|
@@ -786,6 +790,7 @@ router.post(
|
|
|
786
790
|
res.sendAuthWrap(new_user_form, form, getAuthLinks("signup", true));
|
|
787
791
|
return;
|
|
788
792
|
}
|
|
793
|
+
uobj.role_id = +getState().getConfig("signup_role", "80");
|
|
789
794
|
const u = await User.create(uobj);
|
|
790
795
|
await send_verification_email(u, req);
|
|
791
796
|
|
|
@@ -843,6 +848,7 @@ router.post(
|
|
|
843
848
|
res.sendAuthWrap(new_user_form, form, getAuthLinks("signup", true));
|
|
844
849
|
} else {
|
|
845
850
|
try {
|
|
851
|
+
form.values.role_id = +getState().getConfig("signup_role", "80");
|
|
846
852
|
const u = await User.create(form.values);
|
|
847
853
|
await send_verification_email(u, req);
|
|
848
854
|
|
|
@@ -978,6 +984,7 @@ router.post(
|
|
|
978
984
|
getAuthLinks("signup", true)
|
|
979
985
|
);
|
|
980
986
|
} else {
|
|
987
|
+
userObject.role_id = +getState().getConfig("signup_role", "80");
|
|
981
988
|
const u = await User.create(userObject);
|
|
982
989
|
await send_verification_email(u, req);
|
|
983
990
|
|
|
@@ -1010,6 +1017,7 @@ router.post(
|
|
|
1010
1017
|
form.values.password = password;
|
|
1011
1018
|
res.sendAuthWrap(new_user_form, form, getAuthLinks("signup", true));
|
|
1012
1019
|
} else {
|
|
1020
|
+
form.values.role_id = +getState().getConfig("signup_role", "80");
|
|
1013
1021
|
const u = await User.create(form.values);
|
|
1014
1022
|
await send_verification_email(u, req);
|
|
1015
1023
|
if (req.smr)
|
package/errors.js
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
The Cordova builder is a docker image with all dependencies to build Android apps.
|
|
2
|
+
It can be pulled from dockerhub while installing Saltcorn, or you can use the pull button.
|
|
3
|
+
|
|
4
|
+
Please make sure your server has a valid and accessible docker daemon running.
|
|
5
|
+
|
|
6
|
+
For this, either set up a standard docker installation
|
|
7
|
+
or use the [docker rootless mode](https://docs.docker.com/engine/security/rootless/) (recommended).
|
|
8
|
+
|
|
9
|
+
In a standard docker environment, you need root user privileges.
|
|
10
|
+
For this, you can add the user running Saltcorn to the docker group,
|
|
11
|
+
or if you already have the privileges, you are ready to go.
|
|
12
|
+
|
|
13
|
+
A docker daemon in rootless mode doesn't need any further configuration.
|
package/load_plugins.js
CHANGED
|
@@ -6,59 +6,10 @@
|
|
|
6
6
|
* @module load_plugins
|
|
7
7
|
*/
|
|
8
8
|
const db = require("@saltcorn/data/db");
|
|
9
|
-
const { PluginManager } = require("live-plugin-manager");
|
|
10
9
|
const { getState, getRootState } = require("@saltcorn/data/db/state");
|
|
11
10
|
const Plugin = require("@saltcorn/data/models/plugin");
|
|
12
|
-
const fs = require("fs");
|
|
13
|
-
const proc = require("child_process");
|
|
14
|
-
const tmp = require("tmp-promise");
|
|
15
11
|
|
|
16
|
-
const
|
|
17
|
-
"@saltcorn/markup": require("@saltcorn/markup"),
|
|
18
|
-
"@saltcorn/markup/tags": require("@saltcorn/markup/tags"),
|
|
19
|
-
"@saltcorn/markup/layout": require("@saltcorn/markup/layout"),
|
|
20
|
-
"@saltcorn/markup/helpers": require("@saltcorn/markup/helpers"),
|
|
21
|
-
"@saltcorn/markup/layout_utils": require("@saltcorn/markup/layout_utils"),
|
|
22
|
-
"@saltcorn/data": require("@saltcorn/data"),
|
|
23
|
-
"@saltcorn/data/db": require("@saltcorn/data/db"),
|
|
24
|
-
"@saltcorn/data/utils": require("@saltcorn/data/utils"),
|
|
25
|
-
"@saltcorn/data/db/state": require("@saltcorn/data/db/state"),
|
|
26
|
-
"@saltcorn/data/plugin-helper": require("@saltcorn/data/plugin-helper"),
|
|
27
|
-
"@saltcorn/data/plugin-testing": require("@saltcorn/data/plugin-testing"),
|
|
28
|
-
"@saltcorn/data/models/field": require("@saltcorn/data/models/field"),
|
|
29
|
-
"@saltcorn/data/models/fieldrepeat": require("@saltcorn/data/models/fieldrepeat"),
|
|
30
|
-
"@saltcorn/data/models/table": require("@saltcorn/data/models/table"),
|
|
31
|
-
"@saltcorn/data/models/form": require("@saltcorn/data/models/form"),
|
|
32
|
-
"@saltcorn/data/models/discovery": require("@saltcorn/data/models/discovery"),
|
|
33
|
-
"@saltcorn/data/models/config": require("@saltcorn/data/models/config"),
|
|
34
|
-
"@saltcorn/data/models/library": require("@saltcorn/data/models/library"),
|
|
35
|
-
"@saltcorn/data/models/model": require("@saltcorn/data/models/model"),
|
|
36
|
-
"@saltcorn/data/models/model_instance": require("@saltcorn/data/models/model_instance"),
|
|
37
|
-
"@saltcorn/data/models/notification": require("@saltcorn/data/models/notification"),
|
|
38
|
-
"@saltcorn/data/models/role": require("@saltcorn/data/models/role"),
|
|
39
|
-
"@saltcorn/data/models/tag": require("@saltcorn/data/models/tag"),
|
|
40
|
-
"@saltcorn/data/models/tag_entry": require("@saltcorn/data/models/tag_entry"),
|
|
41
|
-
"@saltcorn/data/models/view": require("@saltcorn/data/models/view"),
|
|
42
|
-
"@saltcorn/data/models/page": require("@saltcorn/data/models/page"),
|
|
43
|
-
"@saltcorn/data/models/file": require("@saltcorn/data/models/file"),
|
|
44
|
-
"@saltcorn/data/models/user": require("@saltcorn/data/models/user"),
|
|
45
|
-
"@saltcorn/data/models/layout": require("@saltcorn/data/models/layout"),
|
|
46
|
-
"@saltcorn/data/models/expression": require("@saltcorn/data/models/expression"),
|
|
47
|
-
"@saltcorn/data/models/workflow": require("@saltcorn/data/models/workflow"),
|
|
48
|
-
imapflow: require("imapflow"),
|
|
49
|
-
"node-fetch": require("node-fetch"),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Create plugin manager with default list of core plugins
|
|
54
|
-
* @type {PluginManager}
|
|
55
|
-
*/
|
|
56
|
-
const defaultManager = new PluginManager({
|
|
57
|
-
staticDependencies: {
|
|
58
|
-
contractis: require("contractis"),
|
|
59
|
-
...staticDependencies,
|
|
60
|
-
},
|
|
61
|
-
});
|
|
12
|
+
const PluginInstaller = require("@saltcorn/plugins-loader/plugin_installer");
|
|
62
13
|
|
|
63
14
|
/**
|
|
64
15
|
* Load one plugin
|
|
@@ -68,19 +19,35 @@ const defaultManager = new PluginManager({
|
|
|
68
19
|
*/
|
|
69
20
|
const loadPlugin = async (plugin, force) => {
|
|
70
21
|
// load plugin
|
|
71
|
-
const
|
|
22
|
+
const loader = new PluginInstaller(plugin);
|
|
23
|
+
const res = await loader.install(force);
|
|
72
24
|
const configuration =
|
|
73
25
|
typeof plugin.configuration === "string"
|
|
74
26
|
? JSON.parse(plugin.configuration)
|
|
75
27
|
: plugin.configuration;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
28
|
+
try {
|
|
29
|
+
// register plugin
|
|
30
|
+
getState().registerPlugin(
|
|
31
|
+
res.plugin_module.plugin_name || plugin.name,
|
|
32
|
+
res.plugin_module,
|
|
33
|
+
configuration,
|
|
34
|
+
res.location,
|
|
35
|
+
res.name
|
|
36
|
+
);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (force) {
|
|
39
|
+
// remove the install dir and try again
|
|
40
|
+
await loader.remove();
|
|
41
|
+
await loader.install(force);
|
|
42
|
+
getState().registerPlugin(
|
|
43
|
+
res.plugin_module.plugin_name || plugin.name,
|
|
44
|
+
res.plugin_module,
|
|
45
|
+
configuration,
|
|
46
|
+
res.location,
|
|
47
|
+
res.name
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
84
51
|
if (res.plugin_module.onLoad) {
|
|
85
52
|
try {
|
|
86
53
|
await res.plugin_module.onLoad(plugin.configuration);
|
|
@@ -91,105 +58,48 @@ const loadPlugin = async (plugin, force) => {
|
|
|
91
58
|
return res;
|
|
92
59
|
};
|
|
93
60
|
|
|
94
|
-
/**
|
|
95
|
-
* Git pull or clone
|
|
96
|
-
* @param plugin
|
|
97
|
-
*/
|
|
98
|
-
const gitPullOrClone = async (plugin) => {
|
|
99
|
-
await fs.promises.mkdir("git_plugins", { recursive: true });
|
|
100
|
-
let keyfnm,
|
|
101
|
-
setKey = `-c core.sshCommand="ssh -oBatchMode=yes -o 'StrictHostKeyChecking no'" `;
|
|
102
|
-
if (plugin.deploy_private_key) {
|
|
103
|
-
keyfnm = await tmp.tmpName();
|
|
104
|
-
await fs.promises.writeFile(
|
|
105
|
-
keyfnm,
|
|
106
|
-
plugin.deploy_private_key.replace(/[\r]+/g, "") + "\n",
|
|
107
|
-
{
|
|
108
|
-
mode: 0o600,
|
|
109
|
-
encoding: "ascii",
|
|
110
|
-
}
|
|
111
|
-
);
|
|
112
|
-
setKey = `-c core.sshCommand="ssh -oBatchMode=yes -o 'StrictHostKeyChecking no' -i ${keyfnm}" `;
|
|
113
|
-
}
|
|
114
|
-
const dir = `git_plugins/${plugin.name}`;
|
|
115
|
-
if (fs.existsSync(dir)) {
|
|
116
|
-
proc.execSync(`git ${setKey} -C ${dir} pull`);
|
|
117
|
-
} else {
|
|
118
|
-
proc.execSync(`git ${setKey} clone ${plugin.location} ${dir}`);
|
|
119
|
-
}
|
|
120
|
-
if (plugin.deploy_private_key) await fs.promises.unlink(keyfnm);
|
|
121
|
-
return dir;
|
|
122
|
-
};
|
|
123
61
|
/**
|
|
124
62
|
* Install plugin
|
|
125
63
|
* @param plugin - plugin name
|
|
126
64
|
* @param force - force flag
|
|
127
|
-
* @param manager - plugin manager
|
|
128
65
|
* @returns {Promise<{plugin_module: *}|{plugin_module: any}>}
|
|
129
66
|
*/
|
|
130
|
-
const requirePlugin = async (plugin, force
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
["@saltcorn/base-plugin", "@saltcorn/sbadmin2"].includes(plugin.location)
|
|
135
|
-
) {
|
|
136
|
-
return { plugin_module: require(plugin.location) };
|
|
137
|
-
} else if (plugin.source === "npm") {
|
|
138
|
-
if (force || !installed_plugins.includes(plugin.location)) {
|
|
139
|
-
const plinfo = await manager.install(plugin.location, plugin.version);
|
|
140
|
-
return { plugin_module: manager.require(plugin.location), ...plinfo };
|
|
141
|
-
} else {
|
|
142
|
-
const plinfo = manager.getInfo(plugin.location);
|
|
143
|
-
return { plugin_module: manager.require(plugin.location), ...plinfo };
|
|
144
|
-
}
|
|
145
|
-
} else if (plugin.source === "local") {
|
|
146
|
-
const plinfo = await manager.installFromPath(plugin.location, {
|
|
147
|
-
force: true,
|
|
148
|
-
});
|
|
149
|
-
return { plugin_module: manager.require(plugin.name), ...plinfo };
|
|
150
|
-
} else if (plugin.source === "git") {
|
|
151
|
-
const loc = await gitPullOrClone(plugin);
|
|
152
|
-
const plinfo = await manager.installFromPath(loc, {
|
|
153
|
-
force: true,
|
|
154
|
-
});
|
|
155
|
-
return { plugin_module: manager.require(plugin.name), ...plinfo };
|
|
156
|
-
} else if (plugin.source === "github") {
|
|
157
|
-
if (force || !installed_plugins.includes(plugin.location)) {
|
|
158
|
-
const plinfo = await manager.installFromGithub(plugin.location, {
|
|
159
|
-
force: true,
|
|
160
|
-
});
|
|
161
|
-
return { plugin_module: manager.require(plugin.name), ...plinfo };
|
|
162
|
-
} else {
|
|
163
|
-
const plinfo = manager.getInfo(plugin.location);
|
|
164
|
-
return { plugin_module: manager.require(plugin.location), ...plinfo };
|
|
165
|
-
}
|
|
166
|
-
} else throw new Error("Unknown plugin source: " + plugin.source);
|
|
67
|
+
const requirePlugin = async (plugin, force) => {
|
|
68
|
+
const loader = new PluginInstaller(plugin);
|
|
69
|
+
return await loader.install(force);
|
|
167
70
|
};
|
|
71
|
+
|
|
168
72
|
/**
|
|
169
73
|
* Load all plugins
|
|
170
74
|
* @returns {Promise<void>}
|
|
171
75
|
*/
|
|
172
|
-
const loadAllPlugins = async () => {
|
|
76
|
+
const loadAllPlugins = async (force) => {
|
|
173
77
|
await getState().refresh(true);
|
|
174
78
|
const plugins = await db.select("_sc_plugins");
|
|
175
79
|
for (const plugin of plugins) {
|
|
176
80
|
try {
|
|
177
|
-
await loadPlugin(plugin);
|
|
81
|
+
await loadPlugin(plugin, force);
|
|
178
82
|
} catch (e) {
|
|
179
83
|
console.error(e);
|
|
180
84
|
}
|
|
181
85
|
}
|
|
182
86
|
await getState().refresh(true);
|
|
183
87
|
};
|
|
88
|
+
|
|
184
89
|
/**
|
|
185
90
|
* Load Plugin and its dependencies and save into local installation
|
|
186
91
|
* @param plugin
|
|
187
92
|
* @param force
|
|
188
93
|
* @param noSignalOrDB
|
|
189
|
-
* @param
|
|
94
|
+
* @param __ translation function
|
|
190
95
|
* @returns {Promise<void>}
|
|
191
96
|
*/
|
|
192
|
-
const loadAndSaveNewPlugin = async (
|
|
97
|
+
const loadAndSaveNewPlugin = async (
|
|
98
|
+
plugin,
|
|
99
|
+
force,
|
|
100
|
+
noSignalOrDB,
|
|
101
|
+
__ = (str) => str
|
|
102
|
+
) => {
|
|
193
103
|
const tenants_unsafe_plugins = getRootState().getConfig(
|
|
194
104
|
"tenants_unsafe_plugins",
|
|
195
105
|
false
|
|
@@ -215,11 +125,10 @@ const loadAndSaveNewPlugin = async (plugin, force, noSignalOrDB, manager) => {
|
|
|
215
125
|
return;
|
|
216
126
|
}
|
|
217
127
|
}
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
);
|
|
128
|
+
const msgs = [];
|
|
129
|
+
const loader = new PluginInstaller(plugin);
|
|
130
|
+
const { version, plugin_module, location, loadedWithReload } =
|
|
131
|
+
await loader.install(force);
|
|
223
132
|
|
|
224
133
|
// install dependecies
|
|
225
134
|
for (const loc of plugin_module.dependencies || []) {
|
|
@@ -232,13 +141,43 @@ const loadAndSaveNewPlugin = async (plugin, force, noSignalOrDB, manager) => {
|
|
|
232
141
|
);
|
|
233
142
|
}
|
|
234
143
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
144
|
+
let registeredWithReload = false;
|
|
145
|
+
try {
|
|
146
|
+
getState().registerPlugin(
|
|
147
|
+
plugin_module.plugin_name || plugin.name,
|
|
148
|
+
plugin_module,
|
|
149
|
+
plugin.configuration,
|
|
150
|
+
location,
|
|
151
|
+
plugin.name
|
|
152
|
+
);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (force) {
|
|
155
|
+
getState().log(
|
|
156
|
+
2,
|
|
157
|
+
`Error registering plugin ${plugin.name}. Removing and trying again.`
|
|
158
|
+
);
|
|
159
|
+
await loader.remove();
|
|
160
|
+
await loader.install(force);
|
|
161
|
+
getState().registerPlugin(
|
|
162
|
+
plugin_module.plugin_name || plugin.name,
|
|
163
|
+
plugin_module,
|
|
164
|
+
plugin.configuration,
|
|
165
|
+
location,
|
|
166
|
+
plugin.name
|
|
167
|
+
);
|
|
168
|
+
registeredWithReload = true;
|
|
169
|
+
} else {
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (loadedWithReload || registeredWithReload) {
|
|
174
|
+
msgs.push(
|
|
175
|
+
__(
|
|
176
|
+
"The plugin was corrupted and had to be repaired. We recommend restarting your server.",
|
|
177
|
+
plugin.name
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
242
181
|
if (plugin_module.onLoad) {
|
|
243
182
|
try {
|
|
244
183
|
await plugin_module.onLoad(plugin.configuration);
|
|
@@ -252,9 +191,10 @@ const loadAndSaveNewPlugin = async (plugin, force, noSignalOrDB, manager) => {
|
|
|
252
191
|
getState().processSend({
|
|
253
192
|
installPlugin: plugin,
|
|
254
193
|
tenant: db.getTenantSchema(),
|
|
255
|
-
force,
|
|
194
|
+
force: false, // okay ??
|
|
256
195
|
});
|
|
257
196
|
}
|
|
197
|
+
return msgs;
|
|
258
198
|
};
|
|
259
199
|
|
|
260
200
|
module.exports = {
|
|
@@ -262,5 +202,4 @@ module.exports = {
|
|
|
262
202
|
loadAllPlugins,
|
|
263
203
|
loadPlugin,
|
|
264
204
|
requirePlugin,
|
|
265
|
-
staticDependencies,
|
|
266
205
|
};
|
package/locales/en.json
CHANGED
|
@@ -1382,5 +1382,14 @@
|
|
|
1382
1382
|
"Open a connection to TLS server with self-signed or invalid TLS certificate": "Open a connection to TLS server with self-signed or invalid TLS certificate",
|
|
1383
1383
|
"Allow self-signed": "Allow self-signed",
|
|
1384
1384
|
"Optionally associate a table with this trigger": "Optionally associate a table with this trigger",
|
|
1385
|
-
"Delete table": "Delete table"
|
|
1386
|
-
|
|
1385
|
+
"Delete table": "Delete table",
|
|
1386
|
+
"Signup role": "Signup role",
|
|
1387
|
+
"The initial role of signed up users": "The initial role of signed up users",
|
|
1388
|
+
"Cordova builder": "Cordova builder",
|
|
1389
|
+
"not available": "not available",
|
|
1390
|
+
"pull": "pull",
|
|
1391
|
+
"refresh": "refresh",
|
|
1392
|
+
"installed": "installed",
|
|
1393
|
+
"Include table history in backup": "Include table history in backup",
|
|
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
|
+
}
|
package/markup/admin.js
CHANGED
|
@@ -23,6 +23,8 @@ const Form = require("@saltcorn/data/models/form");
|
|
|
23
23
|
const Table = require("@saltcorn/data/models/table");
|
|
24
24
|
const View = require("@saltcorn/data/models/view");
|
|
25
25
|
const User = require("@saltcorn/data/models/user");
|
|
26
|
+
const FieldRepeat = require("@saltcorn/data/models/fieldrepeat");
|
|
27
|
+
const Field = require("@saltcorn/data/models/field");
|
|
26
28
|
|
|
27
29
|
/**
|
|
28
30
|
* Restore Backup
|
|
@@ -437,6 +439,26 @@ const config_fields_form = async ({
|
|
|
437
439
|
const label = configTypes[name].label || name;
|
|
438
440
|
const sublabel = configTypes[name].sublabel || configTypes[name].blurb;
|
|
439
441
|
|
|
442
|
+
if (configTypes[name].type === "Repeat") {
|
|
443
|
+
const repFields = configTypes[name].fields;
|
|
444
|
+
const filledFields = [];
|
|
445
|
+
for (const rfield of repFields) {
|
|
446
|
+
const fillField = new Field(rfield);
|
|
447
|
+
await fillField.fill_fkey_options();
|
|
448
|
+
filledFields.push(fillField);
|
|
449
|
+
}
|
|
450
|
+
fields.push(
|
|
451
|
+
new FieldRepeat({
|
|
452
|
+
name,
|
|
453
|
+
label,
|
|
454
|
+
fields: filledFields,
|
|
455
|
+
showIf,
|
|
456
|
+
})
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
|
|
440
462
|
fields.push({
|
|
441
463
|
name,
|
|
442
464
|
...configTypes[name],
|
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@saltcorn/server",
|
|
3
|
-
"version": "0.9.5-beta.
|
|
3
|
+
"version": "0.9.5-beta.10",
|
|
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/
|
|
10
|
+
"@saltcorn/base-plugin": "0.9.5-beta.10",
|
|
11
|
+
"@saltcorn/builder": "0.9.5-beta.10",
|
|
12
|
+
"@saltcorn/data": "0.9.5-beta.10",
|
|
13
|
+
"@saltcorn/admin-models": "0.9.5-beta.10",
|
|
14
|
+
"@saltcorn/filemanager": "0.9.5-beta.10",
|
|
15
|
+
"@saltcorn/markup": "0.9.5-beta.10",
|
|
16
|
+
"@saltcorn/plugins-loader": "0.9.5-beta.10",
|
|
17
|
+
"@saltcorn/sbadmin2": "0.9.5-beta.10",
|
|
17
18
|
"@socket.io/cluster-adapter": "^0.2.1",
|
|
18
19
|
"@socket.io/sticky": "^1.0.1",
|
|
19
20
|
"adm-zip": "0.5.10",
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"cors": "2.8.5",
|
|
27
28
|
"csurf": "^1.11.0",
|
|
28
29
|
"csv-stringify": "^5.5.0",
|
|
30
|
+
"dockerode": "~4.0.2",
|
|
29
31
|
"express": "^4.17.1",
|
|
30
32
|
"express-fileupload": "^1.1.8",
|
|
31
33
|
"express-promise-router": "^3.0.3",
|
|
@@ -37,7 +39,6 @@
|
|
|
37
39
|
"i18n": "^0.15.1",
|
|
38
40
|
"imapflow": "1.0.123",
|
|
39
41
|
"jsonwebtoken": "^9.0.0",
|
|
40
|
-
"live-plugin-manager": "^0.17.1",
|
|
41
42
|
"markdown-it": "^13.0.2",
|
|
42
43
|
"moment": "^2.29.4",
|
|
43
44
|
"multer": "1.4.5-lts.1",
|
|
@@ -70,8 +71,8 @@
|
|
|
70
71
|
},
|
|
71
72
|
"repository": "github:saltcorn/saltcorn",
|
|
72
73
|
"devDependencies": {
|
|
73
|
-
"jest": "
|
|
74
|
-
"jest-environment-jsdom": "
|
|
74
|
+
"jest": "^28.1.3",
|
|
75
|
+
"jest-environment-jsdom": "28.1.3",
|
|
75
76
|
"supertest": "^6.3.3"
|
|
76
77
|
},
|
|
77
78
|
"scripts": {
|
|
@@ -84,11 +85,13 @@
|
|
|
84
85
|
"testEnvironment": "node",
|
|
85
86
|
"testPathIgnorePatterns": [
|
|
86
87
|
"/node_modules/",
|
|
87
|
-
"/plugin_packages/"
|
|
88
|
+
"/plugin_packages/",
|
|
89
|
+
"/plugins_folder/"
|
|
88
90
|
],
|
|
89
91
|
"coveragePathIgnorePatterns": [
|
|
90
92
|
"/node_modules/",
|
|
91
|
-
"/plugin_packages/"
|
|
93
|
+
"/plugin_packages/",
|
|
94
|
+
"/plugins_folder/"
|
|
92
95
|
],
|
|
93
96
|
"moduleNameMapper": {
|
|
94
97
|
"@saltcorn/sqlite/(.*)": "<rootDir>/../sqlite/dist/$1",
|