@opengis/fastify-table 1.3.21 → 1.3.23
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/index.js +17 -6
- package/package.json +6 -3
- package/server/helpers/format/formatAuto.js +13 -0
- package/server/helpers/format/formatDate.js +258 -0
- package/server/helpers/format/formatDigit.js +21 -0
- package/server/helpers/format/formatNum.js +361 -0
- package/server/helpers/format/formatNumber.js +54 -0
- package/server/helpers/format/formatRelative.js +106 -0
- package/server/helpers/format/formatUnit.js +38 -0
- package/server/helpers/format/num_format.js +42 -0
- package/server/helpers/format/set.js +26 -0
- package/server/helpers/funcs/_math.js +50 -0
- package/server/helpers/funcs/contentList.js +58 -0
- package/server/helpers/funcs/empty.js +21 -0
- package/server/helpers/funcs/ifCond.js +106 -0
- package/server/helpers/funcs/ifCondAnd.js +96 -0
- package/server/helpers/funcs/ifCondOr.js +98 -0
- package/server/helpers/funcs/inc.js +21 -0
- package/server/helpers/funcs/json.js +3 -0
- package/server/helpers/funcs/qrcode.js +66 -0
- package/server/helpers/funcs/round.js +28 -0
- package/server/helpers/funcs/select.js +50 -0
- package/server/helpers/index.js +70 -3
- package/server/helpers/string/coalesce.js +31 -0
- package/server/helpers/string/concat.js +28 -0
- package/server/helpers/string/split.js +20 -0
- package/server/helpers/string/str_replace.js +61 -0
- package/server/helpers/string/substr.js +32 -0
- package/server/helpers/string/translit.js +23 -0
- package/server/helpers/string/utils/alphabet.js +76 -0
- package/server/plugins/crud/funcs/utils/logChanges.js +1 -1
- package/server/plugins/pg/funcs/getMeta.js +1 -1
- package/server/plugins/table/funcs/metaFormat/index.js +1 -1
- package/utils.js +2 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Handlebars from 'handlebars';
|
|
2
|
+
|
|
3
|
+
import getPG from '../../plugins/pg/funcs/getPG.js';
|
|
4
|
+
|
|
5
|
+
const maxLimit = 100;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Відображення даних з таблиці або запиту до БД на сторінці. За запитом отримуємо масив json із рядків бази даних.
|
|
9
|
+
* Є можливість застосування синтаксису sql у змінній query для формування запиту до БД.
|
|
10
|
+
*
|
|
11
|
+
* @summary Відображення контенту на сторінці. Є можливість застосування синтаксису sql у змінній query.
|
|
12
|
+
* @priority 5
|
|
13
|
+
* @type helper
|
|
14
|
+
* @alias contentList
|
|
15
|
+
* @example
|
|
16
|
+
* {{#contentList table="help.doc_function" query="type='api'" sql1=1 limit=1}}{{#each rows}}{{{JSON 2 this}}}{{/each}}{{/contentList}}
|
|
17
|
+
* @example
|
|
18
|
+
* {{#contentList table="help.doc_function" sql1=1 query="name like '%form%'" limit=1}}{{#each rows}}{{{JSON 2 this}}}{{/each}}{{/contentList}}
|
|
19
|
+
* @example
|
|
20
|
+
* {{#contentList table="help.article" sql=1 query="module='CORE'" limit=1}}{{#each rows}}{{{JSON 2 this}}}{{/each}}{{/contentList}}
|
|
21
|
+
* @param {String} table Таблиця в базі або конфіг таблиця
|
|
22
|
+
* @param {String} query Запит до бази
|
|
23
|
+
* @param {Number} limit Кількість рядків на сторінці
|
|
24
|
+
* @param {String} sql Вивід sql запиту
|
|
25
|
+
* @returns {String} Returns HTML
|
|
26
|
+
*/
|
|
27
|
+
export default async function contentList(options) {
|
|
28
|
+
const { table, limit, query, order, sql, debug } = options.hash;
|
|
29
|
+
if (!table) { return 'Table undefined'; }
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const pg = getPG();
|
|
33
|
+
|
|
34
|
+
const hasBrackets = table.trim().startsWith('(') && table.trim().endsWith(')');
|
|
35
|
+
|
|
36
|
+
const where = `where ${query ? query : '1=1'}`;
|
|
37
|
+
const _limit = limit !== undefined && limit !== null ? Math.min(maxLimit, +limit) : 15;
|
|
38
|
+
const _order = order ? `order by ${order}` : '';
|
|
39
|
+
|
|
40
|
+
const SQL = `select *,${pg.pk[table] || '1'}::text from ${hasBrackets ? table + ' t' : table} ${where} ${_order} limit ${_limit}`;
|
|
41
|
+
const compiledSQL = Handlebars.compile(SQL)(options.data.root);
|
|
42
|
+
|
|
43
|
+
if (sql) {
|
|
44
|
+
return compiledSQL.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"')
|
|
45
|
+
.replaceAll("'", ''');
|
|
46
|
+
}
|
|
47
|
+
const { rows } = await pg.query(compiledSQL);
|
|
48
|
+
const data = { rows, total: rows.length, ...options.data?.root };
|
|
49
|
+
|
|
50
|
+
if (debug) {
|
|
51
|
+
return JSON.stringify(data, null, 2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return options.fn(data);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return `Сталася помилка, зверніться до відділу підтримки.<!-- err: ${err.toString()} -->`;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Повертає пустий рядок замість змісту іншого хелпера при компіляції шаблону (сторінки). Пишеться на початку хелпера та дозволяє ігнорувати подальший зміст. Аналогічно коментуванню ділянок коду лапками, проте не погіршує зовнішній вигляд коду
|
|
3
|
+
*
|
|
4
|
+
* @summary Повертає пустий рядок замість змісту іншого хелпера при компіляції шаблону.
|
|
5
|
+
* @priority 4
|
|
6
|
+
* @alias empty
|
|
7
|
+
* @type helper
|
|
8
|
+
* @tag condition
|
|
9
|
+
* @example
|
|
10
|
+
* {{{empty widget.widget_adverts}}}
|
|
11
|
+
* @example
|
|
12
|
+
* {{{empty _hb template="change-password-email-template"}}}
|
|
13
|
+
* @example
|
|
14
|
+
* {{empty formatDate bt_on_work_date format="dd.MM.yy / hh:mi:sec"}}
|
|
15
|
+
* @example
|
|
16
|
+
* {{{empty mls 'alertinfo'}}}
|
|
17
|
+
* @returns {String} Returns HTML
|
|
18
|
+
*/
|
|
19
|
+
export default function empty() {
|
|
20
|
+
return '';
|
|
21
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Перетинає два масиви
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* // returns [1, 4, 5, 6]
|
|
6
|
+
* intersect([1,2,3,4,5,6],[1,4,5,6,7,8,9,11])
|
|
7
|
+
* @param {Array} a
|
|
8
|
+
* @param {Array} b
|
|
9
|
+
* @returns {Array} Returns new intersect array
|
|
10
|
+
*/
|
|
11
|
+
function intersect(a, b) {
|
|
12
|
+
let aN = a; let bN = b;
|
|
13
|
+
if (b.length > a.length) {
|
|
14
|
+
[aN, bN] = [bN, aN];
|
|
15
|
+
}
|
|
16
|
+
return aN.filter((e) => bN.includes(e));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Створення шаблона або його частини внаслідок перевірки значення із веб-запиту та заздалегідь прописаного значення.
|
|
21
|
+
* Дозволяє змінювати наповнення сторінки через ряд перевірок. Є можливість внесення додаткової умови - що робити, коли умова не виконується.
|
|
22
|
+
*
|
|
23
|
+
* @summary Перевірка двох значень та виконання коду при виконанні умови, а також у всіх інших випадках.
|
|
24
|
+
* @priority 5
|
|
25
|
+
* @alias ifCond
|
|
26
|
+
* @type helper
|
|
27
|
+
* @tag condition
|
|
28
|
+
* @example
|
|
29
|
+
* {{#ifCond @root.req.domain 'in' 'help.softpro.ua,123'}} {{select user.uid data="get_full_uid"}} {{^}} Умова не виконана {{/ifCond}}
|
|
30
|
+
* @example
|
|
31
|
+
* {{#ifCond "1234567890" 'in' @root.user.group_list}} 1=1 {{^}} uid='{{uid}}' {{/ifCond}}
|
|
32
|
+
* @example
|
|
33
|
+
* {{#ifCond 'debug' 'in' @root.setting.core.setting}}Умова виконана{{^}}Не виконана умова{{/ifCond}}
|
|
34
|
+
* @param {Array} args Параметри для значень і умов
|
|
35
|
+
* @param {Array} args[0]] Перше значення
|
|
36
|
+
* @param {Array} args[1]] Оператор
|
|
37
|
+
* @param {Array} args[2]] Друге значення
|
|
38
|
+
* @returns {String} Returns HTML
|
|
39
|
+
*/
|
|
40
|
+
export default function ifCond(v1, operator, v2, options) {
|
|
41
|
+
const __obj = this;
|
|
42
|
+
|
|
43
|
+
const isEmpty = (val) => val === null || val === undefined || (Array.isArray(val) && val.length === 0) || val === '';
|
|
44
|
+
|
|
45
|
+
switch (operator) {
|
|
46
|
+
case '==':
|
|
47
|
+
return (v1 == v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
48
|
+
case '!=':
|
|
49
|
+
return (v1 != v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
50
|
+
case '===':
|
|
51
|
+
return (v1 === v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
52
|
+
case '!==':
|
|
53
|
+
return (v1 !== v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
54
|
+
case '&&':
|
|
55
|
+
return (v1 && v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
56
|
+
case '||':
|
|
57
|
+
return (v1 || v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
58
|
+
case '<':
|
|
59
|
+
return (v1 < v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
60
|
+
case '<=':
|
|
61
|
+
return (v1 <= v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
62
|
+
case '>':
|
|
63
|
+
return (v1 > v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
64
|
+
case '>=':
|
|
65
|
+
return (v1 >= v2) ? options.fn(__obj) : options.inverse(__obj);
|
|
66
|
+
case '&':
|
|
67
|
+
return (!isEmpty(v1) && !isEmpty(v2) && intersect(v1, v2).length !== 0)
|
|
68
|
+
? options.fn(__obj)
|
|
69
|
+
: options.inverse(__obj);
|
|
70
|
+
case '!~':
|
|
71
|
+
return (v1 || '').indexOf(v2) === -1
|
|
72
|
+
? options.fn(__obj)
|
|
73
|
+
: options.inverse(__obj);
|
|
74
|
+
case '~':
|
|
75
|
+
return (v1 || '').indexOf(v2) !== -1
|
|
76
|
+
? options.fn(__obj)
|
|
77
|
+
: options.inverse(__obj);
|
|
78
|
+
case 'period':
|
|
79
|
+
return (!isEmpty(v1) && !isEmpty(v2) && new Date(v1) < new Date() && new Date(v2) > new Date())
|
|
80
|
+
? options.fn(__obj)
|
|
81
|
+
: options.inverse(__obj);
|
|
82
|
+
case 'in': {
|
|
83
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
|
84
|
+
if (isEmpty(v1) || isEmpty(v2)) return options.inverse(__obj);
|
|
85
|
+
|
|
86
|
+
if (Array.isArray(v1)) {
|
|
87
|
+
return v1.some((value) => v2.includes(value.toString()))
|
|
88
|
+
? options.fn(__obj)
|
|
89
|
+
: options.inverse(__obj);
|
|
90
|
+
}
|
|
91
|
+
return v2.includes(v1.toString())
|
|
92
|
+
? options.fn(__obj)
|
|
93
|
+
: options.inverse(__obj);
|
|
94
|
+
}
|
|
95
|
+
case 'not in': {
|
|
96
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
|
97
|
+
if (isEmpty(v1) || isEmpty(v2)) return options.inverse(__obj);
|
|
98
|
+
|
|
99
|
+
return !v2.includes(v1.toString())
|
|
100
|
+
? options.fn(__obj)
|
|
101
|
+
: options.inverse(__obj);
|
|
102
|
+
}
|
|
103
|
+
default:
|
|
104
|
+
return options.inverse(__obj);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
function intersect(a, b) {
|
|
2
|
+
const aN = Array.isArray(a) ? a : typeof a === 'string' ? a.split(',') : [];
|
|
3
|
+
const bN = Array.isArray(b) ? b : typeof b === 'string' ? b.split(',') : [];
|
|
4
|
+
|
|
5
|
+
return aN.filter((e) => bN.includes(e));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Створення шаблона або його частини внаслідок виконання одразу декількох перевірок, кожна з яких повинна повернути true.
|
|
10
|
+
* Якщо хоча б одна умова не виконується, код у межах хелпера також не виконується.
|
|
11
|
+
*
|
|
12
|
+
* @summary Створення шаблона за перевіркою виконання декількох умов.
|
|
13
|
+
* @priority 4
|
|
14
|
+
* @type helper
|
|
15
|
+
* @tag condition
|
|
16
|
+
* @alias ifCondAnd
|
|
17
|
+
* @example
|
|
18
|
+
* {{#ifCondAnd 1 '==' 1 2 'in' '2,3' 2 'in' '1,2' }}1{{^}}2{{/ifCondAnd}}
|
|
19
|
+
* @descr Виконання перевірки порівняння параметрів та їх входження до певного масиву (множини)
|
|
20
|
+
* @example
|
|
21
|
+
*/
|
|
22
|
+
export default function ifCondAnd() {
|
|
23
|
+
const args = Array.from(arguments);
|
|
24
|
+
const options = args.pop();
|
|
25
|
+
|
|
26
|
+
const conditions = [];
|
|
27
|
+
for (let i = 0; i < args.length; i += 3) {
|
|
28
|
+
const v1 = args[i];
|
|
29
|
+
const operator = args[i + 1];
|
|
30
|
+
let v2 = args[i + 2];
|
|
31
|
+
|
|
32
|
+
switch (operator) {
|
|
33
|
+
case '==':
|
|
34
|
+
conditions.push(v1 == v2);
|
|
35
|
+
break;
|
|
36
|
+
case '!=':
|
|
37
|
+
conditions.push(v1 != v2);
|
|
38
|
+
break;
|
|
39
|
+
case '===':
|
|
40
|
+
conditions.push(v1 === v2);
|
|
41
|
+
break;
|
|
42
|
+
case '!==':
|
|
43
|
+
conditions.push(v1 !== v2);
|
|
44
|
+
break;
|
|
45
|
+
case '&&':
|
|
46
|
+
conditions.push(v1 && v2);
|
|
47
|
+
break;
|
|
48
|
+
case '||':
|
|
49
|
+
conditions.push(v1 || v2);
|
|
50
|
+
break;
|
|
51
|
+
case '<':
|
|
52
|
+
conditions.push(v1 < v2);
|
|
53
|
+
break;
|
|
54
|
+
case '<=':
|
|
55
|
+
conditions.push(v1 <= v2);
|
|
56
|
+
break;
|
|
57
|
+
case '>':
|
|
58
|
+
conditions.push(v1 > v2);
|
|
59
|
+
break;
|
|
60
|
+
case '>=':
|
|
61
|
+
conditions.push(v1 >= v2);
|
|
62
|
+
break;
|
|
63
|
+
case '&':
|
|
64
|
+
conditions.push(intersect(v1, v2).length > 0);
|
|
65
|
+
break;
|
|
66
|
+
case '!~':
|
|
67
|
+
conditions.push((v1 || '').indexOf(v2) === -1);
|
|
68
|
+
break;
|
|
69
|
+
case '~':
|
|
70
|
+
conditions.push((v1 || '').indexOf(v2) !== -1);
|
|
71
|
+
break;
|
|
72
|
+
case 'period':
|
|
73
|
+
conditions.push(new Date(v1) < new Date() && new Date(v2) > new Date());
|
|
74
|
+
break;
|
|
75
|
+
case 'in': {
|
|
76
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
|
77
|
+
if (Array.isArray(v1)) {
|
|
78
|
+
conditions.push(v1.some((value) => v2.includes(value.toString())));
|
|
79
|
+
} else {
|
|
80
|
+
conditions.push(v2.includes(v1.toString()));
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case 'not in': {
|
|
85
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
|
86
|
+
conditions.push(!v2.includes(v1.toString()));
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
default:
|
|
90
|
+
conditions.push(false);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return conditions.every(Boolean) ? options.fn(this) : options.inverse(this);
|
|
96
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
function intersect(a, b) {
|
|
2
|
+
const aN = Array.isArray(a) ? a : typeof a === 'string' ? a.split(',') : [];
|
|
3
|
+
const bN = Array.isArray(b) ? b : typeof b === 'string' ? b.split(',') : [];
|
|
4
|
+
|
|
5
|
+
return aN.filter((e) => bN.includes(e));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Створення шаблона або його частини внаслідок виконання одразу декількох перевірок, хоча б одна з яких повинна повернути true
|
|
11
|
+
*
|
|
12
|
+
* @summary Створення шаблона за перевіркою "АБО". Повинна виконатись хоча б одна умова.
|
|
13
|
+
* @priority 4
|
|
14
|
+
* @type helper
|
|
15
|
+
* @tag condition
|
|
16
|
+
* @alias ifCondOr
|
|
17
|
+
* @example
|
|
18
|
+
* {{#ifCondOr 1 '==' 1 2 'in' '2,3' 2 'in' '1,2' }}Хоча б одна умова виконана{{^}}Жодна умова не була виконана{{/ifCondOr}}
|
|
19
|
+
* @descr Виконання перевірки порівняння параметрів та їх входження до певного масиву (множини)
|
|
20
|
+
* @example
|
|
21
|
+
* @param {Array} args Параметри для значень і умов
|
|
22
|
+
* @returns {String} Returns HTML
|
|
23
|
+
*/
|
|
24
|
+
export default function ifCondOr() {
|
|
25
|
+
const args = Array.from(arguments);
|
|
26
|
+
const options = args.pop();
|
|
27
|
+
|
|
28
|
+
const conditions = [];
|
|
29
|
+
for (let i = 0; i < args.length; i += 3) {
|
|
30
|
+
const v1 = args[i];
|
|
31
|
+
const operator = args[i + 1];
|
|
32
|
+
let v2 = args[i + 2];
|
|
33
|
+
|
|
34
|
+
switch (operator) {
|
|
35
|
+
case '==':
|
|
36
|
+
conditions.push(v1 == v2);
|
|
37
|
+
break;
|
|
38
|
+
case '!=':
|
|
39
|
+
conditions.push(v1 != v2);
|
|
40
|
+
break;
|
|
41
|
+
case '===':
|
|
42
|
+
conditions.push(v1 === v2);
|
|
43
|
+
break;
|
|
44
|
+
case '!==':
|
|
45
|
+
conditions.push(v1 !== v2);
|
|
46
|
+
break;
|
|
47
|
+
case '&&':
|
|
48
|
+
conditions.push(v1 && v2);
|
|
49
|
+
break;
|
|
50
|
+
case '||':
|
|
51
|
+
conditions.push(v1 || v2);
|
|
52
|
+
break;
|
|
53
|
+
case '<':
|
|
54
|
+
conditions.push(v1 < v2);
|
|
55
|
+
break;
|
|
56
|
+
case '<=':
|
|
57
|
+
conditions.push(v1 <= v2);
|
|
58
|
+
break;
|
|
59
|
+
case '>':
|
|
60
|
+
conditions.push(v1 > v2);
|
|
61
|
+
break;
|
|
62
|
+
case '>=':
|
|
63
|
+
conditions.push(v1 >= v2);
|
|
64
|
+
break;
|
|
65
|
+
case '&':
|
|
66
|
+
conditions.push(intersect(v1, v2).length > 0);
|
|
67
|
+
break;
|
|
68
|
+
case '!~':
|
|
69
|
+
conditions.push((v1 || '').indexOf(v2) === -1);
|
|
70
|
+
break;
|
|
71
|
+
case '~':
|
|
72
|
+
conditions.push((v1 || '').indexOf(v2) !== -1);
|
|
73
|
+
break;
|
|
74
|
+
case 'period':
|
|
75
|
+
conditions.push(new Date(v1) < new Date() && new Date(v2) > new Date());
|
|
76
|
+
break;
|
|
77
|
+
case 'in': {
|
|
78
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
|
79
|
+
if (Array.isArray(v1)) {
|
|
80
|
+
conditions.push(v1.some((value) => v2.includes(value.toString())));
|
|
81
|
+
} else {
|
|
82
|
+
conditions.push(v2.includes(v1.toString()));
|
|
83
|
+
}
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'not in': {
|
|
87
|
+
if (typeof v2 === 'string') v2 = v2.split(',').map(item => item.trim());
|
|
88
|
+
conditions.push(!v2.includes(v1.toString()));
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
default:
|
|
92
|
+
conditions.push(false);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return conditions.some(Boolean) ? options.fn(this) : options.inverse(this);
|
|
98
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Складення двох значень з масива
|
|
3
|
+
*
|
|
4
|
+
* @summary Складення двох значень з масива. Є можливість використання у якості порядокового номеру
|
|
5
|
+
* @priority 3
|
|
6
|
+
* @type helper
|
|
7
|
+
* @alias inc
|
|
8
|
+
* @tag math
|
|
9
|
+
* @example
|
|
10
|
+
* {{{inc (coalesce @index 0)}}}
|
|
11
|
+
* @descr При використанні в межах циклу, дозволяє відображати віртуальний індекс
|
|
12
|
+
* @param {Array} args Масив чисел для складання
|
|
13
|
+
* @returns {String} Returns HTML
|
|
14
|
+
*/
|
|
15
|
+
export default function inc(...args) {
|
|
16
|
+
const firstValue = args[0] || 0;
|
|
17
|
+
const secondValue = typeof args[1] === 'number' ? args[1] : 1;
|
|
18
|
+
|
|
19
|
+
return firstValue + secondValue;
|
|
20
|
+
}
|
|
21
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import qr from 'qrcode';
|
|
2
|
+
|
|
3
|
+
function isAllowedType(type) {
|
|
4
|
+
return Boolean({
|
|
5
|
+
png: 1, svg: 1, eps: 1, pdf: 1,
|
|
6
|
+
}[type]);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isAllowedErrorCorrectionLevel(ecLevel) {
|
|
10
|
+
return Boolean({
|
|
11
|
+
L: 1, M: 1, Q: 1, H: 1,
|
|
12
|
+
}[ecLevel]);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Генерує зображення з QR кодом, що перенаправляє на посилання.
|
|
17
|
+
*
|
|
18
|
+
* @summary Генератор QR код. Є можливість задання відступу та встановлення розширення вихідного зображення
|
|
19
|
+
* @priority 3
|
|
20
|
+
* @type helper
|
|
21
|
+
* @tag file
|
|
22
|
+
* @alias qrcode-generator-base64
|
|
23
|
+
* @example
|
|
24
|
+
* {{{qrcode-generator-base64 text=@root.req.domain type='png' margin=1}}}
|
|
25
|
+
* @param {String} text Змінна, що містить посилання на файл або сторінку
|
|
26
|
+
* @param {String} type Розширення зображення
|
|
27
|
+
* @param {String} ec_level (?)
|
|
28
|
+
* @param {Number} margin Відступ
|
|
29
|
+
* @param {Number} size Розмір
|
|
30
|
+
* @returns {String} Returns HTML
|
|
31
|
+
*/
|
|
32
|
+
export default async function qrcodeGenerator({ hash }) {
|
|
33
|
+
const text = String(hash?.text || '');
|
|
34
|
+
const type = String(hash?.type || 'png').toLowerCase();
|
|
35
|
+
const ecLevel = String(hash?.ec_level || 'M').toUpperCase();
|
|
36
|
+
const margin = Math.abs(hash?.margin || (type === 'png' ? 4 : 1));
|
|
37
|
+
let size;
|
|
38
|
+
|
|
39
|
+
if (type === 'png') {
|
|
40
|
+
size = Math.abs(hash?.size) || 5;
|
|
41
|
+
} else if (type === 'svg') {
|
|
42
|
+
size = Math.abs(hash?.size) || undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!text) {
|
|
46
|
+
return 'Qr text is required.';
|
|
47
|
+
}
|
|
48
|
+
if (!isAllowedType(type)) {
|
|
49
|
+
return `Type "${type}" is not allowed.`;
|
|
50
|
+
}
|
|
51
|
+
if (!isAllowedErrorCorrectionLevel(ecLevel)) {
|
|
52
|
+
return `Error correction level "${ecLevel}" is not allowed.`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const options = (type === 'png' || type === 'svg') ? {
|
|
56
|
+
ec_level: ecLevel, type, margin, size,
|
|
57
|
+
} : { ec_level: ecLevel, type, margin };
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const qrString = await qr.toDataURL(text, options);
|
|
61
|
+
|
|
62
|
+
return qrString;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
return err.toString();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Округлення числа до певної точності
|
|
3
|
+
*
|
|
4
|
+
* @summary Округлення числа до певної точності. Є можливість округлення до низу.
|
|
5
|
+
* @priority 3
|
|
6
|
+
* @type helper
|
|
7
|
+
* @alias round
|
|
8
|
+
* @tag format
|
|
9
|
+
* @example
|
|
10
|
+
* {{round 3.14159265359 dec="6"}}
|
|
11
|
+
* @descr Округлення числа із змінної x до 6 знаків після роздільника
|
|
12
|
+
* @param {Number} dec Підрахування числа після роздільника
|
|
13
|
+
* @param {Any} floor False - округлення до низу
|
|
14
|
+
* @param {Number} data Передане число
|
|
15
|
+
* @returns {String} Returns HTML
|
|
16
|
+
*/
|
|
17
|
+
export default function round(data, options) {
|
|
18
|
+
const _data = parseFloat(data);
|
|
19
|
+
if (isNaN(_data)) return '';
|
|
20
|
+
|
|
21
|
+
const dec = options.hash.dec ? parseInt(options.hash.dec, 10) : 0;
|
|
22
|
+
|
|
23
|
+
if (options.hash.floor) {
|
|
24
|
+
return Math.floor(_data).toFixed(dec);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return _data.toFixed(dec);
|
|
28
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import getPG from '../../plugins/pg/funcs/getPG.js';
|
|
2
|
+
import getSelect from '../../plugins/table/funcs/getSelect.js';
|
|
3
|
+
|
|
4
|
+
const pg = getPG();
|
|
5
|
+
|
|
6
|
+
export default async function select(ids, options) {
|
|
7
|
+
if (!ids && ids !== false && ids !== 0) {
|
|
8
|
+
return ids;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(ids) && ids.length === 0) {
|
|
11
|
+
return '';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const data = options.hash?.data;
|
|
15
|
+
if (!data) return '';
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const idsArray = Array.isArray(ids) ? ids : [ids];
|
|
19
|
+
|
|
20
|
+
const classifier = await getSelect(data);
|
|
21
|
+
|
|
22
|
+
if (!classifier) return `Не знайдено класифікатор ${data}`;
|
|
23
|
+
|
|
24
|
+
if (classifier.sql && typeof classifier.sql === 'string') {
|
|
25
|
+
const metaQuery = `SELECT * FROM (${classifier.sql})q LIMIT 0`;
|
|
26
|
+
const meta = await pg.query(metaQuery);
|
|
27
|
+
const idColumn = meta.fields[0].name;
|
|
28
|
+
const textColumn = meta.fields[1].name;
|
|
29
|
+
|
|
30
|
+
const sql = `SELECT "${idColumn}" AS id, "${textColumn}" AS text FROM (${classifier.sql}) q WHERE "${idColumn}" = ANY($1::text[])`;
|
|
31
|
+
const values = [idsArray.map(id => String(id))];
|
|
32
|
+
|
|
33
|
+
const { rows } = await pg.query(sql, values);
|
|
34
|
+
classifier.arr = rows;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!Array.isArray(classifier?.arr) || !classifier.arr.length || !classifier.arr.every(item => typeof item === 'object' && item !== null)) {
|
|
38
|
+
return ids;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const results = idsArray.map(id => {
|
|
42
|
+
const result = classifier.arr.find(el => String(el.id) == String(id));
|
|
43
|
+
return result ? result.text : '';
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return results.filter(Boolean).join(', ');
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return `Сталася помилка.<!-- err: ${error.toString()} -->`;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/server/helpers/index.js
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
|
-
import { handlebars } from '
|
|
2
|
-
|
|
1
|
+
import { logger, handlebars } from '../../utils.js';
|
|
2
|
+
|
|
3
|
+
// funcs
|
|
4
|
+
import _math from './funcs/_math.js'
|
|
5
|
+
import ifCond from './funcs/ifCond.js'
|
|
6
|
+
import ifCondAnd from './funcs/ifCondAnd.js'
|
|
7
|
+
import ifCondOr from './funcs/ifCondOr.js'
|
|
8
|
+
import qrcode from './funcs/qrcode.js'
|
|
9
|
+
import select from './funcs/select.js'
|
|
10
|
+
import json from './funcs/json.js'
|
|
11
|
+
import empty from './funcs/empty.js'
|
|
12
|
+
import round from './funcs/round.js'
|
|
13
|
+
import contentList from './funcs/contentList.js'
|
|
14
|
+
import inc from './funcs/inc.js'
|
|
15
|
+
|
|
16
|
+
// format
|
|
17
|
+
import formatAuto from './format/formatAuto.js'
|
|
18
|
+
import formatDate from './format/formatDate.js'
|
|
19
|
+
import formatDigit from './format/formatDigit.js'
|
|
20
|
+
import formatNum from './format/formatNum.js'
|
|
21
|
+
import formatNumber from './format/formatNumber.js'
|
|
22
|
+
import formatRelative from './format/formatRelative.js'
|
|
23
|
+
import formatUnit from './format/formatUnit.js'
|
|
24
|
+
import num_format from './format/num_format.js'
|
|
25
|
+
import set from './format/set.js'
|
|
26
|
+
|
|
27
|
+
// string
|
|
28
|
+
import str_replace from './string/str_replace.js'
|
|
29
|
+
import coalesce from './string/coalesce.js'
|
|
30
|
+
import concat from './string/concat.js'
|
|
31
|
+
import split from './string/split.js'
|
|
32
|
+
import translit from './string/translit.js'
|
|
33
|
+
import substr from './string/substr.js'
|
|
3
34
|
|
|
4
35
|
function getKeysRecursive(obj, prefix = '') {
|
|
5
36
|
if (!obj || typeof obj !== 'object' || obj?.constructor?.name !== 'Object') return [];
|
|
@@ -26,4 +57,40 @@ export default async function helpers() {
|
|
|
26
57
|
}
|
|
27
58
|
return args;
|
|
28
59
|
});
|
|
29
|
-
|
|
60
|
+
|
|
61
|
+
// format
|
|
62
|
+
handlebars.registerHelper('formatAuto', formatAuto);
|
|
63
|
+
handlebars.registerHelper('formatDate', formatDate);
|
|
64
|
+
handlebars.registerHelper('formatDigit', formatDigit);
|
|
65
|
+
handlebars.registerHelper('formatNum', formatNum);
|
|
66
|
+
handlebars.registerHelper('formatNumber', formatNumber);
|
|
67
|
+
handlebars.registerHelper('formatRelative', formatRelative);
|
|
68
|
+
handlebars.registerHelper('formatUnit', formatUnit);
|
|
69
|
+
handlebars.registerHelper('num_format', num_format);
|
|
70
|
+
handlebars.registerHelper('set', set);
|
|
71
|
+
|
|
72
|
+
// string
|
|
73
|
+
handlebars.registerHelper('str_replace', str_replace);
|
|
74
|
+
handlebars.registerHelper('coalesce', coalesce);
|
|
75
|
+
handlebars.registerHelper('concat', concat);
|
|
76
|
+
handlebars.registerHelper('split', split);
|
|
77
|
+
handlebars.registerHelper('translit', translit);
|
|
78
|
+
handlebars.registerHelper('substr', substr);
|
|
79
|
+
handlebars.registerHelper('mls', (value) => value);
|
|
80
|
+
|
|
81
|
+
// funcs
|
|
82
|
+
handlebars.registerHelper('json', json);
|
|
83
|
+
handlebars.registerHelper('_math', _math);
|
|
84
|
+
handlebars.registerHelper('ifCond', ifCond);
|
|
85
|
+
handlebars.registerHelper('ifCondAnd', ifCondAnd);
|
|
86
|
+
handlebars.registerHelper('ifCondOr', ifCondOr);
|
|
87
|
+
handlebars.registerHelper('select', select);
|
|
88
|
+
handlebars.registerHelper('empty', empty);
|
|
89
|
+
handlebars.registerHelper('round', round);
|
|
90
|
+
handlebars.registerHelper('contentList', contentList);
|
|
91
|
+
handlebars.registerHelper('inc', inc);
|
|
92
|
+
handlebars.registerHelper('qrcode', qrcode);
|
|
93
|
+
|
|
94
|
+
// Підтримка старого коду
|
|
95
|
+
handlebars.registerHelper('qrcode-generator-base64', qrcode);
|
|
96
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Повертає перший елемент, який не є пустим або відсутнім
|
|
3
|
+
*
|
|
4
|
+
* @summary Повертає перший не null елемент. Є можливість застосування разом із іншими хелперами.
|
|
5
|
+
* @priority 4
|
|
6
|
+
* @type helper
|
|
7
|
+
* @alias coalesce
|
|
8
|
+
* @tag string
|
|
9
|
+
* @example
|
|
10
|
+
* {{coalesce color '#337ab7'}}
|
|
11
|
+
* @descr Повертає значення змінної color або '#337ab7', якщо така змінна відсутня
|
|
12
|
+
* @example
|
|
13
|
+
* {{coalesce @root.lang 'ua'}}
|
|
14
|
+
* @example
|
|
15
|
+
* {{coalesce (select uid data="user_id") (select uid data="uid")}}
|
|
16
|
+
* @descr За відсутості співпадіння в межах пошуку по першому класифікатору, виводить результат другого селекту
|
|
17
|
+
* @param {Array} args Масив аргументів для фільтрування
|
|
18
|
+
* @returns {String} Return Html
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export default function coalesce(...args) {
|
|
22
|
+
const options = args.pop();
|
|
23
|
+
|
|
24
|
+
for (const arg of args) {
|
|
25
|
+
if (arg != null && arg !== '') {
|
|
26
|
+
return arg;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return options.fn ? options.fn(this) : (options.inverse ? options.inverse(this) : '');
|
|
31
|
+
}
|