strapi-plugin-navigation 2.0.0-rc.1 → 2.0.3
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 +55 -8
- package/__mocks__/pages.settings.json +25 -0
- package/__mocks__/strapi.js +207 -0
- package/admin/src/components/ConfirmationDialog/index.js +56 -0
- package/admin/src/components/Item/ItemCardBadge/index.js +2 -1
- package/admin/src/components/Item/ItemCardHeader/index.js +8 -13
- package/admin/src/components/Item/index.js +10 -13
- package/admin/src/components/RestartAlert/index.js +8 -0
- package/admin/src/components/Search/index.js +21 -23
- package/admin/src/hooks/useAllContentTypes.js +13 -0
- package/admin/src/hooks/useNavigationConfig.js +58 -0
- package/admin/src/index.js +24 -1
- package/admin/src/pages/SettingsPage/index.js +311 -0
- package/admin/src/pages/View/components/NavigationHeader/index.js +39 -23
- package/admin/src/pages/View/components/NavigationItemForm/index.js +49 -9
- package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupFooter.js +3 -6
- package/admin/src/pages/View/components/NavigationItemPopup/NavigationItemPopupHeader.js +3 -7
- package/admin/src/pages/View/components/NavigationItemPopup/index.js +3 -5
- package/admin/src/pages/View/index.js +29 -20
- package/admin/src/pages/View/utils/parsers.js +7 -3
- package/admin/src/translations/en.json +52 -10
- package/admin/src/translations/fr.json +4 -4
- package/admin/src/utils/api.js +51 -0
- package/admin/src/utils/index.js +20 -0
- package/package.json +7 -6
- package/server/bootstrap.js +30 -1
- package/server/content-types/navigation/schema.json +45 -0
- package/server/content-types/navigation-item/schema.json +1 -1
- package/server/controllers/navigation.js +30 -5
- package/server/graphql/index.js +3 -4
- package/server/graphql/queries/render-navigation.js +4 -3
- package/server/graphql/types/content-types-name-fields.js +4 -2
- package/server/graphql/types/navigation-related.js +2 -2
- package/server/routes/admin.js +24 -1
- package/server/services/__tests__/functions.test.js +48 -0
- package/server/services/__tests__/navigation.test.js +84 -77
- package/server/services/navigation.js +58 -18
- package/server/services/utils/functions.js +45 -12
- package/strapi-server.js +0 -2
- package/yarn-error.log +5263 -0
- package/.circleci/config.yml +0 -48
- package/.eslintrc +0 -35
- package/.github/pull_request_template.md +0 -13
- package/.github/stale.yml +0 -15
- package/.nvmrc +0 -1
- package/codecov.yml +0 -3
- package/public/assets/logo.png +0 -0
- package/public/assets/preview.png +0 -0
- package/server/content-types/navigation/schema.js +0 -45
- package/server/register.js +0 -5
|
@@ -3,17 +3,13 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { ButtonText } from '@strapi/design-system/Text';
|
|
5
5
|
import { ModalHeader } from '@strapi/design-system/ModalLayout';
|
|
6
|
-
import {
|
|
7
|
-
import { getTrad } from '../../../../translations';
|
|
6
|
+
import { getMessage } from '../../../../utils';
|
|
8
7
|
|
|
9
|
-
export const NavigationItemPopupHeader = () => {
|
|
10
|
-
const { formatMessage } = useIntl();
|
|
8
|
+
export const NavigationItemPopupHeader = ({isNewItem}) => {
|
|
11
9
|
return (
|
|
12
10
|
<ModalHeader>
|
|
13
11
|
<ButtonText textColor="neutral800" as="h2" id="asset-dialog-title">
|
|
14
|
-
{
|
|
15
|
-
getTrad('popup.item.header'),
|
|
16
|
-
)}
|
|
12
|
+
{getMessage(`popup.item.header.${isNewItem ? 'new' : 'edit'}`)}
|
|
17
13
|
</ButtonText>
|
|
18
14
|
</ModalHeader>
|
|
19
15
|
);
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import React from 'react';
|
|
8
|
-
import { useIntl } from 'react-intl';
|
|
9
8
|
import PropTypes from 'prop-types';
|
|
10
9
|
import { find } from 'lodash';
|
|
11
10
|
|
|
@@ -15,8 +14,8 @@ import { ModalLayout } from '@strapi/design-system/ModalLayout';
|
|
|
15
14
|
import NavigationItemForm from '../NavigationItemForm';
|
|
16
15
|
import { extractRelatedItemLabel, isRelationCorrect, isRelationPublished } from '../../utils/parsers';
|
|
17
16
|
import { navigationItemType } from '../../utils/enums';
|
|
18
|
-
import { getTrad } from '../../../../translations';
|
|
19
17
|
import { NavigationItemPopupHeader } from './NavigationItemPopupHeader';
|
|
18
|
+
import { getMessage } from '../../../../utils';
|
|
20
19
|
|
|
21
20
|
const NavigationItemPopUp = ({
|
|
22
21
|
isOpen,
|
|
@@ -30,7 +29,6 @@ const NavigationItemPopUp = ({
|
|
|
30
29
|
usedContentTypesData,
|
|
31
30
|
}) => {
|
|
32
31
|
|
|
33
|
-
const { formatMessage } = useIntl();
|
|
34
32
|
|
|
35
33
|
const handleOnSubmit = (payload) => {
|
|
36
34
|
onSubmit(payload);
|
|
@@ -51,7 +49,7 @@ const NavigationItemPopUp = ({
|
|
|
51
49
|
relatedRef: item,
|
|
52
50
|
type: item.isSingle ? navigationItemType.INTERNAL : item.type,
|
|
53
51
|
isCollection,
|
|
54
|
-
}) ? '' : `[${
|
|
52
|
+
}) ? '' : `[${getMessage('notification.navigation.item.relation.status.draft')}] `.toUpperCase();
|
|
55
53
|
return `${appendix}${label}`;
|
|
56
54
|
};
|
|
57
55
|
|
|
@@ -80,7 +78,7 @@ const NavigationItemPopUp = ({
|
|
|
80
78
|
|
|
81
79
|
return (
|
|
82
80
|
<ModalLayout labelledBy="condition-modal-breadcrumbs" onClose={onClose} isOpen={isOpen}>
|
|
83
|
-
<NavigationItemPopupHeader />
|
|
81
|
+
<NavigationItemPopupHeader isNewItem={!data.viewId}/>
|
|
84
82
|
<NavigationItemForm
|
|
85
83
|
data={prepareFormData(data)}
|
|
86
84
|
isLoading={isLoading}
|
|
@@ -10,11 +10,13 @@ import { isEmpty, get } from "lodash";
|
|
|
10
10
|
|
|
11
11
|
// Design System
|
|
12
12
|
import { Main } from '@strapi/design-system/Main';
|
|
13
|
+
import { Flex } from '@strapi/design-system/Flex';
|
|
13
14
|
import { ContentLayout } from '@strapi/design-system/Layout';
|
|
15
|
+
import { Typography } from '@strapi/design-system/Typography';
|
|
14
16
|
import { Box } from '@strapi/design-system/Box';
|
|
17
|
+
import { Icon } from '@strapi/design-system/Icon';
|
|
15
18
|
import { Button } from '@strapi/design-system/Button';
|
|
16
19
|
import { LoadingIndicatorPage } from "@strapi/helper-plugin";
|
|
17
|
-
import { EmptyStateLayout } from '@strapi/design-system/EmptyStateLayout';
|
|
18
20
|
import EmptyDocumentsIcon from '@strapi/icons/EmptyDocuments';
|
|
19
21
|
import PlusIcon from "@strapi/icons/Plus";
|
|
20
22
|
|
|
@@ -23,6 +25,7 @@ import List from '../../components/NavigationItemList';
|
|
|
23
25
|
import NavigationContentHeader from './components/NavigationContentHeader';
|
|
24
26
|
import NavigationHeader from './components/NavigationHeader';
|
|
25
27
|
import NavigationItemPopUp from "./components/NavigationItemPopup";
|
|
28
|
+
import Search from '../../components/Search';
|
|
26
29
|
import useDataManager from "../../hooks/useDataManager";
|
|
27
30
|
import { getTrad } from '../../translations';
|
|
28
31
|
import {
|
|
@@ -31,7 +34,6 @@ import {
|
|
|
31
34
|
usedContentTypes,
|
|
32
35
|
validateNavigationStructure,
|
|
33
36
|
} from './utils/parsers';
|
|
34
|
-
import Search from '../../components/Search';
|
|
35
37
|
|
|
36
38
|
const View = () => {
|
|
37
39
|
const {
|
|
@@ -150,11 +152,19 @@ const View = () => {
|
|
|
150
152
|
changeNavigationItemPopupState(false);
|
|
151
153
|
};
|
|
152
154
|
|
|
155
|
+
const handleChangeNavigationSelection = (...args) => {
|
|
156
|
+
handleChangeSelection(...args);
|
|
157
|
+
setSearchValue('');
|
|
158
|
+
}
|
|
159
|
+
|
|
153
160
|
return (
|
|
154
161
|
<Main labelledBy="title" aria-busy={isLoadingForSubmit}>
|
|
155
162
|
<NavigationHeader
|
|
156
163
|
structureHasErrors={structureHasErrors}
|
|
157
|
-
|
|
164
|
+
structureHasChanged={structureChanged}
|
|
165
|
+
availableNavigations={availableNavigations}
|
|
166
|
+
activeNavigation={activeNavigation}
|
|
167
|
+
handleChangeSelection={handleChangeNavigationSelection}
|
|
158
168
|
handleSave={handleSave}
|
|
159
169
|
/>
|
|
160
170
|
<ContentLayout>
|
|
@@ -162,7 +172,7 @@ const View = () => {
|
|
|
162
172
|
{changedActiveNavigation && (
|
|
163
173
|
<>
|
|
164
174
|
<NavigationContentHeader
|
|
165
|
-
startActions={<Search value={searchValue} setValue={setSearchValue}/>}
|
|
175
|
+
startActions={<Search value={searchValue} setValue={setSearchValue} />}
|
|
166
176
|
endActions={<Button
|
|
167
177
|
onClick={addNewNavigationItem}
|
|
168
178
|
startIcon={<PlusIcon />}
|
|
@@ -172,22 +182,21 @@ const View = () => {
|
|
|
172
182
|
{formatMessage(getTrad('header.action.newItem'))}
|
|
173
183
|
</Button>}
|
|
174
184
|
/>
|
|
175
|
-
{isEmpty(changedActiveNavigation.items || []) && (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
</Box>
|
|
185
|
+
{isEmpty(changedActiveNavigation.items || []) && (
|
|
186
|
+
<Flex direction="column" minHeight="400px" justifyContent="center">
|
|
187
|
+
<Icon as={EmptyDocumentsIcon} width="160px" height="88px" color=""/>
|
|
188
|
+
<Box padding={4}>
|
|
189
|
+
<Typography variant="beta" textColor="neutral600">{formatMessage(getTrad('empty'))}</Typography>
|
|
190
|
+
</Box>
|
|
191
|
+
<Button
|
|
192
|
+
variant='secondary'
|
|
193
|
+
startIcon={<PlusIcon />}
|
|
194
|
+
label={formatMessage(getTrad('empty.cta'))}
|
|
195
|
+
onClick={addNewNavigationItem}
|
|
196
|
+
>
|
|
197
|
+
{formatMessage(getTrad('empty.cta'))}
|
|
198
|
+
</Button>
|
|
199
|
+
</Flex>
|
|
191
200
|
)}
|
|
192
201
|
{
|
|
193
202
|
!isEmpty(changedActiveNavigation.items || [])
|
|
@@ -7,6 +7,7 @@ export const transformItemToRESTPayload = (
|
|
|
7
7
|
parent = undefined,
|
|
8
8
|
master = undefined,
|
|
9
9
|
config = {},
|
|
10
|
+
parentAttachedToMenu = true,
|
|
10
11
|
) => {
|
|
11
12
|
const {
|
|
12
13
|
id,
|
|
@@ -34,7 +35,7 @@ export const transformItemToRESTPayload = (
|
|
|
34
35
|
find(contentTypes,
|
|
35
36
|
ct => ct.uid === relatedType) :
|
|
36
37
|
undefined;
|
|
37
|
-
|
|
38
|
+
const itemAttachedToMenu = menuAttached && parentAttachedToMenu
|
|
38
39
|
return {
|
|
39
40
|
id,
|
|
40
41
|
parent,
|
|
@@ -45,7 +46,7 @@ export const transformItemToRESTPayload = (
|
|
|
45
46
|
removed,
|
|
46
47
|
order,
|
|
47
48
|
uiRouterKey,
|
|
48
|
-
menuAttached,
|
|
49
|
+
menuAttached: itemAttachedToMenu,
|
|
49
50
|
audience: audience.map((audienceItem) =>
|
|
50
51
|
isObject(audienceItem) ? audienceItem.value : audienceItem,
|
|
51
52
|
),
|
|
@@ -60,7 +61,7 @@ export const transformItemToRESTPayload = (
|
|
|
60
61
|
field: relatedContentType && relatedContentType.relatedField ? relatedContentType.relatedField : 'navigation',
|
|
61
62
|
},
|
|
62
63
|
],
|
|
63
|
-
items: items.map((iItem) => transformItemToRESTPayload(iItem, id, master, config)),
|
|
64
|
+
items: items.map((iItem) => transformItemToRESTPayload(iItem, id, master, config, itemAttachedToMenu)),
|
|
64
65
|
};
|
|
65
66
|
};
|
|
66
67
|
|
|
@@ -251,6 +252,9 @@ export const prepareItemToViewPayload = (items = [], viewParentId = null, config
|
|
|
251
252
|
}));
|
|
252
253
|
|
|
253
254
|
export const extractRelatedItemLabel = (item = {}, fields = {}, config = {}) => {
|
|
255
|
+
if (get(item, 'isSingle', false)) {
|
|
256
|
+
return get(item, 'labelSingular', '');
|
|
257
|
+
}
|
|
254
258
|
const { contentTypes = [] } = config;
|
|
255
259
|
const { __collectionUid } = item;
|
|
256
260
|
const contentType = contentTypes.find(_ => _.uid === __collectionUid)
|
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
"header.action.newItem": "New Item",
|
|
6
6
|
"submit.cta.cancel": "Cancel",
|
|
7
7
|
"submit.cta.save": "Save",
|
|
8
|
-
"empty": "
|
|
8
|
+
"empty": "Your navigation is empty",
|
|
9
9
|
"empty.cta": "Create first item",
|
|
10
|
-
"popup.item.header": "
|
|
10
|
+
"popup.item.header.edit": "Edit navigation item",
|
|
11
|
+
"popup.item.header.new": "New navigation item",
|
|
11
12
|
"popup.item.form.title.label": "Title",
|
|
12
13
|
"popup.item.form.title.placeholder": "Enter the item title or leave blank to pull from related entity",
|
|
13
14
|
"popup.item.form.uiRouterKey.label": "UI router key",
|
|
@@ -17,17 +18,18 @@
|
|
|
17
18
|
"popup.item.form.path.preview": "Preview:",
|
|
18
19
|
"popup.item.form.externalPath.label": "External URL",
|
|
19
20
|
"popup.item.form.externalPath.placeholder": "Link to the external source",
|
|
20
|
-
"popup.item.form.externalPath.validation.type": "This value is not a proper url."
|
|
21
|
+
"popup.item.form.externalPath.validation.type": "This value is not a proper url.",
|
|
21
22
|
"popup.item.form.menuAttached.label": "Attach to menu",
|
|
22
23
|
"popup.item.form.type.label": "Internal link",
|
|
23
24
|
"popup.item.form.type.internal.label": "Internal source",
|
|
24
25
|
"popup.item.form.type.external.label": "External source",
|
|
25
26
|
"popup.item.form.type.external.description": "Output path: {value}",
|
|
26
27
|
"popup.item.form.audience.label": "Audience",
|
|
27
|
-
"popup.item.form.audience.placeholder": "
|
|
28
|
+
"popup.item.form.audience.placeholder": "Select audience...",
|
|
28
29
|
"popup.item.form.relatedSection.label": "Relation to",
|
|
29
30
|
"popup.item.form.relatedType.label": "Content Type",
|
|
30
31
|
"popup.item.form.relatedType.placeholder": "Select content type...",
|
|
32
|
+
"popup.item.form.relatedType.empty": "There are no content types to select",
|
|
31
33
|
"popup.item.form.related.label": "Entity",
|
|
32
34
|
"popup.item.form.related.empty": "There are no more entities of \"{ contentTypeName }\" to select",
|
|
33
35
|
"popup.item.form.button.create": "Create item",
|
|
@@ -41,9 +43,49 @@
|
|
|
41
43
|
"notification.navigation.item.relation": "Entity relation does not exist!",
|
|
42
44
|
"notification.navigation.item.relation.status.draft": "draft",
|
|
43
45
|
"notification.navigation.item.relation.status.published": "published",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
"pages.settings.general.title": "General settings",
|
|
47
|
+
"pages.settings.additional.title": "Additional settings",
|
|
48
|
+
"pages.settings.nameField.title": "Content types settings",
|
|
49
|
+
"pages.settings.restoring.title": "Restoring",
|
|
50
|
+
"pages.settings.section.title": "Navigation Plugin",
|
|
51
|
+
"pages.settings.section.subtitle": "Configuration",
|
|
52
|
+
"pages.settings.header.title": "Navigation",
|
|
53
|
+
"pages.settings.header.description": "Configure the navigation plugin",
|
|
54
|
+
"pages.settings.actions.restart": "Restart Strapi",
|
|
55
|
+
"pages.settings.actions.submit": "Save configuration",
|
|
56
|
+
"pages.settings.actions.restore": "Restore configuration",
|
|
57
|
+
"pages.settings.actions.restore.confirmation.header": "Do you want to continue?",
|
|
58
|
+
"pages.settings.actions.restore.confirmation.confirm": "Restore",
|
|
59
|
+
"pages.settings.actions.restore.confirmation.description": "Plugin config will be restored from plugins.js file.",
|
|
60
|
+
"pages.settings.actions.restore.description": "Restoring the plugin configuration will cause it to be replaced with configuration saved inside 'plugins.js' file.",
|
|
61
|
+
"pages.settings.actions.restart.alert.title": "Strapi requires restart",
|
|
62
|
+
"pages.settings.actions.restart.alert.description": "You've made a configuration changes which requires your Strapi application to be restarted to take an effect in GraphQL schema. Do it manually or by using below trigger.",
|
|
63
|
+
"pages.settings.actions.restart.alert.close": "Discard",
|
|
64
|
+
"pages.settings.actions.restart.alert.cancel": "Cancel",
|
|
65
|
+
"pages.settings.notification.fetch.error": "Failed to fetch configuration. Retrying...",
|
|
66
|
+
"pages.settings.notification.submit.success": "Config has been updated successfully",
|
|
67
|
+
"pages.settings.notification.restore.success": "Config has been restored successfully",
|
|
68
|
+
"pages.settings.notification.restart.success": "Application has been restarted successfully",
|
|
69
|
+
"pages.settings.notification.submit.error": "Config update has failed",
|
|
70
|
+
"pages.settings.notification.restore.error": "Config restore has failed",
|
|
71
|
+
"pages.settings.notification.restart.error": "Failed to restart your application. Try to do it manually.",
|
|
72
|
+
"pages.settings.form.contentTypes.label": "Enable for Collection(s)",
|
|
73
|
+
"pages.settings.form.contentTypes.placeholder": "eg. Pages, Posts",
|
|
74
|
+
"pages.settings.form.contentTypes.hint": "Content types that can be related with navigation items. This configuration is applicable both for REST & GraphQL",
|
|
75
|
+
"pages.settings.form.allowedLevels.label": "Allowed levels",
|
|
76
|
+
"pages.settings.form.allowedLevels.placeholder": "eg. 2",
|
|
77
|
+
"pages.settings.form.allowedLevels.hint": "Maximum level for which you're able to mark item as \"Menu attached\"",
|
|
78
|
+
"pages.settings.form.audience.label": "Audience",
|
|
79
|
+
"pages.settings.form.audience.hint": "Enable audience field",
|
|
80
|
+
"pages.settings.form.nameField.label": "Name fields",
|
|
81
|
+
"pages.settings.form.nameField.placeholder": "Select at least one or leave empty to apply defaults",
|
|
82
|
+
"pages.settings.form.nameField.hint": "If left empty name field is going to take following ordered fields: \"title\", \"subject\" and \"name\"",
|
|
83
|
+
"components.navigationItem.action.newItem": "Add nested item",
|
|
84
|
+
"components.navigationItem.badge.removed": "Removed",
|
|
85
|
+
"components.navigationItem.badge.draft": "{type}: Draft",
|
|
86
|
+
"components.navigationItem.badge.published": "{type}: Published",
|
|
87
|
+
"components.confirmation.dialog.button.cancel": "Cancel",
|
|
88
|
+
"components.confirmation.dialog.button.confirm": "Confirm",
|
|
89
|
+
"components.confirmation.dialog.description": "Do you want to continue?",
|
|
90
|
+
"components.confirmation.dialog.header": "Confirmation"
|
|
91
|
+
}
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"notification.navigation.item.relation": "L'entitée n'a pas de relations!",
|
|
39
39
|
"notification.navigation.item.relation.status.draft": "brouillon",
|
|
40
40
|
"notification.navigation.item.relation.status.published": "publié",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
41
|
+
"components.navigationItem.action.newItem": "Nouvel élément imbriqué",
|
|
42
|
+
"components.navigationItem.badge.removed": "Supprimé",
|
|
43
|
+
"components.navigationItem.badge.draft": "Brouillon",
|
|
44
|
+
"components.navigationItem.badge.published": "Publié"
|
|
45
45
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { request } from '@strapi/helper-plugin';
|
|
2
|
+
import pluginId from '../pluginId';
|
|
3
|
+
|
|
4
|
+
export const fetchNavigationConfig = async () => {
|
|
5
|
+
try {
|
|
6
|
+
const data = await request(`/${pluginId}/settings/config`, { method: 'GET' });
|
|
7
|
+
return data;
|
|
8
|
+
} catch (err) {
|
|
9
|
+
toggleNotification({
|
|
10
|
+
type: 'warning',
|
|
11
|
+
message: { id: 'notification.error' },
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return { err };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const updateNavigationConfig = async ({ body }) =>
|
|
19
|
+
request(`/${pluginId}/config`, { method: 'PUT', body }, true);
|
|
20
|
+
|
|
21
|
+
export const restoreNavigationConfig = async () =>
|
|
22
|
+
request(`/${pluginId}/config`, { method: 'DELETE' }, true);
|
|
23
|
+
|
|
24
|
+
export const fetchAllContentTypes = async () => {
|
|
25
|
+
try {
|
|
26
|
+
const { data } = await request('/content-manager/content-types');
|
|
27
|
+
return { ...data }
|
|
28
|
+
} catch (err) {
|
|
29
|
+
toggleNotification({
|
|
30
|
+
type: 'warning',
|
|
31
|
+
message: { id: 'notification.error' },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return { err };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const restartStrapi = async (toggleNotification) => {
|
|
39
|
+
try {
|
|
40
|
+
const { data } = await request(`/${pluginId}/settings/restart`);
|
|
41
|
+
|
|
42
|
+
return data;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
toggleNotification({
|
|
45
|
+
type: 'warning',
|
|
46
|
+
message: { id: 'notification.error' },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return { err };
|
|
50
|
+
}
|
|
51
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useIntl } from 'react-intl';
|
|
2
|
+
import { isString } from 'lodash';
|
|
3
|
+
|
|
4
|
+
import pluginId from '../pluginId';
|
|
5
|
+
|
|
6
|
+
const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
|
|
7
|
+
const { formatMessage } = useIntl();
|
|
8
|
+
let formattedId = ''
|
|
9
|
+
if (isString(input)) {
|
|
10
|
+
formattedId = input;
|
|
11
|
+
} else {
|
|
12
|
+
formattedId = input?.id;
|
|
13
|
+
}
|
|
14
|
+
return formatMessage({
|
|
15
|
+
id: `${inPluginScope ? pluginId : 'app.components'}.${formattedId}`,
|
|
16
|
+
defaultMessage,
|
|
17
|
+
}, input?.props || undefined)
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { getMessage };
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-plugin-navigation",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Strapi - Navigation plugin",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"name": "navigation",
|
|
7
|
-
"
|
|
8
|
-
"description": "
|
|
7
|
+
"displayName": "Navigation",
|
|
8
|
+
"description": "Create consumable navigation with a simple and straightforward visual builder",
|
|
9
9
|
"kind": "plugin"
|
|
10
10
|
},
|
|
11
11
|
"repository": {
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
"url": "https://github.com/VirtusLab/strapi-plugin-navigation"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
+
"publish:latest": "npm publish --tag latest",
|
|
16
17
|
"test:unit": "jest --verbose --coverage"
|
|
17
18
|
},
|
|
18
19
|
"dependencies": {
|
|
19
|
-
"@strapi/utils": "^4.0
|
|
20
|
+
"@strapi/utils": "^4.1.0",
|
|
20
21
|
"uuid": "^8.3.0",
|
|
21
22
|
"bad-words": "^3.0.3",
|
|
22
23
|
"lodash": "^4.17.11",
|
|
@@ -43,8 +44,8 @@
|
|
|
43
44
|
"@strapi/strapi": "4.x"
|
|
44
45
|
},
|
|
45
46
|
"author": {
|
|
46
|
-
"name": "VirtusLab
|
|
47
|
-
"email": "
|
|
47
|
+
"name": "VirtusLab",
|
|
48
|
+
"email": "strapi@virtuslab.com",
|
|
48
49
|
"url": "https://virtuslab.com"
|
|
49
50
|
},
|
|
50
51
|
"maintainers": [
|
package/server/bootstrap.js
CHANGED
|
@@ -23,7 +23,9 @@ module.exports = async ({ strapi }) => {
|
|
|
23
23
|
pluginName: "navigation",
|
|
24
24
|
},
|
|
25
25
|
];
|
|
26
|
+
await strapi.admin.services.permission.actionProvider.registerMany(actions);
|
|
26
27
|
|
|
28
|
+
// Initialize first navigation
|
|
27
29
|
const navigations = await strapi
|
|
28
30
|
.query("plugin::navigation.navigation")
|
|
29
31
|
.findMany();
|
|
@@ -38,5 +40,32 @@ module.exports = async ({ strapi }) => {
|
|
|
38
40
|
}
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
|
-
|
|
43
|
+
|
|
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
|
+
}
|
|
66
|
+
|
|
67
|
+
if (strapi.plugin('graphql')) {
|
|
68
|
+
const graphqlConfiguration = require('./graphql')
|
|
69
|
+
await graphqlConfiguration({ strapi, config: config || defaultConfigValue });
|
|
70
|
+
}
|
|
42
71
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"collectionName": "navigations",
|
|
3
|
+
"info": {
|
|
4
|
+
"singularName": "navigation",
|
|
5
|
+
"pluralName": "navigations",
|
|
6
|
+
"displayName": "Navigation",
|
|
7
|
+
"name": "navigation"
|
|
8
|
+
},
|
|
9
|
+
"options": {
|
|
10
|
+
"increments": true,
|
|
11
|
+
"comment": ""
|
|
12
|
+
},
|
|
13
|
+
"pluginOptions": {
|
|
14
|
+
"content-manager": {
|
|
15
|
+
"visible": true
|
|
16
|
+
},
|
|
17
|
+
"content-type-builder": {
|
|
18
|
+
"visible": false
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"attributes": {
|
|
22
|
+
"name": {
|
|
23
|
+
"type": "text",
|
|
24
|
+
"configurable": false,
|
|
25
|
+
"required": true
|
|
26
|
+
},
|
|
27
|
+
"slug": {
|
|
28
|
+
"type": "uid",
|
|
29
|
+
"target": "name",
|
|
30
|
+
"configurable": false,
|
|
31
|
+
"required": true
|
|
32
|
+
},
|
|
33
|
+
"visible": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"default": false,
|
|
36
|
+
"configurable": false
|
|
37
|
+
},
|
|
38
|
+
"items": {
|
|
39
|
+
"type": "relation",
|
|
40
|
+
"relation": "oneToMany",
|
|
41
|
+
"target": "plugin::navigation.navigation-item",
|
|
42
|
+
"configurable": false
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -19,10 +19,34 @@ const errorHandler = (ctx) => (error) => {
|
|
|
19
19
|
throw error;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
module.exports = {
|
|
22
|
+
module.exports = ({strapi}) => ({
|
|
23
23
|
async config() {
|
|
24
24
|
return getService().config();
|
|
25
25
|
},
|
|
26
|
+
|
|
27
|
+
async updateConfig(ctx) {
|
|
28
|
+
await getService().updateConfig(ctx.request.body)
|
|
29
|
+
return ctx.send({ status: 200 });
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async restoreConfig(ctx) {
|
|
33
|
+
await getService().restoreConfig()
|
|
34
|
+
return ctx.send({ status: 200 })
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async settingsConfig() {
|
|
38
|
+
return getService().config(true);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
async settingsRestart(ctx) {
|
|
42
|
+
try {
|
|
43
|
+
await getService().restart();
|
|
44
|
+
return ctx.send({ status: 200 });
|
|
45
|
+
} catch (e) {
|
|
46
|
+
errorHandler(ctx, e);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
|
|
26
50
|
async get() {
|
|
27
51
|
return getService().get();
|
|
28
52
|
},
|
|
@@ -50,13 +74,14 @@ module.exports = {
|
|
|
50
74
|
},
|
|
51
75
|
async render(ctx) {
|
|
52
76
|
const { params, query = {} } = ctx;
|
|
53
|
-
const { type, menu: menuOnly } = query;
|
|
77
|
+
const { type, menu: menuOnly, path: rootPath } = query;
|
|
54
78
|
const { idOrSlug } = parseParams(params);
|
|
55
|
-
return getService().render(
|
|
79
|
+
return getService().render({
|
|
56
80
|
idOrSlug,
|
|
57
81
|
type,
|
|
58
82
|
menuOnly,
|
|
59
|
-
|
|
83
|
+
rootPath
|
|
84
|
+
});
|
|
60
85
|
},
|
|
61
86
|
async renderChild(ctx) {
|
|
62
87
|
const { params, query = {} } = ctx;
|
|
@@ -69,4 +94,4 @@ module.exports = {
|
|
|
69
94
|
menuOnly
|
|
70
95
|
);
|
|
71
96
|
},
|
|
72
|
-
};
|
|
97
|
+
});
|
package/server/graphql/index.js
CHANGED
|
@@ -2,16 +2,15 @@ const getTypes = require('./types');
|
|
|
2
2
|
const getQueries = require('./queries');
|
|
3
3
|
const getResolversConfig = require('./resolvers-config');
|
|
4
4
|
|
|
5
|
-
module.exports = () => {
|
|
5
|
+
module.exports = async ({ strapi, config }) => {
|
|
6
6
|
const extensionService = strapi.plugin('graphql').service('extension');
|
|
7
|
-
|
|
8
7
|
extensionService.shadowCRUD('plugin::navigation.audience').disable();
|
|
9
8
|
extensionService.shadowCRUD('plugin::navigation.navigation').disable();
|
|
10
9
|
extensionService.shadowCRUD('plugin::navigation.navigation-item').disable();
|
|
11
10
|
extensionService.shadowCRUD('plugin::navigation.navigations-items-related').disable();
|
|
12
11
|
|
|
13
|
-
extensionService.use(({ nexus
|
|
14
|
-
const types = getTypes({ strapi, nexus });
|
|
12
|
+
extensionService.use(({strapi, nexus}) => {
|
|
13
|
+
const types = getTypes({ strapi, nexus, config });
|
|
15
14
|
const queries = getQueries({ strapi, nexus });
|
|
16
15
|
const resolversConfig = getResolversConfig({ strapi });
|
|
17
16
|
|
|
@@ -5,11 +5,12 @@ module.exports = ({ strapi, nexus }) => {
|
|
|
5
5
|
args: {
|
|
6
6
|
navigationIdOrSlug: nonNull(stringArg()),
|
|
7
7
|
type: 'NavigationRenderType',
|
|
8
|
-
menuOnly: booleanArg()
|
|
8
|
+
menuOnly: booleanArg(),
|
|
9
|
+
path: stringArg(),
|
|
9
10
|
},
|
|
10
11
|
resolve(obj, args) {
|
|
11
|
-
const { navigationIdOrSlug, type, menuOnly } = args;
|
|
12
|
-
return strapi.plugin('navigation').service('navigation').render(
|
|
12
|
+
const { navigationIdOrSlug: idOrSlug, type, menuOnly, path: rootPath } = args;
|
|
13
|
+
return strapi.plugin('navigation').service('navigation').render({idOrSlug, type, menuOnly, rootPath});
|
|
13
14
|
},
|
|
14
15
|
};
|
|
15
16
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
module.exports = ({ nexus }) => nexus.objectType({
|
|
2
2
|
name: "ContentTypesNameFields",
|
|
3
|
-
definition(t) {
|
|
3
|
+
async definition(t) {
|
|
4
4
|
t.nonNull.list.nonNull.string("default")
|
|
5
|
-
const
|
|
5
|
+
const pluginStore = strapi.store({ type: 'plugin', name: 'navigation' });
|
|
6
|
+
const config = await pluginStore.get({ key: 'config' });
|
|
7
|
+
const contentTypesNameFields = config.contentTypesNameFields;
|
|
6
8
|
Object.keys(contentTypesNameFields || {}).forEach(key => t.nonNull.list.string(key))
|
|
7
9
|
}
|
|
8
10
|
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
module.exports = ({ strapi, nexus }) => {
|
|
2
|
-
const related =
|
|
1
|
+
module.exports = ({ strapi, nexus, config }) => {
|
|
2
|
+
const related = config.gql?.navigationItemRelated;
|
|
3
3
|
const name = "NavigationRelated";
|
|
4
4
|
|
|
5
5
|
if (related?.length) {
|