strapi-plugin-navigation 1.1.1 → 2.0.0-beta.3

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