strapi-plugin-navigation 2.0.9 → 2.0.12
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/Alert/styles.js +8 -0
- package/admin/src/components/Item/ItemCardHeader/index.js +1 -1
- package/admin/src/components/Item/index.js +10 -3
- package/admin/src/pages/SettingsPage/index.js +28 -6
- package/admin/src/pages/SettingsPage/utils/functions.js +30 -0
- package/admin/src/pages/View/components/NavigationItemForm/index.js +11 -6
- package/admin/src/pages/View/utils/parsers.js +5 -2
- package/admin/src/translations/en.json +4 -2
- package/package.json +1 -1
- package/server/controllers/navigation.js +11 -3
- package/server/graphql/types/navigation-related.js +3 -1
- package/server/services/navigation.js +34 -32
- package/server/services/utils/constant.js +10 -1
- package/server/services/utils/functions.js +7 -1
|
@@ -18,7 +18,7 @@ justify-content: center;
|
|
|
18
18
|
height: ${32 / 16}rem;
|
|
19
19
|
width: ${32 / 16}rem;
|
|
20
20
|
|
|
21
|
-
cursor:
|
|
21
|
+
cursor: move;
|
|
22
22
|
padding: ${({ theme }) => theme.spaces[2]};
|
|
23
23
|
border-radius: ${({ theme }) => theme.borderRadius};
|
|
24
24
|
background: ${({ theme }) => theme.colors.neutral0};
|
|
@@ -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>
|
|
@@ -22,7 +22,7 @@ import { ToggleInput } from '@strapi/design-system/ToggleInput';
|
|
|
22
22
|
import { NumberInput } from '@strapi/design-system/NumberInput';
|
|
23
23
|
import { Select, Option } from '@strapi/design-system/Select';
|
|
24
24
|
import { Tooltip } from '@strapi/design-system/Tooltip';
|
|
25
|
-
import { Check, Refresh, Play, Information } from '@strapi/icons';
|
|
25
|
+
import { Check, Refresh, Play, Information, ExclamationMarkCircle } from '@strapi/icons';
|
|
26
26
|
|
|
27
27
|
import permissions from '../../permissions';
|
|
28
28
|
import useNavigationConfig from '../../hooks/useNavigationConfig';
|
|
@@ -31,6 +31,8 @@ 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';
|
|
35
|
+
import { PermanentAlert } from '../../components/Alert/styles';
|
|
34
36
|
|
|
35
37
|
const SettingsPage = () => {
|
|
36
38
|
const { lockApp, unlockApp } = useOverlayBlocker();
|
|
@@ -56,7 +58,7 @@ const SettingsPage = () => {
|
|
|
56
58
|
additionalFields: audienceFieldChecked ? [navigationItemAdditionalFields.AUDIENCE] : [],
|
|
57
59
|
allowedLevels: allowedLevels,
|
|
58
60
|
gql: {
|
|
59
|
-
navigationItemRelated: selectedContentTypes.map(uid =>
|
|
61
|
+
navigationItemRelated: selectedContentTypes.map(uid => resolveGlobalLikeId(uid)),
|
|
60
62
|
}
|
|
61
63
|
});
|
|
62
64
|
|
|
@@ -108,8 +110,24 @@ const SettingsPage = () => {
|
|
|
108
110
|
)
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
+
const configContentTypes = navigationConfigData?.contentTypes || [];
|
|
114
|
+
|
|
115
|
+
const allContentTypes = !isLoading && Object.values(allContentTypesData).filter(({ uid }) => isContentTypeEligible(uid, {
|
|
116
|
+
allowedContentTypes: navigationConfigData?.allowedContentTypes,
|
|
117
|
+
restrictedContentTypes: navigationConfigData?.restrictedContentTypes,
|
|
118
|
+
})).map(ct => {
|
|
119
|
+
const type = configContentTypes.find(_ => _.uid === ct.uid);
|
|
120
|
+
if (type) {
|
|
121
|
+
const { available, isSingle } = type;
|
|
122
|
+
return {
|
|
123
|
+
...ct,
|
|
124
|
+
available,
|
|
125
|
+
isSingle,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return ct;
|
|
129
|
+
});
|
|
130
|
+
const selectedContentTypes = configContentTypes.map(item => item.uid);
|
|
113
131
|
const audienceFieldChecked = navigationConfigData?.additionalFields.includes(navigationItemAdditionalFields.AUDIENCE);
|
|
114
132
|
const allowedLevels = navigationConfigData?.allowedLevels || 2;
|
|
115
133
|
const nameFields = navigationConfigData?.contentTypesNameFields || {}
|
|
@@ -184,7 +202,7 @@ const SettingsPage = () => {
|
|
|
184
202
|
<Information aria-hidden={true} />
|
|
185
203
|
</Tooltip>}>
|
|
186
204
|
{orderBy(values.selectedContentTypes).map(uid => {
|
|
187
|
-
const { attributes, info: { displayName } } = allContentTypes.find(item => item.uid == uid);
|
|
205
|
+
const { attributes, info: { displayName }, available, isSingle } = allContentTypes.find(item => item.uid == uid);
|
|
188
206
|
const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
|
|
189
207
|
const relationAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'relation');
|
|
190
208
|
const key = `collectionSettings-${uid}`;
|
|
@@ -194,10 +212,14 @@ const SettingsPage = () => {
|
|
|
194
212
|
key={key}
|
|
195
213
|
id={key}
|
|
196
214
|
size="S">
|
|
197
|
-
<AccordionToggle title={displayName} togglePosition="left" />
|
|
215
|
+
<AccordionToggle title={displayName} togglePosition="left" startIcon={(isSingle && !available) && (<ExclamationMarkCircle aria-hidden={true} />)} />
|
|
198
216
|
<AccordionContent>
|
|
199
217
|
<Box padding={6}>
|
|
200
218
|
<Stack spacing={4}>
|
|
219
|
+
{ (isSingle && !available) && (
|
|
220
|
+
<PermanentAlert title={getMessage('pages.settings.form.contentTypesSettings.initializationWarning.title')} variant="danger" onClose={(e) => e.preventDefault()}>
|
|
221
|
+
{ getMessage('pages.settings.form.contentTypesSettings.initializationWarning.content') }
|
|
222
|
+
</PermanentAlert>)}
|
|
201
223
|
<Select
|
|
202
224
|
name={`collectionSettings-${uid}-entryLabel`}
|
|
203
225
|
label={getMessage('pages.settings.form.nameField.label')}
|
|
@@ -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
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState, useCallback } from 'react';
|
|
2
|
-
import { debounce, find, get, isEmpty, isEqual, isNil, isString } from 'lodash';
|
|
2
|
+
import { debounce, find, get, first, isEmpty, isEqual, isNil, isString } from 'lodash';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import { Formik } from 'formik'
|
|
5
5
|
import slugify from 'slugify';
|
|
@@ -75,11 +75,11 @@ const NavigationItemForm = ({
|
|
|
75
75
|
|
|
76
76
|
const sanitizePayload = (payload = {}) => {
|
|
77
77
|
const { onItemClick, onItemLevelAddClick, related, relatedType, menuAttached, type, ...purePayload } = payload;
|
|
78
|
-
const relatedId = related
|
|
78
|
+
const relatedId = related;
|
|
79
|
+
const singleRelatedItem = isSingleSelected ? first(contentTypeEntities) : undefined;
|
|
79
80
|
const relatedCollectionType = relatedType;
|
|
80
|
-
const title =
|
|
81
|
-
|
|
82
|
-
payload.title || relatedSelectOptions.find(v => v.key == relatedId)?.label;
|
|
81
|
+
const title = payload.title;
|
|
82
|
+
|
|
83
83
|
return {
|
|
84
84
|
...purePayload,
|
|
85
85
|
title,
|
|
@@ -90,6 +90,7 @@ const NavigationItemForm = ({
|
|
|
90
90
|
related: type === navigationItemType.INTERNAL ? relatedId : undefined,
|
|
91
91
|
relatedType: type === navigationItemType.INTERNAL ? relatedCollectionType : undefined,
|
|
92
92
|
isSingle: isSingleSelected,
|
|
93
|
+
singleRelatedItem,
|
|
93
94
|
uiRouterKey: generateUiRouterKey(title, relatedId, relatedCollectionType),
|
|
94
95
|
};
|
|
95
96
|
};
|
|
@@ -136,6 +137,7 @@ const NavigationItemForm = ({
|
|
|
136
137
|
return undefined;
|
|
137
138
|
};
|
|
138
139
|
|
|
140
|
+
const initialRelatedTypeSelected = data?.relatedType?.value;
|
|
139
141
|
const relatedTypeSelectValue = form.relatedType;
|
|
140
142
|
const relatedSelectValue = form.related;
|
|
141
143
|
|
|
@@ -221,6 +223,9 @@ const NavigationItemForm = ({
|
|
|
221
223
|
() => contentTypes
|
|
222
224
|
.filter((contentType) => {
|
|
223
225
|
if (contentType.isSingle) {
|
|
226
|
+
if (relatedTypeSelectValue && [relatedTypeSelectValue, initialRelatedTypeSelected].includes(contentType.uid)) {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
224
229
|
return !usedContentTypesData.some((_) => _.__collectionUid === contentType.uid && _.__collectionUid !== form.relatedType);
|
|
225
230
|
}
|
|
226
231
|
return true;
|
|
@@ -236,7 +241,7 @@ const NavigationItemForm = ({
|
|
|
236
241
|
value: get(item, 'uid'),
|
|
237
242
|
label: get(item, 'label', get(item, 'name')),
|
|
238
243
|
})),
|
|
239
|
-
[contentTypes, usedContentTypesData],
|
|
244
|
+
[contentTypes, usedContentTypesData, relatedTypeSelectValue],
|
|
240
245
|
);
|
|
241
246
|
|
|
242
247
|
const thereAreNoMoreContentTypes = isEmpty(relatedSelectOptions) && !contentTypeSearchQuery;
|
|
@@ -25,6 +25,7 @@ export const transformItemToRESTPayload = (
|
|
|
25
25
|
audience = [],
|
|
26
26
|
items = [],
|
|
27
27
|
collapsed,
|
|
28
|
+
isSingle
|
|
28
29
|
} = item;
|
|
29
30
|
const isExternal = type === navigationItemType.EXTERNAL;
|
|
30
31
|
const isWrapper = type === navigationItemType.WRAPPER;
|
|
@@ -37,7 +38,7 @@ export const transformItemToRESTPayload = (
|
|
|
37
38
|
find(contentTypes,
|
|
38
39
|
ct => ct.uid === relatedType) :
|
|
39
40
|
undefined;
|
|
40
|
-
const itemAttachedToMenu = menuAttached && parentAttachedToMenu
|
|
41
|
+
const itemAttachedToMenu = menuAttached && parentAttachedToMenu;
|
|
41
42
|
return {
|
|
42
43
|
id,
|
|
43
44
|
parent,
|
|
@@ -59,7 +60,7 @@ export const transformItemToRESTPayload = (
|
|
|
59
60
|
? undefined
|
|
60
61
|
: [
|
|
61
62
|
{
|
|
62
|
-
refId: relatedId,
|
|
63
|
+
refId: isSingle && !relatedId ? 1 : relatedId,
|
|
63
64
|
ref: relatedContentType ? relatedContentType.uid : relatedType,
|
|
64
65
|
field: relatedContentType && relatedContentType.relatedField ? relatedContentType.relatedField : 'navigation',
|
|
65
66
|
},
|
|
@@ -89,10 +90,12 @@ const linkRelations = (item, config) => {
|
|
|
89
90
|
|
|
90
91
|
if (isSingle && relatedType) {
|
|
91
92
|
const relatedContentType = contentTypes.find(_ => relatedType === _.uid) || {};
|
|
93
|
+
const { singleRelatedItem = {} } = item;
|
|
92
94
|
return {
|
|
93
95
|
...item,
|
|
94
96
|
relatedType,
|
|
95
97
|
relatedRef: {
|
|
98
|
+
...singleRelatedItem,
|
|
96
99
|
...omit(relatedContentType, 'collectionName'),
|
|
97
100
|
isSingle,
|
|
98
101
|
__collectionUid: relatedContentType.uid,
|
|
@@ -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\"",
|
|
@@ -92,6 +92,8 @@
|
|
|
92
92
|
"pages.settings.form.populate.empty": "This content type doesn't have any relation fields",
|
|
93
93
|
"pages.settings.form.contentTypesSettings.label": "Content types",
|
|
94
94
|
"pages.settings.form.contentTypesSettings.tooltip": "Custom configuration per content type",
|
|
95
|
+
"pages.settings.form.contentTypesSettings.initializationWarning.title": "Warning",
|
|
96
|
+
"pages.settings.form.contentTypesSettings.initializationWarning.content": "- Content Type hasn't yet been initialized. Initialize it first to be able to use in a Visual Editor.",
|
|
95
97
|
"components.navigationItem.action.newItem": "Add nested item",
|
|
96
98
|
"components.navigationItem.badge.removed": "Removed",
|
|
97
99
|
"components.navigationItem.badge.draft": "Draft",
|
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
|
|
|
@@ -8,7 +8,9 @@ module.exports = ({ strapi, nexus, config }) => {
|
|
|
8
8
|
definition(t) {
|
|
9
9
|
t.members(...related)
|
|
10
10
|
},
|
|
11
|
-
resolveType: (item) =>
|
|
11
|
+
resolveType: (item) => {
|
|
12
|
+
return strapi.contentTypes[item.__contentType]?.globalId
|
|
13
|
+
}
|
|
12
14
|
});
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -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(viaSettingsPage),
|
|
82
83
|
contentTypesNameFields: {
|
|
83
84
|
default: contentTypesNameFieldsDefaults,
|
|
84
85
|
...(isObject(contentTypesNameFields) ? contentTypesNameFields : {}),
|
|
@@ -95,9 +96,7 @@ module.exports = ({ strapi }) => {
|
|
|
95
96
|
const audienceItems = await strapi
|
|
96
97
|
.query(audienceModel.uid)
|
|
97
98
|
.findMany({
|
|
98
|
-
|
|
99
|
-
limit: -1,
|
|
100
|
-
}
|
|
99
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
101
100
|
});
|
|
102
101
|
extendedResult = {
|
|
103
102
|
...extendedResult,
|
|
@@ -111,7 +110,7 @@ module.exports = ({ strapi }) => {
|
|
|
111
110
|
},
|
|
112
111
|
|
|
113
112
|
async updateConfig(newConfig) {
|
|
114
|
-
const pluginStore = await
|
|
113
|
+
const pluginStore = await this.getPluginStore()
|
|
115
114
|
await pluginStore.set({ key: 'config', value: newConfig });
|
|
116
115
|
},
|
|
117
116
|
|
|
@@ -120,7 +119,7 @@ module.exports = ({ strapi }) => {
|
|
|
120
119
|
},
|
|
121
120
|
|
|
122
121
|
async setDefaultConfig() {
|
|
123
|
-
const pluginStore = await
|
|
122
|
+
const pluginStore = await this.getPluginStore()
|
|
124
123
|
const config = await pluginStore.get({ key: 'config' });
|
|
125
124
|
const pluginDefaultConfig = await strapi.plugin('navigation').config
|
|
126
125
|
|
|
@@ -140,24 +139,22 @@ module.exports = ({ strapi }) => {
|
|
|
140
139
|
},
|
|
141
140
|
|
|
142
141
|
async restoreConfig() {
|
|
143
|
-
const pluginStore = await
|
|
142
|
+
const pluginStore = await this.getPluginStore()
|
|
144
143
|
await pluginStore.delete({ key: 'config' });
|
|
145
144
|
await strapi.plugin('navigation').service('navigation').setDefaultConfig();
|
|
146
145
|
},
|
|
147
146
|
|
|
148
|
-
async configContentTypes() {
|
|
149
|
-
const pluginStore = await
|
|
147
|
+
async configContentTypes(viaSettingsPage = false) {
|
|
148
|
+
const pluginStore = await this.getPluginStore()
|
|
150
149
|
const config = await pluginStore.get({ key: 'config' });
|
|
151
150
|
const eligibleContentTypes =
|
|
152
151
|
await Promise.all(
|
|
153
152
|
config.contentTypes
|
|
154
|
-
.filter(contentType => !!strapi.contentTypes[contentType])
|
|
153
|
+
.filter(contentType => !!strapi.contentTypes[contentType] && utilsFunctions.isContentTypeEligible(contentType))
|
|
155
154
|
.map(
|
|
156
155
|
async (key) => {
|
|
157
|
-
if (find(excludedContentTypes, name => key.includes(name))) { // exclude internal content types
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
156
|
const item = strapi.contentTypes[key];
|
|
157
|
+
|
|
161
158
|
const { kind, options, uid } = item;
|
|
162
159
|
const { draftAndPublish } = options;
|
|
163
160
|
|
|
@@ -179,7 +176,9 @@ module.exports = ({ strapi }) => {
|
|
|
179
176
|
return returnType(itemsCountOrBypass !== 0);
|
|
180
177
|
}
|
|
181
178
|
const isAvailable = await strapi.query(uid).count();
|
|
182
|
-
return isAvailable === 1 ?
|
|
179
|
+
return isAvailable === 1 ?
|
|
180
|
+
returnType(true) :
|
|
181
|
+
(viaSettingsPage ? returnType(false) : undefined);
|
|
183
182
|
}
|
|
184
183
|
return returnType(true);
|
|
185
184
|
},
|
|
@@ -190,9 +189,10 @@ module.exports = ({ strapi }) => {
|
|
|
190
189
|
.map(({ key, available }) => {
|
|
191
190
|
const item = strapi.contentTypes[key];
|
|
192
191
|
const relatedField = (item.associations || []).find(_ => _.model === 'navigationitem');
|
|
193
|
-
const { uid, options, info, collectionName, modelName, apiName, plugin, kind } = item;
|
|
192
|
+
const { uid, options, info, collectionName, modelName, apiName, plugin, kind, pluginOptions } = item;
|
|
193
|
+
const { visible = true } = pluginOptions['content-manager'] || {};
|
|
194
194
|
const { name, description } = info;
|
|
195
|
-
const {
|
|
195
|
+
const { hidden, templateName } = options;
|
|
196
196
|
const findRouteConfig = find(get(strapi.api, `[${modelName}].config.routes`, []),
|
|
197
197
|
route => route.handler.includes('.find'));
|
|
198
198
|
const findRoutePath = findRouteConfig && findRouteConfig.path.split('/')[1];
|
|
@@ -217,12 +217,12 @@ module.exports = ({ strapi }) => {
|
|
|
217
217
|
labelSingular: utilsFunctions.singularize(labelSingular),
|
|
218
218
|
endpoint,
|
|
219
219
|
plugin,
|
|
220
|
-
available,
|
|
221
|
-
visible
|
|
220
|
+
available: available && !hidden,
|
|
221
|
+
visible,
|
|
222
222
|
templateName,
|
|
223
223
|
};
|
|
224
224
|
})
|
|
225
|
-
.filter((item) => item && item.
|
|
225
|
+
.filter((item) => viaSettingsPage || (item && item.available));
|
|
226
226
|
},
|
|
227
227
|
|
|
228
228
|
async getRelatedItems(entityItems) {
|
|
@@ -395,9 +395,7 @@ module.exports = ({ strapi }) => {
|
|
|
395
395
|
master: entity.id,
|
|
396
396
|
...itemCriteria,
|
|
397
397
|
},
|
|
398
|
-
|
|
399
|
-
limit: -1,
|
|
400
|
-
},
|
|
398
|
+
limit: Number.MAX_SAFE_INTEGER,
|
|
401
399
|
sort: ['order:asc'],
|
|
402
400
|
populate: ['related', 'audience', 'parent'],
|
|
403
401
|
});
|
|
@@ -424,6 +422,7 @@ module.exports = ({ strapi }) => {
|
|
|
424
422
|
id: item.id,
|
|
425
423
|
title: utilsFunctions.composeItemTitle(item, contentTypesNameFields, contentTypes),
|
|
426
424
|
menuAttached: item.menuAttached,
|
|
425
|
+
order: item.order,
|
|
427
426
|
path: isExternal ? item.externalPath : parentPath,
|
|
428
427
|
type: item.type,
|
|
429
428
|
uiRouterKey: item.uiRouterKey,
|
|
@@ -507,7 +506,10 @@ module.exports = ({ strapi }) => {
|
|
|
507
506
|
.filter(utilsFunctions.filterOutUnpublished)
|
|
508
507
|
.map(item => itemParser({
|
|
509
508
|
...item,
|
|
510
|
-
}, path, field))
|
|
509
|
+
}, path, field))
|
|
510
|
+
.sort((x, y) => {
|
|
511
|
+
return x.order - y.order;
|
|
512
|
+
});
|
|
511
513
|
},
|
|
512
514
|
|
|
513
515
|
renderRFR({ items, parent = null, parentNavItem = null, contentTypes = [] }) {
|
|
@@ -768,7 +770,7 @@ module.exports = ({ strapi }) => {
|
|
|
768
770
|
related_id: relatedItem.refId,
|
|
769
771
|
related_type: relatedItem.ref,
|
|
770
772
|
};
|
|
771
|
-
return model.delete({ where: entityToRemove })
|
|
773
|
+
return model.delete({ where: entityToRemove });
|
|
772
774
|
}));
|
|
773
775
|
},
|
|
774
776
|
|
|
@@ -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
|
}
|