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.
- package/admin/src/components/Item/index.js +10 -3
- package/admin/src/pages/SettingsPage/index.js +6 -8
- package/admin/src/pages/SettingsPage/utils/functions.js +30 -0
- package/admin/src/translations/en.json +2 -2
- package/package.json +1 -1
- package/server/controllers/navigation.js +11 -3
- package/server/services/navigation.js +23 -24
- package/server/services/utils/constant.js +10 -1
- package/server/services/utils/functions.js +7 -1
|
@@ -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} / </Typography>
|
|
181
188
|
<Typography variant="omega" textColor='neutral800'>{relatedItemLabel}</Typography>
|
|
182
|
-
|
|
183
|
-
to={
|
|
184
|
-
endIcon={<ArrowRight />}> </Link>
|
|
189
|
+
{ contentType?.visible && (<Link
|
|
190
|
+
to={generatePreviewUrl(relatedRef)}
|
|
191
|
+
endIcon={<ArrowRight />}> </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(
|
|
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
|
|
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": "
|
|
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
|
@@ -25,12 +25,20 @@ module.exports = ({strapi}) => ({
|
|
|
25
25
|
},
|
|
26
26
|
|
|
27
27
|
async updateConfig(ctx) {
|
|
28
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
71
|
-
const pluginStore = await
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
217
|
+
available: available && !hidden,
|
|
218
|
+
visible,
|
|
220
219
|
templateName,
|
|
221
220
|
};
|
|
222
221
|
})
|
|
223
|
-
.filter((item) => item && item.
|
|
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:
|
|
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
|
}
|