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,8 +1,10 @@
1
1
  {
2
2
  "collectionName": "navigations_items",
3
3
  "info": {
4
- "name": "navigationItem",
5
- "description": ""
4
+ "singularName": "navigation-item",
5
+ "pluralName": "navigation-items",
6
+ "displayName": "Navigation Item",
7
+ "tableName": "navigation-item"
6
8
  },
7
9
  "options": {
8
10
  "increments": true,
@@ -64,26 +66,28 @@
64
66
  "configurable": false
65
67
  },
66
68
  "related": {
67
- "collection": "navigations_items_related",
68
- "plugin": "navigation",
69
+ "type": "relation",
70
+ "relation": "oneToOne",
71
+ "target": "plugin::navigation.navigations-items-related",
69
72
  "configurable": false
70
73
  },
71
74
  "parent": {
72
- "columnName": "parent",
73
- "model": "navigationItem",
74
- "plugin": "navigation",
75
+ "type": "relation",
76
+ "relation": "oneToOne",
77
+ "target": "plugin::navigation.navigation-item",
75
78
  "configurable": false,
76
79
  "default": null
77
80
  },
78
81
  "master": {
79
- "columnName": "master",
80
- "model": "navigation",
81
- "plugin": "navigation",
82
+ "type": "relation",
83
+ "relation": "manyToOne",
84
+ "target": "plugin::navigation.navigation",
82
85
  "configurable": false
83
86
  },
84
87
  "audience": {
85
- "collection": "audience",
86
- "plugin": "navigation"
88
+ "type": "relation",
89
+ "relation": "oneToMany",
90
+ "target": "plugin::navigation.audience"
87
91
  }
88
92
  }
89
93
  }
@@ -0,0 +1,9 @@
1
+ "use strict"
2
+
3
+ const schema = require("./schema");
4
+ const lifecycle = require("./lifecycle");
5
+
6
+ module.exports = {
7
+ lifecycle,
8
+ schema
9
+ };
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "collectionName": "navigations_items_related",
3
3
  "info": {
4
- "name": "navigations_items_related",
5
- "description": ""
4
+ "singularName": "navigations-items-related",
5
+ "pluralName": "navigations-items-relateds",
6
+ "displayName": "Navigations Items Related",
7
+ "name": "navigations_items_related"
6
8
  },
7
9
  "options": {
8
10
  "increments": true,
@@ -0,0 +1,7 @@
1
+ "use strict"
2
+
3
+ const navigation = require("./navigation");
4
+
5
+ module.exports = {
6
+ navigation
7
+ };
@@ -1,11 +1,6 @@
1
- 'use strict';
1
+ const { NavigationError } = require('../../utils/NavigationError');
2
2
 
3
- const { NavigationError } = require('../utils/NavigationError');
4
- /**
5
- * navigation.js controller
6
- *
7
- * @description: A set of functions called "actions" of the `navigation` plugin.
8
- */
3
+ const getService = () => strapi.plugin('navigation').service('navigation');
9
4
 
10
5
  const parseParams = (params) =>
11
6
  Object.keys(params).reduce((prev, curr) => {
@@ -23,56 +18,29 @@ const errorHandler = (ctx) => (error) => {
23
18
  }
24
19
  throw error;
25
20
  };
26
- const getService = () => strapi.plugins.navigation.services.navigation;
27
21
 
28
22
  module.exports = {
29
- /**
30
- * Default action.
31
- *
32
- * @return {Object}
33
- */
34
23
  async config() {
35
24
  return getService().config();
36
25
  },
37
-
38
26
  async get() {
39
27
  return getService().get();
40
28
  },
41
-
42
29
  async getById(ctx) {
43
30
  const { params } = ctx;
44
31
  const { id } = parseParams(params);
45
32
  return getService().getById(id);
46
33
  },
47
-
48
- async render(ctx) {
49
- const { params, query = {} } = ctx;
50
- const { type, menu: menuOnly } = query;
51
- const { idOrSlug } = parseParams(params);
52
- return getService().render(
53
- idOrSlug,
54
- type,
55
- menuOnly,
56
- );
57
- },
58
- async renderChild(ctx) {
59
- const { params, query = {} } = ctx;
60
- const { type, menu: menuOnly } = query;
61
- const { idOrSlug, childUIKey } = parseParams(params);
62
- return getService().renderChildren(
63
- idOrSlug,
64
- childUIKey,
65
- type,
66
- menuOnly
67
- );
34
+ async getContentTypeItems(ctx) {
35
+ const { params } = ctx;
36
+ const { model } = parseParams(params);
37
+ return getService().getContentTypeItems(model)
68
38
  },
69
-
70
39
  post(ctx) {
71
40
  const { auditLog } = ctx;
72
41
  const { body = {} } = ctx.request;
73
42
  return getService().post(body, auditLog);
74
43
  },
75
-
76
44
  put(ctx) {
77
45
  const { params, auditLog } = ctx;
78
46
  const { id } = parseParams(params);
@@ -80,4 +48,4 @@ module.exports = {
80
48
  return getService().put(id, body, auditLog)
81
49
  .catch(errorHandler(ctx));
82
50
  },
83
- };
51
+ };
@@ -0,0 +1,38 @@
1
+ module.exports = {
2
+ type: 'admin',
3
+ routes: [
4
+ {
5
+ method: 'GET',
6
+ path: '/',
7
+ handler: 'navigation.get',
8
+ },
9
+ {
10
+ method: 'POST',
11
+ path: '/',
12
+ handler: 'navigation.post',
13
+ },
14
+ {
15
+ method: 'GET',
16
+ path: '/config',
17
+ handler: 'navigation.config',
18
+ },
19
+ {
20
+ method: 'GET',
21
+ path: '/:id',
22
+ handler: 'navigation.getById',
23
+ },
24
+ {
25
+ method: 'PUT',
26
+ path: '/:id',
27
+ handler: 'navigation.put',
28
+ },
29
+ {
30
+ method: 'GET',
31
+ path: '/content-type-items/:model',
32
+ handler: 'navigation.getContentTypeItems',
33
+ policies: [
34
+ 'admin::isAuthenticatedAdmin'
35
+ ]
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ 'admin': require('./admin')
3
+ };
@@ -0,0 +1,7 @@
1
+ "use strict"
2
+
3
+ const navigation = require("./navigation");
4
+
5
+ module.exports = {
6
+ navigation
7
+ };
@@ -0,0 +1,463 @@
1
+ const pluralize = require('pluralize');
2
+ const {
3
+ find,
4
+ get,
5
+ isNil,
6
+ isObject,
7
+ isEmpty,
8
+ last,
9
+ upperFirst,
10
+ map,
11
+ } = require('lodash');
12
+ const { KIND_TYPES } = require('./utils/constant');
13
+ const utilsFunctionsFactory = require('./utils/functions');
14
+ const { additionalFields: configAdditionalFields } = require('../content-types/navigation-item').lifecycle;
15
+
16
+ const excludedContentTypes = ['strapi::'];
17
+ const contentTypesNameFieldsDefaults = ['title', 'subject', 'name'];
18
+
19
+ module.exports = ({ strapi }) => {
20
+ const utilsFunctions = utilsFunctionsFactory(strapi);
21
+
22
+ return {
23
+ // Get all available navigations
24
+ async get() {
25
+ const { masterModel } = utilsFunctions.extractMeta(strapi.plugins);
26
+ const entities = await strapi
27
+ .query(masterModel.uid)
28
+ .findMany({
29
+ paggination: {
30
+ limit: -1,
31
+ }
32
+ });
33
+ return entities;
34
+ },
35
+
36
+ async getById(id) {
37
+ const { masterModel, itemModel } = utilsFunctions.extractMeta(strapi.plugins);
38
+ const entity = await strapi
39
+ .query(masterModel.uid)
40
+ .findOne({ where: { id }});
41
+
42
+ const entityItems = await strapi
43
+ .query(itemModel.uid)
44
+ .findMany({
45
+ where: {
46
+ master: id,
47
+ },
48
+ paggination: {
49
+ limit: -1,
50
+ },
51
+ sort: ['order:asc'],
52
+ populate: ['related', 'parent']
53
+ });
54
+ const entities = await this.getRelatedItems(entityItems);
55
+ return {
56
+ ...entity,
57
+ items: utilsFunctions.buildNestedStructure(entities),
58
+ };
59
+ },
60
+
61
+ // Get plugin config
62
+ async config() {
63
+ const { pluginName, audienceModel } = utilsFunctions.extractMeta(strapi.plugins);
64
+ const additionalFields = strapi.plugin(pluginName).config('additionalFields')
65
+ const contentTypesNameFields = strapi.plugin(pluginName).config('contentTypesNameFields');
66
+ const allowedLevels = strapi.plugin(pluginName).config('allowedLevels');
67
+
68
+ let extendedResult = {};
69
+ const result = {
70
+ contentTypes: await strapi.plugin(pluginName).service('navigation').configContentTypes(),
71
+ contentTypesNameFields: {
72
+ default: contentTypesNameFieldsDefaults,
73
+ ...(isObject(contentTypesNameFields) ? contentTypesNameFields : {}),
74
+ },
75
+ allowedLevels,
76
+ additionalFields,
77
+ };
78
+
79
+ if (additionalFields.includes(configAdditionalFields.AUDIENCE)) {
80
+ const audienceItems = await strapi
81
+ .query(`plugin::${pluginName}.${audienceModel.modelName}`)
82
+ .findMany({
83
+ paggination: {
84
+ limit: -1,
85
+ }
86
+ });
87
+ extendedResult = {
88
+ ...extendedResult,
89
+ availableAudience: audienceItems,
90
+ };
91
+ }
92
+ return {
93
+ ...result,
94
+ ...extendedResult,
95
+ };
96
+ },
97
+
98
+ async configContentTypes() {
99
+ const eligibleContentTypes =
100
+ await Promise.all(
101
+ strapi.plugin('navigation').config('contentTypes')
102
+ .filter(contentType => !!strapi.contentTypes[contentType])
103
+ .map(
104
+ async (key) => {
105
+ if (find(excludedContentTypes, name => key.includes(name))) { // exclude internal content types
106
+ return;
107
+ }
108
+ const item = strapi.contentTypes[key];
109
+ const { kind, options, uid } = item;
110
+ const { draftAndPublish } = options;
111
+
112
+ const isSingleType = kind === KIND_TYPES.SINGLE;
113
+ const isSingleTypeWithPublishFlow = isSingleType && draftAndPublish;
114
+
115
+ const returnType = (available) => ({
116
+ key,
117
+ available,
118
+ });
119
+
120
+ if (isSingleType) {
121
+ if (isSingleTypeWithPublishFlow) {
122
+ const itemsCountOrBypass = isSingleTypeWithPublishFlow ?
123
+ await strapi.query(uid).count({
124
+ publicationState: 'live',
125
+ }) :
126
+ true;
127
+ return returnType(itemsCountOrBypass !== 0);
128
+ }
129
+ const isAvailable = await strapi.query(uid).count();
130
+ return isAvailable === 1 ? returnType(true) : undefined;
131
+ }
132
+ return returnType(true);
133
+ },
134
+ ),
135
+ );
136
+ return eligibleContentTypes
137
+ .filter(key => key)
138
+ .map(({ key, available }) => {
139
+ const item = strapi.contentTypes[key];
140
+ const relatedField = (item.associations || []).find(_ => _.model === 'navigationitem');
141
+ const { uid, options, info, collectionName, modelName, apiName, plugin, kind } = item;
142
+ const { name, description } = info;
143
+ const { isManaged, hidden, templateName } = options;
144
+ const findRouteConfig = find(get(strapi.api, `[${modelName}].config.routes`, []),
145
+ route => route.handler.includes('.find'));
146
+ const findRoutePath = findRouteConfig && findRouteConfig.path.split('/')[1];
147
+ const apiPath = findRoutePath && (findRoutePath !== apiName) ? findRoutePath : apiName || modelName;
148
+ const isSingle = kind === KIND_TYPES.SINGLE;
149
+ const endpoint = isSingle ? apiPath : pluralize(apiPath);
150
+ const relationName = utilsFunctions.singularize(modelName);
151
+ const relationNameParts = last(uid.split('.')).split('-');
152
+ const contentTypeName = relationNameParts.length > 1 ? relationNameParts.reduce(
153
+ (prev, curr) => `${prev}${upperFirst(curr)}`, '') : upperFirst(modelName);
154
+ const labelSingular = name ||
155
+ (upperFirst(relationNameParts.length > 1 ? relationNameParts.join(' ') : relationName));
156
+ return {
157
+ uid,
158
+ name: relationName,
159
+ isSingle,
160
+ description,
161
+ collectionName,
162
+ contentTypeName,
163
+ label: isSingle ? labelSingular : pluralize(name || labelSingular),
164
+ relatedField: relatedField ? relatedField.alias : undefined,
165
+ labelSingular: utilsFunctions.singularize(labelSingular),
166
+ endpoint,
167
+ plugin,
168
+ available,
169
+ visible: (isManaged || isNil(isManaged)) && !hidden,
170
+ templateName,
171
+ };
172
+ })
173
+ .filter((item) => item && item.visible);
174
+ },
175
+
176
+ async getRelatedItems(entityItems) {
177
+ const relatedTypes = new Set(entityItems.flatMap((item) => get(item.related, 'related_type')));
178
+ const groupedItems = Array.from(relatedTypes).filter((relatedType) => relatedType).reduce(
179
+ (acc, relatedType) => Object.assign(acc, {
180
+ [relatedType]: [
181
+ ...(acc[relatedType] || []),
182
+ ...entityItems
183
+ .filter((item => item.related?.related_type === relatedType))
184
+ .flatMap((item) => Object.assign(item.related, { navigationItemId: item.id })),
185
+ ],
186
+ }),
187
+ {});
188
+
189
+ const data = new Map(
190
+ (
191
+ await Promise.all(
192
+ Object.entries(groupedItems)
193
+ .map(async ([model, related]) => {
194
+ const relationData = await strapi
195
+ .query(model)
196
+ .findMany({where: {
197
+ id: { $in: map(related, 'related_id') }
198
+ }});
199
+ return relationData
200
+ .flatMap(_ =>
201
+ Object.assign(
202
+ _,
203
+ {
204
+ __contentType: model,
205
+ navigationItemId: related.find(
206
+ ({ related_id }) => related_id === _.id.toString())?.navigationItemId,
207
+ },
208
+ ),
209
+ );
210
+ }),
211
+ )
212
+ )
213
+ .flat(1)
214
+ .map(_ => [_.navigationItemId, _]),
215
+ );
216
+ return entityItems
217
+ .map(({ related, ...item }) => {
218
+ const relatedData = data.get(item.id);
219
+ if (relatedData) {
220
+ return Object.assign(item, { related: [relatedData] });
221
+ }
222
+ return item;
223
+ });
224
+ },
225
+
226
+ async getContentTypeItems(model) {
227
+ try {
228
+ const contentTypeItems = await strapi.query(model).findMany()
229
+ return contentTypeItems;
230
+ } catch (err) {
231
+ return [];
232
+ }
233
+ },
234
+
235
+ async post(payload, auditLog) {
236
+ const { masterModel, service } = utilsFunctions.extractMeta(strapi.plugins);
237
+ const { name, visible } = payload;
238
+ const data = {
239
+ name,
240
+ slug: slugify(name).toLowerCase(),
241
+ visible,
242
+ }
243
+
244
+ const existingEntity = await strapi
245
+ .query(masterModel.uid)
246
+ .create({ data });
247
+
248
+ return service
249
+ .createBranch(payload.items, existingEntity, null)
250
+ .then(() => service.getById(existingEntity.id))
251
+ .then((newEntity) => {
252
+ utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation',
253
+ { actionType: 'CREATE', oldEntity: existingEntity, newEntity });
254
+ return newEntity;
255
+ });
256
+ },
257
+
258
+ async put(id, payload, auditLog) {
259
+ const { masterModel, service } = utilsFunctions.extractMeta(strapi.plugins);
260
+ const { name, visible } = payload;
261
+
262
+ const existingEntity = await service.getById(id);
263
+ const entityNameHasChanged = existingEntity.name !== name || existingEntity.visible !== visible;
264
+ if (entityNameHasChanged) {
265
+
266
+ await strapi.query(masterModel.uid).update({
267
+ where: { id },
268
+ data: {
269
+ name: entityNameHasChanged ? name : existingEntity.name,
270
+ slug: entityNameHasChanged ? slugify(name).toLowerCase() : existingEntity.slug,
271
+ visible,
272
+ },
273
+ });
274
+ }
275
+ return service
276
+ .analyzeBranch(payload.items, existingEntity, null)
277
+ .then((auditLogsOperations) =>
278
+ Promise.all([
279
+ auditLog ? utilsFunctions.prepareAuditLog((auditLogsOperations || []).flat(Number.MAX_SAFE_INTEGER)) : [],
280
+ service.getById(existingEntity.id)],
281
+ ))
282
+ .then(([actionType, newEntity]) => {
283
+ utilsFunctions.sendAuditLog(auditLog, 'onChangeNavigation',
284
+ { actionType, oldEntity: existingEntity, newEntity });
285
+ return newEntity;
286
+ });
287
+ },
288
+
289
+ createBranch(items = [], masterEntity = null, parentItem = null, operations = {}) {
290
+ const { itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
291
+ return Promise.all(
292
+ items.map(async (item) => {
293
+ operations.create = true;
294
+ const { parent, master, related, ...params } = item;
295
+ const relatedItems = await this.getIdsRelated(related, master);
296
+ const data = {
297
+ ...params,
298
+ related: relatedItems,
299
+ master: masterEntity,
300
+ parent: parentItem ? { ...parentItem, _id: parentItem.id } : null,
301
+ }
302
+ const navigationItem = await strapi
303
+ .query(itemModel.uid)
304
+ .create({ data });
305
+ return !isEmpty(item.items)
306
+ ? service.createBranch(
307
+ item.items,
308
+ masterEntity,
309
+ navigationItem,
310
+ operations,
311
+ )
312
+ : operations;
313
+ }),
314
+ );
315
+ },
316
+
317
+ removeBranch(items = [], operations = {}) {
318
+ const { itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
319
+ return Promise.all(
320
+ items
321
+ .filter(item => item.id)
322
+ .map(async (item) => {
323
+ operations.remove = true;
324
+ const { id, related, master } = item;
325
+ await Promise.all([
326
+ strapi
327
+ .query(itemModel.uid)
328
+ .delete({ where: { id } }),
329
+ this.removeRelated(related, master),
330
+ ]);
331
+ return !isEmpty(item.items)
332
+ ? service.removeBranch(
333
+ item.items,
334
+ operations,
335
+ )
336
+ : operations;
337
+ }),
338
+ );
339
+ },
340
+
341
+ async updateBranch(toUpdate, masterEntity, parentItem, operations) {
342
+ const { itemModel, service } = utilsFunctions.extractMeta(strapi.plugins);
343
+ const databaseModel = strapi.query(itemModel.uid);
344
+ return Promise.all(
345
+ toUpdate.map(async (item) => {
346
+ operations.update = true;
347
+ const { id, updated, parent, master, related, items, ...params } = item;
348
+ let currentItem;
349
+ if (updated) {
350
+ const relatedItems = await this.getIdsRelated(related, master);
351
+ currentItem = await databaseModel
352
+ .update({
353
+ where: { id },
354
+ data: {
355
+ ...params,
356
+ related: relatedItems,
357
+ master: masterEntity,
358
+ parent: parentItem ? { ...parentItem, _id: parentItem.id } : null,
359
+ },
360
+ });
361
+ } else {
362
+ currentItem = item;
363
+ }
364
+ return !isEmpty(items)
365
+ ? service.analyzeBranch(
366
+ items,
367
+ masterEntity,
368
+ currentItem,
369
+ operations,
370
+ )
371
+ : operations;
372
+ }),
373
+ );
374
+ },
375
+
376
+ analyzeBranch(items = [], masterEntity = null, parentItem = null, prevOperations = {}) {
377
+ const { service } = utilsFunctions.extractMeta(strapi.plugins);
378
+ const { toCreate, toRemove, toUpdate } = items
379
+ .reduce((acc, _) => {
380
+ const branchName = service.getBranchName(_);
381
+ if (branchName) {
382
+ return { ...acc, [branchName]: [...acc[branchName], _] };
383
+ }
384
+ return acc;
385
+ },
386
+ { toRemove: [], toCreate: [], toUpdate: [] },
387
+ );
388
+ const operations = {
389
+ create: prevOperations.create || !!toCreate.length,
390
+ update: prevOperations.update || !!toUpdate.length,
391
+ remove: prevOperations.remove || !!toRemove.length,
392
+ };
393
+ return utilsFunctions.checkDuplicatePath(parentItem || masterEntity, toCreate.concat(toUpdate))
394
+ .then(() => Promise.all(
395
+ [
396
+ service.createBranch(toCreate, masterEntity, parentItem, operations),
397
+ service.removeBranch(toRemove, operations),
398
+ service.updateBranch(toUpdate, masterEntity, parentItem, operations),
399
+ ],
400
+ ));
401
+ },
402
+
403
+ getIdsRelated(relatedItems, master) {
404
+ if (relatedItems) {
405
+ return Promise.all(relatedItems.map(async relatedItem => {
406
+ try {
407
+
408
+ const model = strapi.query('plugin::navigation.navigations-items-related');
409
+ const entity = await model
410
+ .findOne({
411
+ where: {
412
+ related_id: relatedItem.refId,
413
+ related_type: relatedItem.ref,
414
+ field: relatedItem.field,
415
+ master,
416
+ }
417
+ });
418
+ if (!entity) {
419
+ const newEntity = {
420
+ master,
421
+ order: 1,
422
+ field: relatedItem.field,
423
+ related_id: relatedItem.refId,
424
+ related_type: relatedItem.ref,
425
+ };
426
+ return model.create({ data: newEntity }).then(({ id }) => id);
427
+ }
428
+ return entity.id;
429
+ } catch (e) {
430
+ console.error(e);
431
+ }
432
+ }));
433
+ }
434
+ },
435
+
436
+ removeRelated(relatedItems, master) {
437
+ return Promise.all(relatedItems.map(relatedItem => {
438
+ const model = strapi.query('plugin::navigation.navigations-items-related');
439
+ const entityToRemove = {
440
+ master,
441
+ field: relatedItem.field,
442
+ related_id: relatedItem.refId,
443
+ related_type: relatedItem.ref,
444
+ };
445
+ return model.delete({ where: entityToRemove }).then(({ id }) => id);
446
+ }));
447
+ },
448
+
449
+ getBranchName(item) {
450
+ const hasId = !isNil(item.id);
451
+ const toRemove = item.removed;
452
+ if (hasId && !toRemove) {
453
+ return 'toUpdate';
454
+ }
455
+ if (hasId && toRemove) {
456
+ return 'toRemove';
457
+ }
458
+ if (!hasId && !toRemove) {
459
+ return 'toCreate';
460
+ }
461
+ },
462
+ }
463
+ }