strapi-plugin-navigation 2.0.10 → 2.0.11

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.
@@ -122,6 +122,13 @@ const Item = (props) => {
122
122
  previewRef: dragPreview(previewRef),
123
123
  }
124
124
 
125
+ const contentTypeUid = relatedRef?.__collectionUid;
126
+ const contentType = contentTypes.find(_ => _.uid === contentTypeUid) || {};
127
+ const generatePreviewUrl = entity => {
128
+ const { isSingle } = contentType;
129
+ return `/content-manager/${ isSingle ? 'singleType' : 'collectionType'}/${entity?.__collectionUid}${!isSingle ? '/' + entity?.id : ''}`
130
+ }
131
+
125
132
  return (
126
133
  <Wrapper level={level} isLast={isLast} style={{ opacity: isDragging ? 0.2 : 1 }} ref={refs ? refs.dropRef : null} >
127
134
  <Card style={{ width: "728px", zIndex: 1, position: "relative", overflow: 'hidden' }}>
@@ -179,9 +186,9 @@ const Item = (props) => {
179
186
  </ItemCardBadge>}
180
187
  <Typography variant="omega" textColor='neutral600'>{relatedTypeLabel}&nbsp;/&nbsp;</Typography>
181
188
  <Typography variant="omega" textColor='neutral800'>{relatedItemLabel}</Typography>
182
- <Link
183
- to={`/content-manager/collectionType/${relatedRef?.__collectionUid}/${relatedRef?.id}`}
184
- endIcon={<ArrowRight />}>&nbsp;</Link>
189
+ { contentType?.visible && (<Link
190
+ to={generatePreviewUrl(relatedRef)}
191
+ endIcon={<ArrowRight />}>&nbsp;</Link>) }
185
192
  </Flex>)
186
193
  }
187
194
  </Flex>
@@ -31,6 +31,7 @@ import { navigationItemAdditionalFields } from '../View/utils/enums';
31
31
  import ConfirmationDialog from '../../components/ConfirmationDialog';
32
32
  import RestartAlert from '../../components/RestartAlert';
33
33
  import { getMessage } from '../../utils';
34
+ import { isContentTypeEligible, resolveGlobalLikeId } from './utils/functions';
34
35
 
35
36
  const SettingsPage = () => {
36
37
  const { lockApp, unlockApp } = useOverlayBlocker();
@@ -56,13 +57,7 @@ const SettingsPage = () => {
56
57
  additionalFields: audienceFieldChecked ? [navigationItemAdditionalFields.AUDIENCE] : [],
57
58
  allowedLevels: allowedLevels,
58
59
  gql: {
59
- navigationItemRelated: selectedContentTypes.map(uid => {
60
- const singularName = allContentTypes.find(ct => ct.uid === uid).info.singularName;
61
- const globalIdLike = singularName.split('-')
62
- .map(_ => capitalize(_))
63
- .join('')
64
- return globalIdLike;
65
- })
60
+ navigationItemRelated: selectedContentTypes.map(uid => resolveGlobalLikeId(uid)),
66
61
  }
67
62
  });
68
63
 
@@ -114,7 +109,10 @@ const SettingsPage = () => {
114
109
  )
115
110
  }
116
111
 
117
- const allContentTypes = !isLoading && Object.values(allContentTypesData).filter(item => item.uid.includes('api::'));
112
+ const allContentTypes = !isLoading && Object.values(allContentTypesData).filter(({ uid }) => isContentTypeEligible(uid, {
113
+ allowedContentTypes: navigationConfigData?.allowedContentTypes,
114
+ restrictedContentTypes: navigationConfigData?.restrictedContentTypes,
115
+ }));
118
116
  const selectedContentTypes = navigationConfigData?.contentTypes.map(item => item.uid);
119
117
  const audienceFieldChecked = navigationConfigData?.additionalFields.includes(navigationItemAdditionalFields.AUDIENCE);
120
118
  const allowedLevels = navigationConfigData?.allowedLevels || 2;
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ const { capitalize } = require("lodash");
4
+
5
+ const UID_REGEX = /^(?<type>[a-z0-9-]+)\:{2}(?<api>[a-z0-9-]+)\.{1}(?<contentType>[a-z0-9-]+)$/i;
6
+
7
+ const splitTypeUid = (uid = '') => {
8
+ return uid.split(UID_REGEX).filter((s) => s && s.length > 0);
9
+ };
10
+
11
+ module.exports = {
12
+ resolveGlobalLikeId(uid = '') {
13
+ const parse = (str) => str.split('-')
14
+ .map(_ => capitalize(_))
15
+ .join('');
16
+
17
+ const [type, scope, contentTypeName] = splitTypeUid(uid);
18
+ if (type === 'api') {
19
+ return parse(contentTypeName);
20
+ }
21
+ return `${parse(scope)}${parse(contentTypeName)}`;
22
+ },
23
+
24
+ isContentTypeEligible(uid = '', config = {}) {
25
+ const { allowedContentTypes = [], restrictedContentTypes = []} = config;
26
+ const isOneOfAllowedType = allowedContentTypes.filter(_ => uid.includes(_) || (uid === _)).length > 0;
27
+ const isNoneOfRestricted = restrictedContentTypes.filter(_ => uid.includes(_) || (uid === _)).length === 0;
28
+ return uid && isOneOfAllowedType && isNoneOfRestricted;
29
+ },
30
+ }
@@ -74,9 +74,9 @@
74
74
  "pages.settings.notification.submit.error": "Config update has failed",
75
75
  "pages.settings.notification.restore.error": "Config restore has failed",
76
76
  "pages.settings.notification.restart.error": "Failed to restart your application. Try to do it manually.",
77
- "pages.settings.form.contentTypes.label": "Enable for Collection(s)",
77
+ "pages.settings.form.contentTypes.label": "Enable navigation for",
78
78
  "pages.settings.form.contentTypes.placeholder": "eg. Pages, Posts",
79
- "pages.settings.form.contentTypes.hint": "Content types that can be related with navigation items. This configuration is applicable both for REST & GraphQL",
79
+ "pages.settings.form.contentTypes.hint": "If none is selected, also none of the content types are enabled",
80
80
  "pages.settings.form.allowedLevels.label": "Allowed levels",
81
81
  "pages.settings.form.allowedLevels.placeholder": "eg. 2",
82
82
  "pages.settings.form.allowedLevels.hint": "Maximum level for which you're able to mark item as \"Menu attached\"",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-navigation",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
4
4
  "description": "Strapi - Navigation plugin",
5
5
  "strapi": {
6
6
  "name": "navigation",
@@ -25,12 +25,20 @@ module.exports = ({strapi}) => ({
25
25
  },
26
26
 
27
27
  async updateConfig(ctx) {
28
- await getService().updateConfig(ctx.request.body)
28
+ try {
29
+ await getService().updateConfig(ctx.request.body);
30
+ } catch (e) {
31
+ errorHandler(ctx)(e);
32
+ }
29
33
  return ctx.send({ status: 200 });
30
34
  },
31
35
 
32
36
  async restoreConfig(ctx) {
33
- await getService().restoreConfig()
37
+ try {
38
+ await getService().restoreConfig();
39
+ } catch (e) {
40
+ errorHandler(ctx)(e);
41
+ }
34
42
  return ctx.send({ status: 200 })
35
43
  },
36
44
 
@@ -43,7 +51,7 @@ module.exports = ({strapi}) => ({
43
51
  await getService().restart();
44
52
  return ctx.send({ status: 200 });
45
53
  } catch (e) {
46
- errorHandler(ctx, e);
54
+ errorHandler(ctx)(e);
47
55
  }
48
56
  },
49
57
 
@@ -11,16 +11,14 @@ const {
11
11
  toNumber,
12
12
  isString,
13
13
  first,
14
-
15
14
  } = require('lodash');
16
15
  const { validate: isUuid } = require('uuid');
17
16
  const slugify = require('slugify');
18
- const { KIND_TYPES } = require('./utils/constant');
17
+ const { KIND_TYPES, ALLOWED_CONTENT_TYPES, RESTRICTED_CONTENT_TYPES } = require('./utils/constant');
19
18
  const utilsFunctionsFactory = require('./utils/functions');
20
19
  const { renderType } = require('../content-types/navigation/lifecycle');
21
20
  const { type: itemType, additionalFields: configAdditionalFields } = require('../content-types/navigation-item').lifecycle;
22
21
  const { NotFoundError } = require('@strapi/utils').errors
23
- const excludedContentTypes = ['strapi::'];
24
22
  const contentTypesNameFieldsDefaults = ['title', 'subject', 'name'];
25
23
 
26
24
  module.exports = ({ strapi }) => {
@@ -33,7 +31,7 @@ module.exports = ({ strapi }) => {
33
31
  const entities = await strapi
34
32
  .query(masterModel.uid)
35
33
  .findMany({
36
- limit: 0
34
+ limit: Number.MAX_SAFE_INTEGER,
37
35
  });
38
36
  return entities;
39
37
  },
@@ -50,7 +48,7 @@ module.exports = ({ strapi }) => {
50
48
  where: {
51
49
  master: id,
52
50
  },
53
- limit: 0,
51
+ limit: Number.MAX_SAFE_INTEGER,
54
52
  sort: ['order:asc'],
55
53
  populate: ['related', 'parent', 'audience']
56
54
  });
@@ -67,8 +65,8 @@ module.exports = ({ strapi }) => {
67
65
 
68
66
  // Get plugin config
69
67
  async config(viaSettingsPage = false) {
70
- const { audienceModel, service } = utilsFunctions.extractMeta(strapi.plugins);
71
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
68
+ const { audienceModel } = utilsFunctions.extractMeta(strapi.plugins);
69
+ const pluginStore = await this.getPluginStore()
72
70
  const config = await pluginStore.get({ key: 'config' });
73
71
  const additionalFields = config.additionalFields;
74
72
  const contentTypesNameFields = config.contentTypesNameFields;
@@ -76,9 +74,12 @@ module.exports = ({ strapi }) => {
76
74
  const allowedLevels = config.allowedLevels;
77
75
  const isGQLPluginEnabled = !isNil(strapi.plugin('graphql'));
78
76
 
79
- let extendedResult = {};
77
+ let extendedResult = {
78
+ allowedContentTypes: ALLOWED_CONTENT_TYPES,
79
+ restrictedContentTypes: RESTRICTED_CONTENT_TYPES,
80
+ };
80
81
  const result = {
81
- contentTypes: await service.configContentTypes(),
82
+ contentTypes: await this.configContentTypes(),
82
83
  contentTypesNameFields: {
83
84
  default: contentTypesNameFieldsDefaults,
84
85
  ...(isObject(contentTypesNameFields) ? contentTypesNameFields : {}),
@@ -95,7 +96,7 @@ module.exports = ({ strapi }) => {
95
96
  const audienceItems = await strapi
96
97
  .query(audienceModel.uid)
97
98
  .findMany({
98
- limit: 0
99
+ limit: Number.MAX_SAFE_INTEGER,
99
100
  });
100
101
  extendedResult = {
101
102
  ...extendedResult,
@@ -109,7 +110,7 @@ module.exports = ({ strapi }) => {
109
110
  },
110
111
 
111
112
  async updateConfig(newConfig) {
112
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
113
+ const pluginStore = await this.getPluginStore()
113
114
  await pluginStore.set({ key: 'config', value: newConfig });
114
115
  },
115
116
 
@@ -118,7 +119,7 @@ module.exports = ({ strapi }) => {
118
119
  },
119
120
 
120
121
  async setDefaultConfig() {
121
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
122
+ const pluginStore = await this.getPluginStore()
122
123
  const config = await pluginStore.get({ key: 'config' });
123
124
  const pluginDefaultConfig = await strapi.plugin('navigation').config
124
125
 
@@ -138,23 +139,20 @@ module.exports = ({ strapi }) => {
138
139
  },
139
140
 
140
141
  async restoreConfig() {
141
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
142
+ const pluginStore = await this.getPluginStore()
142
143
  await pluginStore.delete({ key: 'config' });
143
144
  await strapi.plugin('navigation').service('navigation').setDefaultConfig();
144
145
  },
145
146
 
146
147
  async configContentTypes() {
147
- const pluginStore = await strapi.plugin('navigation').service('navigation').getPluginStore()
148
+ const pluginStore = await this.getPluginStore()
148
149
  const config = await pluginStore.get({ key: 'config' });
149
150
  const eligibleContentTypes =
150
151
  await Promise.all(
151
152
  config.contentTypes
152
- .filter(contentType => !!strapi.contentTypes[contentType])
153
+ .filter(contentType => !!strapi.contentTypes[contentType] && utilsFunctions.isContentTypeEligible(contentType))
153
154
  .map(
154
155
  async (key) => {
155
- if (find(excludedContentTypes, name => key.includes(name))) { // exclude internal content types
156
- return;
157
- }
158
156
  const item = strapi.contentTypes[key];
159
157
  const { kind, options, uid } = item;
160
158
  const { draftAndPublish } = options;
@@ -188,9 +186,10 @@ module.exports = ({ strapi }) => {
188
186
  .map(({ key, available }) => {
189
187
  const item = strapi.contentTypes[key];
190
188
  const relatedField = (item.associations || []).find(_ => _.model === 'navigationitem');
191
- const { uid, options, info, collectionName, modelName, apiName, plugin, kind } = item;
189
+ const { uid, options, info, collectionName, modelName, apiName, plugin, kind, pluginOptions } = item;
190
+ const { visible = true } = pluginOptions['content-manager'] || {};
192
191
  const { name, description } = info;
193
- const { isManaged, hidden, templateName } = options;
192
+ const { hidden, templateName } = options;
194
193
  const findRouteConfig = find(get(strapi.api, `[${modelName}].config.routes`, []),
195
194
  route => route.handler.includes('.find'));
196
195
  const findRoutePath = findRouteConfig && findRouteConfig.path.split('/')[1];
@@ -215,12 +214,12 @@ module.exports = ({ strapi }) => {
215
214
  labelSingular: utilsFunctions.singularize(labelSingular),
216
215
  endpoint,
217
216
  plugin,
218
- available,
219
- visible: (isManaged || isNil(isManaged)) && !hidden,
217
+ available: available && !hidden,
218
+ visible,
220
219
  templateName,
221
220
  };
222
221
  })
223
- .filter((item) => item && item.visible);
222
+ .filter((item) => item && item.available);
224
223
  },
225
224
 
226
225
  async getRelatedItems(entityItems) {
@@ -393,7 +392,7 @@ module.exports = ({ strapi }) => {
393
392
  master: entity.id,
394
393
  ...itemCriteria,
395
394
  },
396
- limit: 0,
395
+ limit: Number.MAX_SAFE_INTEGER,
397
396
  sort: ['order:asc'],
398
397
  populate: ['related', 'audience', 'parent'],
399
398
  });
@@ -8,5 +8,14 @@ module.exports = {
8
8
 
9
9
  MODEL_TYPES: {
10
10
  CONTENT_TYPE: 'contentType'
11
- }
11
+ },
12
+ ALLOWED_CONTENT_TYPES: [
13
+ 'api::',
14
+ 'plugin::'
15
+ ],
16
+ RESTRICTED_CONTENT_TYPES: [
17
+ 'plugin::users-permissions',
18
+ 'plugin::i18n.locale',
19
+ 'plugin::navigation',
20
+ ],
12
21
  };
@@ -13,7 +13,7 @@ const {
13
13
 
14
14
  const { type: itemType } = require('../../content-types/navigation-item/lifecycle');
15
15
  const { NavigationError } = require('../../../utils/NavigationError');
16
- const { TEMPLATE_DEFAULT } = require('./constant');
16
+ const { TEMPLATE_DEFAULT, ALLOWED_CONTENT_TYPES, RESTRICTED_CONTENT_TYPES } = require('./constant');
17
17
 
18
18
  module.exports = ({ strapi }) => {
19
19
  return {
@@ -216,5 +216,11 @@ module.exports = ({ strapi }) => {
216
216
  }
217
217
  return (item.type !== itemType.INTERNAL) || relatedItem;
218
218
  },
219
+
220
+ isContentTypeEligible(uid = '') {
221
+ const isOneOfAllowedType = ALLOWED_CONTENT_TYPES.filter(_ => uid.includes(_)).length > 0;
222
+ const isNoneOfRestricted = RESTRICTED_CONTENT_TYPES.filter(_ => uid.includes(_) || (uid === _)).length === 0;
223
+ return uid && isOneOfAllowedType && isNoneOfRestricted;
224
+ },
219
225
  };
220
226
  }