@opengis/cms 0.0.56 → 0.0.58

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 (149) hide show
  1. package/README.md +131 -131
  2. package/dist/AddNewItemInTree-05PSSEFi.js +76 -0
  3. package/dist/ArticlesPage-CFjE_cw_.js +298 -0
  4. package/dist/CollectionsBreadcrumb-BCxeRikP.js +4 -0
  5. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-umRzB5mY.js +53 -0
  6. package/dist/CollectionsPage-DHfPNql6.js +124 -0
  7. package/dist/{CreateForm-BMOBeP4G.js → CreateForm-5FvT45vH.js} +1 -1
  8. package/dist/Dashboard-C1eGscNd.js +358 -0
  9. package/dist/EditCollectionPage-DIr1tdtn.js +187 -0
  10. package/dist/{EmptyData-DaZt_nAm.js → EmptyData-DxPrSXhV.js} +1 -1
  11. package/dist/{MenuAddPage-Bf48Z-ah.js → MenuAddPage-D-p3gFgm.js} +40 -35
  12. package/dist/MenuBody-rN5j4YBu.js +125 -0
  13. package/dist/MenuItemPage-BoJw885D.js +1027 -0
  14. package/dist/MenuList-DFEBS0NB.js +172 -0
  15. package/dist/MenuPage-BCZB_S8j.js +107 -0
  16. package/dist/MenuWrapper-AZ_8s-zd.js +12 -0
  17. package/dist/MonacoEditor-Db-3Jc3E.js +4 -0
  18. package/dist/{UniversalTable.vue_vue_type_script_setup_true_lang-CJGTsd1V.js → UniversalTable-CzqPG-tY.js} +12 -12
  19. package/dist/{UniversalTablePagination.vue_vue_type_script_setup_true_lang-GYZd_gkA.js → UniversalTablePagination-4gL47A7I.js} +1 -1
  20. package/dist/VsFormTags-CMjiu9sY.js +114 -0
  21. package/dist/VsPreview-DwETkOpb.js +63 -0
  22. package/dist/contentForm-CtMhQTG0.js +489 -0
  23. package/dist/getField-CpwVE28P.js +179 -0
  24. package/dist/index.d.ts +8 -0
  25. package/dist/index.html +29 -29
  26. package/dist/index.js +72 -71
  27. package/dist/style.css +1 -1
  28. package/dist/vs-builder-edit-D-q1o8tF.js +604 -0
  29. package/dist/vs-builder-monaco-B3Jj0V31.js +33 -0
  30. package/dist/vs-builder-preview-BH4VAM3a.js +44 -0
  31. package/dist/vs-form-custom-datatable-BDZo48w3.js +317 -0
  32. package/dist/vs-form-integer-BZ855R3g.js +61 -0
  33. package/dist/vs-form-media-select-NY27EaG1.js +837 -0
  34. package/dist/vs-form-reference-list-Dtv8fJJU.js +1536 -0
  35. package/dist/vs-form-reletion-link-BhzNQszm.js +34 -0
  36. package/dist/vs-form-tiptap-DDFQjRjY.js +4 -0
  37. package/dist/vs-form-tiptap.vue_vue_type_script_setup_true_lang-DGgsqXwg.js +11 -0
  38. package/dist/vs-richtext-md-C098v_6Q.js +4 -0
  39. package/dist/vs-richtext-md.vue_vue_type_script_setup_true_lang-Ct8uTV-J.js +14 -0
  40. package/input-types.json +9 -9
  41. package/locales/en.json +815 -814
  42. package/locales/uk.json +813 -812
  43. package/module/cms/cls/content.status.json +17 -17
  44. package/module/cms/cls/user_type.json +9 -9
  45. package/module/cms/form/admin.users.form.json +77 -77
  46. package/module/cms/select/cms.page_type.sql +1 -1
  47. package/module/cms/select/news_tag_id.sql +11 -11
  48. package/module/cms/table/admin.users.table.json +53 -53
  49. package/module/cms/table/collection.default.table.json +96 -96
  50. package/module/cms/table/single.default.table.json +116 -116
  51. package/package.json +68 -68
  52. package/plugin.js +43 -43
  53. package/server/app.js +35 -35
  54. package/server/config.js +4 -4
  55. package/server/functions/getContent.js +45 -45
  56. package/server/functions/getDraftKey.js +22 -22
  57. package/server/functions/getSearchData.js +31 -31
  58. package/server/functions/getTags.js +30 -30
  59. package/server/functions/getUser.js +27 -27
  60. package/server/functions/utils/mock.reply.js +55 -55
  61. package/server/index.js +22 -22
  62. package/server/migrations/fixes.sql +129 -129
  63. package/server/migrations/site.sql +595 -595
  64. package/server/plugins/adminHook.js +78 -78
  65. package/server/plugins/hook.js +59 -59
  66. package/server/plugins/vite.js +75 -75
  67. package/server/routes/category/controllers/cms.category.delete.js +21 -21
  68. package/server/routes/category/controllers/cms.category.get.js +17 -17
  69. package/server/routes/category/controllers/cms.category.list.js +16 -16
  70. package/server/routes/category/controllers/cms.category.post.js +21 -21
  71. package/server/routes/category/controllers/cms.category.put.js +23 -23
  72. package/server/routes/category/index.mjs +22 -22
  73. package/server/routes/cms/controllers/cmsStat.js +55 -55
  74. package/server/routes/cms/controllers/cmsSuggest.js +57 -57
  75. package/server/routes/cms/controllers/deleteContent.js +113 -113
  76. package/server/routes/cms/controllers/deleteMedia.js +76 -76
  77. package/server/routes/cms/controllers/downloadMedia.js +84 -84
  78. package/server/routes/cms/controllers/getContent.js +113 -113
  79. package/server/routes/cms/controllers/getContentBySlug.js +93 -93
  80. package/server/routes/cms/controllers/insertContent.js +217 -217
  81. package/server/routes/cms/controllers/listMedia.js +155 -155
  82. package/server/routes/cms/controllers/metadataMedia.js +39 -39
  83. package/server/routes/cms/controllers/properties.get.js +18 -18
  84. package/server/routes/cms/controllers/properties.post.js +99 -99
  85. package/server/routes/cms/controllers/searchContent.js +214 -214
  86. package/server/routes/cms/controllers/translate.js +89 -89
  87. package/server/routes/cms/controllers/updateContent.js +266 -264
  88. package/server/routes/cms/controllers/uploadMedia.js +79 -79
  89. package/server/routes/cms/functions/getSettings.js +48 -48
  90. package/server/routes/cms/index.mjs +112 -112
  91. package/server/routes/cms/utils/additionalData.js +35 -35
  92. package/server/routes/cms/utils/getCollection.js +89 -89
  93. package/server/routes/cms/utils/getSingle.js +188 -183
  94. package/server/routes/cms/utils/inputTypes.js +5 -5
  95. package/server/routes/cms/utils/insertContentLocalization.js +104 -104
  96. package/server/routes/cms/utils/requestTranslation.js +85 -85
  97. package/server/routes/cms/utils/updateLocalization.js +47 -47
  98. package/server/routes/cmsSpace/controllers/deleteSpace.js +25 -25
  99. package/server/routes/cmsSpace/controllers/getSpaces.js +27 -27
  100. package/server/routes/cmsSpace/controllers/insertSpace.js +21 -21
  101. package/server/routes/cmsSpace/controllers/updateSpace.js +23 -23
  102. package/server/routes/cmsSpace/index.mjs +20 -20
  103. package/server/routes/contentType/controllers/addContentType.js +160 -160
  104. package/server/routes/contentType/controllers/contentTypeList.js +54 -54
  105. package/server/routes/contentType/controllers/delContentType.js +75 -75
  106. package/server/routes/contentType/controllers/editContentType.js +88 -81
  107. package/server/routes/contentType/controllers/getContentType.js +65 -57
  108. package/server/routes/contentType/index.mjs +35 -35
  109. package/server/routes/contentType/utils/updateContents.js +44 -44
  110. package/server/routes/contentType/utils/updateCustomContentTable.js +53 -53
  111. package/server/routes/feedback/controllers/email.list.js +24 -24
  112. package/server/routes/feedback/controllers/feedback.js +48 -48
  113. package/server/routes/feedback/controllers/feedback.list.js +37 -37
  114. package/server/routes/feedback/controllers/news.subscriptions.js +44 -44
  115. package/server/routes/feedback/index.mjs +71 -71
  116. package/server/routes/logs/controllers/export.user.logs.js +77 -77
  117. package/server/routes/logs/controllers/user.logs.js +44 -44
  118. package/server/routes/logs/index.mjs +9 -9
  119. package/server/routes/menu/controllers/addMenu.js +37 -37
  120. package/server/routes/menu/controllers/delMenu.js +31 -31
  121. package/server/routes/menu/controllers/editMenu.js +41 -41
  122. package/server/routes/menu/controllers/getMenu.js +24 -24
  123. package/server/routes/menu/functions/getMenu.js +50 -50
  124. package/server/routes/menu/index.mjs +13 -13
  125. package/server/routes/migration/controllers/collectionToCustom.js +137 -137
  126. package/server/routes/migration/index.mjs +8 -8
  127. package/server/routes/root.mjs +8 -8
  128. package/server/routes/tags/controllers/add.tags.js +24 -24
  129. package/server/routes/tags/controllers/del.tags.js +19 -19
  130. package/server/routes/tags/controllers/edit.tags.js +25 -25
  131. package/server/routes/tags/controllers/get.tags.js +15 -15
  132. package/server/routes/tags/index.mjs +14 -14
  133. package/server/templates/cls/cms.category_type.json +9 -9
  134. package/server/templates/cls/cms.content_review_status.json +9 -9
  135. package/server/templates/cls/cms.content_status.json +9 -9
  136. package/server/templates/cls/cms.content_type.json +9 -9
  137. package/server/templates/cls/cms.lang.json +9 -9
  138. package/server/templates/page/login.html +126 -126
  139. package/utils.d.ts +52 -52
  140. package/utils.js +8 -8
  141. package/dist/ArticlesPage-BcR1hbds.js +0 -286
  142. package/dist/BuilderPage-CK_osM89.js +0 -386
  143. package/dist/CollectionsBreadcrumb.vue_vue_type_script_setup_true_lang-CnOe9ORD.js +0 -45
  144. package/dist/CollectionsPage-JfmrHNR_.js +0 -110
  145. package/dist/EditCollectionPage-Cw3GQYRe.js +0 -809
  146. package/dist/MenuItemPage-CXn5HC8j.js +0 -1366
  147. package/dist/MenuPage-tJZtK46W.js +0 -106
  148. package/dist/contentForm-B6gHgGkz.js +0 -586
  149. package/dist/getField-Y5WXnRR0.js +0 -2948
@@ -1,90 +1,90 @@
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 = [], called = false,
7
- }, reply, pg = pgClients.client) {
8
- const meta = table ? await getMeta({ pg, table: 'data.' + table }) : {};
9
-
10
- if (!table || !meta?.pk) {
11
- return { message: 'content table not found', status: 404 };
12
- }
13
-
14
- const isPinExists = await pg.query(`
15
- SELECT 1
16
- FROM pg_catalog.pg_attribute a
17
- JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
18
- JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
19
- WHERE n.nspname = 'data'
20
- AND c.relname = $1
21
- AND a.attname = 'is_pin'
22
- AND a.attnum > 0
23
- AND NOT a.attisdropped;
24
- `, [table]).then(el => el.rowCount);
25
-
26
- const defaultOrder = `case when status='archived' then true else false end ${isPinExists ? ', is_pin desc' : ''}, published_at desc nulls last`;
27
-
28
- 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;
29
- 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;
30
- const cQuery = [id ? `'${id}' in (id,slug)` : contextQuery, localeQuery, ((columns || []).concat(defaultColumns || [])).find(col => col.name === 'status') ? statusQuery : null, tagQuery].filter(Boolean).join(' and ');
31
-
32
- const columnList = await getMeta({
33
- pg,
34
- table: 'data.' + `"${table}"`
35
- }).then(el => el.columns?.map?.(col => col.name) || []);
36
- const existingFields = fields && !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).join(',') : null;
37
-
38
- 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 })));
39
-
40
- const res = await getData({
41
- pg,
42
- table: 'data.' + `"${table}"`,
43
- columns: existingFields,
44
- filterList,
45
- query: {
46
- filter,
47
- state,
48
- limit: Math.min(limit, maxLimit),
49
- page,
50
- search,
51
- order: order ? order : defaultOrder,
52
- desc,
53
- sql
54
- },
55
- user,
56
- contextQuery: cQuery,
57
- actions: ['view']
58
- }, reply, true);
59
-
60
- 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 || []);
61
-
62
- // Apply localization and tags etc.
63
- if (res?.rows?.length) {
64
- await additionalData(pg, res.rows, locale, id ? null : existingFields);
65
- }
66
-
67
- const columns1 = !id
68
- ? columns?.map(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
69
- : columns?.filter?.(el => !el.hidden);
70
-
71
- const finalColumns = (defaultColumns || []).concat(columns1.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
72
-
73
- finalColumns.filter(el => el.default).forEach(col => {
74
- const { name, localization } = columns1.find(item => item.name === col.name) || {};
75
- if (name) {
76
- Object.assign(col, { localization });
77
- }
78
- });
79
-
80
- if (res?.columns) {
81
- Object.assign(res, { type: 'collection', locales, preview_path: preview, columns: finalColumns });
82
- }
83
-
84
- // add table name for cache via getContent function
85
- if (called) {
86
- Object.assign(res, { table: 'data.' + `"${table}"` });
87
- }
88
-
89
- 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 = [], called = false,
7
+ }, reply, pg = pgClients.client) {
8
+ const meta = table ? await getMeta({ pg, table: 'data.' + table }) : {};
9
+
10
+ if (!table || !meta?.pk) {
11
+ return { message: 'content table not found', status: 404 };
12
+ }
13
+
14
+ const isPinExists = await pg.query(`
15
+ SELECT 1
16
+ FROM pg_catalog.pg_attribute a
17
+ JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
18
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
19
+ WHERE n.nspname = 'data'
20
+ AND c.relname = $1
21
+ AND a.attname = 'is_pin'
22
+ AND a.attnum > 0
23
+ AND NOT a.attisdropped;
24
+ `, [table]).then(el => el.rowCount);
25
+
26
+ const defaultOrder = `case when status='archived' then true else false end ${isPinExists ? ', is_pin desc' : ''}, published_at desc nulls last`;
27
+
28
+ 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;
29
+ 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;
30
+ const cQuery = [id ? `'${id}' in (id,slug)` : contextQuery, localeQuery, ((columns || []).concat(defaultColumns || [])).find(col => col.name === 'status') ? statusQuery : null, tagQuery].filter(Boolean).join(' and ');
31
+
32
+ const columnList = await getMeta({
33
+ pg,
34
+ table: 'data.' + `"${table}"`
35
+ }).then(el => el.columns?.map?.(col => col.name) || []);
36
+ const existingFields = fields && !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).join(',') : null;
37
+
38
+ 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 })));
39
+
40
+ const res = await getData({
41
+ pg,
42
+ table: 'data.' + `"${table}"`,
43
+ columns: existingFields,
44
+ filterList,
45
+ query: {
46
+ filter,
47
+ state,
48
+ limit: Math.min(limit, maxLimit),
49
+ page,
50
+ search,
51
+ order: order ? order : defaultOrder,
52
+ desc,
53
+ sql
54
+ },
55
+ user,
56
+ contextQuery: cQuery,
57
+ actions: ['view']
58
+ }, reply, true);
59
+
60
+ 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 || []);
61
+
62
+ // Apply localization and tags etc.
63
+ if (res?.rows?.length) {
64
+ await additionalData(pg, res.rows, locale, id ? null : existingFields);
65
+ }
66
+
67
+ const columns1 = !id
68
+ ? columns?.map(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
69
+ : columns?.filter?.(el => !el.hidden);
70
+
71
+ const finalColumns = (defaultColumns || []).concat(columns1.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
72
+
73
+ finalColumns.filter(el => el.default).forEach(col => {
74
+ const { name, localization } = columns1.find(item => item.name === col.name) || {};
75
+ if (name) {
76
+ Object.assign(col, { localization });
77
+ }
78
+ });
79
+
80
+ if (res?.columns) {
81
+ Object.assign(res, { type: 'collection', locales, preview_path: preview, columns: finalColumns });
82
+ }
83
+
84
+ // add table name for cache via getContent function
85
+ if (called) {
86
+ Object.assign(res, { table: 'data.' + `"${table}"` });
87
+ }
88
+
89
+ return res;
90
90
  }
@@ -1,184 +1,189 @@
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
- const customColumns = await pg.query(
15
- `select columns FROM site.content_types where content_type_id=$1`, [id || contentId]
16
- ).then(el => el.rows?.[0]?.columns || []);
17
-
18
- const contentQuery = contentId === 'pages' || !id
19
- ? `(type = 'single' or content_type_id = 'pages')`
20
- : 'content_type_id=(select content_type_id from site.contents where content_id=$2)';
21
-
22
- const columns = defaultColumns.concat(customColumns.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
23
-
24
- columns.filter(el => el.default).forEach(col => {
25
- const { name, localization } = customColumns.find(item => item.name === col.name) || {};
26
- if (name) {
27
- Object.assign(col, { localization });
28
- }
29
- });
30
-
31
- const limit = Math.min(maxLimit, +(queryLimit || 20));
32
- const offset = page && page > 0 && !id ? (page - 1) * limit : 0;
33
-
34
- const fData = filter
35
- ? await getFilterSQL({
36
- pg,
37
- table: 'site.contents',
38
- filter,
39
- search,
40
- filterList: defaultFilters,
41
- query: `
42
- content_type_id IN (
43
- SELECT content_type_id
44
- FROM site.content_types
45
- WHERE ${contentQuery || 'true'}
46
- )
47
- `,
48
- })
49
- : {
50
- optimizedSQL: 'SELECT * FROM site.contents WHERE true',
51
- };
52
-
53
- 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;
54
-
55
- const columnList = await getMeta({
56
- pg,
57
- table: 'site.contents',
58
- }).then(el => el.columns?.map?.(col => col.name) || []);
59
- const existingFields = fields && contentId === 'pages' || !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).concat(['content_id AS id']).join(',') : '';
60
- const extraKeys = fields ? fields.split(',').filter(key => !defaultFields.includes(key)) : [];
61
- const cols = existingFields.length ? existingFields.concat(extraKeys?.length ? `,"extraKeys"` : []) : `content_id AS id,
62
- content_id,
63
- created_by,
64
- title,
65
- slug,
66
- meta,
67
- published_by,
68
- published_at,
69
- status,
70
- content_type_id,
71
- /* content_type_id AS type, */
72
- main_image
73
- ${contentId === 'pages' || !id ? `,"extraKeys"` : ''}`;
74
-
75
- const q = `
76
- SELECT ${cols} FROM (${fData.optimizedSQL}) a
77
- ${extraKeys || id ? `LEFT JOIN LATERAL (
78
- SELECT json_agg(row_to_json(q)) AS "extraKeys"
79
- FROM (
80
- SELECT field_key, field_type, field_value, field_value_object
81
- FROM site.content_data
82
- WHERE content_id = a.content_id
83
- ) q
84
- ) b ON true` : ''}
85
-
86
- LEFT JOIN LATERAL (
87
- SELECT type FROM site.content_types WHERE content_type_id=a.content_type_id LIMIT 1
88
- )c ON true
89
-
90
- WHERE $1=$1 and $2=$2
91
- and ${statusQuery || 'true'}
92
- and ${localeQuery || '1=1'}
93
- and ${contentQuery || 'true'}
94
- and ${id ? `$2 in (slug, content_id)` : 'true'}
95
- and content_id<>'pages'
96
- order by case when status='archived' then true else false end, published_at desc nulls last
97
- LIMIT ${limit} OFFSET ${id ? 0 : offset}
98
- `;
99
-
100
- if (sql && user?.user_type?.includes?.('admin')) {
101
- return q; // Expose raw SQL for debugging
102
- }
103
-
104
- const { total = 0, filtered = 0 } = await pg.query(`
105
- SELECT COUNT(*)::int as total,
106
- count(*) FILTER(WHERE ${fData.q || "true"})::int as filtered
107
- FROM site.contents
108
- WHERE content_type_id IN (
109
- SELECT content_type_id
110
- FROM site.content_types
111
- WHERE ${localeQuery || '1=1'}
112
- and $1=$1 and $2=$2 and ${contentQuery || 'true'}
113
- )
114
- and ${statusQuery || 'true'}
115
- and content_id<>'pages'
116
- `, [contentId, id]).then(el => el.rows?.[0] || {});
117
-
118
- const { rows = [] } = await pg.query(q, [contentId, id || '']);
119
-
120
- const locales = await pg.query(`select array_agg(distinct REVERSE(split_part(REVERSE(field_key), ':', 1))) from site.localization
121
- where object_id in (select content_id from site.contents
122
- where content_type_id IN (
123
- SELECT content_type_id FROM site.content_types where $1=$1 and $2=$2 and ${contentQuery || 'true'}
124
- )
125
- and ${statusQuery || 'true'}
126
- )`, [contentId, id]
127
- ).then(el => el.rows?.[0]?.array_agg || []);
128
-
129
- // Flatten extra fields into main object
130
- const rows1 = rows.map(row => ({
131
- ...row,
132
- ...Object.fromEntries(
133
- row.extraKeys?.filter(k => extraKeys?.length ? extraKeys.includes(k.field_key) : true)?.map(k =>
134
- [k.field_key, inputTypes[k.field_type] === 'json' || k.field_key === 'meta'
135
- ? k.field_value_object
136
- : k.field_value]
137
- ) || []
138
- ),
139
- extraKeys: undefined,
140
- }));
141
-
142
- // Apply localization and tags etc.
143
- await additionalData(pg, rows1, locale, contentId === 'pages' || !id ? [existingFields, extraKeys].filter(Boolean).join(',') : null);
144
-
145
- // Apply metadata formatting
146
- await metaFormat({
147
- rows: rows1,
148
- cls: {
149
- created_by: 'core.user_mentioned',
150
- // content_type_id: 'cms.type_id',
151
- // type: 'cms.type_id',
152
- },
153
- sufix: false,
154
- }, pg);
155
-
156
- const { list: filters = [] } = await getFilter({
157
- 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')`
158
- }) || {};
159
-
160
- const finalColumns = !id
161
- ? columns?.map?.(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
162
- : columns?.filter?.(el => !el.hidden);
163
-
164
- rows1.filter(row => row.single_sections?.length).forEach(row => {
165
- row.single_sections.filter(section => section.documents?.length).forEach(section => {
166
- section.documents.filter(doc => doc.file).forEach(doc => Object.assign(doc, { file_name: path.basename(doc.file) }));
167
- });
168
- });
169
-
170
- // temporary solution, before front makes changes to call by id, not type
171
- rows1.forEach(row => Object.assign(row, { content_type_id: row.content_id }));
172
-
173
- return {
174
- locales,
175
- type: 'single',
176
- total,
177
- filtered,
178
- count: rows1.length,
179
- pk: 'id',
180
- rows: rows1,
181
- columns: finalColumns,
182
- filters,
183
- };
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, contentTypeId, 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
+ // new ? 1 : 2
15
+ const customColumns = contentTypeId === (id || contentId)
16
+ ? await pg.query(
17
+ `SELECT columns FROM site.content_types WHERE content_type_id=$1`, [id || contentId]
18
+ ).then(el => el.rows?.[0]?.columns || [])
19
+ : await pg.query(`
20
+ SELECT columns FROM site.content_types WHERE content_type_id in (select content_type_id from site.contents where content_id=$1) limit 1
21
+ `, [id || contentId]).then(el => el.rows?.[0]?.columns || []);
22
+
23
+ const contentQuery = contentId === 'pages' || !id
24
+ ? `(type = 'single' or content_type_id = 'pages')`
25
+ : 'content_type_id=(select content_type_id from site.contents where content_id=$2)';
26
+
27
+ const columns = defaultColumns.concat(customColumns.filter(col => defaultColumns.findIndex(el => el.name === col.name) === -1));
28
+
29
+ columns.filter(el => el.default).forEach(col => {
30
+ const { name, localization } = customColumns.find(item => item.name === col.name) || {};
31
+ if (name) {
32
+ Object.assign(col, { localization });
33
+ }
34
+ });
35
+
36
+ const limit = Math.min(maxLimit, +(queryLimit || 20));
37
+ const offset = page && page > 0 && !id ? (page - 1) * limit : 0;
38
+
39
+ const fData = filter
40
+ ? await getFilterSQL({
41
+ pg,
42
+ table: 'site.contents',
43
+ filter,
44
+ search,
45
+ filterList: defaultFilters,
46
+ query: `
47
+ content_type_id IN (
48
+ SELECT content_type_id
49
+ FROM site.content_types
50
+ WHERE ${contentQuery || 'true'}
51
+ )
52
+ `,
53
+ })
54
+ : {
55
+ optimizedSQL: 'SELECT * FROM site.contents WHERE true',
56
+ };
57
+
58
+ 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;
59
+
60
+ const columnList = await getMeta({
61
+ pg,
62
+ table: 'site.contents',
63
+ }).then(el => el.columns?.map?.(col => col.name) || []);
64
+ const existingFields = fields && contentId === 'pages' || !id ? defaultFields.concat((fields || '').split(',')).filter(colName => columnList.includes(colName)).concat(['content_id AS id']).join(',') : '';
65
+ const extraKeys = fields ? fields.split(',').filter(key => !defaultFields.includes(key)) : [];
66
+ const cols = existingFields.length ? existingFields.concat(extraKeys?.length ? `,"extraKeys"` : []) : `content_id AS id,
67
+ content_id,
68
+ created_by,
69
+ title,
70
+ slug,
71
+ meta,
72
+ published_by,
73
+ published_at,
74
+ status,
75
+ content_type_id,
76
+ /* content_type_id AS type, */
77
+ main_image
78
+ ${contentId === 'pages' || !id ? `,"extraKeys"` : ''}`;
79
+
80
+ const q = `
81
+ SELECT ${cols} FROM (${fData.optimizedSQL}) a
82
+ ${extraKeys || id ? `LEFT JOIN LATERAL (
83
+ SELECT json_agg(row_to_json(q)) AS "extraKeys"
84
+ FROM (
85
+ SELECT field_key, field_type, field_value, field_value_object
86
+ FROM site.content_data
87
+ WHERE content_id = a.content_id
88
+ ) q
89
+ ) b ON true` : ''}
90
+
91
+ LEFT JOIN LATERAL (
92
+ SELECT type FROM site.content_types WHERE content_type_id=a.content_type_id LIMIT 1
93
+ )c ON true
94
+
95
+ WHERE $1=$1 and $2=$2
96
+ and ${statusQuery || 'true'}
97
+ and ${localeQuery || '1=1'}
98
+ and ${contentQuery || 'true'}
99
+ and ${id ? `$2 in (slug, content_id)` : 'true'}
100
+ and content_id<>'pages'
101
+ order by case when status='archived' then true else false end, published_at desc nulls last
102
+ LIMIT ${limit} OFFSET ${id ? 0 : offset}
103
+ `;
104
+
105
+ if (sql && user?.user_type?.includes?.('admin')) {
106
+ return q; // Expose raw SQL for debugging
107
+ }
108
+
109
+ const { total = 0, filtered = 0 } = await pg.query(`
110
+ SELECT COUNT(*)::int as total,
111
+ count(*) FILTER(WHERE ${fData.q || "true"})::int as filtered
112
+ FROM site.contents
113
+ WHERE content_type_id IN (
114
+ SELECT content_type_id
115
+ FROM site.content_types
116
+ WHERE ${localeQuery || '1=1'}
117
+ and $1=$1 and $2=$2 and ${contentQuery || 'true'}
118
+ )
119
+ and ${statusQuery || 'true'}
120
+ and content_id<>'pages'
121
+ `, [contentId, id]).then(el => el.rows?.[0] || {});
122
+
123
+ const { rows = [] } = await pg.query(q, [contentId, id || '']);
124
+
125
+ const locales = await pg.query(`select array_agg(distinct REVERSE(split_part(REVERSE(field_key), ':', 1))) from site.localization
126
+ where object_id in (select content_id from site.contents
127
+ where content_type_id IN (
128
+ SELECT content_type_id FROM site.content_types where $1=$1 and $2=$2 and ${contentQuery || 'true'}
129
+ )
130
+ and ${statusQuery || 'true'}
131
+ )`, [contentId, id]
132
+ ).then(el => el.rows?.[0]?.array_agg || []);
133
+
134
+ // Flatten extra fields into main object
135
+ const rows1 = rows.map(row => ({
136
+ ...row,
137
+ ...Object.fromEntries(
138
+ row.extraKeys?.filter(k => extraKeys?.length ? extraKeys.includes(k.field_key) : true)?.map(k =>
139
+ [k.field_key, inputTypes[k.field_type] === 'json' || k.field_key === 'meta'
140
+ ? k.field_value_object
141
+ : k.field_value]
142
+ ) || []
143
+ ),
144
+ extraKeys: undefined,
145
+ }));
146
+
147
+ // Apply localization and tags etc.
148
+ await additionalData(pg, rows1, locale, contentId === 'pages' || !id ? [existingFields, extraKeys].filter(Boolean).join(',') : null);
149
+
150
+ // Apply metadata formatting
151
+ await metaFormat({
152
+ rows: rows1,
153
+ cls: {
154
+ created_by: 'core.user_mentioned',
155
+ // content_type_id: 'cms.type_id',
156
+ // type: 'cms.type_id',
157
+ },
158
+ sufix: false,
159
+ }, pg);
160
+
161
+ const { list: filters = [] } = await getFilter({
162
+ 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')`
163
+ }) || {};
164
+
165
+ const finalColumns = !id
166
+ ? columns?.map?.(col => Object.assign(col, { type: col.name === 'published_at' ? 'date' : col.type }))?.filter?.(el => !el.hidden)
167
+ : columns?.filter?.(el => !el.hidden);
168
+
169
+ rows1.filter(row => row.single_sections?.length).forEach(row => {
170
+ row.single_sections.filter(section => section.documents?.length).forEach(section => {
171
+ section.documents.filter(doc => doc.file).forEach(doc => Object.assign(doc, { file_name: path.basename(doc.file) }));
172
+ });
173
+ });
174
+
175
+ // temporary solution, before front makes changes to call by id, not type
176
+ rows1.forEach(row => Object.assign(row, { content_type_id: !row.content_type_id || row.content_type_id === 'pages' ? row.content_id : row.content_type_id }));
177
+
178
+ return {
179
+ locales,
180
+ type: 'single',
181
+ total,
182
+ filtered,
183
+ count: rows1.length,
184
+ pk: 'id',
185
+ rows: rows1,
186
+ columns: finalColumns,
187
+ filters,
188
+ };
184
189
  }
@@ -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