strapi-plugin-navigation 2.0.11 → 2.1.0-beta.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.
Files changed (268) hide show
  1. package/README.md +254 -41
  2. package/admin/src/components/Alert/styles.d.ts +2 -0
  3. package/admin/src/components/Alert/styles.js +14 -0
  4. package/admin/src/components/CollapseButton/index.d.ts +7 -0
  5. package/admin/src/components/CollapseButton/index.js +21 -22
  6. package/admin/src/components/ConfirmationDialog/index.d.ts +28 -0
  7. package/admin/src/components/ConfirmationDialog/index.js +32 -54
  8. package/admin/src/components/EmptyView/index.d.ts +3 -0
  9. package/admin/src/components/EmptyView/index.js +9 -7
  10. package/admin/src/components/Item/ItemCardBadge/index.d.ts +3 -0
  11. package/admin/src/components/Item/ItemCardBadge/index.js +11 -7
  12. package/admin/src/components/Item/ItemCardHeader/Wrapper.d.ts +3 -0
  13. package/admin/src/components/Item/ItemCardHeader/Wrapper.js +10 -6
  14. package/admin/src/components/Item/ItemCardHeader/index.d.ts +12 -0
  15. package/admin/src/components/Item/ItemCardHeader/index.js +34 -53
  16. package/admin/src/components/Item/ItemCardRemovedOverlay/index.d.ts +2 -0
  17. package/admin/src/components/Item/ItemCardRemovedOverlay/index.js +10 -4
  18. package/admin/src/components/Item/Wrapper.d.ts +3 -0
  19. package/admin/src/components/Item/Wrapper.js +10 -6
  20. package/admin/src/components/Item/index.d.ts +31 -0
  21. package/admin/src/components/Item/index.js +169 -242
  22. package/admin/src/components/NavigationItemList/Wrapper.d.ts +3 -0
  23. package/admin/src/components/NavigationItemList/Wrapper.js +9 -5
  24. package/admin/src/components/NavigationItemList/index.d.ts +35 -0
  25. package/admin/src/components/NavigationItemList/index.js +28 -66
  26. package/admin/src/components/RestartAlert/index.d.ts +3 -0
  27. package/admin/src/components/RestartAlert/index.js +10 -5
  28. package/admin/src/components/Search/index.d.ts +6 -0
  29. package/admin/src/components/Search/index.js +57 -46
  30. package/admin/src/components/icons/navigation.d.ts +6 -0
  31. package/admin/src/components/icons/navigation.js +11 -13
  32. package/admin/src/contexts/DataManagerContext.d.ts +3 -0
  33. package/admin/src/contexts/DataManagerContext.js +6 -5
  34. package/admin/src/hooks/useAllContentTypes.d.ts +6 -0
  35. package/admin/src/hooks/useAllContentTypes.js +10 -11
  36. package/admin/src/hooks/useDataManager.d.ts +3 -0
  37. package/admin/src/hooks/useDataManager.js +10 -6
  38. package/admin/src/hooks/useNavigationConfig.d.ts +10 -0
  39. package/admin/src/hooks/useNavigationConfig.js +52 -56
  40. package/admin/src/index.d.ts +14 -0
  41. package/admin/src/index.js +92 -76
  42. package/admin/src/pages/App/index.d.ts +3 -0
  43. package/admin/src/pages/App/index.js +43 -31
  44. package/admin/src/pages/DataManagerProvider/actions.d.ts +19 -0
  45. package/admin/src/pages/DataManagerProvider/actions.js +22 -26
  46. package/admin/src/pages/DataManagerProvider/index.d.ts +12 -0
  47. package/admin/src/pages/DataManagerProvider/index.js +280 -300
  48. package/admin/src/pages/DataManagerProvider/init.d.ts +3 -0
  49. package/admin/src/pages/DataManagerProvider/init.js +5 -3
  50. package/admin/src/pages/DataManagerProvider/reducer.d.ts +19 -0
  51. package/admin/src/pages/DataManagerProvider/reducer.js +119 -121
  52. package/admin/src/pages/SettingsPage/components/DisableI18nModal/index.d.ts +24 -0
  53. package/admin/src/pages/SettingsPage/components/DisableI18nModal/index.js +88 -0
  54. package/admin/src/pages/SettingsPage/index.d.ts +3 -0
  55. package/admin/src/pages/SettingsPage/index.js +242 -317
  56. package/admin/src/pages/SettingsPage/utils/functions.d.ts +5 -0
  57. package/admin/src/pages/SettingsPage/utils/functions.js +3 -8
  58. package/admin/src/pages/View/components/I18nCopyNavigationItems/index.d.ts +20 -0
  59. package/admin/src/pages/View/components/I18nCopyNavigationItems/index.js +60 -0
  60. package/admin/src/pages/View/components/NavigationContentHeader/index.d.ts +6 -0
  61. package/admin/src/pages/View/components/NavigationContentHeader/index.js +13 -17
  62. package/admin/src/pages/View/components/NavigationHeader/index.d.ts +12 -0
  63. package/admin/src/pages/View/components/NavigationHeader/index.js +74 -65
  64. package/admin/src/pages/View/components/NavigationHeader/styles.d.ts +2 -0
  65. package/admin/src/pages/View/components/NavigationHeader/styles.js +10 -5
  66. package/admin/src/pages/View/components/NavigationItemForm/index.d.ts +51 -0
  67. package/admin/src/pages/View/components/NavigationItemForm/index.js +322 -454
  68. package/admin/src/pages/View/components/NavigationItemForm/utils/form.d.ts +45 -0
  69. package/admin/src/pages/View/components/NavigationItemForm/utils/form.js +81 -52
  70. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.d.ts +23 -0
  71. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.js +24 -34
  72. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.d.ts +4 -0
  73. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +16 -16
  74. package/admin/src/pages/View/components/NavigationItemPopup/index.d.ts +27 -0
  75. package/admin/src/pages/View/components/NavigationItemPopup/index.js +59 -108
  76. package/admin/src/pages/View/index.d.ts +4 -0
  77. package/admin/src/pages/View/index.js +229 -304
  78. package/admin/src/pages/View/utils/enums.d.ts +9 -0
  79. package/admin/src/pages/View/utils/enums.js +11 -8
  80. package/admin/src/pages/View/utils/form.d.ts +2 -0
  81. package/admin/src/pages/View/utils/form.js +15 -12
  82. package/admin/src/pages/View/utils/index.d.ts +2 -0
  83. package/admin/src/pages/View/utils/index.js +11 -3
  84. package/admin/src/pages/View/utils/parsers.d.ts +44 -0
  85. package/admin/src/pages/View/utils/parsers.js +231 -277
  86. package/admin/src/permissions.d.ts +12 -0
  87. package/admin/src/permissions.js +11 -7
  88. package/admin/src/pluginId.d.ts +3 -0
  89. package/admin/src/pluginId.js +2 -2
  90. package/admin/src/translations/en.json +126 -102
  91. package/admin/src/translations/fr.json +44 -44
  92. package/admin/src/translations/index.d.ts +12 -0
  93. package/admin/src/translations/index.js +17 -11
  94. package/admin/src/utils/api.d.ts +8 -0
  95. package/admin/src/utils/api.js +54 -50
  96. package/admin/src/utils/getTrad.d.ts +3 -0
  97. package/admin/src/utils/getTrad.js +9 -5
  98. package/admin/src/utils/index.d.ts +5 -0
  99. package/admin/src/utils/index.js +21 -15
  100. package/package.json +19 -17
  101. package/permissions.d.ts +9 -0
  102. package/permissions.js +5 -4
  103. package/server/bootstrap/index.d.ts +4 -0
  104. package/server/bootstrap/index.js +41 -0
  105. package/server/config/index.d.ts +5 -0
  106. package/server/config/index.js +33 -9
  107. package/server/config/setupStrategy.d.ts +3 -0
  108. package/server/config/setupStrategy.js +38 -0
  109. package/server/content-types/audience/index.d.ts +27 -0
  110. package/server/content-types/audience/index.js +9 -8
  111. package/server/content-types/audience/schema.d.ts +25 -0
  112. package/server/content-types/audience/schema.js +26 -0
  113. package/server/content-types/index.d.ts +255 -0
  114. package/server/content-types/index.js +15 -12
  115. package/server/content-types/navigation/index.d.ts +64 -0
  116. package/server/content-types/navigation/index.js +11 -8
  117. package/server/content-types/navigation/lifecycle.d.ts +9 -0
  118. package/server/content-types/navigation/lifecycle.js +8 -12
  119. package/server/content-types/navigation/schema.d.ts +55 -0
  120. package/server/content-types/navigation/schema.js +56 -0
  121. package/server/content-types/navigation-item/index.d.ts +112 -0
  122. package/server/content-types/navigation-item/index.js +11 -8
  123. package/server/content-types/navigation-item/lifecycle.d.ts +16 -0
  124. package/server/content-types/navigation-item/lifecycle.js +28 -32
  125. package/server/content-types/navigation-item/schema.d.ts +97 -0
  126. package/server/content-types/navigation-item/schema.js +102 -0
  127. package/server/content-types/navigations-items-related/index.d.ts +56 -0
  128. package/server/content-types/navigations-items-related/index.js +11 -8
  129. package/server/content-types/navigations-items-related/lifecycle.d.ts +8 -0
  130. package/server/content-types/navigations-items-related/lifecycle.js +20 -17
  131. package/server/content-types/navigations-items-related/schema.d.ts +49 -0
  132. package/server/content-types/navigations-items-related/schema.js +50 -0
  133. package/server/controllers/admin.d.ts +4 -0
  134. package/server/controllers/admin.js +77 -0
  135. package/server/controllers/client.d.ts +4 -0
  136. package/server/controllers/client.js +34 -0
  137. package/server/controllers/index.d.ts +4 -0
  138. package/server/controllers/index.js +12 -6
  139. package/server/destroy.d.ts +3 -0
  140. package/server/destroy.js +5 -0
  141. package/server/graphql/config.d.ts +4 -0
  142. package/server/graphql/config.js +28 -0
  143. package/server/graphql/index.d.ts +2 -0
  144. package/server/graphql/index.js +18 -22
  145. package/server/graphql/queries/index.d.ts +3 -0
  146. package/server/graphql/queries/index.js +15 -15
  147. package/server/graphql/queries/render-navigation-child.d.ts +15 -0
  148. package/server/graphql/queries/render-navigation-child.js +24 -15
  149. package/server/graphql/queries/render-navigation.d.ts +23 -0
  150. package/server/graphql/queries/render-navigation.js +29 -15
  151. package/server/graphql/resolvers-config.d.ts +10 -0
  152. package/server/graphql/resolvers-config.js +5 -3
  153. package/server/graphql/setupStrategy.d.ts +3 -0
  154. package/server/graphql/setupStrategy.js +15 -0
  155. package/server/graphql/types/content-types-name-fields.d.ts +6 -0
  156. package/server/graphql/types/content-types-name-fields.js +13 -9
  157. package/server/graphql/types/content-types.d.ts +5 -0
  158. package/server/graphql/types/content-types.js +17 -15
  159. package/server/graphql/types/create-navigation-item.d.ts +5 -0
  160. package/server/graphql/types/create-navigation-item.js +18 -16
  161. package/server/graphql/types/create-navigation-related.d.ts +5 -0
  162. package/server/graphql/types/create-navigation-related.js +9 -7
  163. package/server/graphql/types/create-navigation.d.ts +5 -0
  164. package/server/graphql/types/create-navigation.js +8 -6
  165. package/server/graphql/types/index.d.ts +3 -0
  166. package/server/graphql/types/index.js +15 -13
  167. package/server/graphql/types/navigation-config.d.ts +5 -0
  168. package/server/graphql/types/navigation-config.js +10 -8
  169. package/server/graphql/types/navigation-details.d.ts +5 -0
  170. package/server/graphql/types/navigation-details.js +11 -9
  171. package/server/graphql/types/navigation-item-related-data.d.ts +5 -0
  172. package/server/graphql/types/navigation-item-related-data.js +9 -0
  173. package/server/graphql/types/navigation-item-related.d.ts +7 -0
  174. package/server/graphql/types/navigation-item-related.js +25 -0
  175. package/server/graphql/types/navigation-item.d.ts +5 -0
  176. package/server/graphql/types/navigation-item.js +28 -29
  177. package/server/graphql/types/navigation-render-type.d.ts +5 -0
  178. package/server/graphql/types/navigation-render-type.js +7 -4
  179. package/server/graphql/types/navigation.d.ts +5 -0
  180. package/server/graphql/types/navigation.js +10 -8
  181. package/server/i18n/constant.d.ts +2 -0
  182. package/server/i18n/constant.js +5 -0
  183. package/server/i18n/errors.d.ts +7 -0
  184. package/server/i18n/errors.js +14 -0
  185. package/server/i18n/graphQLEnhancers.d.ts +10 -0
  186. package/server/i18n/graphQLEnhancers.js +9 -0
  187. package/server/i18n/index.d.ts +8 -0
  188. package/server/i18n/index.js +23 -0
  189. package/server/i18n/navigationSetupStrategy.d.ts +3 -0
  190. package/server/i18n/navigationSetupStrategy.js +119 -0
  191. package/server/i18n/serviceEnhancers.d.ts +12 -0
  192. package/server/i18n/serviceEnhancers.js +117 -0
  193. package/server/i18n/types.d.ts +54 -0
  194. package/server/i18n/types.js +3 -0
  195. package/server/i18n/utils.d.ts +12 -0
  196. package/server/i18n/utils.js +29 -0
  197. package/server/index.d.ts +273 -0
  198. package/server/index.js +26 -0
  199. package/server/navigation/index.d.ts +2 -0
  200. package/server/navigation/index.js +18 -0
  201. package/server/navigation/setupStrategy.d.ts +3 -0
  202. package/server/navigation/setupStrategy.js +37 -0
  203. package/server/register/index.d.ts +3 -0
  204. package/server/register/index.js +4 -0
  205. package/server/routes/admin.d.ts +4 -0
  206. package/server/routes/admin.js +72 -61
  207. package/server/routes/client.d.ts +4 -0
  208. package/server/routes/client.js +25 -21
  209. package/server/routes/index.d.ts +6 -0
  210. package/server/routes/index.js +13 -4
  211. package/server/services/admin.d.ts +5 -0
  212. package/server/services/admin.js +183 -0
  213. package/server/services/client.d.ts +5 -0
  214. package/server/services/client.js +249 -0
  215. package/server/services/common.d.ts +5 -0
  216. package/server/services/common.js +313 -0
  217. package/server/services/index.d.ts +7 -0
  218. package/server/services/index.js +14 -6
  219. package/server/utils/constant.d.ts +27 -0
  220. package/server/utils/constant.js +24 -0
  221. package/server/utils/functions.d.ts +138 -0
  222. package/server/utils/functions.js +224 -0
  223. package/server/utils/index.d.ts +3 -0
  224. package/server/utils/index.js +19 -0
  225. package/strapi-admin.d.ts +3 -0
  226. package/strapi-admin.js +8 -1
  227. package/strapi-server.d.ts +273 -0
  228. package/strapi-server.js +6 -17
  229. package/tsconfig.tsbuildinfo +1 -0
  230. package/types/bootstrap.d.ts +13 -0
  231. package/types/bootstrap.js +3 -0
  232. package/types/config.d.ts +21 -0
  233. package/types/config.js +3 -0
  234. package/types/contentTypes.d.ts +85 -0
  235. package/types/contentTypes.js +3 -0
  236. package/types/controllers.d.ts +35 -0
  237. package/types/controllers.js +5 -0
  238. package/types/graphQL.d.ts +3 -0
  239. package/types/graphQL.js +3 -0
  240. package/types/i18n.d.ts +9 -0
  241. package/types/i18n.js +3 -0
  242. package/types/index.d.ts +9 -0
  243. package/types/index.js +25 -0
  244. package/types/services.d.ts +68 -0
  245. package/types/services.js +3 -0
  246. package/types/utils.d.ts +58 -0
  247. package/types/utils.js +26 -0
  248. package/utils/InvalidParamNavigationError.d.ts +4 -0
  249. package/utils/InvalidParamNavigationError.js +11 -0
  250. package/utils/NavigationError.d.ts +5 -0
  251. package/utils/NavigationError.js +7 -7
  252. package/__mocks__/pages.settings.json +0 -25
  253. package/__mocks__/strapi.js +0 -207
  254. package/jest.config.js +0 -14
  255. package/server/bootstrap.js +0 -52
  256. package/server/config.js +0 -9
  257. package/server/content-types/audience/lifecycle.js +0 -8
  258. package/server/content-types/audience/schema.json +0 -23
  259. package/server/content-types/navigation/schema.json +0 -45
  260. package/server/content-types/navigation-item/schema.json +0 -99
  261. package/server/content-types/navigations-items-related/schema.json +0 -47
  262. package/server/controllers/navigation.js +0 -105
  263. package/server/graphql/types/navigation-related.js +0 -25
  264. package/server/services/__tests__/functions.test.js +0 -48
  265. package/server/services/__tests__/navigation.test.js +0 -91
  266. package/server/services/navigation.js +0 -788
  267. package/server/services/utils/constant.js +0 -21
  268. package/server/services/utils/functions.js +0 -226
@@ -1,788 +0,0 @@
1
- const pluralize = require('pluralize');
2
- const {
3
- find,
4
- get,
5
- isNil,
6
- isObject,
7
- isEmpty,
8
- last,
9
- upperFirst,
10
- map,
11
- toNumber,
12
- isString,
13
- first,
14
- } = require('lodash');
15
- const { validate: isUuid } = require('uuid');
16
- const slugify = require('slugify');
17
- const { KIND_TYPES, ALLOWED_CONTENT_TYPES, RESTRICTED_CONTENT_TYPES } = require('./utils/constant');
18
- const utilsFunctionsFactory = require('./utils/functions');
19
- const { renderType } = require('../content-types/navigation/lifecycle');
20
- const { type: itemType, additionalFields: configAdditionalFields } = require('../content-types/navigation-item').lifecycle;
21
- const { NotFoundError } = require('@strapi/utils').errors
22
- const contentTypesNameFieldsDefaults = ['title', 'subject', 'name'];
23
-
24
- module.exports = ({ strapi }) => {
25
- const utilsFunctions = utilsFunctionsFactory(strapi);
26
-
27
- return {
28
- // Get all available navigations
29
- async get() {
30
- const { masterModel } = utilsFunctions.extractMeta(strapi.plugins);
31
- const entities = await strapi
32
- .query(masterModel.uid)
33
- .findMany({
34
- limit: Number.MAX_SAFE_INTEGER,
35
- });
36
- return entities;
37
- },
38
-
39
- async getById(id) {
40
- const { masterModel, itemModel } = utilsFunctions.extractMeta(strapi.plugins);
41
- const entity = await strapi
42
- .query(masterModel.uid)
43
- .findOne({ where: { id } });
44
-
45
- const entityItems = await strapi
46
- .query(itemModel.uid)
47
- .findMany({
48
- where: {
49
- master: id,
50
- },
51
- limit: Number.MAX_SAFE_INTEGER,
52
- sort: ['order:asc'],
53
- populate: ['related', 'parent', 'audience']
54
- });
55
- const entities = await this.getRelatedItems(entityItems);
56
- return {
57
- ...entity,
58
- items: utilsFunctions.buildNestedStructure(entities),
59
- };
60
- },
61
-
62
- async restart() {
63
- setImmediate(() => strapi.reload());
64
- },
65
-
66
- // Get plugin config
67
- async config(viaSettingsPage = false) {
68
- const { audienceModel } = utilsFunctions.extractMeta(strapi.plugins);
69
- const pluginStore = await this.getPluginStore()
70
- const config = await pluginStore.get({ key: 'config' });
71
- const additionalFields = config.additionalFields;
72
- const contentTypesNameFields = config.contentTypesNameFields;
73
- const contentTypesPopulate = config.contentTypesPopulate;
74
- const allowedLevels = config.allowedLevels;
75
- const isGQLPluginEnabled = !isNil(strapi.plugin('graphql'));
76
-
77
- let extendedResult = {
78
- allowedContentTypes: ALLOWED_CONTENT_TYPES,
79
- restrictedContentTypes: RESTRICTED_CONTENT_TYPES,
80
- };
81
- const result = {
82
- contentTypes: await this.configContentTypes(),
83
- contentTypesNameFields: {
84
- default: contentTypesNameFieldsDefaults,
85
- ...(isObject(contentTypesNameFields) ? contentTypesNameFields : {}),
86
- },
87
- contentTypesPopulate: {
88
- ...(isObject(contentTypesPopulate) ? contentTypesPopulate : {}),
89
- },
90
- allowedLevels,
91
- additionalFields,
92
- isGQLPluginEnabled: viaSettingsPage ? isGQLPluginEnabled : undefined,
93
- };
94
-
95
- if (additionalFields.includes(configAdditionalFields.AUDIENCE)) {
96
- const audienceItems = await strapi
97
- .query(audienceModel.uid)
98
- .findMany({
99
- limit: Number.MAX_SAFE_INTEGER,
100
- });
101
- extendedResult = {
102
- ...extendedResult,
103
- availableAudience: audienceItems,
104
- };
105
- }
106
- return {
107
- ...result,
108
- ...extendedResult,
109
- };
110
- },
111
-
112
- async updateConfig(newConfig) {
113
- const pluginStore = await this.getPluginStore()
114
- await pluginStore.set({ key: 'config', value: newConfig });
115
- },
116
-
117
- async getPluginStore() {
118
- return await strapi.store({ type: 'plugin', name: 'navigation' });
119
- },
120
-
121
- async setDefaultConfig() {
122
- const pluginStore = await this.getPluginStore()
123
- const config = await pluginStore.get({ key: 'config' });
124
- const pluginDefaultConfig = await strapi.plugin('navigation').config
125
-
126
- // If new value gets introduced to the config it either is read from plugin store or from default plugin config
127
- // This is fix for backwards compatibility and migration of config to newer version of the plugin
128
- const defaultConfigValue = {
129
- additionalFields: get(config, 'additionalFields', pluginDefaultConfig('additionalFields')),
130
- contentTypes: get(config, 'contentTypes', pluginDefaultConfig('contentTypes')),
131
- contentTypesNameFields: get(config, 'contentTypesNameFields', pluginDefaultConfig('contentTypesNameFields')),
132
- contentTypesPopulate: get(config, 'contentTypesPopulate', pluginDefaultConfig('contentTypesPopulate')),
133
- allowedLevels: get(config, 'allowedLevels', pluginDefaultConfig('allowedLevels')),
134
- gql: get(config, 'gql', pluginDefaultConfig('gql')),
135
- }
136
- pluginStore.set({ key: 'config', value: defaultConfigValue });
137
-
138
- return defaultConfigValue;
139
- },
140
-
141
- async restoreConfig() {
142
- const pluginStore = await this.getPluginStore()
143
- await pluginStore.delete({ key: 'config' });
144
- await strapi.plugin('navigation').service('navigation').setDefaultConfig();
145
- },
146
-
147
- async configContentTypes() {
148
- const pluginStore = await this.getPluginStore()
149
- const config = await pluginStore.get({ key: 'config' });
150
- const eligibleContentTypes =
151
- await Promise.all(
152
- config.contentTypes
153
- .filter(contentType => !!strapi.contentTypes[contentType] && utilsFunctions.isContentTypeEligible(contentType))
154
- .map(
155
- async (key) => {
156
- const item = strapi.contentTypes[key];
157
- const { kind, options, uid } = item;
158
- const { draftAndPublish } = options;
159
-
160
- const isSingleType = kind === KIND_TYPES.SINGLE;
161
- const isSingleTypeWithPublishFlow = isSingleType && draftAndPublish;
162
-
163
- const returnType = (available) => ({
164
- key,
165
- available,
166
- });
167
-
168
- if (isSingleType) {
169
- if (isSingleTypeWithPublishFlow) {
170
- const itemsCountOrBypass = isSingleTypeWithPublishFlow ?
171
- await strapi.query(uid).count({
172
- publicationState: 'live',
173
- }) :
174
- true;
175
- return returnType(itemsCountOrBypass !== 0);
176
- }
177
- const isAvailable = await strapi.query(uid).count();
178
- return isAvailable === 1 ? returnType(true) : undefined;
179
- }
180
- return returnType(true);
181
- },
182
- ),
183
- );
184
- return eligibleContentTypes
185
- .filter(key => key)
186
- .map(({ key, available }) => {
187
- const item = strapi.contentTypes[key];
188
- const relatedField = (item.associations || []).find(_ => _.model === 'navigationitem');
189
- const { uid, options, info, collectionName, modelName, apiName, plugin, kind, pluginOptions } = item;
190
- const { visible = true } = pluginOptions['content-manager'] || {};
191
- const { name, description } = info;
192
- const { hidden, templateName } = options;
193
- const findRouteConfig = find(get(strapi.api, `[${modelName}].config.routes`, []),
194
- route => route.handler.includes('.find'));
195
- const findRoutePath = findRouteConfig && findRouteConfig.path.split('/')[1];
196
- const apiPath = findRoutePath && (findRoutePath !== apiName) ? findRoutePath : apiName || modelName;
197
- const isSingle = kind === KIND_TYPES.SINGLE;
198
- const endpoint = isSingle ? apiPath : pluralize(apiPath);
199
- const relationName = utilsFunctions.singularize(modelName);
200
- const relationNameParts = last(uid.split('.')).split('-');
201
- const contentTypeName = relationNameParts.length > 1 ? relationNameParts.reduce(
202
- (prev, curr) => `${prev}${upperFirst(curr)}`, '') : upperFirst(modelName);
203
- const labelSingular = name ||
204
- (upperFirst(relationNameParts.length > 1 ? relationNameParts.join(' ') : relationName));
205
- return {
206
- uid,
207
- name: relationName,
208
- isSingle,
209
- description,
210
- collectionName,
211
- contentTypeName,
212
- label: isSingle ? labelSingular : pluralize(name || labelSingular),
213
- relatedField: relatedField ? relatedField.alias : undefined,
214
- labelSingular: utilsFunctions.singularize(labelSingular),
215
- endpoint,
216
- plugin,
217
- available: available && !hidden,
218
- visible,
219
- templateName,
220
- };
221
- })
222
- .filter((item) => item && item.available);
223
- },
224
-
225
- async getRelatedItems(entityItems) {
226
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
227
- const config = await pluginStore.get({ key: 'config' });
228
- const relatedTypes = new Set(entityItems.flatMap((item) => get(item.related, 'related_type')));
229
- const groupedItems = Array.from(relatedTypes).filter((relatedType) => relatedType).reduce(
230
- (acc, relatedType) => Object.assign(acc, {
231
- [relatedType]: [
232
- ...(acc[relatedType] || []),
233
- ...entityItems
234
- .filter((item => item.related?.related_type === relatedType))
235
- .flatMap((item) => Object.assign(item.related, { navigationItemId: item.id })),
236
- ],
237
- }),
238
- {});
239
-
240
- const data = new Map(
241
- (
242
- await Promise.all(
243
- Object.entries(groupedItems)
244
- .map(async ([model, related]) => {
245
- const relationData = await strapi
246
- .query(model)
247
- .findMany({
248
- where: {
249
- id: { $in: map(related, 'related_id') },
250
- },
251
- populate: config.contentTypesPopulate[model] || []
252
- });
253
- return relationData
254
- .flatMap(_ =>
255
- Object.assign(
256
- _,
257
- {
258
- __contentType: model,
259
- navigationItemId: related.find(
260
- ({ related_id }) => related_id === _.id.toString())?.navigationItemId,
261
- },
262
- ),
263
- );
264
- }),
265
- )
266
- )
267
- .flat(1)
268
- .map(_ => [_.navigationItemId, _]),
269
- );
270
- return entityItems
271
- .map(({ related, ...item }) => {
272
- const relatedData = data.get(item.id);
273
- if (relatedData) {
274
- return Object.assign(item, { related: [relatedData] });
275
- }
276
- return item;
277
- });
278
- },
279
-
280
- async getContentTypeItems(model) {
281
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
282
- const config = await pluginStore.get({ key: 'config' });
283
- try {
284
- const contentTypeItems = await strapi.query(model).findMany({
285
- populate: config.contentTypesPopulate[model] || []
286
- })
287
- return contentTypeItems;
288
- } catch (err) {
289
- return [];
290
- }
291
- },
292
-
293
- async post(payload, auditLog) {
294
- const { masterModel, service } = utilsFunctions.extractMeta(strapi.plugins);
295
- const { name, visible } = payload;
296
- const data = {
297
- name,
298
- slug: slugify(name).toLowerCase(),
299
- visible,
300
- }
301
-
302
- const existingEntity = await strapi
303
- .query(masterModel.uid)
304
- .create({ data });
305
-
306
- return service
307
- .createBranch(payload.items, existingEntity, null)
308
- .then(() => service.getById(existingEntity.id))
309
- .then((newEntity) => {
310
- utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation',
311
- { actionType: 'CREATE', oldEntity: existingEntity, newEntity });
312
- return newEntity;
313
- });
314
- },
315
-
316
- async put(id, payload, auditLog) {
317
- const { masterModel, service } = utilsFunctions.extractMeta(strapi.plugins);
318
- const { name, visible } = payload;
319
-
320
- const existingEntity = await service.getById(id);
321
- const entityNameHasChanged = existingEntity.name !== name || existingEntity.visible !== visible;
322
- if (entityNameHasChanged) {
323
-
324
- await strapi.query(masterModel.uid).update({
325
- where: { id },
326
- data: {
327
- name: entityNameHasChanged ? name : existingEntity.name,
328
- slug: entityNameHasChanged ? slugify(name).toLowerCase() : existingEntity.slug,
329
- visible,
330
- },
331
- });
332
- }
333
- return service
334
- .analyzeBranch(payload.items, existingEntity, null)
335
- .then((auditLogsOperations) =>
336
- Promise.all([
337
- auditLog ? utilsFunctions.prepareAuditLog((auditLogsOperations || []).flat(Number.MAX_SAFE_INTEGER)) : [],
338
- service.getById(existingEntity.id)],
339
- ))
340
- .then(([actionType, newEntity]) => {
341
- utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation',
342
- { actionType, oldEntity: existingEntity, newEntity });
343
- return newEntity;
344
- });
345
- },
346
-
347
- async renderChildren(
348
- idOrSlug,
349
- childUIKey,
350
- type = renderType.FLAT,
351
- menuOnly = false,
352
- ) {
353
- const { service } = utilsFunctions.extractMeta(strapi.plugins);
354
- const findById = !isNaN(toNumber(idOrSlug)) || isUuid(idOrSlug);
355
- const criteria = findById ? { id: idOrSlug } : { slug: idOrSlug };
356
- const filter = type === renderType.FLAT ? null : childUIKey;
357
-
358
- const itemCriteria = {
359
- ...(menuOnly && { menuAttached: true }),
360
- ...(type === renderType.FLAT ? { uiRouterKey: childUIKey } : {}),
361
- };
362
-
363
- return service.renderType({ type, criteria, itemCriteria, filter });
364
- },
365
-
366
- async render({ idOrSlug, type = renderType.FLAT, menuOnly = false, rootPath = null }) {
367
- const { service } = utilsFunctions.extractMeta(strapi.plugins);
368
-
369
- const findById = !isNaN(toNumber(idOrSlug)) || isUuid(idOrSlug);
370
- const criteria = findById ? { id: idOrSlug } : { slug: idOrSlug };
371
- const itemCriteria = menuOnly ? { menuAttached: true } : {};
372
-
373
- return service.renderType({ type, criteria, itemCriteria, rootPath });
374
- },
375
-
376
- async renderType({ type = renderType.FLAT, criteria = {}, itemCriteria = {}, filter = null, rootPath = null }) {
377
- const { pluginName, service, masterModel, itemModel } = utilsFunctions.extractMeta(
378
- strapi.plugins,
379
- );
380
-
381
- const entity = await strapi
382
- .query(masterModel.uid)
383
- .findOne({
384
- where: {
385
- ...criteria,
386
- visible: true,
387
- }
388
- });
389
- if (entity && entity.id) {
390
- const entities = await strapi.query(itemModel.uid).findMany({
391
- where: {
392
- master: entity.id,
393
- ...itemCriteria,
394
- },
395
- limit: Number.MAX_SAFE_INTEGER,
396
- sort: ['order:asc'],
397
- populate: ['related', 'audience', 'parent'],
398
- });
399
-
400
- if (!entities) {
401
- return [];
402
- }
403
- const items = await this.getRelatedItems(entities);
404
- const { contentTypes, contentTypesNameFields } = await service.config();
405
-
406
- switch (type?.toLowerCase()) {
407
- case renderType.TREE:
408
- case renderType.RFR:
409
- const getTemplateName = await utilsFunctions.templateNameFactory(items, strapi, contentTypes);
410
- const itemParser = (item, path = '', field) => {
411
- const isExternal = item.type === itemType.EXTERNAL;
412
- const parentPath = isExternal ? undefined : `${path === '/' ? '' : path}/${first(item.path) === '/'
413
- ? item.path.substring(1)
414
- : item.path}`;
415
- const slug = isString(parentPath) ? slugify(
416
- (first(parentPath) === '/' ? parentPath.substring(1) : parentPath).replace(/\//g, '-')) : undefined;
417
- const lastRelated = item.related ? last(item.related) : undefined;
418
- return {
419
- id: item.id,
420
- title: utilsFunctions.composeItemTitle(item, contentTypesNameFields, contentTypes),
421
- menuAttached: item.menuAttached,
422
- order: item.order,
423
- path: isExternal ? item.externalPath : parentPath,
424
- type: item.type,
425
- uiRouterKey: item.uiRouterKey,
426
- slug: !slug && item.uiRouterKey ? slugify(item.uiRouterKey) : slug,
427
- external: isExternal,
428
- related: isExternal || !lastRelated ? undefined : {
429
- ...lastRelated,
430
- __templateName: getTemplateName(lastRelated.relatedType || lastRelated.__contentType, lastRelated.id),
431
- },
432
- audience: !isEmpty(item.audience) ? item.audience.map(aItem => aItem.key) : undefined,
433
- items: isExternal ? undefined : service.renderTree({
434
- items,
435
- id: item.id,
436
- field,
437
- path: parentPath,
438
- itemParser,
439
- }),
440
- };
441
- };
442
-
443
- const {
444
- items: itemsFilteredByPath,
445
- root: rootElement,
446
- } = utilsFunctions.filterByPath(items, rootPath);
447
-
448
- const treeStructure = service.renderTree({
449
- items: isNil(rootPath) ? items : itemsFilteredByPath,
450
- field: 'parent',
451
- id: get(rootElement, 'parent.id'),
452
- path: get(rootElement, 'parent.path'),
453
- itemParser,
454
- });
455
-
456
- const filteredStructure = filter
457
- ? treeStructure.filter((item) => item.uiRouterKey === filter)
458
- : treeStructure;
459
-
460
- if (type === renderType.RFR) {
461
- return service.renderRFR({
462
- items: filteredStructure,
463
- contentTypes,
464
- });
465
- }
466
- return filteredStructure;
467
- default:
468
- const publishedItems = items
469
- .filter(utilsFunctions.filterOutUnpublished)
470
- .map((item) => ({
471
- ...item,
472
- audience: item.audience?.map(_ => _.key),
473
- title: utilsFunctions.composeItemTitle(item, contentTypesNameFields, contentTypes),
474
- related: item.related?.map(({ localizations, ...item }) => item),
475
- items: null,
476
- }));
477
- return isNil(rootPath) ? items : utilsFunctions.filterByPath(publishedItems, rootPath).items;
478
- }
479
- }
480
- throw new NotFoundError();
481
- },
482
-
483
- renderTree({
484
- items = [],
485
- id = null,
486
- field = 'parent',
487
- path = '',
488
- itemParser = (i) => i,
489
- }) {
490
- return items
491
- .filter(
492
- (item) => {
493
- if (item[field] === null && id === null) {
494
- return true;
495
- }
496
- let data = item[field];
497
- if (data && typeof id === 'string') {
498
- data = data.toString();
499
- }
500
- return (data && data === id) || (isObject(item[field]) && (item[field].id === id));
501
- },
502
- )
503
- .filter(utilsFunctions.filterOutUnpublished)
504
- .map(item => itemParser({
505
- ...item,
506
- }, path, field))
507
- .sort((x, y) => {
508
- return x.order - y.order;
509
- });
510
- },
511
-
512
- renderRFR({ items, parent = null, parentNavItem = null, contentTypes = [] }) {
513
- const { service } = utilsFunctions.extractMeta(strapi.plugins);
514
- let pages = {};
515
- let nav = {};
516
- let navItems = [];
517
-
518
- items.forEach(item => {
519
- const { items: itemChilds, ...itemProps } = item;
520
- const itemNav = service.renderRFRNav(itemProps);
521
- const itemPage = service.renderRFRPage({
522
- item: itemProps,
523
- parent,
524
- });
525
-
526
- if (item.type === itemType.INTERNAL) {
527
- pages = {
528
- ...pages,
529
- [itemPage.id]: {
530
- ...itemPage,
531
- },
532
- };
533
- }
534
-
535
- if (item.menuAttached) {
536
- navItems.push(itemNav);
537
- }
538
-
539
- if (!parent) {
540
- nav = {
541
- ...nav,
542
- root: navItems,
543
- };
544
- } else {
545
- const navLevel = navItems
546
- .filter(navItem => navItem.type === itemType.INTERNAL.toLowerCase());
547
- if (!isEmpty(navLevel))
548
- nav = {
549
- ...nav,
550
- [parent]: [].concat(parentNavItem ? parentNavItem : [], navLevel),
551
- };
552
- }
553
-
554
- if (!isEmpty(itemChilds)) {
555
- const { nav: nestedNavs } = service.renderRFR({
556
- items: itemChilds,
557
- parent: itemPage.id,
558
- parentNavItem: itemNav,
559
- contentTypes,
560
- });
561
- const { pages: nestedPages } = service.renderRFR({
562
- items: itemChilds.filter(child => child.type === itemType.INTERNAL),
563
- parent: itemPage.id,
564
- parentNavItem: itemNav,
565
- contentTypes,
566
- });
567
- pages = {
568
- ...pages,
569
- ...nestedPages,
570
- };
571
- nav = {
572
- ...nav,
573
- ...nestedNavs,
574
- };
575
- }
576
- });
577
-
578
- return {
579
- pages,
580
- nav,
581
- };
582
- },
583
-
584
- renderRFRNav(item) {
585
- const { uiRouterKey, title, path, type, audience } = item;
586
- return {
587
- label: title,
588
- type: type.toLowerCase(),
589
- page: type === itemType.INTERNAL ? uiRouterKey : undefined,
590
- url: type === itemType.EXTERNAL ? path : undefined,
591
- audience,
592
- };
593
- },
594
-
595
- renderRFRPage({ item, parent }) {
596
- const { uiRouterKey, title, path, slug, related, type, audience, menuAttached } = item;
597
- const { __contentType, id, __templateName } = related || {};
598
- const contentType = __contentType || '';
599
- return {
600
- id: uiRouterKey,
601
- title,
602
- templateName: __templateName,
603
- related: type === itemType.INTERNAL ? {
604
- contentType,
605
- id,
606
- } : undefined,
607
- path,
608
- slug,
609
- parent,
610
- audience,
611
- menuAttached,
612
- };
613
- },
614
-
615
- createBranch(items = [], masterEntity = null, parentItem = null, operations = {}) {
616
- const { itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
617
- return Promise.all(
618
- items.map(async (item) => {
619
- operations.create = true;
620
- const { parent, master, related, ...params } = item;
621
- const relatedItems = await this.getIdsRelated(related, master);
622
- const data = {
623
- ...params,
624
- related: relatedItems,
625
- master: masterEntity,
626
- parent: parentItem ? { ...parentItem, _id: parentItem.id } : null,
627
- }
628
- const navigationItem = await strapi
629
- .query(itemModel.uid)
630
- .create({ data, populate: ['related', 'items'] });
631
- return !isEmpty(item.items)
632
- ? service.createBranch(
633
- item.items,
634
- masterEntity,
635
- navigationItem,
636
- operations,
637
- )
638
- : operations;
639
- }),
640
- );
641
- },
642
-
643
- removeBranch(items = [], operations = {}) {
644
- const { itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
645
- return Promise.all(
646
- items
647
- .filter(item => item.id)
648
- .map(async (item) => {
649
- operations.remove = true;
650
- const { id, related, master } = item;
651
- await Promise.all([
652
- strapi
653
- .query(itemModel.uid)
654
- .delete({ where: { id } }),
655
- this.removeRelated(related, master),
656
- ]);
657
- return !isEmpty(item.items)
658
- ? service.removeBranch(
659
- item.items,
660
- operations,
661
- )
662
- : operations;
663
- }),
664
- );
665
- },
666
-
667
- async updateBranch(toUpdate, masterEntity, parentItem, operations) {
668
- const { itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
669
- const databaseModel = strapi.query(itemModel.uid);
670
- return Promise.all(
671
- toUpdate.map(async (item) => {
672
- operations.update = true;
673
- const { id, updated, parent, master, related, items, ...params } = item;
674
- let currentItem;
675
- if (updated) {
676
- const relatedItems = await this.getIdsRelated(related, master);
677
- currentItem = await databaseModel
678
- .update({
679
- where: { id },
680
- data: {
681
- ...params,
682
- related: relatedItems,
683
- master: masterEntity,
684
- parent: parentItem ? { ...parentItem, _id: parentItem.id } : null,
685
- },
686
- });
687
- } else {
688
- currentItem = item;
689
- }
690
- return !isEmpty(items)
691
- ? service.analyzeBranch(
692
- items,
693
- masterEntity,
694
- currentItem,
695
- operations,
696
- )
697
- : operations;
698
- }),
699
- );
700
- },
701
-
702
- analyzeBranch(items = [], masterEntity = null, parentItem = null, prevOperations = {}) {
703
- const { service } = utilsFunctions.extractMeta(strapi.plugins);
704
- const { toCreate, toRemove, toUpdate } = items
705
- .reduce((acc, _) => {
706
- const branchName = service.getBranchName(_);
707
- if (branchName) {
708
- return { ...acc, [branchName]: [...acc[branchName], _] };
709
- }
710
- return acc;
711
- },
712
- { toRemove: [], toCreate: [], toUpdate: [] },
713
- );
714
- const operations = {
715
- create: prevOperations.create || !!toCreate.length,
716
- update: prevOperations.update || !!toUpdate.length,
717
- remove: prevOperations.remove || !!toRemove.length,
718
- };
719
- return utilsFunctions.checkDuplicatePath(parentItem || masterEntity, toCreate.concat(toUpdate))
720
- .then(() => Promise.all(
721
- [
722
- service.createBranch(toCreate, masterEntity, parentItem, operations),
723
- service.removeBranch(toRemove, operations),
724
- service.updateBranch(toUpdate, masterEntity, parentItem, operations),
725
- ],
726
- ));
727
- },
728
-
729
- getIdsRelated(relatedItems, master) {
730
- if (relatedItems) {
731
- return Promise.all(relatedItems.map(async relatedItem => {
732
- try {
733
- const model = strapi.query('plugin::navigation.navigations-items-related');
734
- const entity = await model
735
- .findOne({
736
- where: {
737
- related_id: relatedItem.refId,
738
- related_type: relatedItem.ref,
739
- field: relatedItem.field,
740
- master,
741
- }
742
- });
743
- if (!entity) {
744
- const newEntity = {
745
- master,
746
- order: 1,
747
- field: relatedItem.field,
748
- related_id: relatedItem.refId,
749
- related_type: relatedItem.ref,
750
- };
751
- return model.create({ data: newEntity }).then(({ id }) => id);
752
- }
753
- return entity.id;
754
- } catch (e) {
755
- console.error(e);
756
- }
757
- }));
758
- }
759
- },
760
-
761
- removeRelated(relatedItems, master) {
762
- return Promise.all((relatedItems || []).map(relatedItem => {
763
- const model = strapi.query('plugin::navigation.navigations-items-related');
764
- const entityToRemove = {
765
- master,
766
- field: relatedItem.field,
767
- related_id: relatedItem.refId,
768
- related_type: relatedItem.ref,
769
- };
770
- return model.delete({ where: entityToRemove }).then(({ id }) => id);
771
- }));
772
- },
773
-
774
- getBranchName(item) {
775
- const hasId = !isNil(item.id);
776
- const toRemove = item.removed;
777
- if (hasId && !toRemove) {
778
- return 'toUpdate';
779
- }
780
- if (hasId && toRemove) {
781
- return 'toRemove';
782
- }
783
- if (!hasId && !toRemove) {
784
- return 'toCreate';
785
- }
786
- },
787
- }
788
- }