@saltcorn/data 0.6.1-beta.0 → 0.6.1
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/base-plugin/actions.js +172 -1
- package/base-plugin/fieldviews.js +63 -0
- package/base-plugin/fileviews.js +41 -0
- package/base-plugin/index.js +35 -0
- package/base-plugin/types.js +345 -9
- package/base-plugin/viewtemplates/edit.js +107 -0
- package/base-plugin/viewtemplates/feed.js +46 -0
- package/base-plugin/viewtemplates/filter.js +43 -0
- package/base-plugin/viewtemplates/list.js +73 -1
- package/base-plugin/viewtemplates/listshowlist.js +54 -3
- package/base-plugin/viewtemplates/room.js +100 -0
- package/base-plugin/viewtemplates/show.js +86 -0
- package/base-plugin/viewtemplates/viewable_fields.js +124 -0
- package/contracts.js +58 -0
- package/db/connect.js +13 -5
- package/db/db.test.js +0 -154
- package/db/fixtures.js +13 -1
- package/db/index.js +42 -3
- package/db/reset_schema.js +11 -0
- package/db/state.js +105 -36
- package/index.js +13 -0
- package/migrate.js +4 -1
- package/models/backup.js +78 -0
- package/models/config.js +113 -22
- package/models/crash.js +44 -0
- package/models/discovery.js +13 -11
- package/models/email.js +17 -0
- package/models/eventlog.js +51 -0
- package/models/expression.js +49 -1
- package/models/field.js +88 -9
- package/models/fieldrepeat.js +33 -0
- package/models/file.js +23 -4
- package/models/form.js +34 -0
- package/models/index.js +42 -0
- package/models/layout.js +33 -0
- package/models/library.js +44 -0
- package/models/pack.js +88 -0
- package/models/page.js +13 -3
- package/models/plugin.js +9 -2
- package/models/random.js +36 -0
- package/models/role.js +36 -0
- package/models/scheduler.js +28 -0
- package/models/table.js +34 -15
- package/models/table_constraints.js +44 -0
- package/models/tenant.js +46 -9
- package/models/trigger.js +24 -11
- package/models/user.js +33 -8
- package/models/view.js +89 -8
- package/models/workflow.js +31 -0
- package/package.json +7 -5
- package/plugin-helper.js +102 -44
- package/plugin-testing.js +4 -0
- package/tests/exact_views.test.js +5 -5
- package/utils.js +4 -0
- package/db/internal.js +0 -229
- package/db/multi-tenant.js +0 -24
- package/db/pg.js +0 -374
- package/db/single-tenant.js +0 -8
- package/db/sqlite.js +0 -280
- package/db/tenants.js +0 -6
package/models/email.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @category saltcorn-data
|
|
3
|
+
* @module models/email
|
|
4
|
+
* @subcategory models
|
|
5
|
+
*/
|
|
1
6
|
const nodemailer = require("nodemailer");
|
|
2
7
|
const { getState } = require("../db/state");
|
|
3
8
|
const BootstrapEmail = require("bootstrap-email");
|
|
@@ -9,6 +14,9 @@ const { v4: uuidv4 } = require("uuid");
|
|
|
9
14
|
const db = require("../db");
|
|
10
15
|
const { mockReqRes } = require("../tests/mocks");
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* @returns {Transporter}
|
|
19
|
+
*/
|
|
12
20
|
const getMailTransport = () => {
|
|
13
21
|
const port = getState().getConfig("smtp_port");
|
|
14
22
|
const secure = getState().getConfig("smtp_secure", port === 465);
|
|
@@ -23,6 +31,10 @@ const getMailTransport = () => {
|
|
|
23
31
|
});
|
|
24
32
|
};
|
|
25
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @param {object} bsHtml
|
|
36
|
+
* @returns {object}
|
|
37
|
+
*/
|
|
26
38
|
const transformBootstrapEmail = async (bsHtml) => {
|
|
27
39
|
const filename = await tmp.tmpName();
|
|
28
40
|
await fs.writeFile(filename, div({ class: "container" }, bsHtml));
|
|
@@ -33,6 +45,11 @@ const transformBootstrapEmail = async (bsHtml) => {
|
|
|
33
45
|
return email;
|
|
34
46
|
};
|
|
35
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @param {object} user
|
|
50
|
+
* @param {object} [req]
|
|
51
|
+
* @returns {Promise<object>}
|
|
52
|
+
*/
|
|
36
53
|
const send_verification_email = async (user, req) => {
|
|
37
54
|
const verification_view_name = getState().getConfig("verification_view");
|
|
38
55
|
if (verification_view_name) {
|
package/models/eventlog.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventLog Database Access Layer
|
|
3
|
+
* @category saltcorn-data
|
|
4
|
+
* @module models/eventlog
|
|
5
|
+
* @subcategory models
|
|
6
|
+
*/
|
|
1
7
|
const db = require("../db");
|
|
2
8
|
const moment = require("moment");
|
|
3
9
|
|
|
4
10
|
const { contract, is } = require("contractis");
|
|
5
11
|
|
|
12
|
+
/**
|
|
13
|
+
* EventLog Class
|
|
14
|
+
* @category saltcorn-data
|
|
15
|
+
*/
|
|
6
16
|
class EventLog {
|
|
17
|
+
/**
|
|
18
|
+
* EventLog constructor
|
|
19
|
+
* @param {object} o
|
|
20
|
+
*/
|
|
7
21
|
constructor(o) {
|
|
8
22
|
this.id = o.id;
|
|
9
23
|
this.event_type = o.event_type;
|
|
@@ -16,14 +30,30 @@ class EventLog {
|
|
|
16
30
|
typeof o.payload === "string" ? JSON.parse(o.payload) : o.payload;
|
|
17
31
|
contract.class(this);
|
|
18
32
|
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {object} where
|
|
36
|
+
* @param {object} selopts
|
|
37
|
+
* @returns {Promise<EventLog[]>}
|
|
38
|
+
*/
|
|
19
39
|
static async find(where, selopts) {
|
|
20
40
|
const us = await db.select("_sc_event_log", where, selopts);
|
|
21
41
|
return us.map((u) => new EventLog(u));
|
|
22
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {object} where
|
|
46
|
+
* @returns {Promise<EventLog>}
|
|
47
|
+
*/
|
|
23
48
|
static async findOne(where) {
|
|
24
49
|
const u = await db.selectOne("_sc_event_log", where);
|
|
25
50
|
return new EventLog(u);
|
|
26
51
|
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {string} id
|
|
55
|
+
* @returns {Promise<EventLog>}
|
|
56
|
+
*/
|
|
27
57
|
static async findOneWithUser(id) {
|
|
28
58
|
const {
|
|
29
59
|
rows,
|
|
@@ -37,12 +67,25 @@ class EventLog {
|
|
|
37
67
|
return el;
|
|
38
68
|
}
|
|
39
69
|
|
|
70
|
+
/**
|
|
71
|
+
* @param {object} where
|
|
72
|
+
* @returns {Promise<number>}
|
|
73
|
+
*/
|
|
40
74
|
static async count(where) {
|
|
41
75
|
return await db.count("_sc_event_log", where || {});
|
|
42
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @type {string}
|
|
80
|
+
*/
|
|
43
81
|
get reltime() {
|
|
44
82
|
return moment(this.occur_at).fromNow();
|
|
45
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {object} o
|
|
87
|
+
* @returns {Promise<EventLog>}
|
|
88
|
+
*/
|
|
46
89
|
static async create(o) {
|
|
47
90
|
const { getState } = require("../db/state");
|
|
48
91
|
|
|
@@ -64,10 +107,18 @@ class EventLog {
|
|
|
64
107
|
return ev;
|
|
65
108
|
}
|
|
66
109
|
|
|
110
|
+
/**
|
|
111
|
+
* @param {string} evType
|
|
112
|
+
* @returns {boolean}
|
|
113
|
+
*/
|
|
67
114
|
static hasTable(evType) {
|
|
68
115
|
return ["Insert", "Update", "Delete"].includes(evType);
|
|
69
116
|
}
|
|
70
117
|
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} evType
|
|
120
|
+
* @returns {boolean}
|
|
121
|
+
*/
|
|
71
122
|
static hasChannel(evType) {
|
|
72
123
|
const { getState } = require("../db/state");
|
|
73
124
|
const t = getState().eventTypes[evType];
|
package/models/expression.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @category saltcorn-data
|
|
3
|
+
* @module models/expression
|
|
4
|
+
* @subcategory models
|
|
5
|
+
*/
|
|
1
6
|
const vm = require("vm");
|
|
2
7
|
let acorn = require("acorn");
|
|
3
8
|
const estraverse = require("estraverse");
|
|
4
9
|
const astring = require("astring");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} s
|
|
13
|
+
* @returns {boolean|void}
|
|
14
|
+
*/
|
|
5
15
|
function expressionValidator(s) {
|
|
6
16
|
if (!s || s.length == 0) return "Missing formula";
|
|
7
17
|
try {
|
|
@@ -11,11 +21,21 @@ function expressionValidator(s) {
|
|
|
11
21
|
return e.message;
|
|
12
22
|
}
|
|
13
23
|
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} expression
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
14
29
|
function jsexprToSQL(expression) {
|
|
15
30
|
if (!expression) return expression;
|
|
16
31
|
return expression.replace(/===/g, "=").replace(/==/g, "=").replace(/"/g, "'");
|
|
17
32
|
}
|
|
18
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} expression
|
|
36
|
+
* @throws {Error}
|
|
37
|
+
* @returns {object}
|
|
38
|
+
*/
|
|
19
39
|
function jsexprToWhere(expression) {
|
|
20
40
|
if (!expression) return {};
|
|
21
41
|
try {
|
|
@@ -77,6 +97,11 @@ function jsexprToWhere(expression) {
|
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
99
|
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} expression
|
|
102
|
+
* @param {object[]} statefuns
|
|
103
|
+
* @returns {object}
|
|
104
|
+
*/
|
|
80
105
|
function transform_for_async(expression, statefuns) {
|
|
81
106
|
var isAsync = false;
|
|
82
107
|
const ast = acorn.parseExpressionAt(expression, 0, {
|
|
@@ -99,6 +124,11 @@ function transform_for_async(expression, statefuns) {
|
|
|
99
124
|
return { isAsync, expr_string: astring.generate(ast) };
|
|
100
125
|
}
|
|
101
126
|
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} expression
|
|
129
|
+
* @param {object[]} fields
|
|
130
|
+
* @returns {any}
|
|
131
|
+
*/
|
|
102
132
|
function get_expression_function(expression, fields) {
|
|
103
133
|
const field_names = fields.map((f) => f.name);
|
|
104
134
|
const args = field_names.includes("user")
|
|
@@ -110,6 +140,13 @@ function get_expression_function(expression, fields) {
|
|
|
110
140
|
getState().function_context
|
|
111
141
|
);
|
|
112
142
|
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {string} expression
|
|
146
|
+
* @param {object[]} fields
|
|
147
|
+
* @param {object} [extraContext = {}]
|
|
148
|
+
* @returns {any}
|
|
149
|
+
*/
|
|
113
150
|
function get_async_expression_function(expression, fields, extraContext = {}) {
|
|
114
151
|
const field_names = fields.map((f) => f.name);
|
|
115
152
|
const args = field_names.includes("user")
|
|
@@ -124,6 +161,11 @@ function get_async_expression_function(expression, fields, extraContext = {}) {
|
|
|
124
161
|
});
|
|
125
162
|
}
|
|
126
163
|
|
|
164
|
+
/**
|
|
165
|
+
* @param {object[]} rows
|
|
166
|
+
* @param {object[]} fields
|
|
167
|
+
* @returns {object[]}
|
|
168
|
+
*/
|
|
127
169
|
function apply_calculated_fields(rows, fields) {
|
|
128
170
|
let hasExprs = false;
|
|
129
171
|
let transform = (x) => x;
|
|
@@ -152,6 +194,12 @@ function apply_calculated_fields(rows, fields) {
|
|
|
152
194
|
return rows.map(transform);
|
|
153
195
|
} else return rows;
|
|
154
196
|
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* @param {*} row
|
|
200
|
+
* @param {*} fields
|
|
201
|
+
* @returns {Promise<any>}
|
|
202
|
+
*/
|
|
155
203
|
const apply_calculated_fields_stored = async (row, fields) => {
|
|
156
204
|
let hasExprs = false;
|
|
157
205
|
let transform = (x) => x;
|
|
@@ -182,7 +230,7 @@ const apply_calculated_fields_stored = async (row, fields) => {
|
|
|
182
230
|
};
|
|
183
231
|
/**
|
|
184
232
|
* Recalculate calculated columns that are stored in db
|
|
185
|
-
* @param table - table object
|
|
233
|
+
* @param {object} table - table object
|
|
186
234
|
* @returns {Promise<void>}
|
|
187
235
|
*/
|
|
188
236
|
const recalculate_for_stored = async (table) => {
|
package/models/field.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
2
|
* Field Data Access Layer
|
|
3
|
+
* @category saltcorn-data
|
|
4
|
+
* @module models/field
|
|
5
|
+
* @subcategory models
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
const db = require("../db");
|
|
7
9
|
const { contract, is } = require("contractis");
|
|
8
10
|
const { recalculate_for_stored } = require("./expression");
|
|
9
|
-
const { sqlsanitize } = require("
|
|
11
|
+
const { sqlsanitize } = require("@saltcorn/db-common/internal.js");
|
|
10
12
|
const { InvalidAdminAction } = require("../utils");
|
|
11
13
|
const { mkWhere } = require("../db");
|
|
12
14
|
|
|
@@ -22,6 +24,7 @@ const readKey = (v, field) => {
|
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Field Class
|
|
27
|
+
* @category saltcorn-data
|
|
25
28
|
*/
|
|
26
29
|
class Field {
|
|
27
30
|
/**
|
|
@@ -99,7 +102,7 @@ class Field {
|
|
|
99
102
|
|
|
100
103
|
/**
|
|
101
104
|
* To Json
|
|
102
|
-
* @
|
|
105
|
+
* @type {object}
|
|
103
106
|
*/
|
|
104
107
|
get toJson() {
|
|
105
108
|
return {
|
|
@@ -126,8 +129,8 @@ class Field {
|
|
|
126
129
|
|
|
127
130
|
/**
|
|
128
131
|
* Label 2 Name
|
|
129
|
-
* @param label
|
|
130
|
-
* @returns {
|
|
132
|
+
* @param {string} label
|
|
133
|
+
* @returns {string}
|
|
131
134
|
*/
|
|
132
135
|
// todo from internalization point of view better to separate label, name. sqlname
|
|
133
136
|
// because label can contain characters that cannot be used in PG for sql names
|
|
@@ -137,7 +140,7 @@ class Field {
|
|
|
137
140
|
|
|
138
141
|
/**
|
|
139
142
|
* ???
|
|
140
|
-
* @returns {string
|
|
143
|
+
* @returns {string}
|
|
141
144
|
*/
|
|
142
145
|
get form_name() {
|
|
143
146
|
if (this.parent_field) return `${this.parent_field}_${this.name}`;
|
|
@@ -146,7 +149,8 @@ class Field {
|
|
|
146
149
|
|
|
147
150
|
/**
|
|
148
151
|
* Fill fkey options???
|
|
149
|
-
* @param force_allow_none
|
|
152
|
+
* @param {boolean} [force_allow_none = false]
|
|
153
|
+
* @param {object} where
|
|
150
154
|
* @returns {Promise<void>}
|
|
151
155
|
*/
|
|
152
156
|
async fill_fkey_options(force_allow_none = false, where) {
|
|
@@ -177,8 +181,8 @@ class Field {
|
|
|
177
181
|
|
|
178
182
|
/**
|
|
179
183
|
* Distinct Values
|
|
180
|
-
* @param req
|
|
181
|
-
* @returns {Promise<
|
|
184
|
+
* @param {object} [req]
|
|
185
|
+
* @returns {Promise<void>}
|
|
182
186
|
*/
|
|
183
187
|
async distinct_values(req, where) {
|
|
184
188
|
const __ = req && req.__ ? req.__ : (s) => s;
|
|
@@ -226,6 +230,9 @@ class Field {
|
|
|
226
230
|
return [{ label: "", value: "" }, ...dbOpts];
|
|
227
231
|
}
|
|
228
232
|
|
|
233
|
+
/**
|
|
234
|
+
* @type {string}
|
|
235
|
+
*/
|
|
229
236
|
get sql_type() {
|
|
230
237
|
if (this.is_fkey) {
|
|
231
238
|
const schema = db.getTenantSchemaPrefix();
|
|
@@ -241,12 +248,18 @@ class Field {
|
|
|
241
248
|
}
|
|
242
249
|
}
|
|
243
250
|
|
|
251
|
+
/**
|
|
252
|
+
* @type {string}
|
|
253
|
+
*/
|
|
244
254
|
get pretty_type() {
|
|
245
255
|
if (this.reftable_name === "_sc_files") return "File";
|
|
246
256
|
if (this.is_fkey) return `Key to ${this.reftable_name}`;
|
|
247
257
|
else return this.type ? this.type.name : "?";
|
|
248
258
|
}
|
|
249
259
|
|
|
260
|
+
/**
|
|
261
|
+
* @type {string}
|
|
262
|
+
*/
|
|
250
263
|
get sql_bare_type() {
|
|
251
264
|
if (this.is_fkey) {
|
|
252
265
|
const { getState } = require("../db/state");
|
|
@@ -257,6 +270,9 @@ class Field {
|
|
|
257
270
|
}
|
|
258
271
|
}
|
|
259
272
|
|
|
273
|
+
/**
|
|
274
|
+
* @returns {Promise<any>}
|
|
275
|
+
*/
|
|
260
276
|
async generate() {
|
|
261
277
|
if (this.is_fkey) {
|
|
262
278
|
const rows = await db.select(
|
|
@@ -271,6 +287,10 @@ class Field {
|
|
|
271
287
|
}
|
|
272
288
|
}
|
|
273
289
|
|
|
290
|
+
/**
|
|
291
|
+
* @param {object} whole_rec
|
|
292
|
+
* @returns {object}
|
|
293
|
+
*/
|
|
274
294
|
validate(whole_rec) {
|
|
275
295
|
const type = this.is_fkey ? { name: "Key" } : this.type;
|
|
276
296
|
const readval = this.is_fkey
|
|
@@ -295,26 +315,47 @@ class Field {
|
|
|
295
315
|
else return { error: "Not accepted" };
|
|
296
316
|
}
|
|
297
317
|
|
|
318
|
+
/**
|
|
319
|
+
*
|
|
320
|
+
* @param {object} where
|
|
321
|
+
* @param {object} [selectopts]
|
|
322
|
+
* @returns {Field[]}
|
|
323
|
+
*/
|
|
298
324
|
static async find(where, selectopts = { orderBy: "name", nocase: true }) {
|
|
299
325
|
const db_flds = await db.select("_sc_fields", where, selectopts);
|
|
300
326
|
return db_flds.map((dbf) => new Field(dbf));
|
|
301
327
|
}
|
|
302
328
|
|
|
329
|
+
/**
|
|
330
|
+
* @param {object} where
|
|
331
|
+
* @returns {Promise<Field>}
|
|
332
|
+
*/
|
|
303
333
|
static async findOne(where) {
|
|
304
334
|
const db_fld = await db.selectOne("_sc_fields", where);
|
|
305
335
|
return new Field(db_fld);
|
|
306
336
|
}
|
|
307
337
|
|
|
338
|
+
/**
|
|
339
|
+
* @returns {Promise<void>}
|
|
340
|
+
*/
|
|
308
341
|
async add_unique_constraint() {
|
|
309
342
|
await this.fill_table();
|
|
310
343
|
await db.add_unique_constraint(this.table.name, [this.name]);
|
|
311
344
|
}
|
|
312
345
|
|
|
346
|
+
/**
|
|
347
|
+
* @returns {Promise<void>}
|
|
348
|
+
*/
|
|
313
349
|
async remove_unique_constraint() {
|
|
314
350
|
await this.fill_table();
|
|
315
351
|
await db.drop_unique_constraint(this.table.name, [this.name]);
|
|
316
352
|
}
|
|
317
353
|
|
|
354
|
+
/**
|
|
355
|
+
*
|
|
356
|
+
* @param {boolean} not_null
|
|
357
|
+
* @returns {Promise<void>}
|
|
358
|
+
*/
|
|
318
359
|
async toggle_not_null(not_null) {
|
|
319
360
|
const schema = db.getTenantSchemaPrefix();
|
|
320
361
|
await this.fill_table();
|
|
@@ -328,6 +369,10 @@ class Field {
|
|
|
328
369
|
await require("../db/state").getState().refresh_tables();
|
|
329
370
|
}
|
|
330
371
|
|
|
372
|
+
/**
|
|
373
|
+
* @param {object} new_field
|
|
374
|
+
* @returns {Promise<void>}
|
|
375
|
+
*/
|
|
331
376
|
async alter_sql_type(new_field) {
|
|
332
377
|
let new_sql_type = new_field.sql_type;
|
|
333
378
|
let def = "";
|
|
@@ -365,6 +410,9 @@ class Field {
|
|
|
365
410
|
await require("../db/state").getState().refresh_tables();
|
|
366
411
|
}
|
|
367
412
|
|
|
413
|
+
/**
|
|
414
|
+
* @returns {Promise<void>}
|
|
415
|
+
*/
|
|
368
416
|
async fill_table() {
|
|
369
417
|
if (!this.table) {
|
|
370
418
|
const Table = require("./table");
|
|
@@ -372,6 +420,10 @@ class Field {
|
|
|
372
420
|
}
|
|
373
421
|
}
|
|
374
422
|
|
|
423
|
+
/**
|
|
424
|
+
* @param {object} v
|
|
425
|
+
* @returns {Promise<void>}
|
|
426
|
+
*/
|
|
375
427
|
async update(v) {
|
|
376
428
|
if (
|
|
377
429
|
typeof v.is_unique !== "undefined" &&
|
|
@@ -405,6 +457,10 @@ class Field {
|
|
|
405
457
|
});
|
|
406
458
|
await require("../db/state").getState().refresh_tables();
|
|
407
459
|
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* @type {string}
|
|
463
|
+
*/
|
|
408
464
|
get listKey() {
|
|
409
465
|
return this.type.listAs
|
|
410
466
|
? (r) => this.type.listAs(r[this.name])
|
|
@@ -412,6 +468,10 @@ class Field {
|
|
|
412
468
|
? (r) => this.type.showAs(r[this.name])
|
|
413
469
|
: this.name;
|
|
414
470
|
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* @type {object}
|
|
474
|
+
*/
|
|
415
475
|
get presets() {
|
|
416
476
|
if (this.type && this.type.presets) return this.type.presets;
|
|
417
477
|
|
|
@@ -420,6 +480,11 @@ class Field {
|
|
|
420
480
|
|
|
421
481
|
return null;
|
|
422
482
|
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* @throws {InvalidAdminAction}
|
|
486
|
+
* @returns {Promise<void>}
|
|
487
|
+
*/
|
|
423
488
|
async delete() {
|
|
424
489
|
const Table = require("./table");
|
|
425
490
|
const table = await Table.findOne({ id: this.table_id });
|
|
@@ -457,6 +522,10 @@ class Field {
|
|
|
457
522
|
await require("../db/state").getState().refresh_tables();
|
|
458
523
|
}
|
|
459
524
|
|
|
525
|
+
/**
|
|
526
|
+
* @param {object} table
|
|
527
|
+
* @returns {Promise<void>}
|
|
528
|
+
*/
|
|
460
529
|
async enable_fkey_constraint(table) {
|
|
461
530
|
if (this.is_fkey && !db.isSQLite) {
|
|
462
531
|
const schema = db.getTenantSchemaPrefix();
|
|
@@ -472,6 +541,11 @@ class Field {
|
|
|
472
541
|
}
|
|
473
542
|
}
|
|
474
543
|
|
|
544
|
+
/**
|
|
545
|
+
* @param {object} fld
|
|
546
|
+
* @param {boolean} [bare = false]
|
|
547
|
+
* @returns {Promise<Field>}
|
|
548
|
+
*/
|
|
475
549
|
static async create(fld, bare = false) {
|
|
476
550
|
const f = new Field(fld);
|
|
477
551
|
const schema = db.getTenantSchemaPrefix();
|
|
@@ -568,6 +642,11 @@ class Field {
|
|
|
568
642
|
return f;
|
|
569
643
|
}
|
|
570
644
|
|
|
645
|
+
/**
|
|
646
|
+
* @param {function|object[]} [typeattribs]
|
|
647
|
+
* @param {number} [table_id]
|
|
648
|
+
* @returns {*}
|
|
649
|
+
*/
|
|
571
650
|
static getTypeAttributes(typeattribs, table_id) {
|
|
572
651
|
const Table = require("./table");
|
|
573
652
|
|
package/models/fieldrepeat.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FieldRepeat Database Access Layer
|
|
3
|
+
* @category saltcorn-data
|
|
4
|
+
* @module models/fieldrepeat
|
|
5
|
+
* @subcategory models
|
|
6
|
+
*/
|
|
1
7
|
const { contract, is } = require("contractis");
|
|
2
8
|
const Field = require("./field");
|
|
3
9
|
|
|
10
|
+
/**
|
|
11
|
+
* FieldRepeat Class
|
|
12
|
+
* @category saltcorn-data
|
|
13
|
+
*/
|
|
4
14
|
class FieldRepeat {
|
|
15
|
+
/**
|
|
16
|
+
* FieldRepeat constructor
|
|
17
|
+
* @param {object} o
|
|
18
|
+
*/
|
|
5
19
|
constructor(o) {
|
|
6
20
|
this.label = o.label || o.name;
|
|
7
21
|
this.name = o.name;
|
|
@@ -11,6 +25,10 @@ class FieldRepeat {
|
|
|
11
25
|
this.isRepeat = true;
|
|
12
26
|
contract.class(this);
|
|
13
27
|
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @returns {Promise<void>}
|
|
31
|
+
*/
|
|
14
32
|
async generate() {
|
|
15
33
|
const nrepeats = Math.round(Math.random() * 5);
|
|
16
34
|
var r = {};
|
|
@@ -22,9 +40,20 @@ class FieldRepeat {
|
|
|
22
40
|
}
|
|
23
41
|
}
|
|
24
42
|
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {*} whole_rec
|
|
46
|
+
* @returns {object}
|
|
47
|
+
*/
|
|
25
48
|
validate(whole_rec) {
|
|
26
49
|
return this.validate_from_ix(whole_rec, 0);
|
|
27
50
|
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {*} whole_rec
|
|
54
|
+
* @param {*} ix
|
|
55
|
+
* @returns {object}
|
|
56
|
+
*/
|
|
28
57
|
validate_from_ix(whole_rec, ix) {
|
|
29
58
|
var has_any = false;
|
|
30
59
|
var res = {};
|
|
@@ -41,6 +70,10 @@ class FieldRepeat {
|
|
|
41
70
|
return { success: [res, ...rest.success] };
|
|
42
71
|
} else return { success: [] };
|
|
43
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {string}
|
|
76
|
+
*/
|
|
44
77
|
get form_name() {
|
|
45
78
|
return this.name;
|
|
46
79
|
}
|
package/models/file.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* @
|
|
2
|
+
* File Database Access Layer
|
|
3
|
+
* @category saltcorn-data
|
|
4
|
+
* @module models/file
|
|
5
|
+
* @subcategory models
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
const db = require("../db");
|
|
@@ -19,8 +21,13 @@ const fs = require("fs").promises;
|
|
|
19
21
|
* 3. List of files stored in _sc_files table in Saltcorn database.
|
|
20
22
|
* 4. Each tenant has own file list and file storage.
|
|
21
23
|
* 5. This class provides file descriptor and basic functions to manipulate with files.
|
|
24
|
+
* @category saltcorn-data
|
|
22
25
|
*/
|
|
23
26
|
class File {
|
|
27
|
+
/**
|
|
28
|
+
* Constructor
|
|
29
|
+
* @param o {object}
|
|
30
|
+
*/
|
|
24
31
|
constructor(o) {
|
|
25
32
|
this.filename = o.filename;
|
|
26
33
|
this.location = o.location;
|
|
@@ -43,7 +50,14 @@ class File {
|
|
|
43
50
|
* @param selectopts
|
|
44
51
|
* @returns {Promise<*>}
|
|
45
52
|
*/
|
|
46
|
-
static async find(where, selectopts) {
|
|
53
|
+
static async find(where, selectopts = {}) {
|
|
54
|
+
if (selectopts.cached) {
|
|
55
|
+
const { getState } = require("../db/state");
|
|
56
|
+
const files = Object.values(getState().files).sort((a, b) =>
|
|
57
|
+
a.filename > b.filename ? 1 : -1
|
|
58
|
+
);
|
|
59
|
+
return files.map((t) => new File(t));
|
|
60
|
+
}
|
|
47
61
|
const db_flds = await db.select("_sc_files", where, selectopts);
|
|
48
62
|
return db_flds.map((dbf) => new File(dbf));
|
|
49
63
|
}
|
|
@@ -106,7 +120,7 @@ class File {
|
|
|
106
120
|
* @param file
|
|
107
121
|
* @param user_id
|
|
108
122
|
* @param min_role_read
|
|
109
|
-
* @returns {Promise<File
|
|
123
|
+
* @returns {Promise<File>}
|
|
110
124
|
*/
|
|
111
125
|
static async from_req_files(file, user_id, min_role_read = 1) {
|
|
112
126
|
if (Array.isArray(file)) {
|
|
@@ -150,6 +164,11 @@ class File {
|
|
|
150
164
|
return { error: e.message };
|
|
151
165
|
}
|
|
152
166
|
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* MIME type of the file
|
|
170
|
+
* @type {string}
|
|
171
|
+
*/
|
|
153
172
|
get mimetype() {
|
|
154
173
|
return `${this.mime_super}/${this.mime_sub}`;
|
|
155
174
|
}
|