strapi-plugin-navigation 1.0.3 → 1.1.2
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/.circleci/config.yml +1 -1
- package/README.md +41 -31
- package/__mocks__/helpers/strapi.js +17 -5
- package/admin/src/containers/View/components/NavigationItemForm/utils/form.js +1 -1
- package/admin/src/containers/View/utils/parsers.js +11 -12
- package/config/functions/bootstrap.js +107 -11
- package/config/schema.graphql.js +204 -0
- package/controllers/navigation.js +8 -10
- package/models/navigation.settings.json +1 -1
- package/models/navigationItem.js +21 -3
- package/models/navigationItem.settings.json +15 -4
- package/models/navigations_items_related.js +19 -0
- package/models/navigations_items_related.settings.json +45 -0
- package/package.json +1 -1
- package/services/__tests__/navigation.test.js +71 -72
- package/services/navigation.js +198 -103
- package/services/utils/functions.js +10 -10
package/.circleci/config.yml
CHANGED
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ Complete installation requirements are exact same as for Strapi itself and can b
|
|
|
55
55
|
|
|
56
56
|
**Supported Strapi versions**:
|
|
57
57
|
|
|
58
|
-
- Strapi v3.6.
|
|
58
|
+
- Strapi v3.6.8 (recently tested)
|
|
59
59
|
- Strapi v3.x
|
|
60
60
|
|
|
61
61
|
(This plugin may work with the older Strapi versions, but these are not tested nor officially supported at this time.)
|
|
@@ -73,40 +73,22 @@ Complete installation requirements are exact same as for Strapi itself and can b
|
|
|
73
73
|
|
|
74
74
|
## Content Type model relation to Navigation Item
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
inside the `attributes` section like in example below:
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
-
"attributes": {
|
|
92
|
-
...,
|
|
93
|
-
"navigation": {
|
|
94
|
-
"model": "navigationitem",
|
|
95
|
-
"plugin": "navigation",
|
|
96
|
-
"via": "related",
|
|
97
|
-
"configurable": false,
|
|
98
|
-
"hidden": true
|
|
99
|
-
},
|
|
100
|
-
...
|
|
101
|
-
},
|
|
76
|
+
We can define in `config/plugins.js`
|
|
77
|
+
```js
|
|
78
|
+
navigation: {
|
|
79
|
+
...
|
|
80
|
+
relatedContentTypes: [
|
|
81
|
+
'application::pages.pages'
|
|
82
|
+
],
|
|
83
|
+
...
|
|
84
|
+
},
|
|
102
85
|
```
|
|
103
86
|
|
|
104
87
|
## Configuration
|
|
105
|
-
To setup the plugin properly we recommend to put following snippet as part of `config/
|
|
88
|
+
To setup the plugin properly we recommend to put following snippet as part of `config/plugins.js` or `config/<env>/plugins.js` file. If you've got already configurations for other plugins stores by this way, use just the `navigation` part within exising `plugins` item.
|
|
106
89
|
|
|
107
|
-
```
|
|
90
|
+
```js
|
|
108
91
|
...
|
|
109
|
-
plugins: {
|
|
110
92
|
navigation: {
|
|
111
93
|
additionalFields: ['audience'],
|
|
112
94
|
allowedLevels: 2,
|
|
@@ -114,8 +96,8 @@ To setup the plugin properly we recommend to put following snippet as part of `c
|
|
|
114
96
|
'blog_posts': ['altTitle'],
|
|
115
97
|
'pages': ['title'],
|
|
116
98
|
},
|
|
99
|
+
gql: { ... }
|
|
117
100
|
},
|
|
118
|
-
},
|
|
119
101
|
...
|
|
120
102
|
```
|
|
121
103
|
|
|
@@ -123,6 +105,34 @@ To setup the plugin properly we recommend to put following snippet as part of `c
|
|
|
123
105
|
- `additionalFields` - Additional fields: 'audience', more in the future
|
|
124
106
|
- `allowedLevels` - Maximum level for which your're able to mark item as "Menu attached"
|
|
125
107
|
- `contentTypesNameFields` - Definition of content type title fields like `'content_type_name': ['field_name_1', 'field_name_2']`, if not set titles are pulled from fields like `['title', 'subject', 'name']`
|
|
108
|
+
- `gql` - If you're using GraphQL that's the right place to put all necessary settings. More **[ here ](#gql-configuration)**
|
|
109
|
+
|
|
110
|
+
## GQL Configuration
|
|
111
|
+
To properly configure GQL to work with navigation you should provide `gql` prop which should contain union types which will be used for define GQL response format for your data while fetching:
|
|
112
|
+
|
|
113
|
+
```gql
|
|
114
|
+
master: Int
|
|
115
|
+
items: [NavigationItem]
|
|
116
|
+
related: NavigationRelated
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
as follows:
|
|
120
|
+
|
|
121
|
+
```js
|
|
122
|
+
gql: {
|
|
123
|
+
navigationItemRelated: 'union NavigationRelated = <your GQL related entities>',
|
|
124
|
+
},
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
for example:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
gql: {
|
|
131
|
+
navigationItemRelated: 'union NavigationRelated = Pages | UploadFile',
|
|
132
|
+
},
|
|
133
|
+
```
|
|
134
|
+
where `Pages` and `UploadFile` are your types to the **Content Types** you're referring by navigation items relations.
|
|
135
|
+
|
|
126
136
|
|
|
127
137
|
## Public API Navigation Item model
|
|
128
138
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const {get} = require('lodash');
|
|
1
2
|
function setupStrapi() {
|
|
2
3
|
Object.defineProperty(global, 'strapi', {
|
|
3
4
|
value: {
|
|
@@ -14,6 +15,9 @@ function setupStrapi() {
|
|
|
14
15
|
},
|
|
15
16
|
},
|
|
16
17
|
},
|
|
18
|
+
get(path, defaultValue) {
|
|
19
|
+
return get(strapi, path, defaultValue);
|
|
20
|
+
},
|
|
17
21
|
},
|
|
18
22
|
api: {
|
|
19
23
|
'home-page': {
|
|
@@ -40,13 +44,13 @@ function setupStrapi() {
|
|
|
40
44
|
modelName: 'pages',
|
|
41
45
|
associations: [{ model: 'navigationitem' }],
|
|
42
46
|
},
|
|
43
|
-
'blog-post': {
|
|
47
|
+
'application::blog-post.blog-post': {
|
|
44
48
|
...require('./blog-post.settings.json'),
|
|
45
49
|
apiName: 'blog-posts',
|
|
46
50
|
modelName: 'blog-posts',
|
|
47
51
|
associations: [{ model: 'navigationitem' }],
|
|
48
52
|
},
|
|
49
|
-
'my-homepage': {
|
|
53
|
+
'application::my-homepages.my-homepage': {
|
|
50
54
|
...require('./my-homepage.settings.json'),
|
|
51
55
|
apiName: 'my-homepage',
|
|
52
56
|
modelName: 'my-homepage',
|
|
@@ -58,12 +62,12 @@ function setupStrapi() {
|
|
|
58
62
|
modelName: 'home-page',
|
|
59
63
|
associations: [{ model: 'navigationitem' }],
|
|
60
64
|
},
|
|
61
|
-
'plugin
|
|
65
|
+
'plugins::another-plugin.pages': {
|
|
62
66
|
...require('./another-plugin/pages.settings.json'),
|
|
63
67
|
modelName: 'plugin-pages',
|
|
64
68
|
associations: [{ model: 'navigationitem' }],
|
|
65
69
|
},
|
|
66
|
-
'plugin
|
|
70
|
+
'plugins::another-plugin.blog-post': {
|
|
67
71
|
...require('./another-plugin/blog-post.settings.json'),
|
|
68
72
|
modelName: 'plugin-blog-posts',
|
|
69
73
|
associations: [{ model: 'navigationitem' }],
|
|
@@ -73,7 +77,15 @@ function setupStrapi() {
|
|
|
73
77
|
navigation: {
|
|
74
78
|
services: {
|
|
75
79
|
navigation: jest.fn().mockImplementation(),
|
|
76
|
-
}
|
|
80
|
+
},
|
|
81
|
+
relatedContentTypes: [
|
|
82
|
+
'application::pages.pages',
|
|
83
|
+
'application::blog-post.blog-post',
|
|
84
|
+
'application::my-homepages.my-homepage',
|
|
85
|
+
'application::page-homes.home-page',
|
|
86
|
+
'plugins::another-plugin.pages',
|
|
87
|
+
'plugins::another-plugin.blog-post'
|
|
88
|
+
]
|
|
77
89
|
},
|
|
78
90
|
anotherPlugin: {
|
|
79
91
|
models: {
|
|
@@ -29,7 +29,7 @@ export const form = {
|
|
|
29
29
|
is: val => val === navigationItemType.EXTERNAL,
|
|
30
30
|
then: yup.string()
|
|
31
31
|
.required(translatedErrors.required)
|
|
32
|
-
.matches(/(#.*)|(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/, {
|
|
32
|
+
.matches(/(#.*)|(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,}|mailto:.+@(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)+[^.\s]{2,})/, {
|
|
33
33
|
excludeEmptyString: true,
|
|
34
34
|
message: `${pluginId}.popup.item.form.externalPath.validation.type`,
|
|
35
35
|
}),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isUuid, uuid } from 'uuidv4';
|
|
2
|
-
import { find, get, isArray, isEmpty, isNil, isNumber, isObject, isString,
|
|
2
|
+
import { find, get, isArray, isEmpty, isNil, isNumber, isObject, isString, last, omit, orderBy } from 'lodash';
|
|
3
3
|
import { navigationItemType } from './enums';
|
|
4
4
|
|
|
5
5
|
export const transformItemToRESTPayload = (
|
|
@@ -46,8 +46,7 @@ export const transformItemToRESTPayload = (
|
|
|
46
46
|
order,
|
|
47
47
|
uiRouterKey,
|
|
48
48
|
menuAttached,
|
|
49
|
-
audience: audience.map((audienceItem) =>
|
|
50
|
-
isObject(audienceItem) ? audienceItem.value : audienceItem,
|
|
49
|
+
audience: audience.map((audienceItem) => isObject(audienceItem) ? audienceItem.value || audienceItem.id : audienceItem,
|
|
51
50
|
),
|
|
52
51
|
path: isExternal ? undefined : path,
|
|
53
52
|
externalPath: isExternal ? externalPath : undefined,
|
|
@@ -65,7 +64,7 @@ export const transformItemToRESTPayload = (
|
|
|
65
64
|
};
|
|
66
65
|
|
|
67
66
|
export const transformToRESTPayload = (payload, config = {}) => {
|
|
68
|
-
|
|
67
|
+
const { id, name, visible, items } = payload;
|
|
69
68
|
return {
|
|
70
69
|
id,
|
|
71
70
|
name,
|
|
@@ -120,7 +119,7 @@ const linkRelations = (item, config) => {
|
|
|
120
119
|
const shouldBuildRelated = !relatedRef || (relatedRef && (relatedRef.id !== relatedId));
|
|
121
120
|
if (shouldBuildRelated && !shouldFindRelated) {
|
|
122
121
|
const relatedContentType = find(contentTypes,
|
|
123
|
-
ct =>
|
|
122
|
+
ct => ct.uid === relatedItem.__contentType, {});
|
|
124
123
|
const { uid, labelSingular, isSingle } = relatedContentType;
|
|
125
124
|
relation = {
|
|
126
125
|
related: relatedItem.id,
|
|
@@ -262,7 +261,7 @@ export const usedContentTypes = (items = []) => items.flatMap(
|
|
|
262
261
|
if (item.relatedRef) {
|
|
263
262
|
return [item.relatedRef, ...used];
|
|
264
263
|
}
|
|
265
|
-
return used;
|
|
264
|
+
return used;
|
|
266
265
|
},
|
|
267
266
|
);
|
|
268
267
|
|
|
@@ -284,11 +283,11 @@ export const isRelationPublished = ({ relatedRef, relatedType = {}, type, isColl
|
|
|
284
283
|
return true;
|
|
285
284
|
};
|
|
286
285
|
|
|
287
|
-
export const validateNavigationStructure = (items = []) =>
|
|
288
|
-
items.map(item =>
|
|
289
|
-
(item.removed || isRelationCorrect({
|
|
290
|
-
related: item.related,
|
|
286
|
+
export const validateNavigationStructure = (items = []) =>
|
|
287
|
+
items.map(item =>
|
|
288
|
+
(item.removed || isRelationCorrect({
|
|
289
|
+
related: item.related,
|
|
291
290
|
type: item.type,
|
|
292
|
-
})) &&
|
|
291
|
+
})) &&
|
|
293
292
|
validateNavigationStructure(item.items)
|
|
294
|
-
).filter(item => !item).length === 0;
|
|
293
|
+
).filter(item => !item).length === 0;
|
|
@@ -1,26 +1,112 @@
|
|
|
1
|
-
const { isEmpty } = require(
|
|
1
|
+
const { isEmpty, get, last } = require('lodash');
|
|
2
|
+
|
|
3
|
+
const saveJSONParse = (value) => {
|
|
4
|
+
try {
|
|
5
|
+
return JSON.parse(value).map((_) => ({ ..._, id: _._id }));
|
|
6
|
+
} catch (e) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const getDefaultConnectionName = (strapi) => strapi.config.get('database.defaultConnection');
|
|
12
|
+
|
|
13
|
+
const isMongo = (strapi) => {
|
|
14
|
+
const connectionName = getDefaultConnectionName(strapi);
|
|
15
|
+
return strapi.config.get(`database.connections.${connectionName}.connector`).includes('mongo');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const getNavigationMorphData = (strapi) => {
|
|
19
|
+
const connectionName = getDefaultConnectionName(strapi);
|
|
20
|
+
const { [connectionName]: knex } = strapi.connections;
|
|
21
|
+
return knex.schema.hasTable('navigations_items_morph').then((exist)=> exist ? knex('navigations_items_morph').select('*') : []);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getNavigationItemsModel = (strapi) => strapi.query('navigationitem', 'navigation');
|
|
25
|
+
|
|
26
|
+
const getRelatedModel = (strapi) => strapi.query('navigations_items_related', 'navigation');
|
|
27
|
+
|
|
28
|
+
const createRelatedData = (relatedModel, navigationItemsModel, items) => ({
|
|
29
|
+
field,
|
|
30
|
+
order,
|
|
31
|
+
related_id,
|
|
32
|
+
related_type,
|
|
33
|
+
navigations_items_id,
|
|
34
|
+
}) => {
|
|
35
|
+
const item = items.find(item => item.id === navigations_items_id);
|
|
36
|
+
const modelUID = get(strapi.query(related_type), 'model.uid');
|
|
37
|
+
if (item && modelUID) {
|
|
38
|
+
const relatedData = {
|
|
39
|
+
field,
|
|
40
|
+
order,
|
|
41
|
+
related_id,
|
|
42
|
+
related_type: modelUID,
|
|
43
|
+
master: get(item.master, 'id', item.master),
|
|
44
|
+
};
|
|
45
|
+
return relatedModel.create(relatedData)
|
|
46
|
+
.then(
|
|
47
|
+
({ id }) => navigationItemsModel.update({ id: navigations_items_id }, { related: id }),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return Promise.resolve();
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const migrateNavigationItemsSQL = async (strapi) => {
|
|
54
|
+
const morphData = await getNavigationMorphData(strapi);
|
|
55
|
+
if (morphData.length) {
|
|
56
|
+
const relatedModel = getRelatedModel(strapi);
|
|
57
|
+
const navigationItemsModel = getNavigationItemsModel(strapi);
|
|
58
|
+
const items = await navigationItemsModel.find({});
|
|
59
|
+
await Promise.all(morphData.map(createRelatedData(relatedModel, navigationItemsModel, items)));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const migrateNavigationItemsMongo = async (strapi) => {
|
|
64
|
+
const navigationItemsModel = getNavigationItemsModel(strapi);
|
|
65
|
+
const connectionName = getDefaultConnectionName(strapi);
|
|
66
|
+
const models = strapi.connections[connectionName].models;
|
|
67
|
+
const items = (await models.NavigationNavigationitem.find({}))
|
|
68
|
+
// workaround to change type from object to int
|
|
69
|
+
.map(_ => ({ ..._.toObject(), related: last(saveJSONParse(get(_.errors, 'related.properties.value', null))) }))
|
|
70
|
+
.filter(_ => _.related);
|
|
71
|
+
|
|
72
|
+
if (items.length) {
|
|
73
|
+
await Promise.all(items.map(item => {
|
|
74
|
+
const data = {
|
|
75
|
+
related_id: item.related.ref,
|
|
76
|
+
related_type: models[item.related.kind].uid,
|
|
77
|
+
field: item.related.field,
|
|
78
|
+
order: 1,
|
|
79
|
+
master: item.master,
|
|
80
|
+
};
|
|
81
|
+
return getRelatedModel(strapi)
|
|
82
|
+
.create(data)
|
|
83
|
+
.then(result => navigationItemsModel.update({ id: item.id }, { related: [result.id] }));
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
};
|
|
2
88
|
|
|
3
89
|
module.exports = async () => {
|
|
4
90
|
// Check if the plugin users-permissions is installed because the navigation needs it
|
|
5
|
-
if (Object.keys(strapi.plugins).indexOf(
|
|
91
|
+
if (Object.keys(strapi.plugins).indexOf('users-permissions') === -1) {
|
|
6
92
|
throw new Error(
|
|
7
|
-
|
|
93
|
+
'In order to make the navigation plugin work the users-permissions plugin is required',
|
|
8
94
|
);
|
|
9
95
|
}
|
|
10
96
|
|
|
11
97
|
// Add permissions
|
|
12
98
|
const actions = [
|
|
13
99
|
{
|
|
14
|
-
section:
|
|
15
|
-
displayName:
|
|
16
|
-
uid:
|
|
17
|
-
pluginName:
|
|
100
|
+
section: 'plugins',
|
|
101
|
+
displayName: 'Access the Navigation',
|
|
102
|
+
uid: 'read',
|
|
103
|
+
pluginName: 'navigation',
|
|
18
104
|
},
|
|
19
105
|
{
|
|
20
|
-
section:
|
|
21
|
-
displayName:
|
|
22
|
-
uid:
|
|
23
|
-
pluginName:
|
|
106
|
+
section: 'plugins',
|
|
107
|
+
displayName: 'Ability to change the Navigation',
|
|
108
|
+
uid: 'update',
|
|
109
|
+
pluginName: 'navigation',
|
|
24
110
|
},
|
|
25
111
|
];
|
|
26
112
|
|
|
@@ -36,6 +122,16 @@ module.exports = async () => {
|
|
|
36
122
|
visible: true,
|
|
37
123
|
});
|
|
38
124
|
}
|
|
125
|
+
const relatedModel = getRelatedModel(global.strapi);
|
|
126
|
+
const isMigrated = !!(await relatedModel.count({}));
|
|
127
|
+
if (!isMigrated) {
|
|
128
|
+
const isMongoDB = isMongo(global.strapi);
|
|
129
|
+
if (isMongoDB) {
|
|
130
|
+
await migrateNavigationItemsMongo(global.strapi);
|
|
131
|
+
} else {
|
|
132
|
+
await migrateNavigationItemsSQL(global.strapi);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
39
135
|
|
|
40
136
|
const { actionProvider } = strapi.admin.services.permission;
|
|
41
137
|
await actionProvider.registerMany(actions);
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
const NAVIGATION_DATE = `
|
|
2
|
+
# SQL
|
|
3
|
+
created_at: String
|
|
4
|
+
updated_at: String
|
|
5
|
+
# MONGO
|
|
6
|
+
createdAt: String
|
|
7
|
+
updatedAt: String
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const NAVIGATION_USER = `
|
|
11
|
+
# SQL
|
|
12
|
+
created_by: String
|
|
13
|
+
updated_by: String
|
|
14
|
+
# MONGO
|
|
15
|
+
createdBy: String
|
|
16
|
+
updatedBy: String
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
const NAVIGATION = `
|
|
20
|
+
id: String!
|
|
21
|
+
name: String!
|
|
22
|
+
slug: String!
|
|
23
|
+
visible: Boolean!
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const getContentTypesNamesFields = () => {
|
|
27
|
+
const contentTypesNameFields = strapi.config.get('plugins.navigation.contentTypesNameFields');
|
|
28
|
+
return Object.keys(contentTypesNameFields || {}).map(key => `${key}: [String]!`).join('\n');
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const getNavigationRelated = () => {
|
|
32
|
+
const related = strapi.config.get('plugins.navigation.gql.navigationItemRelated');
|
|
33
|
+
if (related) {
|
|
34
|
+
return related;
|
|
35
|
+
}
|
|
36
|
+
return `
|
|
37
|
+
type NavigationRelated {
|
|
38
|
+
id: Int
|
|
39
|
+
title: String
|
|
40
|
+
name: String
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
module.exports = {
|
|
46
|
+
// language=GraphQL
|
|
47
|
+
definition: `
|
|
48
|
+
enum NavigationRenderType {
|
|
49
|
+
FLAT,
|
|
50
|
+
TREE,
|
|
51
|
+
RFR
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
${getNavigationRelated()}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
type NavigationItem {
|
|
58
|
+
id: Int!
|
|
59
|
+
title: String!
|
|
60
|
+
type: String!
|
|
61
|
+
path: String
|
|
62
|
+
externalPath: String
|
|
63
|
+
uiRouterKey: String!
|
|
64
|
+
menuAttached: Boolean!
|
|
65
|
+
order: Int!
|
|
66
|
+
parent: Int
|
|
67
|
+
master: Int
|
|
68
|
+
items: [NavigationItem]
|
|
69
|
+
related: NavigationRelated
|
|
70
|
+
audience: [String]
|
|
71
|
+
${NAVIGATION_DATE}
|
|
72
|
+
${NAVIGATION_USER}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type Navigation {
|
|
76
|
+
${NAVIGATION}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
type NavigationDetails {
|
|
80
|
+
${NAVIGATION}
|
|
81
|
+
items: [NavigationItem]!
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
type ContentTypesNameFields {
|
|
86
|
+
default: [String!]!
|
|
87
|
+
${getContentTypesNamesFields()}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type ContentTypes {
|
|
91
|
+
uid: String!
|
|
92
|
+
name: String!
|
|
93
|
+
isSingle: Boolean!
|
|
94
|
+
collectionName: String!
|
|
95
|
+
contentTypeName: String!
|
|
96
|
+
label: String!
|
|
97
|
+
relatedField: String!
|
|
98
|
+
labelSingular: String!
|
|
99
|
+
endpoint: String!
|
|
100
|
+
available: Boolean!
|
|
101
|
+
visible: Boolean!
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type NavigationConfig {
|
|
105
|
+
allowedLevels: Int
|
|
106
|
+
availableAudience: [NavigationAudience]!
|
|
107
|
+
additionalFields: [String]!
|
|
108
|
+
contentTypesNameFields: ContentTypesNameFields
|
|
109
|
+
contentTypes: [ContentTypes]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
input CreateNavigationRelated {
|
|
113
|
+
ref: String!
|
|
114
|
+
field: String!
|
|
115
|
+
refId: String!
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
input CreateNavigationItem {
|
|
119
|
+
title: String!
|
|
120
|
+
type: String!
|
|
121
|
+
path: String
|
|
122
|
+
externalPath: String
|
|
123
|
+
uiRouterKey: String!
|
|
124
|
+
menuAttached: Boolean!
|
|
125
|
+
order: Int!
|
|
126
|
+
parent: Int
|
|
127
|
+
master: Int
|
|
128
|
+
items: [CreateNavigationItem]
|
|
129
|
+
audience: [String]
|
|
130
|
+
related: CreateNavigationRelated
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
input CreateNavigation {
|
|
134
|
+
name: String!
|
|
135
|
+
items: [CreateNavigationItem]!
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
query: `
|
|
139
|
+
renderNavigation(navigationIdOrSlug: String!, type: NavigationRenderType, menuOnly: Boolean): [NavigationItem]!
|
|
140
|
+
renderNavigationChild(id: String!, childUIKey: String!, type: NavigationRenderType, menuOnly: Boolean): [NavigationItem]!
|
|
141
|
+
getNavigation: [Navigation]!
|
|
142
|
+
configNavigation: NavigationConfig
|
|
143
|
+
getByIdNavigation(id: String!): NavigationItem
|
|
144
|
+
`,
|
|
145
|
+
type: {},
|
|
146
|
+
mutation: `
|
|
147
|
+
navigationCreate(newNavigation: CreateNavigation!): Navigation!
|
|
148
|
+
navigationUpdate(id: String!, navigation: CreateNavigation!): Navigation!
|
|
149
|
+
`,
|
|
150
|
+
resolver: {
|
|
151
|
+
Query: {
|
|
152
|
+
renderNavigation: {
|
|
153
|
+
resolverOf: 'plugins::navigation.navigation.render',
|
|
154
|
+
resolver(obj, options) {
|
|
155
|
+
const { navigationIdOrSlug, type, menuOnly } = options;
|
|
156
|
+
return strapi.plugins.navigation.services.navigation.render(navigationIdOrSlug, type, menuOnly);
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
renderNavigationChild: {
|
|
160
|
+
resolverOf: 'plugins::navigation.navigation.renderChild',
|
|
161
|
+
async resolver(obj, options) {
|
|
162
|
+
const { id, childUIKey, type, menuOnly } = options;
|
|
163
|
+
return strapi.plugins.navigation.services.navigation.renderChildren(id, childUIKey, type, menuOnly);
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
getNavigation: {
|
|
167
|
+
resolverOf: 'plugins::navigation.navigation.get',
|
|
168
|
+
resolver() {
|
|
169
|
+
return strapi.plugins.navigation.services.navigation.get();
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
configNavigation: {
|
|
173
|
+
resolverOf: 'plugins::navigation.navigation.config',
|
|
174
|
+
resolver() {
|
|
175
|
+
return strapi.plugins.navigation.services.navigation.config();
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
getByIdNavigation: {
|
|
179
|
+
resolverOf: 'plugins::navigation.navigation.getById',
|
|
180
|
+
async resolver(obj, options) {
|
|
181
|
+
const { id } = options;
|
|
182
|
+
return strapi.plugins.navigation.services.navigation.getById(id);
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
Mutation: {
|
|
187
|
+
navigationCreate: {
|
|
188
|
+
resolverOf: 'plugins::navigation.navigation.post',
|
|
189
|
+
resolver(obj, options) {
|
|
190
|
+
const { newNavigation } = options;
|
|
191
|
+
return strapi.plugins.navigation.services.navigation.post(newNavigation);
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
navigationUpdate: {
|
|
195
|
+
resolverOf: 'plugins::navigation.navigation.put',
|
|
196
|
+
resolver(obj, options) {
|
|
197
|
+
const { id, navigation } = options;
|
|
198
|
+
return strapi.plugins.navigation.services.navigation.put(id, navigation);
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
|
|
@@ -23,35 +23,33 @@ const errorHandler = (ctx) => (error) => {
|
|
|
23
23
|
}
|
|
24
24
|
throw error;
|
|
25
25
|
};
|
|
26
|
+
const getService = () => strapi.plugins.navigation.services.navigation;
|
|
26
27
|
|
|
27
28
|
module.exports = {
|
|
28
|
-
getService() {
|
|
29
|
-
return strapi.plugins.navigation.services.navigation;
|
|
30
|
-
},
|
|
31
29
|
/**
|
|
32
30
|
* Default action.
|
|
33
31
|
*
|
|
34
32
|
* @return {Object}
|
|
35
33
|
*/
|
|
36
34
|
async config() {
|
|
37
|
-
return
|
|
35
|
+
return getService().config();
|
|
38
36
|
},
|
|
39
37
|
|
|
40
38
|
async get() {
|
|
41
|
-
return
|
|
39
|
+
return getService().get();
|
|
42
40
|
},
|
|
43
41
|
|
|
44
42
|
async getById(ctx) {
|
|
45
43
|
const { params } = ctx;
|
|
46
44
|
const { id } = parseParams(params);
|
|
47
|
-
return
|
|
45
|
+
return getService().getById(id);
|
|
48
46
|
},
|
|
49
47
|
|
|
50
48
|
async render(ctx) {
|
|
51
49
|
const { params, query = {} } = ctx;
|
|
52
50
|
const { type, menu: menuOnly } = query;
|
|
53
51
|
const { idOrSlug } = parseParams(params);
|
|
54
|
-
return
|
|
52
|
+
return getService().render(
|
|
55
53
|
idOrSlug,
|
|
56
54
|
type,
|
|
57
55
|
menuOnly,
|
|
@@ -61,7 +59,7 @@ module.exports = {
|
|
|
61
59
|
const { params, query = {} } = ctx;
|
|
62
60
|
const { type, menu: menuOnly } = query;
|
|
63
61
|
const { idOrSlug, childUIKey } = parseParams(params);
|
|
64
|
-
return
|
|
62
|
+
return getService().renderChildren(
|
|
65
63
|
idOrSlug,
|
|
66
64
|
childUIKey,
|
|
67
65
|
type,
|
|
@@ -72,14 +70,14 @@ module.exports = {
|
|
|
72
70
|
post(ctx) {
|
|
73
71
|
const { auditLog } = ctx;
|
|
74
72
|
const { body = {} } = ctx.request;
|
|
75
|
-
return
|
|
73
|
+
return getService().post(body, auditLog);
|
|
76
74
|
},
|
|
77
75
|
|
|
78
76
|
put(ctx) {
|
|
79
77
|
const { params, auditLog } = ctx;
|
|
80
78
|
const { id } = parseParams(params);
|
|
81
79
|
const { body = {} } = ctx.request;
|
|
82
|
-
return
|
|
80
|
+
return getService().put(id, body, auditLog)
|
|
83
81
|
.catch(errorHandler(ctx));
|
|
84
82
|
},
|
|
85
83
|
};
|