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