diff --git a/README.md b/README.md index d08a19d2ab..8d8a1610c0 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,15 @@ -

+

+ We're hiring! Located in Paris 🇫🇷 and dreaming of being full-time on Strapi? + Join us! +

+ +--- + +

+ + Strapi logo + +

API creation made simple, secure and fast.

The most advanced open-source Content Management Framework to build powerful API with no effort.


@@ -19,7 +30,11 @@
-

+

+ + + +


diff --git a/docs/3.x.x/en/advanced/customize-admin.md b/docs/3.x.x/en/advanced/customize-admin.md index a819fa90d5..efe380e7b4 100644 --- a/docs/3.x.x/en/advanced/customize-admin.md +++ b/docs/3.x.x/en/advanced/customize-admin.md @@ -62,6 +62,8 @@ The panel will be available through [http://localhost:1337/dashboard](http://loc ### Development mode +Note that to modify the administration panel, your project needs to be created with using the `dev` flag, an example of such would be: `strapi new strapi --dev`. + **#1 — Install its own dependencies** Run `npm install` from the `./admin` folder. @@ -99,14 +101,16 @@ Note: make sure the size of your image is the same as the existing one (434px x ## Build -To build the administration, run the following command from the `./admin` folder: +To build the administration, run the following command from the root directory of your project. ``` -npm run build +npm run setup ``` This will replace the folder's content located at `./admin/admin/build`. Visit http://localhost:1337/admin/ to make sure your updates have been taken in account. +After you have built the admininistration you can now create a new project to develop your API with the changes implemented. **Note:** You should now create a project without `--dev` + *** ## Deployment diff --git a/docs/3.x.x/en/advanced/hooks.md b/docs/3.x.x/en/advanced/hooks.md index 972ade67f0..a624aaeca9 100644 --- a/docs/3.x.x/en/advanced/hooks.md +++ b/docs/3.x.x/en/advanced/hooks.md @@ -1,6 +1,6 @@ # Hooks -The hooks are modules that add functionality to the core. They are loaded during the server boot. For example, if your project needs to work with a SQL database, your will have to add the hook `strapi-bookshelf` to be able to connect your app with your database. +The hooks are modules that add functionality to the core. They are loaded during the server boot. For example, if your project needs to work with a SQL database, your will have to add the hook `strapi-hook-bookshelf` to be able to connect your app with your database. **Path —** `./hooks/documentation/lib/index.js`. ```js @@ -76,13 +76,13 @@ The `index.js` is the entry point to your hook. It should look like the example ## Dependencies -It happens that a hook has a dependency to another one. For example, the `strapi-bookshelf` has a dependency to `strapi-knex`. Without it, the `strapi-bookshelf` can't work correctly. It also means that the `strapi-knex` hook has to be loaded before. +It happens that a hook has a dependency to another one. For example, the `strapi-hook-bookshelf` has a dependency to `strapi-hook-knex`. Without it, the `strapi-hook-bookshelf` can't work correctly. It also means that the `strapi-hook-knex` hook has to be loaded before. To handle this case, you need to update the `package.json` at the root of your hook. ```json { - "name": "strapi-bookshelf", + "name": "strapi-hook-bookshelf", "version": "x.x.x", "description": "Bookshelf hook for the Strapi framework", "dependencies": { @@ -90,10 +90,10 @@ To handle this case, you need to update the `package.json` at the root of your h }, "strapi": { "dependencies": [ - "strapi-knex" + "strapi-hook-knex" ] } -} +} ``` ## Custom hooks @@ -107,7 +107,9 @@ The framework allows to load hooks from the project directly without having to i └─── config └─── hooks │ └─── strapi-documentation +│ - index.js │ └─── strapi-server-side-rendering +│ - index.js └─── plugins └─── public - favicon.ico diff --git a/docs/3.x.x/en/advanced/middlewares.md b/docs/3.x.x/en/advanced/middlewares.md index 9adc669171..46e2bd8890 100644 --- a/docs/3.x.x/en/advanced/middlewares.md +++ b/docs/3.x.x/en/advanced/middlewares.md @@ -65,8 +65,12 @@ The core of Strapi embraces a small list of middlewares for performances, securi A middleware needs to follow the structure below: ``` -/lib -- index.js +/middleware +└─── lib + - index.js +- LICENSE.md +- package.json +- README.md ``` The `index.js` is the entry point to your middleware. It should look like the example above. @@ -82,7 +86,9 @@ The framework allows the application to override the default middlewares and add └─── config └─── middlewares │ └─── responseTime // It will override the core default responseTime middleware +│ - index.js │ └─── views // It will be added into the stack of middleware +│ - index.js └─── plugins └─── public - favicon.ico diff --git a/docs/3.x.x/en/advanced/usage-tracking.md b/docs/3.x.x/en/advanced/usage-tracking.md index b9a84d0e58..b6b445700f 100644 --- a/docs/3.x.x/en/advanced/usage-tracking.md +++ b/docs/3.x.x/en/advanced/usage-tracking.md @@ -13,7 +13,7 @@ Here is the list of the collected data and why we need them. *Understand how the developers are using the different configurations? How many projects are started in production mode?* - **Node modules names** *Are developers integrating Strapi with Stripe? It means that we should develop a plugin to simplify the development process with Stripe. - Are developers using Strapi with strapi-bookshelf or strapi-mongoose? It helps us prioritize the issues.* + Are developers using Strapi with strapi-hook-bookshelf or strapi-hook-mongoose? It helps us prioritize the issues.* - **OS** *Is the community using Windows, Linux or Mac? It helps us prioritize the issues.* - **Build configurations** diff --git a/docs/3.x.x/en/cli/CLI.md b/docs/3.x.x/en/cli/CLI.md index daac90433d..9759038fef 100644 --- a/docs/3.x.x/en/cli/CLI.md +++ b/docs/3.x.x/en/cli/CLI.md @@ -11,7 +11,7 @@ Create a new project ```bash strapi new -options: [--dev|--dbclient= --dbhost= --dbport= --dbname= --dbusername= --dbpassword=] +options: [--dev|--dbclient= --dbhost= --dbport= --dbname= --dbusername= --dbpassword= --dbssl= --dbauth=] ``` - **strapi new <name>**
@@ -20,8 +20,8 @@ options: [--dev|--dbclient= --dbhost= --dbport= --dbna - **strapi new <name> --dev**
Generates a new project called **<name>** and creates symlinks for the `./admin` folder and each plugin inside the `./plugin` folder. It means that the Strapi's development workflow has been set up on the machine earlier. -- **strapi new <name> --dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport> --dbname=<dbname> --dbusername=<dbusername> --dbpassword=<dbpassword>**
- Generates a new project called **<name>** and skip the interactive database configuration and initilize with these options. **<dbclient>** can be `mongo`, `postgres`, `mysql`, `sqlite3` or `redis`. **<dbusername>** and **<dbpassword>** are optional. +- **strapi new <name> --dbclient=<dbclient> --dbhost=<dbhost> --dbport=<dbport> --dbname=<dbname> --dbusername=<dbusername> --dbpassword=<dbpassword> --dbssl=<dbssl> --dbauth=<dbauth>**
+ Generates a new project called **<name>** and skip the interactive database configuration and initilize with these options. **<dbclient>** can be `mongo`, `postgres`, `mysql`, `sqlite3` or `redis`. **<dbssl>** and **<dbauth>** are optional. See the [CONTRIBUTING guide](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) for more details. diff --git a/docs/3.x.x/en/configurations/configurations.md b/docs/3.x.x/en/configurations/configurations.md index d038fc99ec..ced94dd32b 100644 --- a/docs/3.x.x/en/configurations/configurations.md +++ b/docs/3.x.x/en/configurations/configurations.md @@ -164,7 +164,7 @@ Most of the application's configurations are defined by environment. It means th - `defaultConnection` (string): Connection by default for models which are not related to a specific `connection`. Default value: `default`. - `connections` List of all available connections. - `default` - - `connector` (string): Connector used by the current connection. Default value: `strapi-mongoose`. + - `connector` (string): Connector used by the current connection. Default value: `strapi-hook-mongoose`. - `client` (string): Client used to store session. Default value: `cookie`. - `key` (string): Cookie key name. Default value: `strapi.sid` - `maxAge` (integer): Time in milliseconds before the session expire. Default value: `86400000`. @@ -192,7 +192,7 @@ Most of the application's configurations are defined by environment. It means th "defaultConnection": "default", "connections": { "default": { - "connector": "strapi-mongoose", + "connector": "strapi-hook-mongoose", "settings": { "client": "mongo", "host": "localhost", @@ -208,7 +208,7 @@ Most of the application's configurations are defined by environment. It means th } }, "postgres": { - "connector": "strapi-bookshelf", + "connector": "strapi-hook-bookshelf", "settings": { "client": "postgres", "host": "localhost", @@ -223,7 +223,7 @@ Most of the application's configurations are defined by environment. It means th } }, "mysql": { - "connector": "strapi-bookshelf", + "connector": "strapi-hook-bookshelf", "settings": { "client": "mysql", "host": "localhost", @@ -367,7 +367,7 @@ In any JSON configurations files in your project, you can inject dynamic values "defaultConnection": "default", "connections": { "default": { - "connector": "strapi-mongoose", + "connector": "strapi-hook-mongoose", "settings": { "client": "mongo", "uri": "${process.env.DATABASE_URI || ''}", diff --git a/docs/3.x.x/en/guides/email.md b/docs/3.x.x/en/guides/email.md index b9edddd7f8..f852e57929 100644 --- a/docs/3.x.x/en/guides/email.md +++ b/docs/3.x.x/en/guides/email.md @@ -27,8 +27,8 @@ To install a new provider run: $ npm install strapi-email-sendgrid@alpha --save ``` -We have two providers available `strapi-email-sendgrid` and `strapi-upload-mailgun`, use the alpha tag to install one of them. Then, visit `/admin/plugins/email/configurations/development` and configure the provider. +We have two providers available `strapi-email-sendgrid` and `strapi-upload-mailgun`, use the alpha tag to install one of them. Then, visit `/admin/plugins/email/configurations/development` on your web browser and configure the provider. If you want to create your own, make sure the name starts with `strapi-email-` (duplicating an existing one will be easier to create), modify the `auth` config object and customize the `send` functions. -Check all community providers available on npmjs.org - [Providers list](https://www.npmjs.com/search?q=strapi-email-) \ No newline at end of file +Check all community providers available on npmjs.org - [Providers list](https://www.npmjs.com/search?q=strapi-email-) diff --git a/docs/3.x.x/en/guides/upload.md b/docs/3.x.x/en/guides/upload.md index eae41c75f4..67a82b7054 100644 --- a/docs/3.x.x/en/guides/upload.md +++ b/docs/3.x.x/en/guides/upload.md @@ -133,7 +133,7 @@ To install a new provider run: $ npm install strapi-upload-aws-s3@alpha --save ``` -We have two providers available `strapi-upload-aws-s3` and `strapi-upload-cloudinary`, use the alpha tag to install one of them. Then, visit `/admin/plugins/upload/configurations/development` and configure the provider. +We have two providers available `strapi-upload-aws-s3` and `strapi-upload-cloudinary`, use the alpha tag to install one of them. Then, visit `/admin/plugins/upload/configurations/development` on your web browser and configure the provider. If you want to create your own, make sure the name starts with `strapi-upload-` (duplicating an existing one will be easier to create), modify the `auth` config object and customize the `upload` and `delete` functions. diff --git a/packages/strapi-admin/admin/src/components/LeftMenuLinkContainer/index.js b/packages/strapi-admin/admin/src/components/LeftMenuLinkContainer/index.js index 5ea571e596..ce156f2cbe 100755 --- a/packages/strapi-admin/admin/src/components/LeftMenuLinkContainer/index.js +++ b/packages/strapi-admin/admin/src/components/LeftMenuLinkContainer/index.js @@ -69,13 +69,17 @@ function LeftMenuLinkContainer({ layout, plugins }) { // Check if the plugins list is empty or not and display plugins by name const pluginsLinks = !isEmpty(pluginsObject) ? ( map(sortBy(pluginsObject, 'name'), plugin => { - if (plugin.id !== 'email' && plugin.id !== 'content-manager' && plugin.id !== 'settings-manager') { + if (plugin.id !== 'email' && plugin.id !== 'settings-manager') { + const basePath = `/plugins/${get(plugin, 'id')}`; + // NOTE: this should be dynamic + const destination = plugin.id === 'content-manager' ? `${basePath}/ctm-configurations` : basePath; + return ( ); } diff --git a/packages/strapi-admin/admin/src/components/ListPlugins/index.js b/packages/strapi-admin/admin/src/components/ListPlugins/index.js index 6a448a878a..64a6ead46d 100644 --- a/packages/strapi-admin/admin/src/components/ListPlugins/index.js +++ b/packages/strapi-admin/admin/src/components/ListPlugins/index.js @@ -16,7 +16,7 @@ import Row from 'components/Row'; import styles from './styles.scss'; -class ListPlugins extends React.Component { +class ListPlugins extends React.PureComponent { render() { const listSize = size(this.props.plugins); let titleType = listSize === 1 ? 'singular' : 'plural'; diff --git a/packages/strapi-admin/admin/src/components/Row/index.js b/packages/strapi-admin/admin/src/components/Row/index.js index 8ad6128e6c..47b5ef279b 100644 --- a/packages/strapi-admin/admin/src/components/Row/index.js +++ b/packages/strapi-admin/admin/src/components/Row/index.js @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import cn from 'classnames'; import { FormattedMessage } from 'react-intl'; -import { isEmpty } from 'lodash'; +import { includes, isEmpty } from 'lodash'; // Design import IcoContainer from 'components/IcoContainer'; @@ -17,6 +17,8 @@ import PopUpWarning from 'components/PopUpWarning'; import styles from './styles.scss'; +const PLUGINS_WITH_CONFIG = ['content-manager', 'email', 'upload']; + class Row extends React.Component { state = { showModal: false }; @@ -33,8 +35,10 @@ class Row extends React.Component { render() { // const uploadPath = `/plugins/upload/configurations/${this.context.currentEnvironment}`; - const settingsPath = `/plugins/${this.props.name}/configurations/${this.context.currentEnvironment}`; - const icons = this.props.name === 'upload' || this.props.name === 'email' ? [ + // Make sure to match the ctm config URI instead of content-type view URI + const settingsPath = this.props.name === 'content-manager' ? '/plugins/content-manager/ctm-configurations' : `/plugins/${this.props.name}/configurations/${this.context.currentEnvironment}`; + // const icons = this.props.name === 'upload' || this.props.name === 'email' ? [ + const icons = includes(PLUGINS_WITH_CONFIG, this.props.name) ? [ { icoType: 'cog', onClick: (e) => { diff --git a/packages/strapi-admin/admin/src/containers/AdminPage/saga.js b/packages/strapi-admin/admin/src/containers/AdminPage/saga.js index 6f2437af0e..6bb488aeb9 100644 --- a/packages/strapi-admin/admin/src/containers/AdminPage/saga.js +++ b/packages/strapi-admin/admin/src/containers/AdminPage/saga.js @@ -11,15 +11,15 @@ import { GET_GA_STATUS, GET_LAYOUT } from './constants'; function* getGaStatus() { try { - const [allowGa, strapiVersion, currentEnvironment] = yield [ + const [{ allowGa }, { strapiVersion }, { currentEnvironment }] = yield [ call(request, '/admin/gaConfig', { method: 'GET' }), call(request, '/admin/strapiVersion', { method: 'GET' }), call(request, '/admin/currentEnvironment', { method: 'GET' }), ]; - yield put(getCurrEnvSucceeded(currentEnvironment.currentEnvironment)); + yield put(getCurrEnvSucceeded(currentEnvironment)); yield put(getGaStatusSucceeded(allowGa)); - yield put(getStrapiVersionSucceeded(strapiVersion.strapiVersion)); + yield put(getStrapiVersionSucceeded(strapiVersion)); } catch(err) { strapi.notification.error('notification.error'); } diff --git a/packages/strapi-admin/admin/src/containers/LeftMenu/index.js b/packages/strapi-admin/admin/src/containers/LeftMenu/index.js index ba940f7f3d..3303b61d6a 100755 --- a/packages/strapi-admin/admin/src/containers/LeftMenu/index.js +++ b/packages/strapi-admin/admin/src/containers/LeftMenu/index.js @@ -14,16 +14,14 @@ import LeftMenuFooter from 'components/LeftMenuFooter'; import styles from './styles.scss'; -export class LeftMenu extends React.Component { // eslint-disable-line react/prefer-stateless-function - render() { - return ( -
- - - -
- ); - } +function LeftMenu(props) { + return ( +
+ + + +
+ ); } LeftMenu.defaultProps = { diff --git a/packages/strapi-generate-api/jest.config.js b/packages/strapi-generate-api/jest.config.js new file mode 100644 index 0000000000..e8e1096d92 --- /dev/null +++ b/packages/strapi-generate-api/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + name: 'generate-api', + displayName: 'Generated API', + testMatch: ['**/test/?(*.)+(spec|test).js'] +}; diff --git a/packages/strapi-generate-api/templates/bookshelf/service.template b/packages/strapi-generate-api/templates/bookshelf/service.template index 0685e6362e..948a990494 100755 --- a/packages/strapi-generate-api/templates/bookshelf/service.template +++ b/packages/strapi-generate-api/templates/bookshelf/service.template @@ -10,7 +10,7 @@ const _ = require('lodash'); // Strapi utilities. -const utils = require('strapi-bookshelf/lib/utils/'); +const utils = require('strapi-hook-bookshelf/lib/utils/'); module.exports = { diff --git a/packages/strapi-generate-api/templates/mongoose/service.template b/packages/strapi-generate-api/templates/mongoose/service.template index 324fdedd6c..e94e72554e 100755 --- a/packages/strapi-generate-api/templates/mongoose/service.template +++ b/packages/strapi-generate-api/templates/mongoose/service.template @@ -83,7 +83,7 @@ module.exports = { const entry = await <%= globalID %>.create(data); // Create relational data and return the entry. - return <%= globalID %>.updateRelations({ id: entry.id, values: relations }); + return <%= globalID %>.updateRelations({ _id: entry.id, values: relations }); }, /** diff --git a/packages/strapi-generate-api/test/endpoint.test.js b/packages/strapi-generate-api/test/endpoint.test.js new file mode 100644 index 0000000000..9def326869 --- /dev/null +++ b/packages/strapi-generate-api/test/endpoint.test.js @@ -0,0 +1,843 @@ +// Helpers. +const {login} = require('../../../test/helpers/auth'); +const form = require('../../../test/helpers/generators'); +const restart = require('../../../test/helpers/restart'); +const rq = require('../../../test/helpers/request'); + +const cleanDate = (entry) => { + delete entry.updatedAt; + delete entry.createdAt; + delete entry.created_at; + delete entry.updated_at; +}; + +let data; + +describe('App setup auth', () => { + test( + 'Login admin user', + async () => { + await restart(rq); + + const body = await login(); + + rq.defaults({ + headers: { + 'Authorization': `Bearer ${body.jwt}` + } + }); + } + ); +}); + +describe('Generate test APIs', () => { + beforeEach(async () => { + await restart(rq); + }, 60000); + + test( + 'Create new article API', + async () => { + await rq({ + url: `/content-type-builder/models`, + method: 'POST', + body: form.article, + json: true + }); + } + ); + test( + 'Create new tag API', + async () => { + await rq({ + url: `/content-type-builder/models`, + method: 'POST', + body: form.tag, + json: true + }); + } + ); + test( + 'Create new category API', + async () => { + await rq({ + url: `/content-type-builder/models`, + method: 'POST', + body: form.category, + json: true + }); + } + ); + test( + 'Create new reference API', + async () => { + await rq({ + url: `/content-type-builder/models`, + method: 'POST', + body: form.reference, + json: true + }); + } + ); +}); + +describe('Test manyToMany relation (article - tag) with Content Manager', () => { + beforeAll(() => { + data = { + articles: [], + tags: [] + }; + }); + + beforeEach(async () => { + await restart(rq); + }, 60000); + + test( + 'Create tag1', + async () => { + let body = await rq({ + url: `/tag`, + method: 'POST', + body: { + name: 'tag1' + }, + json: true + }); + + data.tags.push(body); + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.name).toBe('tag1'); + } + ); + test( + 'Create tag2', + async () => { + let body = await rq({ + url: `/tag`, + method: 'POST', + body: { + name: 'tag2' + }, + json: true + }); + + data.tags.push(body); + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.name).toBe('tag2'); + } + ); + test( + 'Create tag3', + async () => { + let body = await rq({ + url: `/tag`, + method: 'POST', + body: { + name: 'tag3' + }, + json: true + }); + + data.tags.push(body); + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.name).toBe('tag3'); + } + ); + test( + 'Create article1 without relation', + async () => { + const entry = { + title: 'Article 1', + content: 'My super content 1' + }; + + let body = await rq({ + url: `/article`, + method: 'POST', + body: entry, + json: true + }); + + data.articles.push(body); + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + expect(body.tags.length).toBe(0); + } + ); + test( + 'Create article2 with tag1', + async () => { + const entry = { + title: 'Article 2', + content: 'Content 2', + tags: [data.tags[0]] + }; + + let body = await rq({ + url: `/article`, + method: 'POST', + body: entry, + json: true + }); + + data.articles.push(body); + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + expect(body.tags.length).toBe(1); + expect(body.tags[0].id).toBe(data.tags[0].id); + } + ); + test( + 'Update article1 add tag2', + async () => { + const entry = Object.assign({}, data.articles[0], { + tags: [data.tags[1]] + }); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[0] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + expect(body.tags.length).toBe(1); + expect(body.tags[0].id).toBe(data.tags[1].id); + } + ); + test( + 'Update article1 add tag1 and tag3', + async () => { + const entry = Object.assign({}, data.articles[0]); + entry.tags.push(data.tags[0]); + entry.tags.push(data.tags[2]); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[0] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + expect(body.tags.length).toBe(3); + } + ); + test( + 'Update article1 remove one tag', + async () => { + const entry = Object.assign({}, data.articles[0]); + entry.tags = entry.tags.slice(1); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[0] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + expect(body.tags.length).toBe(2); + } + ); + test( + 'Update article1 remove all tag', + async () => { + const entry = Object.assign({}, data.articles[0], { + tags: [] + }); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[0] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + expect(body.tags.length).toBe(0); + } + ); +}); + +describe('Test oneToMany - manyToOne relation (article - category) with Content Manager', () => { + beforeAll(() => { + data = { + articles: [], + categories: [] + }; + }); + + beforeEach(async () => { + await restart(rq); + }, 60000); + + test( + 'Create cat1', + async () => { + let body = await rq({ + url: `/category`, + method: 'POST', + body: { + name: 'cat1' + }, + json: true + }); + + data.categories.push(body); + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.name).toBe('cat1'); + } + ); + test( + 'Create cat2', + async () => { + let body = await rq({ + url: `/category`, + method: 'POST', + body: { + name: 'cat2' + }, + json: true + }); + + data.categories.push(body); + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.name).toBe('cat2'); + } + ); + test( + 'Create article1 with cat1', + async () => { + const entry = { + title: 'Article 1', + content: 'Content 1', + category: data.categories[0] + }; + + let body = await rq({ + url: `/article`, + method: 'POST', + body: entry, + json: true + }); + + data.articles.push(body); + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(body.category.name).toBe(entry.category.name); + expect(Array.isArray(body.tags)).toBeTruthy(); + } + ); + test( + 'Update article1 with cat2', + async () => { + const entry = Object.assign({}, data.articles[0], { + category: data.categories[1] + }); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[0] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(body.category.name).toBe(entry.category.name); + expect(Array.isArray(body.tags)).toBeTruthy(); + } + ); + test( + 'Create article2', + async () => { + const entry = { + title: 'Article 2', + content: 'Content 2' + }; + + let body = await rq({ + url: `/article`, + method: 'POST', + body: entry, + json: true + }); + + data.articles.push(body); + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(Array.isArray(body.tags)).toBeTruthy(); + } + ); + test( + 'Update article2 with cat2', + async () => { + const entry = Object.assign({}, data.articles[1], { + category: data.categories[1] + }); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[1] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(body.category.name).toBe(entry.category.name); + expect(Array.isArray(body.tags)).toBeTruthy(); + } + ); + test( + 'Update cat1 with article1', + async () => { + const entry = Object.assign({}, data.categories[0]); + entry.articles.push(data.articles[0]); + + cleanDate(entry); + + let body = await rq({ + url: `/category/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.categories[0] = body; + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.articles.length).toBe(1); + expect(body.name).toBe(entry.name); + } + ); + test( + 'Create cat3 with article1', + async () => { + const entry = { + name: 'cat3', + articles: [data.articles[0]] + }; + + let body = await rq({ + url: `/category`, + method: 'POST', + body: entry, + json: true + }); + + data.categories.push(body); + + expect(body.id); + expect(Array.isArray(body.articles)).toBeTruthy(); + expect(body.articles.length).toBe(1); + expect(body.name).toBe(entry.name); + } + ); + test( + 'Get article1 with cat3', + async () => { + let body = await rq({ + url: `/article/${data.articles[0].id}`, + method: 'GET', + json: true + }); + + expect(body.id); + expect(body.category.id).toBe(data.categories[2].id) + } + ); + test( + 'Get article2 with cat2', + async () => { + let body = await rq({ + url: `/article/${data.articles[1].id}`, + method: 'GET', + json: true + }); + + expect(body.id); + expect(body.category.id).toBe(data.categories[1].id) + } + ); + test( + 'Get cat1 without relations', + async () => { + let body = await rq({ + url: `/category/${data.categories[0].id}`, + method: 'GET', + json: true + }); + + expect(body.id); + expect(body.articles.length).toBe(0); + } + ); + test( + 'Get cat2 with article2', + async () => { + let body = await rq({ + url: `/category/${data.categories[1].id}`, + method: 'GET', + json: true + }); + + expect(body.id); + expect(body.articles.length).toBe(1); + expect(body.articles[0].id).toBe(data.articles[1].id); + } + ); + test( + 'Get cat3 with article1', + async () => { + let body = await rq({ + url: `/category/${data.categories[2].id}`, + method: 'GET', + json: true + }); + + expect(body.id); + expect(body.articles.length).toBe(1); + expect(body.articles[0].id).toBe(data.articles[0].id); + } + ); +}); + +describe('Test oneToOne relation (article - reference) with Content Manager', () => { + beforeAll(() => { + data = { + articles: [], + references: [] + }; + }); + + beforeEach(async () => { + await restart(rq); + }, 60000); + + test( + 'Create ref1', + async () => { + let body = await rq({ + url: `/reference`, + method: 'POST', + body: { + name: 'ref1' + }, + json: true + }); + + data.references.push(body); + + expect(body.id); + expect(body.name).toBe('ref1'); + } + ); + test( + 'Create article1', + async () => { + const entry = { + title: 'Article 1', + content: 'Content 1' + }; + + let body = await rq({ + url: `/article`, + method: 'POST', + body: entry, + json: true + }); + + data.articles.push(body); + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + } + ); + test( + 'Update article1 with ref1', + async () => { + const entry = Object.assign({}, data.articles[0], { + reference: data.references[0].id + }); + + cleanDate(entry); + + let body = await rq({ + url: `/article/${entry.id}`, + method: 'PUT', + body: entry, + json: true + }); + + data.articles[0] = body; + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(body.reference.id).toBe(entry.reference); + } + ); + test( + 'Create article2 with ref1', + async () => { + const entry = { + title: 'Article 2', + content: 'Content 2', + reference: data.references[0].id + }; + + let body = await rq({ + url: `/article`, + method: 'POST', + body: entry, + json: true + }); + + data.articles.push(body); + + expect(body.id); + expect(body.title).toBe(entry.title); + expect(body.content).toBe(entry.content); + expect(body.reference.id).toBe(entry.reference); + } + ); + test( + 'Get article1 without relations', + async () => { + let body = await rq({ + url: `/article/${data.articles[0].id}`, + method: 'GET', + json: true + }); + + expect(body.id); + expect(body.reference).toBe(null); + } + ); +}); + +describe('Test oneWay relation (reference - tag) with Content Manager', () => { + beforeEach(async () => { + await restart(rq); + }, 60000); + + test( + 'Attach Tag to a Reference', + async () => { + const tagToCreate = await rq({ + url: `/tag`, + method: 'POST', + json: true, + body: { + name: 'tag111' + } + }); + + const referenceToCreate = await rq({ + url: `/reference`, + method: 'POST', + json: true, + body: { + name: 'cat111', + tag: tagToCreate + } + }); + + expect(referenceToCreate.tag.id).toBe(tagToCreate.id); + } + ); + + test( + 'Detach Tag to a Reference', + async () => { + const tagToCreate = await rq({ + url: `/tag`, + method: 'POST', + json: true, + body: { + name: 'tag111' + } + }); + + const referenceToCreate = await rq({ + url: `/reference`, + method: 'POST', + json: true, + body: { + name: 'cat111', + tag: tagToCreate + } + }); + + expect(referenceToCreate.tag.id).toBe(tagToCreate.id); + + const referenceToUpdate = await rq({ + url: `/reference/${referenceToCreate.id}`, + method: 'PUT', + json: true, + body: { + tag: null + } + }); + + expect(referenceToUpdate.tag).toBe(null); + } + ); + + test( + 'Delete Tag so the relation in the Reference side should be removed', + async () => { + const tagToCreate = await rq({ + url: `/tag`, + method: 'POST', + json: true, + body: { + name: 'tag111' + } + }); + + const referenceToCreate = await rq({ + url: `/reference`, + method: 'POST', + json: true, + body: { + name: 'cat111', + tag: tagToCreate + } + }); + + const tagToDelete = await rq({ + url: `/tag/${tagToCreate.id}`, + method: 'DELETE', + json: true + }); + + const referenceToGet = await rq({ + url: `/reference/${referenceToCreate.id}`, + method: 'GET', + json: true + }); + + try { + if (Object.keys(referenceToGet.tag).length == 0) { + referenceToGet.tag = null; + } + } catch(err) { + // Silent + } + + expect(referenceToGet.tag).toBe(null); + } + ); +}); + +describe('Delete test APIs', () => { + beforeEach(async () => { + await restart(rq); + }, 60000); + + test( + 'Delete article API', + async () => { + await rq({ + url: `/content-type-builder/models/article`, + method: 'DELETE', + json: true + }); + } + ); + test( + 'Delete tag API', + async () => { + await rq({ + url: `/content-type-builder/models/tag`, + method: 'DELETE', + json: true + }); + } + ); + test( + 'Delete category API', + async () => { + await rq({ + url: `/content-type-builder/models/category`, + method: 'DELETE', + json: true + }); + } + ); + test( + 'Delete reference API', + async () => { + await rq({ + url: `/content-type-builder/models/reference`, + method: 'DELETE', + json: true + }); + } + ); +}); diff --git a/packages/strapi-generate-new/files/config/environments/production/database.json b/packages/strapi-generate-new/files/config/environments/production/database.json index 287a8121e0..3075fabf91 100755 --- a/packages/strapi-generate-new/files/config/environments/production/database.json +++ b/packages/strapi-generate-new/files/config/environments/production/database.json @@ -2,7 +2,7 @@ "defaultConnection": "default", "connections": { "default": { - "connector": "strapi-mongoose", + "connector": "strapi-hook-mongoose", "settings": { "client": "mongo", "uri": "${process.env.DATABASE_URI || ''}", diff --git a/packages/strapi-generate-new/files/config/environments/staging/database.json b/packages/strapi-generate-new/files/config/environments/staging/database.json index ec7c26da4c..43edf2421d 100755 --- a/packages/strapi-generate-new/files/config/environments/staging/database.json +++ b/packages/strapi-generate-new/files/config/environments/staging/database.json @@ -2,7 +2,7 @@ "defaultConnection": "default", "connections": { "default": { - "connector": "strapi-mongoose", + "connector": "strapi-hook-mongoose", "settings": { "client": "mongo", "uri": "${process.env.DATABASE_URI || ''}", diff --git a/packages/strapi-generate-new/lib/before.js b/packages/strapi-generate-new/lib/before.js index 9ad9e8c985..4182643982 100755 --- a/packages/strapi-generate-new/lib/before.js +++ b/packages/strapi-generate-new/lib/before.js @@ -69,14 +69,14 @@ module.exports = (scope, cb) => { name: 'MongoDB (recommended)', value: { database: 'mongo', - connector: 'strapi-mongoose' + connector: 'strapi-hook-mongoose' } }, { name: 'Postgres', value: { database: 'postgres', - connector: 'strapi-bookshelf', + connector: 'strapi-hook-bookshelf', module: 'pg' } }, @@ -84,7 +84,7 @@ module.exports = (scope, cb) => { name: 'MySQL', value: { database: 'mysql', - connector: 'strapi-bookshelf', + connector: 'strapi-hook-bookshelf', module: 'mysql' } } @@ -192,7 +192,7 @@ module.exports = (scope, cb) => { ]) .then(answers => { if (hasDatabaseConfig) { - answers = _.omit(scope.database.settings, ['client']); + answers = _.merge((_.omit(scope.database.settings, ['client'])), scope.database.options); } scope.database.settings.host = answers.host; @@ -214,7 +214,7 @@ module.exports = (scope, cb) => { let packageCmd = packageManager.commands('install --prefix', scope.tmpPath); // Manually create the temp directory for yarn if (!isStrapiInstalledWithNPM) { - shell.exec('mkdir tmp'); + shell.exec(`mkdir ${scope.tmpPath}`); } let cmd = `${packageCmd} ${scope.client.connector}@alpha`; @@ -223,18 +223,18 @@ module.exports = (scope, cb) => { cmd += ` ${scope.client.module}`; } - if (scope.client.connector === 'strapi-bookshelf') { - cmd += ` strapi-knex@alpha`; + if (scope.client.connector === 'strapi-hook-bookshelf') { + cmd += ` strapi-hook-knex@alpha`; - scope.additionalsDependencies = ['strapi-knex', 'knex']; + scope.additionalsDependencies = ['strapi-hook-knex', 'knex']; } exec(cmd, () => { if (scope.client.module) { - const lock = require(path.join(`${scope.tmpPath}`,`/node_modules/`,`${scope.client.module}/package.json`)); + const lock = require(path.join(`${scope.tmpPath}`, '/node_modules/', `${scope.client.module}/package.json`)); scope.client.version = lock.version; - if (scope.developerMode === true && scope.client.connector === 'strapi-bookshelf') { + if (scope.developerMode === true && scope.client.connector === 'strapi-hook-bookshelf') { const knexVersion = require(path.join(`${scope.tmpPath}`,`/node_modules/`,`knex/package.json`)); scope.additionalsDependencies[1] = `knex@${knexVersion.version || 'latest'}`; } @@ -248,7 +248,7 @@ module.exports = (scope, cb) => { Promise.all(asyncFn) .then(() => { try { - require(path.join(`${scope.tmpPath}`,`/node_modules/`,`${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation); + require(path.join(`${scope.tmpPath}`, '/node_modules/', `${scope.client.connector}/lib/utils/connectivity.js`))(scope, cb.success, connectionValidation); } catch(err) { shell.rm('-r', scope.tmpPath); cb.success(); diff --git a/packages/strapi-helper-plugin/lib/src/components/InputToggle/styles.scss b/packages/strapi-helper-plugin/lib/src/components/InputToggle/styles.scss index 15586f9ca7..8c257908a4 100644 --- a/packages/strapi-helper-plugin/lib/src/components/InputToggle/styles.scss +++ b/packages/strapi-helper-plugin/lib/src/components/InputToggle/styles.scss @@ -1,13 +1,25 @@ .gradientOff { background-image: linear-gradient( to bottom right, #F65A1D, #F68E0E ); color: white !important; - box-shadow: inset -1px 1px 3px rgba(0,0,0,0.1); + z-index: 0!important; + + &:active, :hover { + box-shadow: inset -1px 1px 3px rgba(0,0,0,0.1); + background-image: linear-gradient( to bottom right, #F65A1D, #F68E0E ); + color: white !important; + z-index: 0!important; + } } .gradientOn { background-image: linear-gradient( to bottom right, #005EEA, #0097F6); color: white !important; box-shadow: inset 1px 1px 3px rgba(0,0,0,0.1); + &:active, :hover { + background-image: linear-gradient( to bottom right, #005EEA, #0097F6); + color: white !important; + z-index: 0!important; + } } .inputToggleContainer { diff --git a/packages/strapi-helper-plugin/lib/src/components/InputsIndex/index.js b/packages/strapi-helper-plugin/lib/src/components/InputsIndex/index.js index ef8e995a99..8e7cfdf2c6 100644 --- a/packages/strapi-helper-plugin/lib/src/components/InputsIndex/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/InputsIndex/index.js @@ -21,8 +21,6 @@ import InputPasswordWithErrors from 'components/InputPasswordWithErrors'; import InputTextAreaWithErrors from 'components/InputTextAreaWithErrors'; import InputTextWithErrors from 'components/InputTextWithErrors'; import InputToggleWithErrors from 'components/InputToggleWithErrors'; -import WysiwygWithErrors from 'components/WysiwygWithErrors'; -import InputJSONWithErrors from 'components/InputJSONWithErrors'; const DefaultInputError = ({ type }) =>
Your input type: {type} does not exist
; @@ -32,7 +30,6 @@ const inputs = { date: InputDateWithErrors, email: InputEmailWithErrors, file: InputFileWithErrors, - json: InputJSONWithErrors, number: InputNumberWithErrors, password: InputPasswordWithErrors, search: InputSearchWithErrors, @@ -41,7 +38,6 @@ const inputs = { text: InputTextWithErrors, textarea: InputTextAreaWithErrors, toggle: InputToggleWithErrors, - wysiwyg: WysiwygWithErrors, }; function InputsIndex(props) { diff --git a/packages/strapi-helper-plugin/lib/src/components/PopUpWarning/styles.scss b/packages/strapi-helper-plugin/lib/src/components/PopUpWarning/styles.scss index c573bda639..4616bcf0b8 100644 --- a/packages/strapi-helper-plugin/lib/src/components/PopUpWarning/styles.scss +++ b/packages/strapi-helper-plugin/lib/src/components/PopUpWarning/styles.scss @@ -48,6 +48,7 @@ } .modalPosition { + max-width: 37.5rem !important; > div { width: 37.5rem; padding: 0 !important; diff --git a/packages/strapi-helper-plugin/package.json b/packages/strapi-helper-plugin/package.json index cebaccf82e..9b759b157d 100755 --- a/packages/strapi-helper-plugin/package.json +++ b/packages/strapi-helper-plugin/package.json @@ -48,11 +48,9 @@ "bootstrap": "^4.0.0-alpha.6", "chalk": "^2.1.0", "classnames": "^2.2.5", - "codemirror": "^5.38.0", "copy-webpack-plugin": "^4.3.1", "cross-env": "^5.0.5", "css-loader": "^0.28.5", - "draft-js": "^0.10.5", "eslint": "4.4.1", "eslint-config-airbnb": "15.1.0", "eslint-config-airbnb-base": "11.3.1", @@ -108,7 +106,6 @@ "rimraf": "^2.6.1", "sass-loader": "^6.0.6", "shelljs": "^0.7.8", - "showdown": "^1.8.6", "style-loader": "^0.18.2", "styled-components": "^3.2.6", "url-loader": "^0.5.9", diff --git a/packages/strapi-bookshelf/.gitignore b/packages/strapi-hook-bookshelf/.gitignore similarity index 100% rename from packages/strapi-bookshelf/.gitignore rename to packages/strapi-hook-bookshelf/.gitignore diff --git a/packages/strapi-bookshelf/.npmignore b/packages/strapi-hook-bookshelf/.npmignore similarity index 100% rename from packages/strapi-bookshelf/.npmignore rename to packages/strapi-hook-bookshelf/.npmignore diff --git a/packages/strapi-bookshelf/LICENSE.md b/packages/strapi-hook-bookshelf/LICENSE.md similarity index 100% rename from packages/strapi-bookshelf/LICENSE.md rename to packages/strapi-hook-bookshelf/LICENSE.md diff --git a/packages/strapi-bookshelf/README.md b/packages/strapi-hook-bookshelf/README.md similarity index 62% rename from packages/strapi-bookshelf/README.md rename to packages/strapi-hook-bookshelf/README.md index 5d1ce7ecb7..96ba285413 100755 --- a/packages/strapi-bookshelf/README.md +++ b/packages/strapi-hook-bookshelf/README.md @@ -1,12 +1,12 @@ -# strapi-bookshelf +# strapi-hook-bookshelf -[![npm version](https://img.shields.io/npm/v/strapi-bookshelf.svg)](https://www.npmjs.org/package/strapi-bookshelf) -[![npm downloads](https://img.shields.io/npm/dm/strapi-bookshelf.svg)](https://www.npmjs.org/package/strapi-bookshelf) -[![npm dependencies](https://david-dm.org/strapi/strapi-bookshelf.svg)](https://david-dm.org/strapi/strapi-bookshelf) -[![Build status](https://travis-ci.org/strapi/strapi-bookshelf.svg?branch=master)](https://travis-ci.org/strapi/strapi-bookshelf) +[![npm version](https://img.shields.io/npm/v/strapi-hook-bookshelf.svg)](https://www.npmjs.org/package/strapi-hook-bookshelf) +[![npm downloads](https://img.shields.io/npm/dm/strapi-hook-bookshelf.svg)](https://www.npmjs.org/package/strapi-hook-bookshelf) +[![npm dependencies](https://david-dm.org/strapi/strapi-hook-bookshelf.svg)](https://david-dm.org/strapi/strapi-hook-bookshelf) +[![Build status](https://travis-ci.org/strapi/strapi-hook-bookshelf.svg?branch=master)](https://travis-ci.org/strapi/strapi-hook-bookshelf) [![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io) -This built-in hook allows you to use the [Bookshelf ORM](http://bookshelfjs.org/) using the `strapi-knex` hook. +This built-in hook allows you to use the [Bookshelf ORM](http://bookshelfjs.org/) using the `strapi-hook-knex` hook. [Bookshelf ORM](http://bookshelfjs.org/) is a JavaScript ORM for Node.js, built on the [Knex node module](http://knexjs.org/) SQL query builder. Featuring both promise based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. diff --git a/packages/strapi-bookshelf/lib/index.js b/packages/strapi-hook-bookshelf/lib/index.js similarity index 99% rename from packages/strapi-bookshelf/lib/index.js rename to packages/strapi-hook-bookshelf/lib/index.js index d73418eba4..39214f7cc8 100755 --- a/packages/strapi-bookshelf/lib/index.js +++ b/packages/strapi-hook-bookshelf/lib/index.js @@ -45,7 +45,7 @@ module.exports = function(strapi) { */ initialize: async cb => { - const connections = _.pickBy(strapi.config.connections, { connector: 'strapi-bookshelf' }); + const connections = _.pickBy(strapi.config.connections, { connector: 'strapi-hook-bookshelf' }); const databaseUpdate = []; diff --git a/packages/strapi-bookshelf/lib/relations.js b/packages/strapi-hook-bookshelf/lib/relations.js similarity index 97% rename from packages/strapi-bookshelf/lib/relations.js rename to packages/strapi-hook-bookshelf/lib/relations.js index 57ab4bf757..4b3083bad9 100644 --- a/packages/strapi-bookshelf/lib/relations.js +++ b/packages/strapi-hook-bookshelf/lib/relations.js @@ -10,15 +10,17 @@ const _ = require('lodash'); // Utils const { models: { getValuePrimaryKey } } = require('strapi-utils'); -const transformToArrayID = (array) => { +const transformToArrayID = (array, association) => { if(_.isArray(array)) { - return array.map(value => { + array = array.map(value => { if (_.isPlainObject(value)) { - return value._id || value.id; + return value._id || value.id || false; } return value; }); + + return array.filter(n => n); } if (association.type === 'model' || (association.type === 'collection' && _.isObject(array))) { @@ -140,8 +142,8 @@ module.exports = { case 'manyToMany': if (response[current] && _.isArray(response[current]) && current !== 'id') { // Compare array of ID to find deleted files. - const currentValue = transformToArrayID(response[current]).map(id => id.toString()); - const storedValue = transformToArrayID(params.values[current]).map(id => id.toString()); + const currentValue = transformToArrayID(response[current], association).map(id => id.toString()); + const storedValue = transformToArrayID(params.values[current], association).map(id => id.toString()); const toAdd = _.difference(storedValue, currentValue); const toRemove = _.difference(currentValue, storedValue); @@ -229,8 +231,8 @@ module.exports = { case 'oneToManyMorph': case 'manyToManyMorph': { // Compare array of ID to find deleted files. - const currentValue = transformToArrayID(response[current]).map(id => id.toString()); - const storedValue = transformToArrayID(params.values[current]).map(id => id.toString()); + const currentValue = transformToArrayID(response[current], association).map(id => id.toString()); + const storedValue = transformToArrayID(params.values[current], association).map(id => id.toString()); const toAdd = _.difference(storedValue, currentValue); const toRemove = _.difference(currentValue, storedValue); diff --git a/packages/strapi-bookshelf/lib/utils/connectivity.js b/packages/strapi-hook-bookshelf/lib/utils/connectivity.js similarity index 100% rename from packages/strapi-bookshelf/lib/utils/connectivity.js rename to packages/strapi-hook-bookshelf/lib/utils/connectivity.js diff --git a/packages/strapi-bookshelf/lib/utils/graphql.js b/packages/strapi-hook-bookshelf/lib/utils/graphql.js similarity index 100% rename from packages/strapi-bookshelf/lib/utils/graphql.js rename to packages/strapi-hook-bookshelf/lib/utils/graphql.js diff --git a/packages/strapi-bookshelf/lib/utils/index.js b/packages/strapi-hook-bookshelf/lib/utils/index.js similarity index 100% rename from packages/strapi-bookshelf/lib/utils/index.js rename to packages/strapi-hook-bookshelf/lib/utils/index.js diff --git a/packages/strapi-bookshelf/package.json b/packages/strapi-hook-bookshelf/package.json similarity index 90% rename from packages/strapi-bookshelf/package.json rename to packages/strapi-hook-bookshelf/package.json index 7b3117939d..6db9ad31c2 100755 --- a/packages/strapi-bookshelf/package.json +++ b/packages/strapi-hook-bookshelf/package.json @@ -1,5 +1,5 @@ { - "name": "strapi-bookshelf", + "name": "strapi-hook-bookshelf", "version": "3.0.0-alpha.12.7.1", "description": "Bookshelf hook for the Strapi framework", "homepage": "http://strapi.io", @@ -20,13 +20,12 @@ "inquirer": "^5.2.0", "lodash": "^4.17.4", "pluralize": "^6.0.0", - "strapi-knex": "3.0.0-alpha.12.7.1", + "strapi-hook-knex": "3.0.0-alpha.12.7.1", "strapi-utils": "3.0.0-alpha.12.7.1" }, "strapi": { - "isHook": true, "dependencies": [ - "strapi-knex" + "knex" ] }, "scripts": { diff --git a/packages/strapi-ejs/.editorconfig b/packages/strapi-hook-ejs/.editorconfig similarity index 100% rename from packages/strapi-ejs/.editorconfig rename to packages/strapi-hook-ejs/.editorconfig diff --git a/packages/strapi-ejs/.gitignore b/packages/strapi-hook-ejs/.gitignore similarity index 100% rename from packages/strapi-ejs/.gitignore rename to packages/strapi-hook-ejs/.gitignore diff --git a/packages/strapi-ejs/.npmignore b/packages/strapi-hook-ejs/.npmignore similarity index 100% rename from packages/strapi-ejs/.npmignore rename to packages/strapi-hook-ejs/.npmignore diff --git a/packages/strapi-ejs/LICENSE.md b/packages/strapi-hook-ejs/LICENSE.md similarity index 100% rename from packages/strapi-ejs/LICENSE.md rename to packages/strapi-hook-ejs/LICENSE.md diff --git a/packages/strapi-ejs/README.md b/packages/strapi-hook-ejs/README.md similarity index 100% rename from packages/strapi-ejs/README.md rename to packages/strapi-hook-ejs/README.md diff --git a/packages/strapi-ejs/lib/index.js b/packages/strapi-hook-ejs/lib/index.js similarity index 100% rename from packages/strapi-ejs/lib/index.js rename to packages/strapi-hook-ejs/lib/index.js diff --git a/packages/strapi-ejs/package.json b/packages/strapi-hook-ejs/package.json similarity index 93% rename from packages/strapi-ejs/package.json rename to packages/strapi-hook-ejs/package.json index 110ec9b2c3..8d2e9af34f 100755 --- a/packages/strapi-ejs/package.json +++ b/packages/strapi-hook-ejs/package.json @@ -1,5 +1,5 @@ { - "name": "strapi-ejs", + "name": "strapi-hook-ejs", "version": "3.0.0-alpha.12.7.1", "description": "EJS hook for the Strapi framework", "homepage": "http://strapi.io", @@ -16,9 +16,6 @@ "co": "^4.6.0", "koa-ejs": "^4.1.0" }, - "strapi": { - "isHook": true - }, "scripts": { "prepublishOnly": "npm prune" }, diff --git a/packages/strapi-knex/.editorconfig b/packages/strapi-hook-knex/.editorconfig similarity index 100% rename from packages/strapi-knex/.editorconfig rename to packages/strapi-hook-knex/.editorconfig diff --git a/packages/strapi-knex/.gitignore b/packages/strapi-hook-knex/.gitignore similarity index 100% rename from packages/strapi-knex/.gitignore rename to packages/strapi-hook-knex/.gitignore diff --git a/packages/strapi-knex/.npmignore b/packages/strapi-hook-knex/.npmignore similarity index 100% rename from packages/strapi-knex/.npmignore rename to packages/strapi-hook-knex/.npmignore diff --git a/packages/strapi-knex/LICENSE.md b/packages/strapi-hook-knex/LICENSE.md similarity index 100% rename from packages/strapi-knex/LICENSE.md rename to packages/strapi-hook-knex/LICENSE.md diff --git a/packages/strapi-knex/README.md b/packages/strapi-hook-knex/README.md similarity index 65% rename from packages/strapi-knex/README.md rename to packages/strapi-hook-knex/README.md index 694ced22bb..eb8fbb5470 100755 --- a/packages/strapi-knex/README.md +++ b/packages/strapi-hook-knex/README.md @@ -1,9 +1,9 @@ -# strapi-knex +# strapi-hook-knex -[![npm version](https://img.shields.io/npm/v/strapi-knex.svg)](https://www.npmjs.org/package/strapi-knex) -[![npm downloads](https://img.shields.io/npm/dm/strapi-knex.svg)](https://www.npmjs.org/package/strapi-knex) -[![npm dependencies](https://david-dm.org/strapi/strapi-knex.svg)](https://david-dm.org/strapi/strapi-knex) -[![Build status](https://travis-ci.org/strapi/strapi-knex.svg?branch=master)](https://travis-ci.org/strapi/strapi-knex) +[![npm version](https://img.shields.io/npm/v/strapi-hook-knex.svg)](https://www.npmjs.org/package/strapi-hook-knex) +[![npm downloads](https://img.shields.io/npm/dm/strapi-hook-knex.svg)](https://www.npmjs.org/package/strapi-hook-knex) +[![npm dependencies](https://david-dm.org/strapi/strapi-hook-knex.svg)](https://david-dm.org/strapi/strapi-hook-knex) +[![Build status](https://travis-ci.org/strapi/strapi-hook-knex.svg?branch=master)](https://travis-ci.org/strapi/strapi-hook-knex) [![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io) This built-in hook allows you to directly make SQL queries from your Strapi connections to your databases thanks to the [Knex node module](http://knexjs.org/). diff --git a/packages/strapi-knex/lib/index.js b/packages/strapi-hook-knex/lib/index.js similarity index 98% rename from packages/strapi-knex/lib/index.js rename to packages/strapi-hook-knex/lib/index.js index f853163ecf..ceacd92131 100755 --- a/packages/strapi-knex/lib/index.js +++ b/packages/strapi-hook-knex/lib/index.js @@ -46,7 +46,7 @@ module.exports = strapi => { initialize: cb => { // For each connection in the config register a new Knex connection. - _.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-bookshelf'}), (connection, name) => { + _.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-hook-bookshelf'}), (connection, name) => { // Make sure we use the client even if the typo is not the exact one. switch (connection.settings.client) { diff --git a/packages/strapi-knex/package.json b/packages/strapi-hook-knex/package.json similarity index 93% rename from packages/strapi-knex/package.json rename to packages/strapi-hook-knex/package.json index 4fa1428fa6..b6dc3c3df5 100755 --- a/packages/strapi-knex/package.json +++ b/packages/strapi-hook-knex/package.json @@ -1,5 +1,5 @@ { - "name": "strapi-knex", + "name": "strapi-hook-knex", "version": "3.0.0-alpha.12.7.1", "description": "Knex hook for the Strapi framework", "homepage": "http://strapi.io", @@ -19,9 +19,6 @@ "knex": "^0.13.0", "lodash": "^4.17.4" }, - "strapi": { - "isHook": true - }, "author": { "email": "hi@strapi.io", "name": "Strapi team", diff --git a/packages/strapi-mongoose/.editorconfig b/packages/strapi-hook-mongoose/.editorconfig similarity index 100% rename from packages/strapi-mongoose/.editorconfig rename to packages/strapi-hook-mongoose/.editorconfig diff --git a/packages/strapi-mongoose/.gitignore b/packages/strapi-hook-mongoose/.gitignore similarity index 100% rename from packages/strapi-mongoose/.gitignore rename to packages/strapi-hook-mongoose/.gitignore diff --git a/packages/strapi-mongoose/.npmignore b/packages/strapi-hook-mongoose/.npmignore similarity index 100% rename from packages/strapi-mongoose/.npmignore rename to packages/strapi-hook-mongoose/.npmignore diff --git a/packages/strapi-mongoose/LICENSE.md b/packages/strapi-hook-mongoose/LICENSE.md similarity index 100% rename from packages/strapi-mongoose/LICENSE.md rename to packages/strapi-hook-mongoose/LICENSE.md diff --git a/packages/strapi-mongoose/README.md b/packages/strapi-hook-mongoose/README.md similarity index 52% rename from packages/strapi-mongoose/README.md rename to packages/strapi-hook-mongoose/README.md index 49c59d8f23..5f45c405c7 100755 --- a/packages/strapi-mongoose/README.md +++ b/packages/strapi-hook-mongoose/README.md @@ -1,9 +1,9 @@ -# strapi-mongoose +# strapi-hook-mongoose -[![npm version](https://img.shields.io/npm/v/strapi-mongoose.svg)](https://www.npmjs.org/package/strapi-mongoose) -[![npm downloads](https://img.shields.io/npm/dm/strapi-mongoose.svg)](https://www.npmjs.org/package/strapi-mongoose) -[![npm dependencies](https://david-dm.org/strapi/strapi-mongoose.svg)](https://david-dm.org/strapi/strapi-mongoose) -[![Build status](https://travis-ci.org/strapi/strapi-mongoose.svg?branch=master)](https://travis-ci.org/strapi/strapi-bookshelf) +[![npm version](https://img.shields.io/npm/v/strapi-hook-mongoose.svg)](https://www.npmjs.org/package/strapi-hook-mongoose) +[![npm downloads](https://img.shields.io/npm/dm/strapi-hook-mongoose.svg)](https://www.npmjs.org/package/strapi-hook-mongoose) +[![npm dependencies](https://david-dm.org/strapi/strapi-hook-mongoose.svg)](https://david-dm.org/strapi/strapi-hook-mongoose) +[![Build status](https://travis-ci.org/strapi/strapi-hook-mongoose.svg?branch=master)](https://travis-ci.org/strapi/strapi-hook-mongoose) [![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io) This built-in hook allows you to use the [Mongoose ORM](http://mongoosejs.com/). diff --git a/packages/strapi-mongoose/lib/index.js b/packages/strapi-hook-mongoose/lib/index.js similarity index 99% rename from packages/strapi-mongoose/lib/index.js rename to packages/strapi-hook-mongoose/lib/index.js index 3b688a5608..a0c3e5e9d4 100755 --- a/packages/strapi-mongoose/lib/index.js +++ b/packages/strapi-hook-mongoose/lib/index.js @@ -49,7 +49,7 @@ module.exports = function (strapi) { */ initialize: cb => { - _.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-mongoose'}), (connection, connectionName) => { + _.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-hook-mongoose'}), (connection, connectionName) => { const instance = new Mongoose(); const { uri, host, port, username, password, database } = _.defaults(connection.settings, strapi.config.hook.settings.mongoose); const uriOptions = uri ? url.parse(uri, true).query : {}; diff --git a/packages/strapi-mongoose/lib/relations.js b/packages/strapi-hook-mongoose/lib/relations.js similarity index 99% rename from packages/strapi-mongoose/lib/relations.js rename to packages/strapi-hook-mongoose/lib/relations.js index 1b1de2c7b5..1fbe976cac 100644 --- a/packages/strapi-mongoose/lib/relations.js +++ b/packages/strapi-hook-mongoose/lib/relations.js @@ -174,7 +174,7 @@ module.exports = { strapi.models[_.toLower(obj.ref)].globalId; // Define the object stored in database. - // The shape is this object is defined by the strapi-mongoose connector. + // The shape is this object is defined by the strapi-hook-mongoose connector. return { ref: obj.refId, kind: globalId, diff --git a/packages/strapi-mongoose/lib/utils/connectivity.js b/packages/strapi-hook-mongoose/lib/utils/connectivity.js similarity index 100% rename from packages/strapi-mongoose/lib/utils/connectivity.js rename to packages/strapi-hook-mongoose/lib/utils/connectivity.js diff --git a/packages/strapi-mongoose/lib/utils/index.js b/packages/strapi-hook-mongoose/lib/utils/index.js similarity index 100% rename from packages/strapi-mongoose/lib/utils/index.js rename to packages/strapi-hook-mongoose/lib/utils/index.js diff --git a/packages/strapi-mongoose/package.json b/packages/strapi-hook-mongoose/package.json similarity index 93% rename from packages/strapi-mongoose/package.json rename to packages/strapi-hook-mongoose/package.json index 0becf7761b..9386fbfa8d 100755 --- a/packages/strapi-mongoose/package.json +++ b/packages/strapi-hook-mongoose/package.json @@ -1,5 +1,5 @@ { - "name": "strapi-mongoose", + "name": "strapi-hook-mongoose", "version": "3.0.0-alpha.12.7.1", "description": "Mongoose hook for the Strapi framework", "homepage": "http://strapi.io", @@ -21,9 +21,6 @@ "pluralize": "^6.0.0", "strapi-utils": "3.0.0-alpha.12.7.1" }, - "strapi": { - "isHook": true - }, "author": { "email": "hi@strapi.io", "name": "Strapi team", diff --git a/packages/strapi-redis/.editorconfig b/packages/strapi-hook-redis/.editorconfig similarity index 100% rename from packages/strapi-redis/.editorconfig rename to packages/strapi-hook-redis/.editorconfig diff --git a/packages/strapi-redis/.gitignore b/packages/strapi-hook-redis/.gitignore similarity index 100% rename from packages/strapi-redis/.gitignore rename to packages/strapi-hook-redis/.gitignore diff --git a/packages/strapi-redis/.npmignore b/packages/strapi-hook-redis/.npmignore similarity index 100% rename from packages/strapi-redis/.npmignore rename to packages/strapi-hook-redis/.npmignore diff --git a/packages/strapi-redis/LICENSE.md b/packages/strapi-hook-redis/LICENSE.md similarity index 100% rename from packages/strapi-redis/LICENSE.md rename to packages/strapi-hook-redis/LICENSE.md diff --git a/packages/strapi-redis/README.md b/packages/strapi-hook-redis/README.md similarity index 100% rename from packages/strapi-redis/README.md rename to packages/strapi-hook-redis/README.md diff --git a/packages/strapi-redis/lib/index.js b/packages/strapi-hook-redis/lib/index.js similarity index 98% rename from packages/strapi-redis/lib/index.js rename to packages/strapi-hook-redis/lib/index.js index 6b26ad51e8..c224615397 100755 --- a/packages/strapi-redis/lib/index.js +++ b/packages/strapi-hook-redis/lib/index.js @@ -38,13 +38,13 @@ module.exports = function(strapi) { initialize: cb => { if (_.isEmpty(strapi.models) || !_.pickBy(strapi.config.connections, { - connector: 'strapi-redis' + connector: 'strapi-hook-redis' })) { return cb(); } const connections = _.pickBy(strapi.config.connections, { - connector: 'strapi-redis' + connector: 'strapi-hook-redis' }); if(_.size(connections) === 0) { diff --git a/packages/strapi-redis/lib/utils/connectivity.js b/packages/strapi-hook-redis/lib/utils/connectivity.js similarity index 100% rename from packages/strapi-redis/lib/utils/connectivity.js rename to packages/strapi-hook-redis/lib/utils/connectivity.js diff --git a/packages/strapi-redis/package.json b/packages/strapi-hook-redis/package.json similarity index 93% rename from packages/strapi-redis/package.json rename to packages/strapi-hook-redis/package.json index d4058c94e3..bd0487a8ff 100755 --- a/packages/strapi-redis/package.json +++ b/packages/strapi-hook-redis/package.json @@ -1,5 +1,5 @@ { - "name": "strapi-redis", + "name": "strapi-hook-redis", "version": "3.0.0-alpha.12.7.1", "description": "Redis hook for the Strapi framework", "homepage": "http://strapi.io", @@ -20,9 +20,6 @@ "stack-trace": "0.0.10", "strapi-utils": "3.0.0-alpha.12.7.1" }, - "strapi": { - "isHook": true - }, "author": { "email": "hi@strapi.io", "name": "Strapi team", diff --git a/packages/strapi-middleware-views/package.json b/packages/strapi-middleware-views/package.json index d11be2e120..77852a7944 100755 --- a/packages/strapi-middleware-views/package.json +++ b/packages/strapi-middleware-views/package.json @@ -1,7 +1,7 @@ { "name": "strapi-middleware-views", "version": "3.0.0-alpha.12.7.1", - "description": "Views hook to enable server-side rendering for the Strapi framework", + "description": "Views middleware to enable server-side rendering for the Strapi framework", "homepage": "http://strapi.io", "keywords": [ "redis", @@ -19,9 +19,6 @@ "koa-views": "^6.1.1", "lodash": "^4.17.4" }, - "strapi": { - "isHook": true - }, "author": { "email": "hi@strapi.io", "name": "Strapi team", diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_barred.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_barred.svg new file mode 100755 index 0000000000..2f3c7b9111 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_barred.svg @@ -0,0 +1 @@ +abc \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_bold.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_bold.svg new file mode 100755 index 0000000000..cee3cf4011 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_bold.svg @@ -0,0 +1 @@ +B \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_bullet-list.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_bullet-list.svg new file mode 100755 index 0000000000..6e060c39f5 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_bullet-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_code-block.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_code-block.svg new file mode 100755 index 0000000000..a47ca10058 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_code-block.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_italic.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_italic.svg new file mode 100755 index 0000000000..6181979e31 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_italic.svg @@ -0,0 +1 @@ +I \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_link.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_link.svg new file mode 100755 index 0000000000..53b6125367 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_media.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_media.svg new file mode 100755 index 0000000000..d7401730c7 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_media.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_numbered-list.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_numbered-list.svg new file mode 100755 index 0000000000..79c8091f86 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_numbered-list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_quote-block.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_quote-block.svg new file mode 100755 index 0000000000..62a7990e92 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_quote-block.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_underline.svg b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_underline.svg new file mode 100755 index 0000000000..7a9b8da4e1 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/icons/icon_underline.svg @@ -0,0 +1 @@ +U \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross-blue.svg b/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross-blue.svg index 58f0a5d8c7..2c77b183ae 100644 --- a/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross-blue.svg +++ b/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross-blue.svg @@ -1,18 +1 @@ - - - - Shape - Created with Sketch. - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross.svg b/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross.svg index aa113a980d..d627d9bdac 100644 --- a/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross.svg +++ b/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-cross.svg @@ -1,14 +1 @@ - - - - Shape - Created with Sketch. - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-edit-blue.svg b/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-edit-blue.svg new file mode 100644 index 0000000000..0da67b8b3b --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/assets/images/icon-edit-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/bootstrap.js b/packages/strapi-plugin-content-manager/admin/src/bootstrap.js index dd8585d266..524be693df 100644 --- a/packages/strapi-plugin-content-manager/admin/src/bootstrap.js +++ b/packages/strapi-plugin-content-manager/admin/src/bootstrap.js @@ -1,13 +1,24 @@ -import { generateMenu } from 'containers/App/sagas'; +import { map, omit } from 'lodash'; +import request from 'utils/request'; // This method is executed before the load of the plugin const bootstrap = (plugin) => new Promise((resolve, reject) => { - generateMenu() - .then(menu => { + request('/content-manager/models', { method: 'GET' }) + .then(models => { + const menu = [{ + name: 'Content Types', + links: map(omit(models.models.models, 'plugins'), (model, key) => ({ + label: model.labelPlural || model.label || key, + destination: key, + })), + }]; plugin.leftMenuSections = menu; resolve(plugin); }) - .catch(e => reject(e)); + .catch(e => { + strapi.notification.error('content-manager.error.model.fetch'); + reject(e); + }); }); export default bootstrap; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/Block/index.js b/packages/strapi-plugin-content-manager/admin/src/components/Block/index.js new file mode 100644 index 0000000000..042ede09d0 --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/components/Block/index.js @@ -0,0 +1,39 @@ +/** + * + * Block + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; + +import styles from './styles.scss'; + +const Block = ({ children, description, title }) => ( +
+
+
+ + + {msg =>

{msg}

} +
+
+ {children} +
+
+); + + +Block.defaultProps = { + children: null, + description: 'app.utils.defaultMessage', + title: 'app.utils.defaultMessage', +}; + +Block.propTypes = { + children: PropTypes.any, + description: PropTypes.string, + title: PropTypes.string, +}; + +export default Block; \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/components/Block/styles.scss b/packages/strapi-plugin-content-manager/admin/src/components/Block/styles.scss new file mode 100644 index 0000000000..a37d677fea --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/components/Block/styles.scss @@ -0,0 +1,22 @@ +.ctmBlock{ + margin-bottom: 35px; + background: #ffffff; + padding: 22px 28px 18px; + border-radius: 2px; + box-shadow: 0 2px 4px #E3E9F3; + -webkit-font-smoothing: antialiased; +} + +.ctmBlockTitle { + padding-top: 0px; + line-height: 18px; + > span { + font-weight: 600; + color: #333740; + font-size: 18px; + } + > p { + color: #787E8F; + font-size: 13px; + } +} \ No newline at end of file diff --git a/packages/strapi-plugin-content-manager/admin/src/components/DraggableAttr/index.js b/packages/strapi-plugin-content-manager/admin/src/components/DraggableAttr/index.js new file mode 100644 index 0000000000..c9cd5ded9a --- /dev/null +++ b/packages/strapi-plugin-content-manager/admin/src/components/DraggableAttr/index.js @@ -0,0 +1,185 @@ +/** + * + * DraggableAttr + */ + +/* eslint-disable react/no-find-dom-node */ +import React from 'react'; +import { findDOMNode } from 'react-dom'; +import { + DragSource, + DropTarget, +} from 'react-dnd'; +import { flow, upperFirst } from 'lodash'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import cn from 'classnames'; + +import styles from './styles.scss'; + +const draggableAttrSource = { + beginDrag: (props) => { + props.updateSiblingHoverState(); + + return { + id: props.id, + index: props.index, + }; + }, + endDrag: (props) => { + props.updateSiblingHoverState(); + + return {}; + }, +}; + +const draggableAttrTarget = { + hover: (props, monitor, component) => { + const dragIndex = monitor.getItem().index; + const hoverIndex = props.index; + + // Don't replace items with themselves + if (dragIndex === hoverIndex) { + return; + } + + // Determine rectangle on screen + const hoverBoundingRect = findDOMNode(component).getBoundingClientRect(); + + // Get vertical middle + const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; + + // Determine mouse position + const clientOffset = monitor.getClientOffset(); + + // Get pixels to the top + const hoverClientY = clientOffset.y - hoverBoundingRect.top; + + // Only perform the move when the mouse has crossed half of the items height + // When dragging downwards, only move when the cursor is below 50% + // When dragging upwards, only move when the cursor is above 50% + + // Dragging downwards + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return; + } + + // Dragging upwards + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return; + } + + // Time to actually perform the action + props.moveAttr(dragIndex, hoverIndex, props.keys); + + // Note: we're mutating the monitor item here! + // Generally it's better to avoid mutations, + // but it's good here for the sake of performance + // to avoid expensive index searches. + monitor.getItem().index = hoverIndex; + }, +}; + +class DraggableAttr extends React.Component { + state = { isOver: false, dragStart: false }; + + componentDidUpdate(prevProps) { + const { isDraggingSibling } = this.props; + + if (isDraggingSibling !== prevProps.isDraggingSibling && isDraggingSibling) { + this.handleMouseLeave(); + } + } + + handleClickEdit = (e) => { + e.preventDefault(); + e.stopPropagation(); + this.props.onClickEditListItem(this.props.index); + } + + handleMouseEnter = () => { + if (!this.props.isDraggingSibling) { + this.setState({ isOver: true }); + } + }; + + handleMouseLeave = () => this.setState({ isOver: false }); + + handleRemove = (e) => { + e.preventDefault(); + e.stopPropagation(); + this.props.onRemove(this.props.index, this.props.keys); + } + + render() { + const { label, name, isDragging, isEditing, connectDragSource, connectDropTarget } = this.props; + const { isOver, dragStart } = this.state; + const opacity = isDragging ? 0.2 : 1; + const overClass = isOver ? styles.draggableAttrOvered : ''; + const className = dragStart ? styles.dragged : styles.draggableAttr; + + return ( + connectDragSource( + connectDropTarget( +
this.setState({ dragStart: true })} + onDragEnd={() => this.setState({ dragStart: false })} + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} + onClick={this.handleClickEdit} + style={{ opacity }} + > +