diff --git a/README.md b/README.md index d56496067c..12d7164b79 100755 --- a/README.md +++ b/README.md @@ -82,4 +82,4 @@ For general help using Strapi, please refer to [the official Strapi documentatio ## License -[MIT License](LICENSE.md) Copyright (c) 2015-2017 [Strapi Solutions](http://strapi.io/). +[MIT License](LICENSE.md) Copyright (c) 2015-2018 [Strapi Solutions](http://strapi.io/). diff --git a/docs/3.x.x/en/README.md b/docs/3.x.x/en/README.md index 3ab6a67901..8b17e2ae75 100644 --- a/docs/3.x.x/en/README.md +++ b/docs/3.x.x/en/README.md @@ -29,8 +29,8 @@ Get to know more about Strapi and how it works. **[Guides](configurations/configurations.md)**
Get familiar with Strapi. Discover concrete examples on how to develop the features you need. -**[Plugins](plugins/development.md)**
-Understand how to install plugins and develop your owns. +**[Plugin Development](plugin-development/quick-start.md)**
+Understand how to develop your own plugin. **[API Reference](api-reference/reference.md)**
Learn about Strapi's API, the `strapi` object that is available in your backend. diff --git a/docs/3.x.x/en/SUMMARY.md b/docs/3.x.x/en/SUMMARY.md index ae5b95987f..240c6098dc 100644 --- a/docs/3.x.x/en/SUMMARY.md +++ b/docs/3.x.x/en/SUMMARY.md @@ -30,12 +30,14 @@ * [Routing](guides/routing.md) * [Services](guides/services.md) -### Plugins -* [Quick start](plugins/quick-start.md) -* [Development](plugins/development.md) -* [Helpers](plugins/utils.md) -* [UI Components](plugins/ui-components.md) -* [Advanced usage](plugins/advanced.md) +### Plugin Development +* [Quick start](plugin-development/quick-start.md) +* [Plugin Folders and Files Architecture](plugin-development/plugin-architecture.md) +* [Back-end Development](plugin-development/backend-development.md) +* [Front-end Development](plugin-development/frontend-development.md) +* [Front-end Use Cases](plugin-development/frontend-use-cases.md) +* [Front-end Helpers](plugin-development/utils.md) +* [Front-end UI Components](plugin-development/ui-components.md) ### Advanced Usage * [Admin panel](advanced/customize-admin.md) diff --git a/docs/3.x.x/en/api-reference/reference.md b/docs/3.x.x/en/api-reference/reference.md index 1f3f31fdcb..c9087acf1d 100644 --- a/docs/3.x.x/en/api-reference/reference.md +++ b/docs/3.x.x/en/api-reference/reference.md @@ -77,7 +77,7 @@ Returns an object of plugins available in the project. Each plugin object contai ## strapi.query -Returns a function that will returns the available queries for this model. This feature is only available inside the plugin's files (controllers, services, custom functions). For more details, see the [ORM queries section](../plugins/development.md#ORM queries). +Returns a function that will returns the available queries for this model. This feature is only available inside the plugin's files (controllers, services, custom functions). For more details, see the [ORM queries section](../plugin-development/backend-development.md#ORM queries). ## strapi.reload diff --git a/docs/3.x.x/en/cli/CLI.md b/docs/3.x.x/en/cli/CLI.md index 58d3c9593e..6a9cef2c2e 100644 --- a/docs/3.x.x/en/cli/CLI.md +++ b/docs/3.x.x/en/cli/CLI.md @@ -170,7 +170,7 @@ strapi generate:plugin Example: `strapi generate:plugin user` will create the plugin at `./plugins/user`. -Please refer to the [plugins documentation](../plugins/development.md) to know more. +Please refer to the [plugin develoment documentation](../plugin-development/quick-start.md) to know more. *** @@ -198,7 +198,7 @@ options: [--dev] > **Note: You have to restart the server to load the plugin into your project.** -Please refer to the [plugins documentation](../plugins/quick-start.md) to know more. +Please refer to the [plugins documentation](../plugin-development/quick-start.md) to know more. *** @@ -215,7 +215,7 @@ strapi uninstall Example: `strapi uninstall content-type-builder` will remove the folder at `./plugins/content-type-builder`. -Please refer to the [plugins documentation](../plugins/quick-start.md) to know more. +Please refer to the [plugins documentation](../plugin-development/quick-start.md) to know more. *** diff --git a/docs/3.x.x/en/concepts/concepts.md b/docs/3.x.x/en/concepts/concepts.md index 20bca1e95d..3acf8ac547 100644 --- a/docs/3.x.x/en/concepts/concepts.md +++ b/docs/3.x.x/en/concepts/concepts.md @@ -200,7 +200,7 @@ Internationalization and localization (i18n) allows to adapt the project to diff A plugin is like a sub-app fully independent. It has its own business logic with its dedicated models, controllers, services, middlewares or hooks. It can also contains an UI integrated into the admin panel to use it easily. It allows to develops or plugs features in a project in a short time span. -> Please refer to the [plugins documentation](../plugins/quick-start.md) for more informations. +> Please refer to the [plugins documentation](../plugin-development/quick-start.md) for more informations. *** @@ -208,7 +208,7 @@ A plugin is like a sub-app fully independent. It has its own business logic with The admin panel uses [Bootstrap](http://getbootstrap.com/) to be styled on top of solid conventions and reusable CSS classes. Also, it uses [PostCSS](https://github.com/postcss/postcss) and [PostCSS SCSS](https://github.com/postcss/postcss-scss) to keep the code maintainable. -> Please refer to the [plugin development](../plugins/development.md#styles) for detailed informations. +> Please refer to the [plugin front-end development](../plugin-development/frontend-development.md#styling) for detailed informations. *** diff --git a/docs/3.x.x/en/configurations/configurations.md b/docs/3.x.x/en/configurations/configurations.md index eb913091fe..cadad1a605 100644 --- a/docs/3.x.x/en/configurations/configurations.md +++ b/docs/3.x.x/en/configurations/configurations.md @@ -53,7 +53,7 @@ These configurations are accessible through `strapi.config.backendURL` and `stra ## Language -As described in the [i18n documentation](../plugins/development.md#i18n), Strapi includes an internationalization system. This is especially useful to translate API messages (errors, etc.). +As described in the [i18n documentation](../plugin-development/frontend-development.md#i18n), Strapi includes an internationalization system. This is especially useful to translate API messages (errors, etc.). **Path —** `./config/language.json`. ```json diff --git a/docs/3.x.x/en/plugin-development/backend-development.md b/docs/3.x.x/en/plugin-development/backend-development.md new file mode 100644 index 0000000000..e0d495d31a --- /dev/null +++ b/docs/3.x.x/en/plugin-development/backend-development.md @@ -0,0 +1,202 @@ +# Back-end Development + +This section explains how the 'back-end part' of your plugin works. + +## Routes + +The plugin API routes are defined in the `./plugins/**/config/routes.json` file. + +> Please refer to [router documentation](../guides/routing.md) for informations. + +**Route prefix** + +Each route of a plugin is prefixed by the name of the plugin (eg: `/my-plugin/my-plugin-route`). + +To disable the prefix, add the `prefix` attribute to each concerned route, like below: +```json +{ + "method": "GET", + "path": "/my-plugin-route", + "handler": "MyPlugin.action", + "prefix": false +} +``` + +## CLI + +The CLI can be used to generate files in the plugins folders. + +Please refer to the [CLI documentation](../cli/CLI.md) for more informations. + +## Controllers + +Controllers contain functions executed according to the requested route. + +Please refer to the [Controllers documentation](../guides/controllers.md) for more informations. + +## Models + +A plugin can have its own models. + +### Table/Collection naming + +Sometimes it happens that the plugins inject models that have the same name as yours. Let's take a quick example. + +You already have `User` model defining in your `./api/user/models/User.settings.json` API. And you decide to install the `Users & Permissions` plugin. This plugin also contains a `User` model. To avoid the conflicts, the plugins' models are not globally exposed which means you cannot access to the plugin's model like this: + +```js +module.exports = { + findUser: async function (params) { + // This `User` global variable will always make a reference the User model defining in your `./api/xxx/models/User.settings.json`. + return await User.find(); + } +} +``` + +Also, the table/collection name won't be `users` because you already have a `User` model. That's why, the framework will automatically prefix the table/collection name for this model with the name of the plugin. Which means in our example, the table/collection name of the `User` model of our plugin `Users & Permissions` will be `users-permissions_users`. If you want to force the table/collection name of the plugin's model, you can add the `collectionName` attribute in your model. + + +Please refer to the [Models documentation](../guides/models.md) for more informations. + +## Policies + +### Global policies + +A plugin can also use a globally exposed policy in the current Strapi project. + +```json +{ + "routes": [ + { + "method": "GET", + "path": "/", + "handler": "MyPlugin.index", + "config": { + "policies": [ + "global.isAuthenticated" + ] + } + } + ] +} +``` + +### Plugin policies + +A plugin can have its own policies, such as adding security rules. For instance, if the plugin includes a policy named `isAuthenticated`, the syntax to use this policy would be: + +```json +{ + "routes": [ + { + "method": "GET", + "path": "/", + "handler": "MyPlugin.index", + "config": { + "policies": [ + "plugins.myPlugin.isAuthenticated" + ] + } + } + ] +} +``` + +Please refer to the [Policies documentation](../guides/policies.md) for more informations. + +## ORM queries + +Strapi supports multiple ORMs in order to let the users choose the database management system that suits their needs. Hence, each plugin must be compatible with at least one ORM. Each plugin contains a folder named `queries` in `./plugins/**/api/queries`. A folder must be created for each ORM (eg. `mongoose`) with a file named `mongoose.js` which exports the Mongoose ORM related queries. + +The queries are accessible through the `strapi.query()` method, which automatically contains the queries according to the ORM used by the model. + +### Example + +Mongoose ORM queries definition: + +**Path —** `./plugins/my-plugin/api/config/queries/mongoose/index.js`. +```js +module.exports = { + getUsers: async (params) => { + return User.find(params); + } +} +``` + +Bookshelf ORM queries definition: + +**Path —** `./plugins/my-plugin/api/config/queries/bookshelf/index.js`. +```js +module.exports = { + getUsers: async (params) => { + return User.fetchAll(params); + } +} +``` + +Usage from the plugin: + +**Path —** `./plugins/my-plugin/api/controllers/index.js`. +```js +module.exports = { + getUsers: async () => { + // Get parameters from the request + const { limit, sort } = ctx.request.query; + + // Get the list of users using the plugins queries + const users = await strapi.query('User').getUsers({ limit, sort }); + + // Send the list of users as response + ctx.body = users; + } +} +``` + +### Advanced usage + +Each function in the query file is bound with the ORM's model. It means that you can create generic query very easily. This feature is useful for CRUD such as we did in the [Content Manager plugin](https://github.com/strapi/strapi/tree/master/packages/strapi-plugin-content-manager/config/queries). + +Mongoose ORM generic queries: + +**Path —** `./plugins/my-plugin/api/config/queries/mongoose/index.js`. +```js +module.exports = { + getAll: async function (params) { + // this refers to the Mongoose model called in the query + // ex: strapi.query('User').getAll(), this will be equal to the User Mongoose model. + return this.find(params); + } +} +``` + +Bookshelf ORM generic queries: + +**Path —** `./plugins/my-plugin/api/config/queries/bookshelf/index.js`. +```js +module.exports = { + getAll: async function (params) { + // this refers to the Bookshelf model called in the query + // ex: strapi.query('User').getAll(), this will be equal to the User Bookshelf model. + return this.fetchAll(params); + } +} +``` + +Usage from the plugin: + +**Path —** `./plugins/my-plugin/api/controllers/index.js`. +```js +module.exports = { + getUsers: async () => { + // Get parameters from the request + const { limit, sort } = ctx.request.query; + + // Get the list of users using the plugin's queries + const users = await strapi.query('User').getAll({ limit, sort }); + + // Send the list of users as response + ctx.body = users; + } +} +``` +*** diff --git a/docs/3.x.x/en/plugin-development/frontend-development.md b/docs/3.x.x/en/plugin-development/frontend-development.md new file mode 100644 index 0000000000..e203b5d8b3 --- /dev/null +++ b/docs/3.x.x/en/plugin-development/frontend-development.md @@ -0,0 +1,142 @@ +# Front-end Development + +This section explains how to create your plugin interface in the admin panel. Refer to the Plugin Development [Quick Start Section](./quick-start.md) to start the project in development mode. + +## Introduction + +Strapi's admin panel and plugins system aim to be an easy and powerful way to create new features. + +The admin panel is a [React](https://facebook.github.io/react/) application which can embed other React applications. These other React applications are the `admin` parts of each Strapi's plugins. + +## Routing + +The routing is based on the [React Router V4](https://reacttraining.com/react-router/web/guides/philosophy), due to it's implementation each route is declared in the `containers/App/index.js` file. + +Also, we chose to use the [Switch Router](https://reacttraining.com/react-router/web/api/Switch) because it renders a route exclusively. + +**Route declaration :** + +Let's say that you want to create a route `/user` with params `/:id` associated with the container UserPage. + +The declaration would be as followed : + +**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`. +```js +import React from 'react'; +import UserPage from 'containers/UserPage'; + +// ... + +class App extends React.Component { + // ... + + render() { + return ( +
+ + + +
+ ); + } +} + +// ... +``` +See the [Front-end Use Cases](./frontend-use-cases.md#handle-user-navigation) for more informations. + +## Data flow + +Each plugin has its own data store, so it stays completely independent from the others. + +Data flow is controlled thanks to Redux and redux-sagas. + +## Styling + +The [Bootstrap styles](http://getbootstrap.com/) are inherited by the plugins. However, each component has its own styles, so it possible to completely customize it. + +**See the [plugin styles](../concepts/concepts.md#plugin-styles) for informations on its concept.** + +To style a plugin component: + - Add a `styles.scss` file in the component directory + - Require it from the `index.js` file (`import styles from './styles.scss';`) + - Add some styles in the `styles.scss` file + +``` +.wrapper { + display: block; + background: red; + height: 100px; + width: 100px; +} +``` + +Use this style in the component: `
`. + +Note: if you want to use several classes: + +```js +import cn from 'classnames'; +import styles from './styles.scss'; + +// ... + +return ( +
{this.props.children}
+); + +// ... + +``` + +## i18n + +[React Intl](https://github.com/yahoo/react-intl) provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations. + +**Usage** + +We recommend to set all your components text inside the translations folder. + +The example below shows how to use i18n inside your plugin. + +**Define all your ids with the associated message:** + +**Path —** `./plugins/my-plugin/admin/src/translations/en.json`. +```json +{ + "notification.error.message": "An error occurred" +} +``` + +**Path —** `./plugins/my-plugin/admin/src/translations/fr.json` +```json +{ + "notification.error.message": "Une erreur est survenue" +} +``` + +**Usage inside a component** + +**Path —** `./plugins/my-plugin/admin/src/components/Foo/index.js`. +```js +import { FormattedMessage } from 'react-intl'; +import SomeOtherComponent from 'components/SomeOtherComponent'; + +const Foo = (props) => ( +
+ + +
+) + +export default Foo; +``` + +See [the documentation](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage) for more extensive usage. + +## Generators + +You can use generators to create React components or containers for your plugin. + +1. In your terminal go to your plugin folder `cd plugins/my-plugin` +2. Run `npm run generate` and choose the type of component your want to create diff --git a/docs/3.x.x/en/plugins/advanced.md b/docs/3.x.x/en/plugin-development/frontend-use-cases.md similarity index 99% rename from docs/3.x.x/en/plugins/advanced.md rename to docs/3.x.x/en/plugin-development/frontend-use-cases.md index 80ffc15bcf..3f6acf137c 100644 --- a/docs/3.x.x/en/plugins/advanced.md +++ b/docs/3.x.x/en/plugin-development/frontend-use-cases.md @@ -1,3 +1,7 @@ +# Front-end Use Cases + +This section gives use cases examples on front-end plugin development. + # Plugin advanced usage This section contains advanced resources to develop plugins. @@ -321,8 +325,6 @@ Just by doing so, the `another-plugin` will add a `Button` which toggles the `lo ## Routeless container store injection -See the basic container's store injection [documentation](./development.md#using-redux-sagas). - If you have a container which can be a child of several other containers (i.e. it doesn't have a route); you'll have to inject it directly in the `./plugins/my-plugin/admin/src/containers/App/index.js` file as follows : **Path —** `./plugins/my-plugin/admin/src/containers/App/index.js`. @@ -383,7 +385,7 @@ export default compose( *** -## Execute a logic before mounting +## Execute logic before mounting the plugin You can execute a business logic before your plugin is being mounted. @@ -416,7 +418,7 @@ export default bootstrap; *** -## Prevent rendering +## Prevent plugin rendering You can prevent your plugin from being rendered if some conditions aren't met. diff --git a/docs/3.x.x/en/plugin-development/plugin-architecture.md b/docs/3.x.x/en/plugin-development/plugin-architecture.md new file mode 100644 index 0000000000..d558c7972a --- /dev/null +++ b/docs/3.x.x/en/plugin-development/plugin-architecture.md @@ -0,0 +1,38 @@ +# Plugin Folders and Files Architecture + +The logic of a plugin is located at his root directory `./plugins/**`. The admin panel related parts of each plugin is contained in the `/admin` folder. +The folders and files structure is the following: + +``` +/plugin +└─── admin // Contains the plugin's front-end +| └─── build // Webpack build of the plugin +| └─── src // Source code directory +| └─── bootstrap.js // (Optional) Contains the logic to execute before rendering the plugin +| └─── components // Contains the list of React components used by the plugin +| └─── containers +| | └─── App // Container used by every others containers +| | └─── HomePage +| | └─── action.js // List of Redux actions used by the current container +| | └─── constants.js // List of actions constants +| | └─── index.js // React component of the current container +| | └─── reducer.js // Redux reducer used by the current container +| | └─── sagas.js // List of sagas functions +| | └─── selectors.js // List of selectors +| | └─── styles.scss // Style of the current container +| | +| └─── requirements.js // (Optional) Contains the logic to prevent a plugin from being rendered +| └─── translations // Contains the translations to make the plugin internationalized +| └─── en.json +| └─── fr.json +└─── config // Contains the configurations of the plugin +| └─── functions +| | └─── bootstrap.js // Asynchronous bootstrap function that runs before the app gets started +| └─── policies // Folder containing the plugin's policies +| └─── queries // Folder containing the plugin's models queries +| └─── routes.json // Contains the plugin's API routes +└─── controllers // Contains the plugin's API controllers +└─── middlewares // Contains the plugin's middlewares +└─── models // Contains the plugin's API models +└─── services // Contains the plugin's API services +``` diff --git a/docs/3.x.x/en/plugins/plugin-left-menu.md b/docs/3.x.x/en/plugin-development/plugin-left-menu.md similarity index 100% rename from docs/3.x.x/en/plugins/plugin-left-menu.md rename to docs/3.x.x/en/plugin-development/plugin-left-menu.md diff --git a/docs/3.x.x/en/plugin-development/quick-start.md b/docs/3.x.x/en/plugin-development/quick-start.md new file mode 100644 index 0000000000..ba349b11e9 --- /dev/null +++ b/docs/3.x.x/en/plugin-development/quick-start.md @@ -0,0 +1,29 @@ +# Quick start + +To facilitate the development of a plugin, we drastically reduce the amount of commands necessary to install the entire development environment. Before getting started, you need to have Node.js (v8) and npm (v5) installed. + +## Development Environment Setup + +To setup the development environment please **follow the instructions below:** + +1. [Fork the repository](https://github.com/strapi/strapi) to your own GitHub account. +2. Clone it to your computer `git clone git@github.com:strapi/strapi.git`. +3. Run `npm run setup` at the root of the directory. + +> Note: If the installation failed, please remove the global packages related to Strapi. The command `npm ls strapi` will help you to find where your packages are installed globally. + +## Plugin development Setup + +Create a development project + +1. Go to a folder on your computer `cd /path/to/my/folder`. +2. Create a new project `strapi new myDevelopmentProject --dev`. + +To generate a new plugin **run the following commands:** +1. In your project folder `cd myDevelopmentProject && strapi generate:plugin my-plugin`. +2. Link the `strapi-helper-plugin` dependency in your project folder `cd pathToMyProject/myDevelopmentProject/plugins/my-plugin && npm link strapi-helper-plugin`. +3. Start the server in the admin folder `cd pathToMyProject/myDevelopmentProject/admin && npm start` and go to the following url [http://localhost:4000/admin](http://localhost:4000/admin). +4. In a new terminal window open at the root of your project launch your Strapi server `strapi start`. + + +Your are now ready to develop your own plugin and live-test your updates! diff --git a/docs/3.x.x/en/plugins/ui-components.md b/docs/3.x.x/en/plugin-development/ui-components.md similarity index 98% rename from docs/3.x.x/en/plugins/ui-components.md rename to docs/3.x.x/en/plugin-development/ui-components.md index f4448f8279..0396a12233 100644 --- a/docs/3.x.x/en/plugins/ui-components.md +++ b/docs/3.x.x/en/plugin-development/ui-components.md @@ -84,9 +84,9 @@ export default Foo; ## ExtendComponent -ExtendComponent allows a plugin to injectDesign into another one. +ExtendComponent allows a plugin to inject components into another one. -> Refer to the advanced plugin [documentation](./advanced.md#inject-design) to see how to use it. +> Refer to the use cases [documentation](./frontend-use-cases.md#inject-design) to see how to use it. *** diff --git a/docs/3.x.x/en/plugins/utils.md b/docs/3.x.x/en/plugin-development/utils.md similarity index 99% rename from docs/3.x.x/en/plugins/utils.md rename to docs/3.x.x/en/plugin-development/utils.md index 22a94f63ef..ed2903421e 100644 --- a/docs/3.x.x/en/plugins/utils.md +++ b/docs/3.x.x/en/plugin-development/utils.md @@ -9,7 +9,7 @@ A request helper is available to handle all requests inside a plugin. It takes three arguments: - `requestUrl`: The url we want to fetch. - `options`: Please refer to this [documentation](https://github.com/github/fetch). -- `true`: This third argument is optional. If true is passed the response will be sent only if the server has restarted check out the [example](#example-with-server-autoReload-watcher). +- `true`: This third argument is optional. If true is passed the response will be sent only if the server has restarted check out the [example](#example-with-server-autoreload-watcher). ### Usage diff --git a/docs/3.x.x/en/plugins/development.md b/docs/3.x.x/en/plugins/development.md deleted file mode 100644 index a459844b20..0000000000 --- a/docs/3.x.x/en/plugins/development.md +++ /dev/null @@ -1,506 +0,0 @@ -# Development - -Any Strapi plugin can contain two parts: an [API](#plugin-api-development) and a [plugin admin interface](#plugin-admin-interface-development). This section explains how to change each of these two parts after plugin creation, or modify an existing plugin. - - - -*** - -## Back-end - -This section explains how the 'backend part' of your plugin works. - -Table of contents: - - [Folders and files structure](#folders-and-files-structure) - - [Routes](#routes) - - [CLI](#cli) - - [Controllers](#controllers) - - [Models](#models) - - [Policies](#policies) - - [ORM queries](#orm-queries) - -### Folders and files structure - -The logic of a plugin is located at his root directory `./plugins/**`. The folders and files structure is the following: -``` -/plugin -└─── admin // Contains the plugin's front-end -└─── config // Contains the configurations of the plugin -| └─── routes.json // Contains the plugin's API routes -└─── controllers // Contains the plugin's API controllers -└─── models // Contains the plugin's API models -└─── services // Contains the plugin's API services -``` - -### Routes - -The plugin API routes are defined in the `./plugins/**/config/routes.json` file. - -> Please refer to [router documentation](../guides/routing.md) for informations. - -**Route prefix** - -Each route of a plugin is prefixed by the name of the plugin (eg: `/my-plugin/my-plugin-route`). - -To disable the prefix, add the `prefix` attribute to each concerned route, like below: -```json -{ - "method": "GET", - "path": "/my-plugin-route", - "handler": "MyPlugin.action", - "prefix": false -} -``` - -### CLI - -The CLI can be used to generate files in the plugins folders. - -Please refer to the [CLI documentation](../cli/CLI.md) for more informations. - -### Controllers - -Controllers contain functions executed according to the requested route. - -Please refer to the [Controllers documentation](../guides/controllers.md) for more informations. - -### Models - -A plugin can have its own models. - -##### Table/Collection naming - -Sometimes it happens that the plugins inject models that have the same name as yours. Let's take a quick example. - -You already have `User` model defining in your `./api/user/models/User.settings.json` API. And you decide to install the `Users & Permissions` plugin. This plugin also contains a `User` model. To avoid the conflicts, the plugins' models are not globally exposed which means you cannot access to the plugin's model like this: - -```js -module.exports = { - findUser: async function (params) { - // This `User` global variable will always make a reference the User model defining in your `./api/xxx/models/User.settings.json`. - return await User.find(); - } -} -``` - -Also, the table/collection name won't be `users` because you already have a `User` model. That's why, the framework will automatically prefix the table/collection name for this model with the name of the plugin. Which means in our example, the table/collection name of the `User` model of our plugin `Users & Permissions` will be `users-permissions_users`. If you want to force the table/collection name of the plugin's model, you can add the `collectionName` attribute in your model. - - -Please refer to the [Models documentation](../guides/models.md) for more informations. - -### Policies - -#### Global policies - -A plugin can also use a globally exposed policy in the current Strapi project. - -```json -{ - "routes": [ - { - "method": "GET", - "path": "/", - "handler": "MyPlugin.index", - "config": { - "policies": [ - "global.isAuthenticated" - ] - } - } - ] -} -``` - -#### Plugin policies - -A plugin can have its own policies, such as adding security rules. For instance, if the plugin includes a policy named `isAuthenticated`, the syntax to use this policy would be: - -```json -{ - "routes": [ - { - "method": "GET", - "path": "/", - "handler": "MyPlugin.index", - "config": { - "policies": [ - "plugins.myPlugin.isAuthenticated" - ] - } - } - ] -} -``` - -Please refer to the [Policies documentation](../guides/policies.md) for more informations. - -### ORM queries - -Strapi supports multiple ORMs in order to let the users choose the database management system that suits their needs. Hence, each plugin must be compatible with at least one ORM. Each plugin contains a folder named `queries` in `./plugins/**/api/queries`. A folder must be created for each ORM (eg. `mongoose`) with a file named `mongoose.js` which exports the Mongoose ORM related queries. - -The queries are accessible through the `strapi.query()` method, which automatically contains the queries according to the ORM used by the model. - -#### Example - -Mongoose ORM queries definition: - -**Path —** `./plugins/my-plugin/api/config/queries/mongoose/index.js`. -```js -module.exports = { - getUsers: async (params) => { - return User.find(params); - } -} -``` - -Bookshelf ORM queries definition: - -**Path —** `./plugins/my-plugin/api/config/queries/bookshelf/index.js`. -```js -module.exports = { - getUsers: async (params) => { - return User.fetchAll(params); - } -} -``` - -Usage from the plugin: - -**Path —** `./plugins/my-plugin/api/controllers/index.js`. -```js -module.exports = { - getUsers: async () => { - // Get parameters from the request - const { limit, sort } = ctx.request.query; - - // Get the list of users using the plugins queries - const users = await strapi.query('User').getUsers({ limit, sort }); - - // Send the list of users as response - ctx.body = users; - } -} -``` - -#### Advanced usage - -Each function in the query file is bound with the ORM's model. It means that you can create generic query very easily. This feature is useful for CRUD such as we did in the [Content Manager plugin](https://github.com/strapi/strapi/tree/master/packages/strapi-plugin-content-manager/config/queries). - -Mongoose ORM generic queries: - -**Path —** `./plugins/my-plugin/api/config/queries/mongoose/index.js`. -```js -module.exports = { - getAll: async function (params) { - // this refers to the Mongoose model called in the query - // ex: strapi.query('User').getAll(), this will be equal to the User Mongoose model. - return this.find(params); - } -} -``` - -Bookshelf ORM generic queries: - -**Path —** `./plugins/my-plugin/api/config/queries/bookshelf/index.js`. -```js -module.exports = { - getAll: async function (params) { - // this refers to the Bookshelf model called in the query - // ex: strapi.query('User').getAll(), this will be equal to the User Bookshelf model. - return this.fetchAll(params); - } -} -``` - -Usage from the plugin: - -**Path —** `./plugins/my-plugin/api/controllers/index.js`. -```js -module.exports = { - getUsers: async () => { - // Get parameters from the request - const { limit, sort } = ctx.request.query; - - // Get the list of users using the plugin's queries - const users = await strapi.query('User').getAll({ limit, sort }); - - // Send the list of users as response - ctx.body = users; - } -} -``` -*** - -## Front-end - -This section explains how to create your plugin interface in the admin panel. - -Table of contents: -- [Development mode](#start-the-project-in-development-mode) -- [Folders and files structure](#folders-and-file-structure) -- [Routing](#routing) -- [Using Redux/sagas](#using-redux-sagas) -- [i18n](#i18n) -- [Styling](#styles) -- [Data flow](#data-flow) -- [API Reference](#api-reference) -- [Tutorial](#tutorial) - -### Introduction - -Strapi's admin panel and plugins system aim to be an easy and powerful way to create new features. - -The admin panel is a [React](https://facebook.github.io/react/) application which can embed other React applications. These other React applications are the `admin` parts of each Strapi's plugins. - -### Start the project in development mode - -To start the project in development mode, read the [Contributing Guide](https://github.com/strapi/strapi/blob/master/.github/CONTRIBUTING.md). - -### Folders and files structure - -The admin panel related parts of each plugin is contained in the `./plugins/my-plugin/admin` folder it has the following structure: - -``` -/admin -└─── build // Webpack build of the plugin -└─── src // Source code directory -| └─── bootstrap.js // (Optional) Contains the logic to execute before rendering the plugin -| └─── components // Contains the list of React components used by the plugin -| └─── containers -| | └─── App // Container used by every others containers -| | └─── HomePage -| | └─── action.js // List of Redux actions used by the current container -| | └─── constants.js // List of actions constants -| | └─── index.js // React component of the current container -| | └─── reducer.js // Redux reducer used by the current container -| | └─── sagas.js // List of sagas functions -| | └─── selectors.js // List of selectors -| | └─── styles.scss // Style of the current container -| | -| └─── requirements.js // (Optional) Contains the logic to prevent a plugin from being rendered -| └─── translations // Contains the translations to make the plugin internationalized -| └─── en.json -| └─── fr.json -└─── package.json // List of the necessary npm dependencies -``` - -### Routing - -The routing is based on the [React Router V4](https://reacttraining.com/react-router/web/guides/philosophy), due to it's implementation each route is declared in the `containers/App/index.js` file. - -Also, we chose to use the [Switch Router](https://reacttraining.com/react-router/web/api/Switch) because it renders a route exclusively. - -**Route declaration :** - -Let's say that you want to create a route `/user` with params `/:id` associated with the container UserPage. - -The declaration would be as followed : - -**Path —** `plugins/my-plugin/admin/src/containers/App/index.js`. -```js -import React from 'react'; -import UserPage from 'containers/UserPage'; - -// ... - -class App extends React.Component { - // ... - - render() { - return ( -
- - - -
- ); - } -} - -// ... -``` -See the [advanced user navigation guide](./advanced.md#handle-user-navigation) for more informations. - -### Using Redux sagas - -Due to React Router V4 your container's store is not directly injected. -To inject your container's store if it's associated with a route you have to do it manually. - -As an example, you created a FooPage container associated with the route `/plugins/my-plugin/bar`, and you want to use redux/action/reducer/sagas. - -Your `plugins/my-plugin/admin/src/containers/App/index.js` file will look as follows : - -```js -render() => ( -
- - - -
-); -``` - -And the `plugins/my-plugin/admin/src/containers/FooPage/index.js` file will be : - -```js -import React from 'react'; -import { connect } from 'react-redux'; -import { createStructuredSelector } from 'reselect'; -import { bindActionCreators, compose } from 'redux'; -import PropTypes from 'prop-types'; - -// Utils to create your container store -import injectReducer from 'utils/injectReducer'; -import injectSaga from 'utils/injectSaga'; - -import { - foo, - bar, -} from './actions'; -import reducer from './reducer'; -import saga from './sagas'; -import { makeSelectFooPage } from './selectors'; - -// Styles -import styles from './styles.scss'; - -export class FooPage extends React.Component { - render() { - return ( -
- Awesome container -
- ); - } -} - -FooPage.propTypes = { - fooPage: PropTypes.any, -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators( - { - foo, - bar, - }, - dispatch - ); -} - -const mapStateToProps = createStructuredSelector({ - fooPage: makeSelectFooPage(), -}); - -const withConnect = connect(mapStateToProps, mapDispatchToProps); - -// This is where you create your container's store -// the key must correspond to your container name in camelCase -const withSagas = injectSaga({ key: 'fooPage', saga }); -const withReducer = injectReducer({ key: 'fooPage', reducer }); - -export default compose( - withReducer, - withSagas, - withConnect, -)(FooPage); -``` - - -Important: see the [advanced container store injection](./advanced.md#routeless-container-store-injection.md) for more informations about how to create your container's store. - -### i18n - -[React Intl](https://github.com/yahoo/react-intl) provides React components and an API to format dates, numbers, and strings, including pluralization and handling translations. - -**Usage** - -We recommend to set all your components text inside the translations folder. - -The example below shows how to use i18n inside your plugin. - -**Define all your ids with the associated message:** - -**Path —** `./plugins/my-plugin/admin/src/translations/en.json`. -```json -{ - "notification.error.message": "An error occurred" -} -``` - -**Path —** `./plugins/my-plugin/admin/src/translations/fr.json` -```json -{ - "notification.error.message": "Une erreur est survenue" -} -``` - -**Usage inside a component** - -**Path —** `./plugins/my-plugin/admin/src/components/Foo/index.js`. -```js -import { FormattedMessage } from 'react-intl'; -import SomeOtherComponent from 'components/SomeOtherComponent'; - -const Foo = (props) => ( -
- - -
-) - -export default Foo; -``` - -See [the documentation](https://github.com/yahoo/react-intl/wiki/Components#formattedmessage) for more extensive usage. - -### Styles - -The [Bootstrap styles](http://getbootstrap.com/) are inherited by the plugins. However, each component has its own styles, so it possible to completely customize it. - -**See the [plugin styles](../concepts/concepts.md#plugin-styles) for informations on its concept.** - -To style a plugin component: - - Add a `styles.scss` file in the component directory - - Require it from the `index.js` file (`import styles from './styles.scss';`) - - Add some styles in the `styles.scss` file - -``` -.wrapper { - display: block; - background: red; - height: 100px; - width: 100px; -} -``` - -Use this style from the component: `
`. - -Note: if you want to use several classes: - -```js -import cn from 'classnames'; - -// ... - -return ( -
{this.props.children}
-); - -// ... - -``` - -### Data flow - -Each plugin has its own data store, so it stays completely independent from the others. - -Data flow is controlled thanks to Redux and redux-sagas. - -### API Reference - -> Refer to the [plugin registration](../api-reference/reference.md#plugin-registration) for details. - -### Tutorial - -For more information, try the [Create your first Strapi plugin](http://strapi.io) tutorial. diff --git a/docs/3.x.x/en/plugins/quick-start.md b/docs/3.x.x/en/plugins/quick-start.md deleted file mode 100644 index 4dce6f67d3..0000000000 --- a/docs/3.x.x/en/plugins/quick-start.md +++ /dev/null @@ -1,31 +0,0 @@ -# Quick start - -You can install and uninstall any plugin you want. - -## Plugin installation - -Look at the [CLI documentation](../cli/CLI.html#strapi-install) to install plugin. - -### Basic usage - -Considering you want to install a plugin named `content-manager` you can run the following command: -```bash -strapi install content-manager -``` - -> Note: This implies that this plugin is published on the npm registry as `strapi-plugin-content-manager`. - -*** - -## Plugin uninstallation - -Look at the [CLI documentation](../cli/CLI.html#strapi-uninstall) to install plugin. - -### Basic usage - -This command will simply removes the plugin folder. -```bash -strapi uninstall content-manager -``` - -Please refer to the [CLI documentation](../cli/CLI.md) for more information. diff --git a/packages/strapi-admin/admin/src/assets/icons/icon_auth-permissions.svg b/packages/strapi-admin/admin/src/assets/icons/icon_auth-permissions.svg new file mode 100644 index 0000000000..c04f87fe51 --- /dev/null +++ b/packages/strapi-admin/admin/src/assets/icons/icon_auth-permissions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-admin/admin/src/assets/icons/icon_content-manager.svg b/packages/strapi-admin/admin/src/assets/icons/icon_content-manager.svg new file mode 100755 index 0000000000..ac6161da78 --- /dev/null +++ b/packages/strapi-admin/admin/src/assets/icons/icon_content-manager.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-admin/admin/src/assets/icons/icon_content-type-builder.svg b/packages/strapi-admin/admin/src/assets/icons/icon_content-type-builder.svg new file mode 100755 index 0000000000..89602f579d --- /dev/null +++ b/packages/strapi-admin/admin/src/assets/icons/icon_content-type-builder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-admin/admin/src/assets/icons/icon_settings-manager.svg b/packages/strapi-admin/admin/src/assets/icons/icon_settings-manager.svg new file mode 100755 index 0000000000..030f2cdfa0 --- /dev/null +++ b/packages/strapi-admin/admin/src/assets/icons/icon_settings-manager.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-admin/admin/src/components/Row/index.js b/packages/strapi-admin/admin/src/components/Row/index.js index 2701189660..de984fc14e 100644 --- a/packages/strapi-admin/admin/src/components/Row/index.js +++ b/packages/strapi-admin/admin/src/components/Row/index.js @@ -13,6 +13,10 @@ import { FormattedMessage } from 'react-intl'; import Ico from 'components/Ico'; import ListRow from 'components/ListRow'; import PopUpWarning from 'components/PopUpWarning'; +import IconAuth from 'assets/icons/icon_auth-permissions.svg'; +import IconCtb from 'assets/icons/icon_content-type-builder.svg'; +import IconCm from 'assets/icons/icon_content-manager.svg'; +import IconSettings from 'assets/icons/icon_settings-manager.svg'; import styles from './styles.scss'; @@ -30,13 +34,35 @@ class Row extends React.Component { this.props.onDeleteClick(e); } + renderImg = () => { + switch (this.props.plugin.name) { + case 'Auth & Permissions': + return logo; + case 'Content Manager': + return logo; + case 'Settings Manager': + return logo; + case 'Content Type Builder': + return logo; + default: + } + } + render() { + const pluginIcon = this.props.plugin.name !== 'Email' ? ( +
+ {this.renderImg()} +
+ ) : ( +
+ +
+ ); + return (
-
- -
+ {pluginIcon}
{this.props.plugin.name} —  diff --git a/packages/strapi-admin/admin/src/components/Row/styles.scss b/packages/strapi-admin/admin/src/components/Row/styles.scss index 0be22b4610..99b6bf3862 100644 --- a/packages/strapi-admin/admin/src/components/Row/styles.scss +++ b/packages/strapi-admin/admin/src/components/Row/styles.scss @@ -31,3 +31,23 @@ display: flex; justify-content: flex-end; } + +.frame { + width: 70px; + height: 36px; + margin: auto 0; + text-align: center; + border: 1px solid rgba(28,93,231,0.1); + border-radius: 3px; + white-space: nowrap; + > img { + max-height: 36px; + vertical-align: baseline; + } +} + +.helper { + display: inline-block; + height: 100%; + vertical-align: middle; +} diff --git a/packages/strapi-generate/lib/target.js b/packages/strapi-generate/lib/target.js index 8cecbdad1b..bf97af252e 100755 --- a/packages/strapi-generate/lib/target.js +++ b/packages/strapi-generate/lib/target.js @@ -233,7 +233,11 @@ function parseTarget(target, scope, cb) { // try requiring `strapi-generate-` to get the core generator. if (!subGenerator && !module.match(/^strapi-generate-/)) { try { - subGenerator = require(path.resolve(process.mainModule.paths[1], 'strapi-generate-' + module)); + if (process.mainModule.filename.indexOf('yarn') !== -1) { + subGenerator = require(path.resolve(process.mainModule.paths[2], 'strapi-generate-' + module)); + } else { + subGenerator = require(path.resolve(process.mainModule.paths[1], 'strapi-generate-' + module)); + } } catch (e1) { requireError = e1; } diff --git a/packages/strapi-knex/lib/index.js b/packages/strapi-knex/lib/index.js index 99ec212935..c0c8a10d35 100755 --- a/packages/strapi-knex/lib/index.js +++ b/packages/strapi-knex/lib/index.js @@ -95,7 +95,9 @@ module.exports = strapi => { charset: _.get(connection.settings, 'charset'), schema: _.get(connection.settings, 'schema') || 'public', port: _.get(connection.settings, 'port'), - socket: _.get(connection.settings, 'socketPath') + socket: _.get(connection.settings, 'socketPath'), + ssl: _.get(connection.settings, 'ssl') || false + }, debug: _.get(connection.options, 'debug') || false, acquireConnectionTimeout: _.get(connection.options, 'acquireConnectionTimeout'), diff --git a/packages/strapi-mongoose/lib/index.js b/packages/strapi-mongoose/lib/index.js index 33f35ee269..0b1cf3ba84 100755 --- a/packages/strapi-mongoose/lib/index.js +++ b/packages/strapi-mongoose/lib/index.js @@ -44,13 +44,9 @@ module.exports = function (strapi) { // Connect to mongo database if (_.isEmpty(username) || _.isEmpty(password)) { - instance.connect(`mongodb://${host}:${port}/${database}`, { - useMongoClient: true - }); + instance.connect(`mongodb://${host}:${port}/${database}`); } else { - instance.connect(`mongodb://${username}:${password}@${host}:${port}/${database}`, { - useMongoClient: true - }); + instance.connect(`mongodb://${username}:${password}@${host}:${port}/${database}`); } // Handle error @@ -251,7 +247,7 @@ module.exports = function (strapi) { const FK = _.find(definition.associations, {alias: name}); const ref = details.plugin ? strapi.plugins[details.plugin].models[details.model].globalId : strapi.models[details.model].globalId; - if (FK && FK.nature !== 'oneToOne' && FK.nature !== 'manyToOne') { + if (FK && FK.nature !== 'oneToOne' && FK.nature !== 'manyToOne' && FK.nature !== 'oneWay') { definition.loadedModel[name] = { type: 'virtual', ref, diff --git a/packages/strapi-mongoose/lib/utils/index.js b/packages/strapi-mongoose/lib/utils/index.js index d64849e492..6820e8d014 100755 --- a/packages/strapi-mongoose/lib/utils/index.js +++ b/packages/strapi-mongoose/lib/utils/index.js @@ -5,11 +5,14 @@ */ module.exports = mongoose => { - require('mongoose-double')(mongoose); require('mongoose-float').loadType(mongoose); const SchemaTypes = mongoose.Schema.Types; + SchemaTypes.Decimal.prototype.cast = function (value) { + return value.toString(); + }; + return { convertType: mongooseType => { switch (mongooseType.toLowerCase()) { @@ -22,9 +25,9 @@ module.exports = mongoose => { case 'biginteger': return 'Number'; case 'float': - return SchemaTypes.Float; + return 'Float'; case 'decimal': - return SchemaTypes.Double; + return 'Decimal'; case 'date': case 'time': case 'datetime': diff --git a/packages/strapi-mongoose/package.json b/packages/strapi-mongoose/package.json index 3679d37897..f74ab912a0 100755 --- a/packages/strapi-mongoose/package.json +++ b/packages/strapi-mongoose/package.json @@ -16,8 +16,7 @@ "main": "./lib", "dependencies": { "lodash": "^4.17.4", - "mongoose": "^4.11.10", - "mongoose-double": "0.0.1", + "mongoose": "^5.0.0-rc1", "mongoose-float": "^1.0.2", "pluralize": "^6.0.0", "strapi-utils": "3.0.0-alpha.7.3" diff --git a/packages/strapi-plugin-content-manager/admin/src/components/EditFormRelations/index.js b/packages/strapi-plugin-content-manager/admin/src/components/EditFormRelations/index.js index aaa820c25d..9d3bdcffca 100755 --- a/packages/strapi-plugin-content-manager/admin/src/components/EditFormRelations/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/EditFormRelations/index.js @@ -35,6 +35,7 @@ class EditFormRelations extends React.Component { // eslint-disable-line react/p const relations = map(currentSchema.relations, (relation, i) => { switch (relation.nature) { + case 'oneWay': case 'oneToOne': case 'manyToOne': if (relation.dominant) { diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/Edit/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/Edit/index.js index 525c83153b..44364f1983 100755 --- a/packages/strapi-plugin-content-manager/admin/src/containers/Edit/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/Edit/index.js @@ -159,7 +159,7 @@ export class Edit extends React.Component { if (isObject(e.target.value) && e.target.value._isAMomentObject === true) { formattedValue = moment(e.target.value, 'YYYY-MM-DD HH:mm:ss').format(); - } else if (['float', 'integer', 'bigint'].indexOf(currentSchema.fields[e.target.name].type) !== -1) { + } else if (['float', 'integer', 'biginteger', 'decimal'].indexOf(currentSchema.fields[e.target.name].type) !== -1) { formattedValue = toNumber(e.target.value); } diff --git a/packages/strapi-plugin-content-manager/config/queries/bookshelf.js b/packages/strapi-plugin-content-manager/config/queries/bookshelf.js index 3aee7e3f79..512b3f32ee 100755 --- a/packages/strapi-plugin-content-manager/config/queries/bookshelf.js +++ b/packages/strapi-plugin-content-manager/config/queries/bookshelf.js @@ -77,6 +77,10 @@ module.exports = { acc[current] = params.values[current]; } else { switch (association.nature) { + case 'oneWay': + acc[current] = _.get(params.values[current], this.primaryKey, params.values[current]) || null; + + break; case 'oneToOne': if (response[current] !== params.values[current]) { const value = _.isNull(params.values[current]) ? response[current] : params.values; diff --git a/packages/strapi-plugin-content-manager/config/queries/mongoose.js b/packages/strapi-plugin-content-manager/config/queries/mongoose.js index 19a12a9767..0735ba5456 100755 --- a/packages/strapi-plugin-content-manager/config/queries/mongoose.js +++ b/packages/strapi-plugin-content-manager/config/queries/mongoose.js @@ -58,6 +58,10 @@ module.exports = { acc[current] = params.values[current]; } else { switch (association.nature) { + case 'oneWay': + acc[current] = _.get(params.values[current], this.primaryKey, params.values[current]) || null; + + break; case 'oneToOne': if (response[current] !== params.values[current]) { const value = _.isNull(params.values[current]) ? response[current] : params.values; diff --git a/packages/strapi-plugin-content-manager/controllers/ContentManager.js b/packages/strapi-plugin-content-manager/controllers/ContentManager.js index 3ee57c5021..df8dbbcb87 100755 --- a/packages/strapi-plugin-content-manager/controllers/ContentManager.js +++ b/packages/strapi-plugin-content-manager/controllers/ContentManager.js @@ -70,7 +70,6 @@ module.exports = { // Create an entry using `queries` system ctx.body = await strapi.plugins['content-manager'].services['contentmanager'].add(ctx.params, ctx.request.body, source); } catch(error) { - console.log(error); ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: error.message, field: error.field }] }] : error.message); } }, diff --git a/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js b/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js index aef8f6497c..a6b3bd4a21 100755 --- a/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js +++ b/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js @@ -51,7 +51,7 @@ module.exports = { params.target = relation.model || relation.collection; params.key = relation.via; params.nature = relation.nature; - params.targetColumnName = _.get(strapi.models[params.target].attributes[params.key], 'columnName', ''); + params.targetColumnName = _.get((params.plugin ? strapi.plugins[params.plugin].models : strapi.models )[params.target].attributes[params.key], 'columnName', ''); } attributes.push({ @@ -106,7 +106,7 @@ module.exports = { // Retrieve where is located the model. // Note: The target is not found when we are creating a new API. That's why, we are returning the lowercased model. const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {}) - .filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), model.toLowerCase()))[0] || model.toLowerCase(); + .filter(x => _.includes(Object.keys(_.get((plugin ? strapi.plugins : strapi.api)[x], 'models', [])), model.toLowerCase()))[0] || model.toLowerCase(); // Retrieve the filename of the model. const filename = fs.readdirSync(plugin ? path.join(strapi.config.appPath, 'plugins', target, 'models') : path.join(strapi.config.appPath, 'api', target, 'models')) @@ -123,7 +123,7 @@ module.exports = { const attrs = {}; const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {}) - .filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), name))[0] || name.toLowerCase(); + .filter(x => _.includes(Object.keys(_.get((plugin ? strapi.plugins : strapi.api)[x], 'models', [])), name))[0] || name.toLowerCase(); const model = (plugin ? _.get(strapi.plugins, [target, 'models', name]) : _.get(strapi.api, [target, 'models', name])) || {}; @@ -209,7 +209,7 @@ module.exports = { if (!_.isEmpty(relationsToDelete)) { // Retrieve where is located the model. const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {}) - .filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), name))[0]; + .filter(x => _.includes(Object.keys(_.get((plugin ? strapi.plugins : strapi.api)[x], 'models', [])), name))[0]; // Retrieve the filename of the model. const filename = fs.readdirSync(plugin ? path.join(strapi.config.appPath, 'plugins', target, 'models') : path.join(strapi.config.appPath, 'api', target, 'models')) @@ -280,7 +280,7 @@ module.exports = { if (!_.isEmpty(relationsToCreate)) { // Retrieve where is located the model. const target = Object.keys((plugin ? strapi.plugins : strapi.api) || {}) - .filter(x => _.includes(Object.keys((plugin ? strapi.plugins : strapi.api)[x].models), name))[0]; + .filter(x => _.includes(Object.keys(_.get((plugin ? strapi.plugins : strapi.api)[x], 'models', [])), name))[0]; // Retrieve the filename of the model. const filename = fs.readdirSync(plugin ? path.join(strapi.config.appPath, 'plugins', target, 'models') : path.join(strapi.config.appPath, 'api', target, 'models')) diff --git a/packages/strapi-plugin-users-permissions/admin/src/components/InputSearch/index.js b/packages/strapi-plugin-users-permissions/admin/src/components/InputSearch/index.js index 08c3465de8..76cd8e19da 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/components/InputSearch/index.js +++ b/packages/strapi-plugin-users-permissions/admin/src/components/InputSearch/index.js @@ -6,7 +6,7 @@ import React from 'react'; import { FormattedMessage } from 'react-intl'; -import { findIndex, has, includes, isEmpty, map, toLower, upperFirst } from 'lodash'; +import { findIndex, has, includes, isEmpty, map, toLower } from 'lodash'; import cn from 'classnames'; import PropTypes from 'prop-types'; @@ -77,7 +77,7 @@ class InputSearch extends React.Component { // eslint-disable-line react/prefer- return (
diff --git a/packages/strapi-plugin-users-permissions/config/policies/permissions.js b/packages/strapi-plugin-users-permissions/config/policies/permissions.js index 95461e10b7..f8efa2f5a3 100644 --- a/packages/strapi-plugin-users-permissions/config/policies/permissions.js +++ b/packages/strapi-plugin-users-permissions/config/policies/permissions.js @@ -10,18 +10,19 @@ module.exports = async (ctx, next) => { ctx.state.user = await strapi.plugins['users-permissions'].services.user.fetch(_.pick(tokenUser, ['_id', 'id'])); - if (!ctx.state.user) { - ctx.unauthorized('This user doesn\'t exit.'); - } - - role = ctx.state.user.role; - - if (role.toString() === '0') { - return await next(); - } } catch (err) { return ctx.unauthorized(err); } + + if (!ctx.state.user) { + return ctx.unauthorized('This user doesn\'t exit.'); + } + + role = ctx.state.user.role; + + if (role.toString() === '0') { + return await next(); + } } const permission = _.get(strapi.plugins['users-permissions'].config, ['roles', role.toString(), 'permissions', route.plugin || 'application', 'controllers', route.controller, route.action]); diff --git a/packages/strapi/bin/strapi-install.js b/packages/strapi/bin/strapi-install.js index 91b8f3da77..94b4d901e9 100755 --- a/packages/strapi/bin/strapi-install.js +++ b/packages/strapi/bin/strapi-install.js @@ -77,7 +77,11 @@ module.exports = function (plugin, cliArguments) { fs.accessSync(path.join(pluginPath, '.gitignore')) } catch (err) { if (err.code === 'ENOENT') { - fs.copySync(path.resolve(__dirname, '..', 'node_modules', 'strapi-generate-plugin', 'templates', 'gitignore'), path.join(pluginPath, '.gitignore')); + if (process.mainModule.filename.indexOf('yarn') !== -1) { + fs.copySync(path.resolve(__dirname, '..', '..', 'strapi-generate-plugin', 'templates', 'gitignore'), path.join(pluginPath, '.gitignore')); + } else { + fs.copySync(path.resolve(__dirname, '..', 'node_modules', 'strapi-generate-plugin', 'templates', 'gitignore'), path.join(pluginPath, '.gitignore')); + } } } diff --git a/packages/strapi/lib/utils/index.js b/packages/strapi/lib/utils/index.js index e6e3ad2c46..19682489aa 100755 --- a/packages/strapi/lib/utils/index.js +++ b/packages/strapi/lib/utils/index.js @@ -78,14 +78,14 @@ module.exports = { const aggregate = files.filter( p => intersection(p.split('/').map(p => p.replace('.json', '')), ['environments', 'database', 'security', 'request', 'response', 'server']).length === 2 || - p.indexOf('functions') !== -1 || + ((p.indexOf('functions') !== -1 || p.indexOf('policies') !== -1 || p.indexOf('locales') !== -1 || p.indexOf('hook') !== -1 || p.indexOf('middleware') !== -1 || p.indexOf('language') !== -1 || p.indexOf('queries') !== -1 || - p.indexOf('layout') !== -1 + p.indexOf('layout') !== -1) && p.indexOf('api') === -1) ); const optional = difference(files, aggregate); diff --git a/packages/strapi/package.json b/packages/strapi/package.json index d4fd8c0c4b..f256c882a9 100755 --- a/packages/strapi/package.json +++ b/packages/strapi/package.json @@ -89,4 +89,4 @@ }, "preferGlobal": true, "license": "MIT" -} \ No newline at end of file +}