strapi-plugin-navigation 1.1.3 → 2.0.0-beta.4

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