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.
- package/README.md +2 -1
- package/admin/src/components/CollapseButton/index.js +31 -0
- package/admin/src/components/ConfirmationDialog/index.js +1 -1
- package/admin/src/components/Item/ItemCardBadge/index.js +4 -4
- package/admin/src/components/Item/ItemCardHeader/Wrapper.js +0 -4
- package/admin/src/components/Item/ItemCardHeader/index.js +28 -6
- package/admin/src/components/Item/Wrapper.js +1 -1
- package/admin/src/components/Item/index.js +59 -44
- package/admin/src/components/NavigationItemList/Wrapper.js +1 -1
- package/admin/src/components/NavigationItemList/index.js +3 -0
- package/admin/src/pages/SettingsPage/index.js +42 -22
- package/admin/src/pages/View/components/NavigationHeader/index.js +1 -1
- package/admin/src/pages/View/components/NavigationItemForm/index.js +47 -30
- package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +3 -3
- package/admin/src/pages/View/index.js +73 -9
- package/admin/src/pages/View/utils/enums.js +1 -0
- package/admin/src/pages/View/utils/parsers.js +6 -3
- package/admin/src/translations/en.json +12 -3
- package/package.json +2 -3
- package/server/bootstrap.js +3 -22
- package/server/config/index.js +1 -0
- package/server/config.js +1 -0
- package/server/content-types/navigation-item/lifecycle.js +1 -0
- package/server/content-types/navigation-item/schema.json +7 -1
- package/server/graphql/types/content-types-name-fields.js +2 -2
- package/server/services/navigation.js +44 -19
- package/server/services/utils/functions.js +3 -2
- 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:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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 =
|
|
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}
|
|
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
|
-
{
|
|
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 {
|
|
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
|
-
<
|
|
11
|
+
<Typography variant="omega" fontWeight="bold" textColor="neutral800" as="h2" id="asset-dialog-title">
|
|
12
12
|
{getMessage(`popup.item.header.${isNewItem ? 'new' : 'edit'}`)}
|
|
13
|
-
</
|
|
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={
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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}
|
|
@@ -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
|
|
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": "
|
|
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": "
|
|
89
|
-
"components.navigationItem.badge.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.
|
|
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.
|
|
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",
|
package/server/bootstrap.js
CHANGED
|
@@ -42,30 +42,11 @@ module.exports = async ({ strapi }) => {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// Initialize configuration
|
|
45
|
-
const
|
|
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
|
|
50
|
+
await graphqlConfiguration({ strapi, config });
|
|
70
51
|
}
|
|
71
52
|
};
|
package/server/config/index.js
CHANGED
package/server/config.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
120
|
-
|
|
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.
|
|
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
|
|
217
|
+
return (item.type !== itemType.INTERNAL) || relatedItem;
|
|
217
218
|
},
|
|
218
219
|
};
|
|
219
220
|
}
|