@opengis/cms 0.0.44 → 0.0.46

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.
Files changed (104) hide show
  1. package/README.md +98 -98
  2. package/dist/{en-OTy187va.js → en-sWmaB9uu.js} +3 -1
  3. package/dist/index.html +29 -29
  4. package/dist/index.js +4208 -4125
  5. package/dist/index.umd.cjs +70 -65
  6. package/dist/{uk-Dy2psXBp.js → uk-rH4IPxG5.js} +4 -2
  7. package/input-types.json +9 -9
  8. package/module/cms/cls/content.status.json +17 -17
  9. package/module/cms/cls/user_type.json +9 -9
  10. package/module/cms/form/admin.users.form.json +77 -77
  11. package/module/cms/select/cms.page_type.sql +1 -1
  12. package/module/cms/select/news_tag_id.sql +11 -11
  13. package/module/cms/table/admin.users.table.json +53 -53
  14. package/module/cms/table/collection.default.table.json +96 -96
  15. package/module/cms/table/single.default.table.json +116 -116
  16. package/package.json +66 -65
  17. package/plugin.js +43 -43
  18. package/server/app.js +35 -35
  19. package/server/config.js +4 -4
  20. package/server/functions/getContent.js +44 -0
  21. package/server/functions/getDraftKey.js +22 -22
  22. package/server/functions/getUser.js +26 -0
  23. package/server/index.js +22 -22
  24. package/server/migrations/fixes.sql +124 -124
  25. package/server/migrations/site.sql +595 -595
  26. package/server/plugins/adminHook.js +78 -78
  27. package/server/plugins/hook.js +59 -59
  28. package/server/plugins/vite.js +75 -75
  29. package/server/routes/category/controllers/cms.category.delete.js +21 -21
  30. package/server/routes/category/controllers/cms.category.get.js +17 -17
  31. package/server/routes/category/controllers/cms.category.list.js +16 -16
  32. package/server/routes/category/controllers/cms.category.post.js +21 -21
  33. package/server/routes/category/controllers/cms.category.put.js +23 -23
  34. package/server/routes/category/index.mjs +22 -22
  35. package/server/routes/cms/controllers/cmsStat.js +55 -55
  36. package/server/routes/cms/controllers/cmsSuggest.js +57 -57
  37. package/server/routes/cms/controllers/deleteContent.js +113 -113
  38. package/server/routes/cms/controllers/deleteMedia.js +76 -76
  39. package/server/routes/cms/controllers/downloadMedia.js +84 -49
  40. package/server/routes/cms/controllers/getContent.js +110 -110
  41. package/server/routes/cms/controllers/getContentBySlug.js +93 -93
  42. package/server/routes/cms/controllers/getPermissions.js +15 -15
  43. package/server/routes/cms/controllers/insertContent.js +217 -217
  44. package/server/routes/cms/controllers/listMedia.js +108 -94
  45. package/server/routes/cms/controllers/metadataMedia.js +39 -39
  46. package/server/routes/cms/controllers/properties.get.js +18 -53
  47. package/server/routes/cms/controllers/properties.post.js +99 -99
  48. package/server/routes/cms/controllers/searchContent.js +205 -205
  49. package/server/routes/cms/controllers/setPermissions.js +49 -49
  50. package/server/routes/cms/controllers/translate.js +89 -89
  51. package/server/routes/cms/controllers/updateContent.js +238 -238
  52. package/server/routes/cms/controllers/uploadMedia.js +79 -79
  53. package/server/routes/cms/functions/getSettings.js +46 -0
  54. package/server/routes/cms/index.mjs +112 -112
  55. package/server/routes/cms/utils/additionalData.js +35 -35
  56. package/server/routes/cms/utils/getCollection.js +82 -82
  57. package/server/routes/cms/utils/getSingle.js +187 -187
  58. package/server/routes/cms/utils/inputTypes.js +5 -5
  59. package/server/routes/cms/utils/insertContentLocalization.js +86 -86
  60. package/server/routes/cms/utils/requestTranslation.js +85 -85
  61. package/server/routes/cms/utils/updateLocalization.js +47 -47
  62. package/server/routes/cmsSpace/controllers/deleteSpace.js +25 -25
  63. package/server/routes/cmsSpace/controllers/getSpaces.js +27 -27
  64. package/server/routes/cmsSpace/controllers/insertSpace.js +21 -21
  65. package/server/routes/cmsSpace/controllers/updateSpace.js +23 -23
  66. package/server/routes/cmsSpace/index.mjs +20 -20
  67. package/server/routes/contentType/controllers/addContentType.js +162 -162
  68. package/server/routes/contentType/controllers/contentTypeList.js +54 -54
  69. package/server/routes/contentType/controllers/delContentType.js +75 -75
  70. package/server/routes/contentType/controllers/editContentType.js +61 -61
  71. package/server/routes/contentType/controllers/getContentType.js +37 -37
  72. package/server/routes/contentType/index.mjs +35 -35
  73. package/server/routes/contentType/utils/updateContents.js +28 -28
  74. package/server/routes/contentType/utils/updateCustomContentTable.js +53 -53
  75. package/server/routes/feedback/controllers/email.list.js +24 -24
  76. package/server/routes/feedback/controllers/feedback.js +48 -48
  77. package/server/routes/feedback/controllers/feedback.list.js +37 -37
  78. package/server/routes/feedback/controllers/news.subscriptions.js +44 -44
  79. package/server/routes/feedback/index.mjs +71 -71
  80. package/server/routes/logs/controllers/export.user.logs.js +77 -77
  81. package/server/routes/logs/controllers/user.logs.js +44 -44
  82. package/server/routes/logs/index.mjs +9 -9
  83. package/server/routes/menu/controllers/addMenu.js +37 -37
  84. package/server/routes/menu/controllers/delMenu.js +31 -31
  85. package/server/routes/menu/controllers/editMenu.js +41 -41
  86. package/server/routes/menu/controllers/getMenu.js +24 -42
  87. package/server/routes/menu/functions/getMenu.js +48 -0
  88. package/server/routes/menu/index.mjs +13 -13
  89. package/server/routes/migration/controllers/collectionToCustom.js +137 -137
  90. package/server/routes/migration/index.mjs +8 -8
  91. package/server/routes/root.mjs +8 -8
  92. package/server/routes/tags/controllers/add.tags.js +24 -24
  93. package/server/routes/tags/controllers/del.tags.js +19 -19
  94. package/server/routes/tags/controllers/edit.tags.js +25 -25
  95. package/server/routes/tags/controllers/get.tags.js +15 -15
  96. package/server/routes/tags/index.mjs +14 -14
  97. package/server/templates/cls/cms.category_type.json +9 -9
  98. package/server/templates/cls/cms.content_review_status.json +9 -9
  99. package/server/templates/cls/cms.content_status.json +9 -9
  100. package/server/templates/cls/cms.content_type.json +9 -9
  101. package/server/templates/cls/cms.lang.json +9 -9
  102. package/server/templates/page/login.html +126 -126
  103. package/server/templates/select/core.user_mentioned.sql +1 -1
  104. package/utils.js +6 -0
@@ -1,83 +1,83 @@
1
- import { pgClients, getData, getMeta } from "@opengis/fastify-table/utils.js";
2
-
3
- import additionalData from "./additionalData.js";
4
-
5
- export default async function getCollection({
6
- table, id, limit, maxLimit, order, search, tags, filter, state, page, desc, sql, locale, contextQuery, statusQuery, columns, preview, user, defaultColumns, fields, defaultFields = [], defaultFilters = []
7
- }, reply, pg = pgClients.client) {
8
- if (!table || !pg.pk?.['data.' + table]) {
9
- return { message: 'content table not found', status: 404 };
10
- }
11
-
12
- const isPinExists = await pg.query(`
13
- SELECT 1
14
- FROM pg_catalog.pg_attribute a
15
- JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
16
- JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
17
- WHERE n.nspname = 'data'
18
- AND c.relname = $1
19
- AND a.attname = 'is_pin'
20
- AND a.attnum > 0
21
- AND NOT a.attisdropped;
22
- `, [table]).then(el => el.rowCount);
23
-
24
- const defaultOrder = `case when status='archived' then true else false end ${isPinExists ? ', is_pin desc' : ''}, published_at desc nulls last`;
25
-
26
- const localeQuery = locale && locale !== 'uk' ? `id in ( select object_id from site.localization where REVERSE(split_part(REVERSE(field_key), ':', 1)) = '${locale.replace(/'/g, "''")}' )` : undefined;
27
- const tagQuery = tags ? `id in (SELECT data_id FROM site.tag_data td left join site.tags ts on td.tag_id=ts.tag_id WHERE ts.slug = any('{ ${tags.replace(/'/g, "''")} }'::text[]) or ts.tag_id = any('{ ${tags.replace(/'/g, "''")} }'::text[]))` : null;
28
- const cQuery = [id ? `'${id}' in (id,slug)` : contextQuery, localeQuery, ((columns || []).concat(defaultColumns || [])).find(col => col.name === 'status') ? statusQuery : null, tagQuery].filter(Boolean).join(' and ');
29
-
30
- const columnList = await getMeta({
31
- pg,
32
- table: 'data.' + `"${table}"`
33
- }).then(el => el.columns?.map?.(col => col.name) || []);
34
- const existingFields = fields && !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).join(',') : null;
35
-
36
- const filterList = defaultFilters.concat(columns.map(col => ({ name: col.name, type: col.type && ['date', 'datetime'].includes(col.type) ? 'Date' : 'Text', label: col.label, sql: col.sql })));
37
-
38
- const res = await getData({
39
- pg,
40
- table: 'data.' + `"${table}"`,
41
- columns: existingFields,
42
- filterList,
43
- query: {
44
- filter,
45
- state,
46
- limit: Math.min(limit, maxLimit),
47
- page,
48
- search,
49
- order: order ? order : defaultOrder,
50
- desc,
51
- sql
52
- },
53
- user,
54
- contextQuery: cQuery,
55
- actions: ['view']
56
- }, reply, true);
57
-
58
- const locales = await pg.query(`select array_agg(distinct REVERSE(split_part(REVERSE(field_key), ':', 1))) from site.localization where object_id in (select id from ${'data.' + `"${table}"`})`).then(el => el.rows?.[0]?.array_agg || []);
59
-
60
- // Apply localization and tags etc.
61
- if (res?.rows?.length) {
62
- await additionalData(pg, res.rows, locale, id ? null : existingFields);
63
- };
64
-
65
- const columns1 = !id
66
- ? columns?.map(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
67
- : columns?.filter?.(el => !el.hidden);
68
-
69
- const finalColumns = (defaultColumns || []).concat(columns1.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
70
-
71
- finalColumns.filter(el => el.default).forEach(col => {
72
- const { name, localization } = columns1.find(item => item.name === col.name) || {};
73
- if (name) {
74
- Object.assign(col, { localization });
75
- }
76
- });
77
-
78
- if (res?.columns) {
79
- Object.assign(res, { type: 'collection', locales, preview_path: preview, columns: finalColumns });
80
- }
81
-
82
- return res;
1
+ import { pgClients, getData, getMeta } from "@opengis/fastify-table/utils.js";
2
+
3
+ import additionalData from "./additionalData.js";
4
+
5
+ export default async function getCollection({
6
+ table, id, limit, maxLimit, order, search, tags, filter, state, page, desc, sql, locale, contextQuery, statusQuery, columns, preview, user, defaultColumns, fields, defaultFields = [], defaultFilters = []
7
+ }, reply, pg = pgClients.client) {
8
+ if (!table || !pg.pk?.['data.' + table]) {
9
+ return { message: 'content table not found', status: 404 };
10
+ }
11
+
12
+ const isPinExists = await pg.query(`
13
+ SELECT 1
14
+ FROM pg_catalog.pg_attribute a
15
+ JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
16
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
17
+ WHERE n.nspname = 'data'
18
+ AND c.relname = $1
19
+ AND a.attname = 'is_pin'
20
+ AND a.attnum > 0
21
+ AND NOT a.attisdropped;
22
+ `, [table]).then(el => el.rowCount);
23
+
24
+ const defaultOrder = `case when status='archived' then true else false end ${isPinExists ? ', is_pin desc' : ''}, published_at desc nulls last`;
25
+
26
+ const localeQuery = locale && locale !== 'uk' ? `id in ( select object_id from site.localization where REVERSE(split_part(REVERSE(field_key), ':', 1)) = '${locale.replace(/'/g, "''")}' )` : undefined;
27
+ const tagQuery = tags ? `id in (SELECT data_id FROM site.tag_data td left join site.tags ts on td.tag_id=ts.tag_id WHERE ts.slug = any('{ ${tags.replace(/'/g, "''")} }'::text[]) or ts.tag_id = any('{ ${tags.replace(/'/g, "''")} }'::text[]))` : null;
28
+ const cQuery = [id ? `'${id}' in (id,slug)` : contextQuery, localeQuery, ((columns || []).concat(defaultColumns || [])).find(col => col.name === 'status') ? statusQuery : null, tagQuery].filter(Boolean).join(' and ');
29
+
30
+ const columnList = await getMeta({
31
+ pg,
32
+ table: 'data.' + `"${table}"`
33
+ }).then(el => el.columns?.map?.(col => col.name) || []);
34
+ const existingFields = fields && !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).join(',') : null;
35
+
36
+ const filterList = defaultFilters.concat(columns.map(col => ({ name: col.name, type: col.type && ['date', 'datetime'].includes(col.type) ? 'Date' : 'Text', label: col.label, sql: col.sql })));
37
+
38
+ const res = await getData({
39
+ pg,
40
+ table: 'data.' + `"${table}"`,
41
+ columns: existingFields,
42
+ filterList,
43
+ query: {
44
+ filter,
45
+ state,
46
+ limit: Math.min(limit, maxLimit),
47
+ page,
48
+ search,
49
+ order: order ? order : defaultOrder,
50
+ desc,
51
+ sql
52
+ },
53
+ user,
54
+ contextQuery: cQuery,
55
+ actions: ['view']
56
+ }, reply, true);
57
+
58
+ const locales = await pg.query(`select array_agg(distinct REVERSE(split_part(REVERSE(field_key), ':', 1))) from site.localization where object_id in (select id from ${'data.' + `"${table}"`})`).then(el => el.rows?.[0]?.array_agg || []);
59
+
60
+ // Apply localization and tags etc.
61
+ if (res?.rows?.length) {
62
+ await additionalData(pg, res.rows, locale, id ? null : existingFields);
63
+ };
64
+
65
+ const columns1 = !id
66
+ ? columns?.map(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
67
+ : columns?.filter?.(el => !el.hidden);
68
+
69
+ const finalColumns = (defaultColumns || []).concat(columns1.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
70
+
71
+ finalColumns.filter(el => el.default).forEach(col => {
72
+ const { name, localization } = columns1.find(item => item.name === col.name) || {};
73
+ if (name) {
74
+ Object.assign(col, { localization });
75
+ }
76
+ });
77
+
78
+ if (res?.columns) {
79
+ Object.assign(res, { type: 'collection', locales, preview_path: preview, columns: finalColumns });
80
+ }
81
+
82
+ return res;
83
83
  }
@@ -1,188 +1,188 @@
1
- import path from 'node:path';
2
-
3
- import { pgClients, metaFormat, getFilterSQL, getFilter, getMeta } from '@opengis/fastify-table/utils.js';
4
-
5
- import inputTypes from '../utils/inputTypes.js';
6
- import additionalData from "./additionalData.js";
7
-
8
- export default async function getSingle({
9
- contentId: contentId1, id: id1, limit: queryLimit, maxLimit, search, filter, locale, statusQuery, user, sql, page, defaultColumns = [], fields, defaultFields = [], defaultFilters = [],
10
- }, pg = pgClients.client) {
11
- const id = id1 || (contentId1 === 'pages' ? undefined : contentId1) || '';
12
- const contentId = id1 ? contentId1 : "pages";
13
-
14
- // defaultColumns.push({
15
- // "name": "content_type_id",
16
- // "label": "Тип сторінки",
17
- // "type": "select",
18
- // "data": "cms.page_type"
19
- // });
20
-
21
- const customColumns = await pg.query(`
22
- SELECT columns FROM site.content_types WHERE content_type_id in (select content_type_id from site.contents where content_id=$1) limit 1
23
- `, [id1 || contentId]).then(el => el.rows?.[0]?.columns || []);
24
-
25
- const contentQuery = contentId === 'pages' || !id
26
- ? `(type = 'single' or content_type_id = 'pages')`
27
- : 'content_type_id=(select content_type_id from site.contents where content_id=$2)';
28
-
29
- const columns = defaultColumns.concat(customColumns.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
30
-
31
- columns.filter(el => el.default).forEach(col => {
32
- const { name, localization } = customColumns.find(item => item.name === col.name) || {};
33
- if (name) {
34
- Object.assign(col, { localization });
35
- }
36
- });
37
-
38
- const limit = Math.min(maxLimit, +(queryLimit || 20));
39
- const offset = page && page > 0 && !id ? (page - 1) * limit : 0;
40
-
41
- const fData = filter
42
- ? await getFilterSQL({
43
- pg,
44
- table: 'site.contents',
45
- filter,
46
- search,
47
- filterList: defaultFilters,
48
- query: `
49
- content_type_id IN (
50
- SELECT content_type_id
51
- FROM site.content_types
52
- WHERE ${contentQuery || 'true'}
53
- )
54
- `,
55
- })
56
- : {
57
- optimizedSQL: 'SELECT * FROM site.contents WHERE true',
58
- };
59
-
60
- const localeQuery = locale && locale !== 'uk' && pg.pk?.['site.contents'] ? `${pg.pk?.['site.contents']} in ( select object_id from site.localization where REVERSE( split_part( REVERSE(field_key), ':', 1) ) = '${locale.replace(/'/g, "''")}' )` : undefined;
61
-
62
- const columnList = await getMeta({
63
- pg,
64
- table: 'site.contents',
65
- }).then(el => el.columns?.map?.(col => col.name) || []);
66
- const existingFields = fields && contentId === 'pages' || !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).concat(['content_id AS id']).join(',') : '';
67
- const extraKeys = fields ? fields.split(',').filter(key => !defaultFields.includes(key)) : [];
68
- const cols = existingFields.length ? existingFields.concat(extraKeys?.length ? `,"extraKeys"` : []) : `content_id AS id,
69
- content_id,
70
- created_by,
71
- title,
72
- slug,
73
- meta,
74
- published_by,
75
- published_at,
76
- status,
77
- content_type_id,
78
- /* content_type_id AS type, */
79
- main_image
80
- ${contentId === 'pages' || !id ? `,"extraKeys"` : ''}`;
81
-
82
- const q = `
83
- SELECT ${cols} FROM (${fData.optimizedSQL}) a
84
- ${extraKeys || id ? `LEFT JOIN LATERAL (
85
- SELECT json_agg(row_to_json(q)) AS "extraKeys"
86
- FROM (
87
- SELECT field_key, field_type, field_value, field_value_object
88
- FROM site.content_data
89
- WHERE content_id = a.content_id
90
- ) q
91
- ) b ON true` : ''}
92
-
93
- LEFT JOIN LATERAL (
94
- SELECT type FROM site.content_types WHERE content_type_id=a.content_type_id LIMIT 1
95
- )c ON true
96
-
97
- WHERE $1=$1 and $2=$2
98
- and ${statusQuery || 'true'}
99
- and ${localeQuery || '1=1'}
100
- and ${contentQuery || 'true'}
101
- and ${id ? `$2 in (slug, content_id)` : 'true'}
102
- and content_id<>'pages'
103
- order by case when status='archived' then true else false end, published_at desc nulls last
104
- LIMIT ${limit} OFFSET ${id ? 0 : offset}
105
- `;
106
-
107
- if (sql && user?.user_type?.includes?.('admin')) {
108
- return q; // Expose raw SQL for debugging
109
- }
110
-
111
- const { total = 0, filtered = 0 } = await pg.query(`
112
- SELECT COUNT(*)::int as total,
113
- count(*) FILTER(WHERE ${fData.q || "true"})::int as filtered
114
- FROM site.contents
115
- WHERE content_type_id IN (
116
- SELECT content_type_id
117
- FROM site.content_types
118
- WHERE ${localeQuery || '1=1'}
119
- and $1=$1 and $2=$2 and ${contentQuery || 'true'}
120
- )
121
- and ${statusQuery || 'true'}
122
- and content_id<>'pages'
123
- `, [contentId, id]).then(el => el.rows?.[0] || {});
124
-
125
- const { rows = [] } = await pg.query(q, [contentId, id || '']);
126
-
127
- const locales = await pg.query(`select array_agg(distinct REVERSE(split_part(REVERSE(field_key), ':', 1))) from site.localization
128
- where object_id in (select content_id from site.contents
129
- where content_type_id IN (
130
- SELECT content_type_id FROM site.content_types where $1=$1 and $2=$2 and ${contentQuery || 'true'}
131
- )
132
- and ${statusQuery || 'true'}
133
- )`, [contentId, id]
134
- ).then(el => el.rows?.[0]?.array_agg || []);
135
-
136
- // Flatten extra fields into main object
137
- const rows1 = rows.map(row => ({
138
- ...row,
139
- ...Object.fromEntries(
140
- row.extraKeys?.filter(k => extraKeys?.length ? extraKeys.includes(k.field_key) : true)?.map(k =>
141
- [k.field_key, inputTypes[k.field_type] === 'json' || k.field_key === 'meta'
142
- ? k.field_value_object
143
- : k.field_value]
144
- ) || []
145
- ),
146
- extraKeys: undefined,
147
- }));
148
-
149
- // Apply localization and tags etc.
150
- await additionalData(pg, rows1, locale, contentId === 'pages' || !id ? [existingFields, extraKeys].filter(Boolean).join(',') : null);
151
-
152
- // Apply metadata formatting
153
- await metaFormat({
154
- rows: rows1,
155
- cls: {
156
- created_by: 'core.user_mentioned',
157
- // content_type_id: 'cms.type_id',
158
- // type: 'cms.type_id',
159
- },
160
- sufix: false,
161
- }, pg);
162
-
163
- const { list: filters = [] } = await getFilter({
164
- pg, table: 'single.default.table', user, filter: `content_type_id in (select content_type_id from site.content_types where type='single' or content_type_id='pages')`
165
- }) || {};
166
-
167
- const finalColumns = !id
168
- ? columns?.map?.(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
169
- : columns?.filter?.(el => !el.hidden);
170
-
171
- rows1.filter(row => row.single_sections?.length).forEach(row => {
172
- row.single_sections.filter(section => section.documents?.length).forEach(section => {
173
- section.documents.filter(doc => doc.file).forEach(doc => Object.assign(doc, { file_name: path.basename(doc.file) }));
174
- });
175
- });
176
-
177
- return {
178
- locales,
179
- type: 'single',
180
- total,
181
- filtered,
182
- count: rows1.length,
183
- pk: 'id',
184
- rows: rows1,
185
- columns: finalColumns,
186
- filters,
187
- };
1
+ import path from 'node:path';
2
+
3
+ import { pgClients, metaFormat, getFilterSQL, getFilter, getMeta } from '@opengis/fastify-table/utils.js';
4
+
5
+ import inputTypes from '../utils/inputTypes.js';
6
+ import additionalData from "./additionalData.js";
7
+
8
+ export default async function getSingle({
9
+ contentId: contentId1, id: id1, limit: queryLimit, maxLimit, search, filter, locale, statusQuery, user, sql, page, defaultColumns = [], fields, defaultFields = [], defaultFilters = [],
10
+ }, pg = pgClients.client) {
11
+ const id = id1 || (contentId1 === 'pages' ? undefined : contentId1) || '';
12
+ const contentId = id1 ? contentId1 : "pages";
13
+
14
+ // defaultColumns.push({
15
+ // "name": "content_type_id",
16
+ // "label": "Тип сторінки",
17
+ // "type": "select",
18
+ // "data": "cms.page_type"
19
+ // });
20
+
21
+ const customColumns = await pg.query(`
22
+ SELECT columns FROM site.content_types WHERE content_type_id in (select content_type_id from site.contents where content_id=$1) limit 1
23
+ `, [id1 || contentId]).then(el => el.rows?.[0]?.columns || []);
24
+
25
+ const contentQuery = contentId === 'pages' || !id
26
+ ? `(type = 'single' or content_type_id = 'pages')`
27
+ : 'content_type_id=(select content_type_id from site.contents where content_id=$2)';
28
+
29
+ const columns = defaultColumns.concat(customColumns.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
30
+
31
+ columns.filter(el => el.default).forEach(col => {
32
+ const { name, localization } = customColumns.find(item => item.name === col.name) || {};
33
+ if (name) {
34
+ Object.assign(col, { localization });
35
+ }
36
+ });
37
+
38
+ const limit = Math.min(maxLimit, +(queryLimit || 20));
39
+ const offset = page && page > 0 && !id ? (page - 1) * limit : 0;
40
+
41
+ const fData = filter
42
+ ? await getFilterSQL({
43
+ pg,
44
+ table: 'site.contents',
45
+ filter,
46
+ search,
47
+ filterList: defaultFilters,
48
+ query: `
49
+ content_type_id IN (
50
+ SELECT content_type_id
51
+ FROM site.content_types
52
+ WHERE ${contentQuery || 'true'}
53
+ )
54
+ `,
55
+ })
56
+ : {
57
+ optimizedSQL: 'SELECT * FROM site.contents WHERE true',
58
+ };
59
+
60
+ const localeQuery = locale && locale !== 'uk' && pg.pk?.['site.contents'] ? `${pg.pk?.['site.contents']} in ( select object_id from site.localization where REVERSE( split_part( REVERSE(field_key), ':', 1) ) = '${locale.replace(/'/g, "''")}' )` : undefined;
61
+
62
+ const columnList = await getMeta({
63
+ pg,
64
+ table: 'site.contents',
65
+ }).then(el => el.columns?.map?.(col => col.name) || []);
66
+ const existingFields = fields && contentId === 'pages' || !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).concat(['content_id AS id']).join(',') : '';
67
+ const extraKeys = fields ? fields.split(',').filter(key => !defaultFields.includes(key)) : [];
68
+ const cols = existingFields.length ? existingFields.concat(extraKeys?.length ? `,"extraKeys"` : []) : `content_id AS id,
69
+ content_id,
70
+ created_by,
71
+ title,
72
+ slug,
73
+ meta,
74
+ published_by,
75
+ published_at,
76
+ status,
77
+ content_type_id,
78
+ /* content_type_id AS type, */
79
+ main_image
80
+ ${contentId === 'pages' || !id ? `,"extraKeys"` : ''}`;
81
+
82
+ const q = `
83
+ SELECT ${cols} FROM (${fData.optimizedSQL}) a
84
+ ${extraKeys || id ? `LEFT JOIN LATERAL (
85
+ SELECT json_agg(row_to_json(q)) AS "extraKeys"
86
+ FROM (
87
+ SELECT field_key, field_type, field_value, field_value_object
88
+ FROM site.content_data
89
+ WHERE content_id = a.content_id
90
+ ) q
91
+ ) b ON true` : ''}
92
+
93
+ LEFT JOIN LATERAL (
94
+ SELECT type FROM site.content_types WHERE content_type_id=a.content_type_id LIMIT 1
95
+ )c ON true
96
+
97
+ WHERE $1=$1 and $2=$2
98
+ and ${statusQuery || 'true'}
99
+ and ${localeQuery || '1=1'}
100
+ and ${contentQuery || 'true'}
101
+ and ${id ? `$2 in (slug, content_id)` : 'true'}
102
+ and content_id<>'pages'
103
+ order by case when status='archived' then true else false end, published_at desc nulls last
104
+ LIMIT ${limit} OFFSET ${id ? 0 : offset}
105
+ `;
106
+
107
+ if (sql && user?.user_type?.includes?.('admin')) {
108
+ return q; // Expose raw SQL for debugging
109
+ }
110
+
111
+ const { total = 0, filtered = 0 } = await pg.query(`
112
+ SELECT COUNT(*)::int as total,
113
+ count(*) FILTER(WHERE ${fData.q || "true"})::int as filtered
114
+ FROM site.contents
115
+ WHERE content_type_id IN (
116
+ SELECT content_type_id
117
+ FROM site.content_types
118
+ WHERE ${localeQuery || '1=1'}
119
+ and $1=$1 and $2=$2 and ${contentQuery || 'true'}
120
+ )
121
+ and ${statusQuery || 'true'}
122
+ and content_id<>'pages'
123
+ `, [contentId, id]).then(el => el.rows?.[0] || {});
124
+
125
+ const { rows = [] } = await pg.query(q, [contentId, id || '']);
126
+
127
+ const locales = await pg.query(`select array_agg(distinct REVERSE(split_part(REVERSE(field_key), ':', 1))) from site.localization
128
+ where object_id in (select content_id from site.contents
129
+ where content_type_id IN (
130
+ SELECT content_type_id FROM site.content_types where $1=$1 and $2=$2 and ${contentQuery || 'true'}
131
+ )
132
+ and ${statusQuery || 'true'}
133
+ )`, [contentId, id]
134
+ ).then(el => el.rows?.[0]?.array_agg || []);
135
+
136
+ // Flatten extra fields into main object
137
+ const rows1 = rows.map(row => ({
138
+ ...row,
139
+ ...Object.fromEntries(
140
+ row.extraKeys?.filter(k => extraKeys?.length ? extraKeys.includes(k.field_key) : true)?.map(k =>
141
+ [k.field_key, inputTypes[k.field_type] === 'json' || k.field_key === 'meta'
142
+ ? k.field_value_object
143
+ : k.field_value]
144
+ ) || []
145
+ ),
146
+ extraKeys: undefined,
147
+ }));
148
+
149
+ // Apply localization and tags etc.
150
+ await additionalData(pg, rows1, locale, contentId === 'pages' || !id ? [existingFields, extraKeys].filter(Boolean).join(',') : null);
151
+
152
+ // Apply metadata formatting
153
+ await metaFormat({
154
+ rows: rows1,
155
+ cls: {
156
+ created_by: 'core.user_mentioned',
157
+ // content_type_id: 'cms.type_id',
158
+ // type: 'cms.type_id',
159
+ },
160
+ sufix: false,
161
+ }, pg);
162
+
163
+ const { list: filters = [] } = await getFilter({
164
+ pg, table: 'single.default.table', user, filter: `content_type_id in (select content_type_id from site.content_types where type='single' or content_type_id='pages')`
165
+ }) || {};
166
+
167
+ const finalColumns = !id
168
+ ? columns?.map?.(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
169
+ : columns?.filter?.(el => !el.hidden);
170
+
171
+ rows1.filter(row => row.single_sections?.length).forEach(row => {
172
+ row.single_sections.filter(section => section.documents?.length).forEach(section => {
173
+ section.documents.filter(doc => doc.file).forEach(doc => Object.assign(doc, { file_name: path.basename(doc.file) }));
174
+ });
175
+ });
176
+
177
+ return {
178
+ locales,
179
+ type: 'single',
180
+ total,
181
+ filtered,
182
+ count: rows1.length,
183
+ pk: 'id',
184
+ rows: rows1,
185
+ columns: finalColumns,
186
+ filters,
187
+ };
188
188
  }
@@ -1,6 +1,6 @@
1
- import path from 'node:path';
2
- import { existsSync, readFileSync } from 'node:fs';
3
-
4
- const inputTypes = ['input-types.json', path.join('node_modules/@opengis/cms', 'input-types.json')].filter(filepath => existsSync(filepath)).reduce((acc, curr) => ({ ...acc, ...JSON.parse(readFileSync(curr) || '{}') }), {});
5
-
1
+ import path from 'node:path';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+
4
+ const inputTypes = ['input-types.json', path.join('node_modules/@opengis/cms', 'input-types.json')].filter(filepath => existsSync(filepath)).reduce((acc, curr) => ({ ...acc, ...JSON.parse(readFileSync(curr) || '{}') }), {});
5
+
6
6
  export default inputTypes