strapi-plugin-navigation 1.0.2 → 1.1.1

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.
@@ -6,7 +6,7 @@ jobs:
6
6
  environment:
7
7
  CODECOV_TOKEN: a9097475-0b5b-481f-8733-7b84574d583e
8
8
  docker:
9
- - image: circleci/node:12.13.0
9
+ - image: circleci/node:14.17.2
10
10
  working_directory: ~/repo
11
11
  steps:
12
12
  - checkout
@@ -0,0 +1,15 @@
1
+ daysUntilStale: 30
2
+ daysUntilClose: 7
3
+ exemptLabels:
4
+ - pinned
5
+ - security
6
+ - backlog
7
+ - enhancement
8
+ - feature request
9
+ staleLabel: wontfix
10
+ # Comment to post when marking an issue as stale. Set to `false` to disable
11
+ markComment: >
12
+ This issue has been automatically marked as stale because it has not had
13
+ recent activity. It will be closed if no further activity occurs. Thank you
14
+ for your contributions.
15
+ closeComment: false
package/.nvmrc CHANGED
@@ -1 +1 @@
1
- v12.13.0
1
+ v14.17.3
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.0 (recently tested)
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
- To enable Content Type to work with Navigation Item, you've to add following field to your model `*.settings.json`:
77
-
78
- ```
79
- "navigation": {
80
- "model": "navigationitem",
81
- "plugin": "navigation",
82
- "via": "related",
83
- "configurable": false,
84
- "hidden": true
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/custom.js` or `config/<env>/custom.js` file. If you've got already configurations for other plugins stores by this way, use just the `navigation` part within exising `plugins` item.
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
 
@@ -0,0 +1,31 @@
1
+ {
2
+ "uid": "plugins::another-plugin.blog-post",
3
+ "plugin": "another-plugin",
4
+ "kind": "collectionType",
5
+ "collectionName": "blog_posts",
6
+ "info": {
7
+ "name": "Blog post"
8
+ },
9
+ "options": {
10
+ "increments": true,
11
+ "timestamps": true,
12
+ "searchable": true,
13
+ "previewable": true
14
+ },
15
+ "attributes": {
16
+ "title": {
17
+ "type": "string",
18
+ "required": true
19
+ },
20
+ "altTitle": {
21
+ "type": "string"
22
+ },
23
+ "navigation": {
24
+ "model": "navigationitem",
25
+ "plugin": "navigation",
26
+ "via": "related",
27
+ "configurable": false,
28
+ "hidden": true
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "uid": "plugins::another-plugin.pages",
3
+ "plugin": "another-plugin",
4
+ "kind": "collectionType",
5
+ "collectionName": "pages",
6
+ "info": {
7
+ "name": "Pages"
8
+ },
9
+ "options": {
10
+ "increments": true,
11
+ "timestamps": true,
12
+ "searchable": true,
13
+ "previewable": true
14
+ },
15
+ "attributes": {
16
+ "title": {
17
+ "type": "string",
18
+ "required": true
19
+ },
20
+ "navigation": {
21
+ "model": "navigationitem",
22
+ "plugin": "navigation",
23
+ "via": "related",
24
+ "configurable": false,
25
+ "hidden": true
26
+ }
27
+ }
28
+ }
@@ -9,7 +9,8 @@
9
9
  "increments": true,
10
10
  "timestamps": true,
11
11
  "searchable": true,
12
- "previewable": true
12
+ "previewable": true,
13
+ "draftAndPublish": true
13
14
  },
14
15
  "attributes": {
15
16
  "title": {
@@ -0,0 +1,4 @@
1
+ {
2
+
3
+ "uid": "application::page-homes.home-page",
4
+ "kind": "singleType", "collectionName": "page_homes", "info": { "name": "Page Home" }, "options": { "increments": true, "timestamps": true, "draftAndPublish": true }, "attributes": { "Subtitle": { "type": "string" }, "Title": { "type": "string" }, "Summary": { "type": "text" }, "Image": { "model": "file", "via": "related", "allowedTypes": [ "images" ], "plugin": "upload", "required": false }, "Components": { "type": "dynamiczone", "components": [] }, "navigation": { "model": "navigationitem", "plugin": "navigation", "via": "related", "configurable": false, "hidden": true } } }
@@ -0,0 +1,27 @@
1
+ {
2
+ "uid": "application::my-homepages.my-homepage",
3
+ "kind": "singleType",
4
+ "collectionName": "my-homepages",
5
+ "info": {
6
+ "name": "My Homepage",
7
+ "description": ""
8
+ },
9
+ "options": {
10
+ "increments": true,
11
+ "timestamps": true,
12
+ "draftAndPublish": true,
13
+ "templateName": "AwesomeTemplate"
14
+ },
15
+ "attributes": {
16
+ "Welcome": {
17
+ "type": "string"
18
+ },
19
+ "navigation": {
20
+ "model": "navigationitem",
21
+ "plugin": "navigation",
22
+ "via": "related",
23
+ "configurable": false,
24
+ "hidden": true
25
+ }
26
+ }
27
+ }
@@ -1,6 +1,10 @@
1
+ const {get} = require('lodash');
1
2
  function setupStrapi() {
2
3
  Object.defineProperty(global, 'strapi', {
3
4
  value: {
5
+ query: jest.fn().mockImplementation(() => ({
6
+ count: jest.fn().mockImplementation(),
7
+ })),
4
8
  config: {
5
9
  custom: {
6
10
  plugins: {
@@ -11,27 +15,82 @@ function setupStrapi() {
11
15
  },
12
16
  },
13
17
  },
18
+ get(path, defaultValue) {
19
+ return get(strapi, path, defaultValue);
20
+ },
21
+ },
22
+ api: {
23
+ 'home-page': {
24
+ config: {
25
+ routes: [
26
+ {
27
+ method: 'GET',
28
+ path: '/custom-api',
29
+ handler: 'home-page.find',
30
+ },
31
+ {
32
+ method: 'PUT',
33
+ path: '/custom-api',
34
+ handler: 'home-page.update',
35
+ },
36
+ ]
37
+ }
38
+ }
14
39
  },
15
40
  contentTypes: {
16
- 'page': {
41
+ 'pages': {
17
42
  ...require('./pages.settings.json'),
18
43
  apiName: 'pages',
44
+ modelName: 'pages',
19
45
  associations: [{ model: 'navigationitem' }],
20
46
  },
21
- 'blog-post': {
47
+ 'application::blog-post.blog-post': {
22
48
  ...require('./blog-post.settings.json'),
23
49
  apiName: 'blog-posts',
50
+ modelName: 'blog-posts',
51
+ associations: [{ model: 'navigationitem' }],
52
+ },
53
+ 'application::my-homepages.my-homepage': {
54
+ ...require('./my-homepage.settings.json'),
55
+ apiName: 'my-homepage',
56
+ modelName: 'my-homepage',
24
57
  associations: [{ model: 'navigationitem' }],
25
58
  },
59
+ 'application::page-homes.home-page': {
60
+ ...require('./home-page.settings.json'),
61
+ apiName: 'custom-api',
62
+ modelName: 'home-page',
63
+ associations: [{ model: 'navigationitem' }],
64
+ },
65
+ 'plugins::another-plugin.pages': {
66
+ ...require('./another-plugin/pages.settings.json'),
67
+ modelName: 'plugin-pages',
68
+ associations: [{ model: 'navigationitem' }],
69
+ },
70
+ 'plugins::another-plugin.blog-post': {
71
+ ...require('./another-plugin/blog-post.settings.json'),
72
+ modelName: 'plugin-blog-posts',
73
+ associations: [{ model: 'navigationitem' }],
74
+ }
26
75
  },
27
76
  plugins: {
28
77
  navigation: {
29
78
  services: {
30
79
  navigation: jest.fn().mockImplementation(),
31
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
+ ]
89
+ },
90
+ anotherPlugin: {
32
91
  models: {
33
- 'pages': require('./pages.settings.json'),
34
- 'blog-post': require('./blog-post.settings.json'),
92
+ 'plugin-pages': require('./another-plugin/pages.settings.json'),
93
+ 'plugin-blog-post': require('./another-plugin/blog-post.settings.json'),
35
94
  }
36
95
  }
37
96
  },
@@ -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
  }),
@@ -58,13 +58,13 @@ const View = () => {
58
58
  const navigationSelectValue = get(activeNavigation, "id", null);
59
59
  const actions = [
60
60
  {
61
- label: "Cancel",
61
+ label: formatMessage(getTrad('submit.cta.cancel')),
62
62
  onClick: () => isLoadingForSubmit ? null : handleResetNavigationData(),
63
63
  color: "cancel",
64
64
  type: "button",
65
65
  },
66
66
  {
67
- label: "Save",
67
+ label: formatMessage(getTrad('submit.cta.save')),
68
68
  onClick: () =>
69
69
  isLoadingForSubmit || structureHasErrors ? null : handleSubmitNavigation(formatMessage, transformToRESTPayload(changedActiveNavigation, config)),
70
70
  color: "success",
@@ -1,5 +1,5 @@
1
1
  import { isUuid, uuid } from 'uuidv4';
2
- import { find, get, isArray, isEmpty, isNil, isNumber, isObject, isString, kebabCase, last, omit, orderBy } from 'lodash';
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
- const { id, name, visible, items } = payload;
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 => kebabCase(ct.contentTypeName) === kebabCase(relatedItem.__contentType), {});
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;
@@ -2,6 +2,8 @@
2
2
  "plugin.name": "UI Navigation",
3
3
  "header.title": "Navigation",
4
4
  "header.description": "Define your portal navigation",
5
+ "submit.cta.cancel": "Cancel",
6
+ "submit.cta.save": "Save",
5
7
  "empty": "Navigation container is empty",
6
8
  "empty.cta": "Create first item",
7
9
  "popup.item.header": "Manage navigation item",
@@ -0,0 +1,39 @@
1
+ {
2
+ "plugin.name": "UI Navigation",
3
+ "header.title": "Navigation",
4
+ "header.description": "Définisser votre menu de navigation",
5
+ "submit.cta.cancel": "Annuler",
6
+ "submit.cta.save": "Sauvegarder",
7
+ "empty": "Le menu de navigation est vide",
8
+ "empty.cta": "Créer un premier élément",
9
+ "popup.item.header": "Modifiez les éléments de navigation",
10
+ "popup.item.form.title.label": "Titre",
11
+ "popup.item.form.title.placeholder": "Saisissez le titre de l'élément ou laissez le champ vide pour utiliser celui de l'entité associée",
12
+ "popup.item.form.uiRouterKey.label": "Clé de routeur pour l'interface utilisateur",
13
+ "popup.item.form.uiRouterKey.placeholder": "Si vide, généré automatiquement par \"Title\"",
14
+ "popup.item.form.path.label": "URL",
15
+ "popup.item.form.path.placeholder": "Une partie d'URL unique identifie cet élément",
16
+ "popup.item.form.path.preview": "Aperçu:",
17
+ "popup.item.form.externalPath.label": "URL externe",
18
+ "popup.item.form.externalPath.placeholder": "Lien vers la source externe",
19
+ "popup.item.form.externalPath.validation.type": "Cette valeur d'URL n'est pas correcte.",
20
+ "popup.item.form.menuAttached.label": "Ajouter au menu",
21
+ "popup.item.form.type.label": "Type",
22
+ "popup.item.form.type.internal.label": "Interne",
23
+ "popup.item.form.type.external.label": "Externe",
24
+ "popup.item.form.audience.label": "Audience",
25
+ "popup.item.form.audience.placeholder": "Écrivez pour lancer la recherche...",
26
+ "popup.item.form.relatedSection.label": "Relation avec",
27
+ "popup.item.form.relatedType.label": "Type de contenu",
28
+ "popup.item.form.related.label": "Entitée",
29
+ "popup.item.form.related.empty": "Il n'y a plus d'entitée \"{ contentTypeName }\", que vous pouvez sélectionner",
30
+ "popup.item.form.button.create": "Créer un élément",
31
+ "popup.item.form.button.update": "Modifier l'élément",
32
+ "popup.item.form.button.restore": "Restaurer l'élément",
33
+ "popup.item.form.button.remove": "Supprimer",
34
+ "notification.navigation.submit": "Les modifications de navigation ont été enregistrées",
35
+ "notification.navigation.error": "Chemin indisponible: { path } dans le parent: { parentTitle } pour l'élément { errorTitles }",
36
+ "notification.navigation.item.relation": "L'entitée n'a pas de relations!",
37
+ "notification.navigation.item.relation.status.draft": "brouillon",
38
+ "notification.navigation.item.relation.status.published": "publié"
39
+ }
@@ -1,8 +1,10 @@
1
1
  import pluginId from "../pluginId";
2
2
  import en from "./en.json";
3
+ import fr from "./fr.json";
3
4
 
4
5
  const trads = {
5
- en,
6
+ en,
7
+ fr,
6
8
  };
7
9
 
8
10
  export const getTradId = (msg) => `${pluginId}.${msg}`;
@@ -1,26 +1,112 @@
1
- const { isEmpty } = require("lodash");
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("users-permissions") === -1) {
91
+ if (Object.keys(strapi.plugins).indexOf('users-permissions') === -1) {
6
92
  throw new Error(
7
- "In order to make the navigation plugin work the users-permissions plugin is required",
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: "plugins",
15
- displayName: "Access the Navigation",
16
- uid: "read",
17
- pluginName: "navigation",
100
+ section: 'plugins',
101
+ displayName: 'Access the Navigation',
102
+ uid: 'read',
103
+ pluginName: 'navigation',
18
104
  },
19
105
  {
20
- section: "plugins",
21
- displayName: "Ability to change the Navigation",
22
- uid: "update",
23
- pluginName: "navigation",
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);
@@ -47,6 +47,14 @@
47
47
  "config": {
48
48
  "policies": []
49
49
  }
50
+ },
51
+ {
52
+ "method": "GET",
53
+ "path": "/render/:idOrSlug/:childUIKey",
54
+ "handler": "navigation.renderChild",
55
+ "config": {
56
+ "policies": []
57
+ }
50
58
  }
51
59
  ]
52
60
  }