strapi-plugin-navigation 2.0.5 → 2.0.8

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 (28) hide show
  1. package/README.md +2 -1
  2. package/admin/src/components/CollapseButton/index.js +31 -0
  3. package/admin/src/components/ConfirmationDialog/index.js +1 -1
  4. package/admin/src/components/Item/ItemCardBadge/index.js +4 -4
  5. package/admin/src/components/Item/ItemCardHeader/Wrapper.js +0 -4
  6. package/admin/src/components/Item/ItemCardHeader/index.js +28 -6
  7. package/admin/src/components/Item/Wrapper.js +1 -1
  8. package/admin/src/components/Item/index.js +59 -44
  9. package/admin/src/components/NavigationItemList/Wrapper.js +1 -1
  10. package/admin/src/components/NavigationItemList/index.js +3 -0
  11. package/admin/src/pages/SettingsPage/index.js +42 -22
  12. package/admin/src/pages/View/components/NavigationHeader/index.js +1 -1
  13. package/admin/src/pages/View/components/NavigationItemForm/index.js +47 -30
  14. package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +3 -3
  15. package/admin/src/pages/View/index.js +73 -9
  16. package/admin/src/pages/View/utils/enums.js +1 -0
  17. package/admin/src/pages/View/utils/parsers.js +6 -3
  18. package/admin/src/translations/en.json +12 -3
  19. package/package.json +2 -3
  20. package/server/bootstrap.js +3 -22
  21. package/server/config/index.js +1 -0
  22. package/server/config.js +1 -0
  23. package/server/content-types/navigation-item/lifecycle.js +1 -0
  24. package/server/content-types/navigation-item/schema.json +7 -1
  25. package/server/graphql/types/content-types-name-fields.js +2 -2
  26. package/server/services/navigation.js +44 -19
  27. package/server/services/utils/functions.js +3 -2
  28. package/yarn-error.log +0 -5263
@@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState, useCallback } from 'react';
2
2
  import { debounce, find, get, isEmpty, isEqual, isNil, isString } from 'lodash';
3
3
  import PropTypes from 'prop-types';
4
4
  import { Formik } from 'formik'
5
+ import slugify from 'slugify';
5
6
 
6
7
  // Design System
7
8
  import { ModalBody } from '@strapi/design-system/ModalLayout';
@@ -10,10 +11,7 @@ import { Grid, GridItem } from '@strapi/design-system/Grid';
10
11
  import { Form, GenericInput } from '@strapi/helper-plugin';
11
12
 
12
13
  import { NavigationItemPopupFooter } from '../NavigationItemPopup/NavigationItemPopupFooter';
13
-
14
-
15
14
  import { navigationItemAdditionalFields, navigationItemType } from '../../utils/enums';
16
- import slugify from 'slugify';
17
15
  import { extractRelatedItemLabel } from '../../utils/parsers';
18
16
  import { form as formDefinition } from './utils/form';
19
17
  import { checkFormValidity } from '../../utils/form';
@@ -76,20 +74,19 @@ const NavigationItemForm = ({
76
74
  };
77
75
 
78
76
  const sanitizePayload = (payload = {}) => {
79
- const { onItemClick, onItemLevelAddClick, related, relatedType, menuAttached, ...purePayload } = payload;
80
- const sanitizedType = purePayload.type || navigationItemType.INTERNAL;
77
+ const { onItemClick, onItemLevelAddClick, related, relatedType, menuAttached, type, ...purePayload } = payload;
81
78
  const relatedId = related
82
79
  const relatedCollectionType = relatedType;
83
80
  const title = payload.title || relatedSelectOptions.find(v => v.key == relatedId)?.label
84
81
  return {
85
82
  ...purePayload,
86
83
  title,
84
+ type,
87
85
  menuAttached: isNil(menuAttached) ? false : menuAttached,
88
- type: sanitizedType,
89
- path: sanitizedType === navigationItemType.INTERNAL ? purePayload.path : undefined,
90
- externalPath: sanitizedType === navigationItemType.EXTERNAL ? purePayload.externalPath : undefined,
91
- related: relatedId,
92
- relatedType: relatedCollectionType,
86
+ path: type !== navigationItemType.EXTERNAL ? purePayload.path : undefined,
87
+ externalPath: type === navigationItemType.EXTERNAL ? purePayload.externalPath : undefined,
88
+ related: type === navigationItemType.INTERNAL ? relatedId : undefined,
89
+ relatedType: type === navigationItemType.INTERNAL ? relatedCollectionType : undefined,
93
90
  isSingle: isSingleSelected,
94
91
  uiRouterKey: generateUiRouterKey(purePayload.title, relatedId, relatedCollectionType),
95
92
  };
@@ -109,11 +106,8 @@ const NavigationItemForm = ({
109
106
  }
110
107
  };
111
108
 
112
- const onTypeChange = ({ target: { name, value } }) =>
113
- onChange({ target: { name, value: value ? navigationItemType.INTERNAL : navigationItemType.EXTERNAL } });
114
-
115
109
  const onAudienceChange = (value) => {
116
- onChange({target: {name: `${inputsPrefix}audience`, value}});
110
+ onChange({ target: { name: `${inputsPrefix}audience`, value } });
117
111
  }
118
112
 
119
113
  const onChange = ({ target: { name, value } }) => {
@@ -148,6 +142,20 @@ const NavigationItemForm = ({
148
142
  [relatedTypeSelectValue, contentTypes],
149
143
  );
150
144
 
145
+ const navigationItemTypeOptions = Object.keys(navigationItemType).map(key => {
146
+ const value = navigationItemType[key].toLowerCase();
147
+ return {
148
+ key,
149
+ value: navigationItemType[key],
150
+ metadatas: {
151
+ intlLabel: {
152
+ id: getTradId(`popup.item.form.type.${value}.label`),
153
+ defaultMessage: getTradId(`popup.item.form.type.${value}.label`),
154
+ }
155
+ }
156
+ }
157
+ });
158
+
151
159
  const relatedSelectOptions = contentTypeEntities
152
160
  .filter((item) => {
153
161
  const usedContentTypeEntitiesOfSameType = usedContentTypeEntities
@@ -178,7 +186,9 @@ const NavigationItemForm = ({
178
186
  const isExternal = form.type === navigationItemType.EXTERNAL;
179
187
  const pathSourceName = isExternal ? 'externalPath' : 'path';
180
188
 
181
- const submitDisabled = (form.type !== navigationItemType.EXTERNAL) && isNil(form.related);
189
+ const submitDisabled =
190
+ (form.type === navigationItemType.INTERNAL && !isSingleSelected && isNil(get(form, `${inputsPrefix}related`))) ||
191
+ (form.type === navigationItemType.WRAPPER && isNil(get(form, `${inputsPrefix}title`)));
182
192
 
183
193
  const debouncedSearch = useCallback(
184
194
  debounce(nextValue => setContentTypeSearchQuery(nextValue), 500),
@@ -285,7 +295,21 @@ const NavigationItemForm = ({
285
295
  value={get(form, `${inputsPrefix}title`, '')}
286
296
  />
287
297
  </GridItem>
288
- <GridItem key={`${inputsPrefix}menuAttached`} col={6} lg={12}>
298
+ <GridItem key={`${inputsPrefix}type`} col={4} lg={12}>
299
+ <GenericInput
300
+ intlLabel={{
301
+ id: getTradId('popup.item.form.type.label'),
302
+ defaultMessage: 'Internal link',
303
+ }}
304
+ name={`${inputsPrefix}type`}
305
+ options={navigationItemTypeOptions}
306
+ type='select'
307
+ error={get(formErrors, `${inputsPrefix}type.id`)}
308
+ onChange={onChange}
309
+ value={get(form, `${inputsPrefix}type`, '')}
310
+ />
311
+ </GridItem>
312
+ <GridItem key={`${inputsPrefix}menuAttached`} col={4} lg={12}>
289
313
  <GenericInput
290
314
  intlLabel={{
291
315
  id: getTradId('popup.item.form.menuAttached.label'),
@@ -299,19 +323,6 @@ const NavigationItemForm = ({
299
323
  disabled={!(data.isMenuAllowedLevel && data.parentAttachedToMenu)}
300
324
  />
301
325
  </GridItem>
302
- <GridItem key={`${inputsPrefix}type`} col={6} lg={12}>
303
- <GenericInput
304
- intlLabel={{
305
- id: getTradId('popup.item.form.type.label'),
306
- defaultMessage: 'Internal link',
307
- }}
308
- name={`${inputsPrefix}type`}
309
- type='bool'
310
- error={get(formErrors, `${inputsPrefix}type.id`)}
311
- onChange={onTypeChange}
312
- value={get(form, `${inputsPrefix}type`, '') === navigationItemType.INTERNAL}
313
- />
314
- </GridItem>
315
326
  <GridItem key={`${inputsPrefix}path`} col={12}>
316
327
  <GenericInput
317
328
  intlLabel={{
@@ -330,7 +341,7 @@ const NavigationItemForm = ({
330
341
  description={generatePreviewPath()}
331
342
  />
332
343
  </GridItem>
333
- {!isExternal && (
344
+ {get(form, `${inputsPrefix}type`) === navigationItemType.INTERNAL && (
334
345
  <>
335
346
  <GridItem col={6} lg={12}>
336
347
  <GenericInput
@@ -402,8 +413,14 @@ const NavigationItemForm = ({
402
413
  label={getMessage('popup.item.form.audience.label')}
403
414
  onChange={onAudienceChange}
404
415
  value={audience}
416
+ hint={
417
+ !isLoading && isEmpty(audienceOptions)
418
+ ? getMessage('popup.item.form.audience.empty', 'There are no more audiences')
419
+ : undefined
420
+ }
405
421
  multi
406
422
  withTags
423
+ disabled={isEmpty(audienceOptions)}
407
424
  >
408
425
  {audienceOptions.map(({ value, label }) => <Option key={value} value={value}>{label}</Option>)}
409
426
  </Select>
@@ -1,16 +1,16 @@
1
1
 
2
2
 
3
3
  import React from 'react';
4
- import { ButtonText } from '@strapi/design-system/Text';
4
+ import { Typography } from '@strapi/design-system/Typography';
5
5
  import { ModalHeader } from '@strapi/design-system/ModalLayout';
6
6
  import { getMessage } from '../../../../utils';
7
7
 
8
8
  export const NavigationItemPopupHeader = ({isNewItem}) => {
9
9
  return (
10
10
  <ModalHeader>
11
- <ButtonText textColor="neutral800" as="h2" id="asset-dialog-title">
11
+ <Typography variant="omega" fontWeight="bold" textColor="neutral800" as="h2" id="asset-dialog-title">
12
12
  {getMessage(`popup.item.header.${isNewItem ? 'new' : 'edit'}`)}
13
- </ButtonText>
13
+ </Typography>
14
14
  </ModalHeader>
15
15
  );
16
16
  };
@@ -120,6 +120,37 @@ const View = () => {
120
120
  }, []);
121
121
  const filteredList = !isSearchEmpty ? filteredListFactory(changedActiveNavigation.items, (item) => item?.title.includes(searchValue)) : [];
122
122
 
123
+ const changeCollapseItemDeep = (item, collapse) => {
124
+ if (item.collapsed !== collapse) {
125
+ return {
126
+ ...item,
127
+ collapsed: collapse,
128
+ updated: true,
129
+ items: item.items?.map(el => changeCollapseItemDeep(el, collapse))
130
+ }
131
+ }
132
+ return {
133
+ ...item,
134
+ items: item.items?.map(el => changeCollapseItemDeep(el, collapse))
135
+ }
136
+ }
137
+
138
+ const handleCollapseAll = () => {
139
+ handleChangeNavigationData({
140
+ ...changedActiveNavigation,
141
+ items: changedActiveNavigation.items.map(item => changeCollapseItemDeep(item, true))
142
+ }, true);
143
+ setStructureChanged(true);
144
+ }
145
+
146
+ const handleExpandAll = () => {
147
+ handleChangeNavigationData({
148
+ ...changedActiveNavigation,
149
+ items: changedActiveNavigation.items.map(item => changeCollapseItemDeep(item, false))
150
+ }, true);
151
+ setStructureChanged(true);
152
+ }
153
+
123
154
  const handleItemReOrder = (item, newOrder) => {
124
155
  handleSubmitNavigationItem({
125
156
  ...item,
@@ -141,6 +172,14 @@ const View = () => {
141
172
  });
142
173
  };
143
174
 
175
+ const handleItemToggleCollapse = (item) => {
176
+ handleSubmitNavigationItem({
177
+ ...item,
178
+ collapsed: !item.collapsed,
179
+ updated: true,
180
+ });
181
+ }
182
+
144
183
  const handleItemEdit = (
145
184
  item,
146
185
  levelPath = '',
@@ -164,6 +203,33 @@ const View = () => {
164
203
  setSearchValue('');
165
204
  }
166
205
 
206
+ const endActions = [
207
+ {
208
+ onClick: handleExpandAll,
209
+ disabled: isLoadingForSubmit,
210
+ type: "submit",
211
+ variant: 'tertiary',
212
+ tradId: 'header.action.expandAll',
213
+ margin: '8px',
214
+ },
215
+ {
216
+ onClick: handleCollapseAll,
217
+ disabled: isLoadingForSubmit,
218
+ type: "submit",
219
+ variant: 'tertiary',
220
+ tradId: 'header.action.collapseAll',
221
+ margin: '8px',
222
+ },
223
+ {
224
+ onClick: addNewNavigationItem,
225
+ startIcon: <PlusIcon />,
226
+ disabled: isLoadingForSubmit,
227
+ type: "submit",
228
+ tradId: 'header.action.newItem',
229
+ margin: '16px',
230
+ },
231
+ ]
232
+
167
233
  return (
168
234
  <Main labelledBy="title" aria-busy={isLoadingForSubmit}>
169
235
  <NavigationHeader
@@ -180,18 +246,15 @@ const View = () => {
180
246
  <>
181
247
  <NavigationContentHeader
182
248
  startActions={<Search value={searchValue} setValue={setSearchValue} />}
183
- endActions={<Button
184
- onClick={addNewNavigationItem}
185
- startIcon={<PlusIcon />}
186
- disabled={isLoadingForSubmit}
187
- type="submit"
188
- >
189
- {formatMessage(getTrad('header.action.newItem'))}
190
- </Button>}
249
+ endActions={endActions.map(({ tradId, margin, ...item }, i) =>
250
+ <Box marginLeft={margin} key={i}>
251
+ <Button {...item}> {formatMessage(getTrad(tradId))} </Button>
252
+ </Box>
253
+ )}
191
254
  />
192
255
  {isEmpty(changedActiveNavigation.items || []) && (
193
256
  <Flex direction="column" minHeight="400px" justifyContent="center">
194
- <Icon as={EmptyDocumentsIcon} width="160px" height="88px" color=""/>
257
+ <Icon as={EmptyDocumentsIcon} width="160px" height="88px" color="" />
195
258
  <Box padding={4}>
196
259
  <Typography variant="beta" textColor="neutral600">{formatMessage(getTrad('empty'))}</Typography>
197
260
  </Box>
@@ -214,6 +277,7 @@ const View = () => {
214
277
  onItemEdit={handleItemEdit}
215
278
  onItemRestore={handleItemRestore}
216
279
  onItemReOrder={handleItemReOrder}
280
+ onItemToggleCollapse={handleItemToggleCollapse}
217
281
  displayFlat={!isSearchEmpty}
218
282
  root
219
283
  error={error}
@@ -1,6 +1,7 @@
1
1
  export const navigationItemType = {
2
2
  INTERNAL: "INTERNAL",
3
3
  EXTERNAL: "EXTERNAL",
4
+ WRAPPER: "WRAPPER",
4
5
  };
5
6
 
6
7
  export const navigationItemAdditionalFields = {
@@ -24,12 +24,14 @@ export const transformItemToRESTPayload = (
24
24
  order,
25
25
  audience = [],
26
26
  items = [],
27
+ collapsed,
27
28
  } = item;
28
29
  const isExternal = type === navigationItemType.EXTERNAL;
30
+ const isWrapper = type === navigationItemType.WRAPPER;
29
31
  const { contentTypes = [] } = config;
30
32
 
31
33
  const parsedRelated = Number(related);
32
- const relatedId = isExternal || isNaN(parsedRelated) ? related?.value || related : parsedRelated;
34
+ const relatedId = isExternal || isWrapper || isNaN(parsedRelated) ? related?.value || related : parsedRelated;
33
35
 
34
36
  const relatedContentType = relatedType ?
35
37
  find(contentTypes,
@@ -46,13 +48,14 @@ export const transformItemToRESTPayload = (
46
48
  removed,
47
49
  order,
48
50
  uiRouterKey,
51
+ collapsed,
49
52
  menuAttached: itemAttachedToMenu,
50
53
  audience: audience.map((audienceItem) =>
51
54
  isObject(audienceItem) ? audienceItem.value : audienceItem,
52
55
  ),
53
56
  path: isExternal ? undefined : path,
54
57
  externalPath: isExternal ? externalPath : undefined,
55
- related: isExternal
58
+ related: isExternal || isWrapper
56
59
  ? undefined
57
60
  : [
58
61
  {
@@ -274,7 +277,7 @@ export const usedContentTypes = (items = []) => items.flatMap(
274
277
 
275
278
  export const isRelationCorrect = ({ related, type }) => {
276
279
  const isRelationDefined = !isNil(related);
277
- return type === navigationItemType.EXTERNAL || (type === navigationItemType.INTERNAL && isRelationDefined);
280
+ return type !== navigationItemType.INTERNAL || (type === navigationItemType.INTERNAL && isRelationDefined);
278
281
  };
279
282
 
280
283
  export const isRelationPublished = ({ relatedRef, relatedType = {}, type, isCollection }) => {
@@ -3,6 +3,8 @@
3
3
  "header.title": "Navigation",
4
4
  "header.description": "Define your portal navigation",
5
5
  "header.action.newItem": "New Item",
6
+ "header.action.collapseAll": "Collapse All",
7
+ "header.action.expandAll": "Expand All",
6
8
  "submit.cta.cancel": "Cancel",
7
9
  "submit.cta.save": "Save",
8
10
  "empty": "Your navigation is empty",
@@ -20,12 +22,14 @@
20
22
  "popup.item.form.externalPath.placeholder": "Link to the external source",
21
23
  "popup.item.form.externalPath.validation.type": "This value is not a proper url.",
22
24
  "popup.item.form.menuAttached.label": "Attach to menu",
23
- "popup.item.form.type.label": "Internal link",
25
+ "popup.item.form.type.label": "Navigation item type",
24
26
  "popup.item.form.type.internal.label": "Internal source",
25
27
  "popup.item.form.type.external.label": "External source",
28
+ "popup.item.form.type.wrapper.label": "Wrapper element",
26
29
  "popup.item.form.type.external.description": "Output path: {value}",
27
30
  "popup.item.form.audience.label": "Audience",
28
31
  "popup.item.form.audience.placeholder": "Select audience...",
32
+ "popup.item.form.audience.empty": "There are no more audiences",
29
33
  "popup.item.form.relatedSection.label": "Relation to",
30
34
  "popup.item.form.relatedType.label": "Content Type",
31
35
  "popup.item.form.relatedType.placeholder": "Select content type...",
@@ -81,12 +85,17 @@
81
85
  "pages.settings.form.nameField.label": "Name fields",
82
86
  "pages.settings.form.nameField.placeholder": "Select at least one or leave empty to apply defaults",
83
87
  "pages.settings.form.nameField.hint": "If left empty name field is going to take following ordered fields: \"title\", \"subject\" and \"name\"",
88
+ "pages.settings.form.nameField.empty": "This content type doesn't have any string attributes",
89
+ "pages.settings.form.populate.label": "Fields to populate",
90
+ "pages.settings.form.populate.placeholder": "Select at least one or leave empty to disable populating relation fields",
91
+ "pages.settings.form.populate.hint": "Selected relation fields will be populated inside API responses",
92
+ "pages.settings.form.populate.empty": "This content type doesn't have any relation fields",
84
93
  "pages.settings.form.contentTypesSettings.label": "Content types",
85
94
  "pages.settings.form.contentTypesSettings.tooltip": "Custom configuration per content type",
86
95
  "components.navigationItem.action.newItem": "Add nested item",
87
96
  "components.navigationItem.badge.removed": "Removed",
88
- "components.navigationItem.badge.draft": "{type}: Draft",
89
- "components.navigationItem.badge.published": "{type}: Published",
97
+ "components.navigationItem.badge.draft": "Draft",
98
+ "components.navigationItem.badge.published": "Published",
90
99
  "components.confirmation.dialog.button.cancel": "Cancel",
91
100
  "components.confirmation.dialog.button.confirm": "Confirm",
92
101
  "components.confirmation.dialog.description": "Do you want to continue?",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-navigation",
3
- "version": "2.0.5",
3
+ "version": "2.0.8",
4
4
  "description": "Strapi - Navigation plugin",
5
5
  "strapi": {
6
6
  "name": "navigation",
@@ -17,9 +17,8 @@
17
17
  "test:unit": "jest --verbose --coverage"
18
18
  },
19
19
  "dependencies": {
20
- "@strapi/utils": "^4.1.0",
20
+ "@strapi/utils": "^4.1.5",
21
21
  "uuid": "^8.3.0",
22
- "bad-words": "^3.0.3",
23
22
  "lodash": "^4.17.11",
24
23
  "react": "^16.9.0",
25
24
  "react-dom": "^16.9.0",
@@ -42,30 +42,11 @@ module.exports = async ({ strapi }) => {
42
42
  }
43
43
 
44
44
  // Initialize configuration
45
- const pluginStore = strapi.store({
46
- environment: '',
47
- type: 'plugin',
48
- name: 'navigation',
49
- });
50
-
51
- const config = await pluginStore.get({ key: 'config' });
52
- const pluginDefaultConfig = await strapi.plugin('navigation').config
53
- const defaultConfigValue = {
54
- additionalFields: pluginDefaultConfig('additionalFields'),
55
- contentTypes: pluginDefaultConfig('contentTypes'),
56
- contentTypesNameFields: pluginDefaultConfig('contentTypesNameFields'),
57
- allowedLevels: pluginDefaultConfig('allowedLevels'),
58
- gql: pluginDefaultConfig('gql'),
59
- }
60
-
61
- if (!config) {
62
- pluginStore.set({
63
- key: 'config', value: defaultConfigValue
64
- });
65
- }
45
+ const config = await strapi.plugin('navigation').service('navigation').setDefaultConfig()
66
46
 
47
+ // Initialize graphql configuration
67
48
  if (strapi.plugin('graphql')) {
68
49
  const graphqlConfiguration = require('./graphql')
69
- await graphqlConfiguration({ strapi, config: config || defaultConfigValue });
50
+ await graphqlConfiguration({ strapi, config });
70
51
  }
71
52
  };
@@ -3,6 +3,7 @@ module.exports = {
3
3
  additionalFields: [],
4
4
  contentTypes: [],
5
5
  contentTypesNameFields: {},
6
+ contentTypesPopulate: {},
6
7
  allowedLevels: 2
7
8
  }
8
9
  }
package/server/config.js CHANGED
@@ -3,6 +3,7 @@ module.exports = {
3
3
  additionalFields: [],
4
4
  contentTypes: [],
5
5
  contentTypesNameFields: {},
6
+ contentTypesPopulate: {},
6
7
  allowedLevels: 2
7
8
  }
8
9
  }
@@ -10,6 +10,7 @@ module.exports = {
10
10
  type: {
11
11
  INTERNAL: 'INTERNAL',
12
12
  EXTERNAL: 'EXTERNAL',
13
+ WRAPPER: 'WRAPPER',
13
14
  },
14
15
  additionalFields: {
15
16
  AUDIENCE: 'audience',
@@ -37,7 +37,8 @@
37
37
  "type": "enumeration",
38
38
  "enum": [
39
39
  "INTERNAL",
40
- "EXTERNAL"
40
+ "EXTERNAL",
41
+ "WRAPPER"
41
42
  ],
42
43
  "default": "INTERNAL",
43
44
  "configurable": false
@@ -65,6 +66,11 @@
65
66
  "default": 0,
66
67
  "configurable": false
67
68
  },
69
+ "collapsed": {
70
+ "type": "boolean",
71
+ "default": false,
72
+ "configurable": false
73
+ },
68
74
  "related": {
69
75
  "type": "relation",
70
76
  "relation": "oneToOne",
@@ -1,8 +1,8 @@
1
- module.exports = ({ nexus }) => nexus.objectType({
1
+ module.exports = ({ nexus, strapi }) => nexus.objectType({
2
2
  name: "ContentTypesNameFields",
3
3
  async definition(t) {
4
4
  t.nonNull.list.nonNull.string("default")
5
- const pluginStore = strapi.store({ type: 'plugin', name: 'navigation' });
5
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore();
6
6
  const config = await pluginStore.get({ key: 'config' });
7
7
  const contentTypesNameFields = config.contentTypesNameFields;
8
8
  Object.keys(contentTypesNameFields || {}).forEach(key => t.nonNull.list.string(key))
@@ -72,10 +72,11 @@ module.exports = ({ strapi }) => {
72
72
  // Get plugin config
73
73
  async config(viaSettingsPage = false) {
74
74
  const { audienceModel, service } = utilsFunctions.extractMeta(strapi.plugins);
75
- const pluginStore = await strapi.store({ type: 'plugin', name: 'navigation' });
75
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
76
76
  const config = await pluginStore.get({ key: 'config' });
77
77
  const additionalFields = config.additionalFields;
78
78
  const contentTypesNameFields = config.contentTypesNameFields;
79
+ const contentTypesPopulate = config.contentTypesPopulate;
79
80
  const allowedLevels = config.allowedLevels;
80
81
  const isGQLPluginEnabled = !isNil(strapi.plugin('graphql'));
81
82
 
@@ -86,6 +87,9 @@ module.exports = ({ strapi }) => {
86
87
  default: contentTypesNameFieldsDefaults,
87
88
  ...(isObject(contentTypesNameFields) ? contentTypesNameFields : {}),
88
89
  },
90
+ contentTypesPopulate: {
91
+ ...(isObject(contentTypesPopulate) ? contentTypesPopulate : {}),
92
+ },
89
93
  allowedLevels,
90
94
  additionalFields,
91
95
  isGQLPluginEnabled: viaSettingsPage ? isGQLPluginEnabled : undefined,
@@ -111,28 +115,42 @@ module.exports = ({ strapi }) => {
111
115
  },
112
116
 
113
117
  async updateConfig(newConfig) {
114
- const pluginStore = await strapi.store({ type: 'plugin', name: 'navigation' });
118
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
115
119
  await pluginStore.set({ key: 'config', value: newConfig });
116
120
  },
117
121
 
122
+ async getPluginStore() {
123
+ return await strapi.store({ type: 'plugin', name: 'navigation' });
124
+ },
125
+
126
+ async setDefaultConfig() {
127
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
128
+ const config = await pluginStore.get({ key: 'config' });
129
+ const pluginDefaultConfig = await strapi.plugin('navigation').config
130
+
131
+ // If new value gets introduced to the config it either is read from plugin store or from default plugin config
132
+ // This is fix for backwards compatibility and migration of config to newer version of the plugin
133
+ const defaultConfigValue = {
134
+ additionalFields: get(config, 'additionalFields', pluginDefaultConfig('additionalFields')),
135
+ contentTypes: get(config, 'contentTypes', pluginDefaultConfig('contentTypes')),
136
+ contentTypesNameFields: get(config, 'contentTypesNameFields', pluginDefaultConfig('contentTypesNameFields')),
137
+ contentTypesPopulate: get(config, 'contentTypesPopulate', pluginDefaultConfig('contentTypesPopulate')),
138
+ allowedLevels: get(config, 'allowedLevels', pluginDefaultConfig('allowedLevels')),
139
+ gql: get(config, 'gql', pluginDefaultConfig('gql')),
140
+ }
141
+ pluginStore.set({ key: 'config', value: defaultConfigValue });
142
+
143
+ return defaultConfigValue;
144
+ },
145
+
118
146
  async restoreConfig() {
119
- const pluginStore = await strapi.store({ type: 'plugin', name: 'navigation' });
120
- const defaultConfig = await strapi.plugin('navigation').config
121
-
122
- await pluginStore.delete({ key: 'config' })
123
- await pluginStore.set({
124
- key: 'config', value: {
125
- additionalFields: defaultConfig('additionalFields'),
126
- contentTypes: defaultConfig('contentTypes'),
127
- contentTypesNameFields: defaultConfig('contentTypesNameFields'),
128
- allowedLevels: defaultConfig('allowedLevels'),
129
- gql: defaultConfig('gql'),
130
- }
131
- });
147
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
148
+ await pluginStore.delete({ key: 'config' });
149
+ await strapi.plugin('navigation').service('navigation').setDefaultConfig();
132
150
  },
133
151
 
134
152
  async configContentTypes() {
135
- const pluginStore = strapi.store({ type: 'plugin', name: 'navigation' });
153
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
136
154
  const config = await pluginStore.get({ key: 'config' });
137
155
  const eligibleContentTypes =
138
156
  await Promise.all(
@@ -212,6 +230,8 @@ module.exports = ({ strapi }) => {
212
230
  },
213
231
 
214
232
  async getRelatedItems(entityItems) {
233
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
234
+ const config = await pluginStore.get({ key: 'config' });
215
235
  const relatedTypes = new Set(entityItems.flatMap((item) => get(item.related, 'related_type')));
216
236
  const groupedItems = Array.from(relatedTypes).filter((relatedType) => relatedType).reduce(
217
237
  (acc, relatedType) => Object.assign(acc, {
@@ -233,8 +253,9 @@ module.exports = ({ strapi }) => {
233
253
  .query(model)
234
254
  .findMany({
235
255
  where: {
236
- id: { $in: map(related, 'related_id') }
237
- }
256
+ id: { $in: map(related, 'related_id') },
257
+ },
258
+ populate: config.contentTypesPopulate[model] || []
238
259
  });
239
260
  return relationData
240
261
  .flatMap(_ =>
@@ -264,8 +285,12 @@ module.exports = ({ strapi }) => {
264
285
  },
265
286
 
266
287
  async getContentTypeItems(model) {
288
+ const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
289
+ const config = await pluginStore.get({ key: 'config' });
267
290
  try {
268
- const contentTypeItems = await strapi.query(model).findMany()
291
+ const contentTypeItems = await strapi.query(model).findMany({
292
+ populate: config.contentTypesPopulate[model] || []
293
+ })
269
294
  return contentTypeItems;
270
295
  } catch (err) {
271
296
  return [];
@@ -7,7 +7,8 @@ const {
7
7
  isString,
8
8
  get,
9
9
  isNil,
10
- isArray
10
+ isArray,
11
+ first,
11
12
  } = require('lodash');
12
13
 
13
14
  const { type: itemType } = require('../../content-types/navigation-item/lifecycle');
@@ -213,7 +214,7 @@ module.exports = ({ strapi }) => {
213
214
  false;
214
215
  return item.type === itemType.INTERNAL ? isRelatedDefinedAndPublished : true;
215
216
  }
216
- return (item.type === itemType.EXTERNAL) || relatedItem;
217
+ return (item.type !== itemType.INTERNAL) || relatedItem;
217
218
  },
218
219
  };
219
220
  }