Merge branch 'master' into feature/instagram_provider
@ -42,6 +42,7 @@ module.exports = {
 | 
			
		||||
  rules: {
 | 
			
		||||
    'generator-star-spacing': 0,
 | 
			
		||||
    'no-console': 0,
 | 
			
		||||
    'require-atomic-updates': 0,
 | 
			
		||||
    'react-hooks/rules-of-hooks': 'error',
 | 
			
		||||
    'react-hooks/exhaustive-deps': 'warn',
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
@ -71,6 +71,8 @@ jobs:
 | 
			
		||||
    - <<: *e2e_tests
 | 
			
		||||
      name: 'E2E Mysql'
 | 
			
		||||
      before_install:
 | 
			
		||||
        - sudo cp $TRAVIS_BUILD_DIR/_travis/mysql.cnf /etc/mysql/conf.d/
 | 
			
		||||
        - sudo service mysql restart
 | 
			
		||||
        - mysql -e 'CREATE DATABASE strapi_test;'
 | 
			
		||||
      env:
 | 
			
		||||
        - DB_STRING='--dbclient=mysql --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=travis --dbpassword='
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										3
									
								
								_travis/mysql.cnf
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,3 @@
 | 
			
		||||
[mysqld]
 | 
			
		||||
collation-server=utf8_unicode_ci
 | 
			
		||||
character-set-server=utf8
 | 
			
		||||
@ -24,6 +24,7 @@ module.exports = strapi => {
 | 
			
		||||
 | 
			
		||||
    async initialize() {
 | 
			
		||||
      // await someAsyncCode()
 | 
			
		||||
      // this().defaults['your_config'] to access to your configs.
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
		 After Width: | Height: | Size: 962 KiB  | 
| 
		 After Width: | Height: | Size: 958 KiB  | 
| 
		 After Width: | Height: | Size: 956 KiB  | 
| 
		 After Width: | Height: | Size: 794 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.2 MiB  | 
| 
		 After Width: | Height: | Size: 892 KiB  | 
| 
		 After Width: | Height: | Size: 1022 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 881 KiB  | 
| 
		 After Width: | Height: | Size: 919 KiB  | 
| 
		 After Width: | Height: | Size: 1016 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1019 KiB  | 
| 
		 After Width: | Height: | Size: 1024 KiB  | 
| 
		 After Width: | Height: | Size: 880 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 882 KiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 984 KiB  | 
| 
		 After Width: | Height: | Size: 802 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.4 MiB  | 
| 
		 After Width: | Height: | Size: 850 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.1 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 1013 KiB  | 
| 
		 After Width: | Height: | Size: 819 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
| 
		 After Width: | Height: | Size: 968 KiB  | 
| 
		 After Width: | Height: | Size: 1.3 MiB  | 
| 
		 After Width: | Height: | Size: 1.3 MiB  | 
| 
		 After Width: | Height: | Size: 1.2 MiB  | 
| 
		 After Width: | Height: | Size: 1.2 MiB  | 
| 
		 After Width: | Height: | Size: 963 KiB  | 
| 
		 After Width: | Height: | Size: 1015 KiB  | 
| 
		 After Width: | Height: | Size: 899 KiB  | 
| 
		 After Width: | Height: | Size: 700 KiB  | 
| 
		 After Width: | Height: | Size: 833 KiB  | 
| 
		 After Width: | Height: | Size: 1.0 MiB  | 
@ -1,365 +1,634 @@
 | 
			
		||||
# Tutorial
 | 
			
		||||
 | 
			
		||||
This Tutorial is written for developers who prefer a more detailed step-by-step introduction. (A less detailed version can be found at [Quick Start Guide](/3.0.0-beta.x/getting-started/quick-start.html).)
 | 
			
		||||
This **tutorial** is written for developers to **teach and explain** a step-by-step introduction to Strapi. (The [Quick Start Guide](/3.0.0-beta.x/getting-started/quick-start.html) is a more concise **How-to** version.) This tutorial takes you through the beginning steps of how you start a project like **"FoodAdvisor"** ([Github](https://github.com/strapi/foodadvisor/))([Demo](https://foodadvisor.strapi.io/)).
 | 
			
		||||
 | 
			
		||||
<div class="video-container">
 | 
			
		||||
  <iframe width="800" height="450" src="https://www.youtube.com/embed/_uSZpxy7B8E" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
 | 
			
		||||
</div>
 | 
			
		||||
You get a good overview of the features developers love found in Strapi.
 | 
			
		||||
 | 
			
		||||
By following this tutorial, you will install Strapi globally on your system, and then you will create your first Strapi project.
 | 
			
		||||
<iframe width="800" height="450" src="https://www.youtube.com/embed/vulcVRQ4X8A" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
 | 
			
		||||
 | 
			
		||||
By following this tutorial, you install and create your first Strapi project.
 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
You need to have **_Node.js and npm_** installed on your system prior to following these steps. If you do not have Node.js and npm installed or are not sure, please visit our [Installation Requirements](/3.0.0-beta.x/getting-started/install-requirements.html).
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
**Table of contents:**
 | 
			
		||||
 | 
			
		||||
1. [Install Strapi globally](#_1-install-strapi-globally)
 | 
			
		||||
2. [Create a new project](#_2-create-a-new-project)
 | 
			
		||||
3. [Create an admin user](#_3-create-an-admin-user)
 | 
			
		||||
4. [Create a Content Type](#_4-create-a-content-type)
 | 
			
		||||
5. [Manage and add data to Content Type](#_5-manage-and-add-data-to-content-type)
 | 
			
		||||
6. [Set roles and permissions](#_6-set-roles-and-permissions)
 | 
			
		||||
7. [Consume the Content Type API](#_7-consume-the-content-type-api)
 | 
			
		||||
 | 
			
		||||
## 1. Install Strapi globally
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm install strapi@beta -g
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Strapi is now installed globally on your computer. Type `strapi -h` in your command line to access available Strapi commands.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
strapi -h
 | 
			
		||||
 | 
			
		||||
## You will get the following available commands
 | 
			
		||||
Usage: strapi [options] [command]
 | 
			
		||||
 | 
			
		||||
Options:
 | 
			
		||||
  -v, --version                                  output the version number
 | 
			
		||||
  -h, --help                                     output usage information
 | 
			
		||||
 | 
			
		||||
Commands:
 | 
			
		||||
  version                                        output your version of Strapi
 | 
			
		||||
  console                                        open the Strapi framework console
 | 
			
		||||
  new [options] [name]                           create a new application
 | 
			
		||||
  start                                          Start your Strapi application
 | 
			
		||||
  develop|dev [options]                          Start your Strapi application in development mode
 | 
			
		||||
  generate:api [options] <id> [attributes...]    generate a basic API
 | 
			
		||||
  generate:controller [options] <id>             generate a controller for an API
 | 
			
		||||
  generate:model [options] <id> [attributes...]  generate a model for an API
 | 
			
		||||
  generate:policy [options] <id>                 generate a policy for an API
 | 
			
		||||
  generate:service [options] <id>                generate a service for an API
 | 
			
		||||
  generate:plugin [options] <id>                 generate a basic plugin
 | 
			
		||||
  build                                          Builds the strapi admin app
 | 
			
		||||
  install [plugins...]                           install a Strapi plugin
 | 
			
		||||
  uninstall [options] [plugins...]               uninstall a Strapi plugin
 | 
			
		||||
  help                                           output the help
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 2. Create a new project
 | 
			
		||||
 | 
			
		||||
Navigate to your parent `Projects/` directory in your command line. Enter the following command to create a Strapi Quick Start project.
 | 
			
		||||
 | 
			
		||||
Path: `~/Desktop/Projects/ $`
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
strapi new my-project --quickstart
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The command will automatically create a Strapi project `my-project` folder within your parent `Projects/` directory.
 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
When you create a new Quick Start(`--quickstart`) project in Strapi, you will download all the node modules, the Strapi files necessary and the Strapi plugin files. **THIS STEP CAN TAKE SEVERAL MINUTES DEPENDING ON YOUR INTERNET CONNECTION SPEED**.
 | 
			
		||||
 | 
			
		||||
Please wait for the process to complete before cancelling or trying to continue.
 | 
			
		||||
You need to have **_Node.js and npm_** installed on your system before following these steps. If you do not have Node.js and npm installed (or are not sure), please visit our [Installation Requirements](/3.0.0-beta.x/getting-started/install-requirements.html).
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
You can replace the `my-project` name with any name you want. E.g. `strapi new newProjectName --quickstart`, this creates a folder `./Projects/newProjectName`.
 | 
			
		||||
**Table of Contents**
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
1. [Install Strapi and create project](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_1-install-strapi-and-create-a-project)
 | 
			
		||||
2. [Create an Administrator and front-end User](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_2-create-an-adminstrator-and-front-end-user)
 | 
			
		||||
3. [Create a new Content Type called, "Restaurant"](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_3-create-a-new-content-type-called-restaurant)
 | 
			
		||||
4. [Create a new Content Type called, "Category"](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_4-create-a-new-content-type-called-category)
 | 
			
		||||
5. [Create a new Group and Repeatable Field called, "Hours of Operations"](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_5-create-a-new-group-and-repeatable-field-called-hours-of-operation)
 | 
			
		||||
6. [Manage and add content to the "Restaurant" Content Type](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_6-manage-and-add-content-to-a-restaurant-content-type)
 | 
			
		||||
7. [Set Roles and Permissions](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_7-set-roles-and-permissions)
 | 
			
		||||
8. [Consume the Content Type API](/3.0.0-beta.x/getting-started/quick-start-tutorial.html#_8-consume-the-content-type-api)
 | 
			
		||||
 | 
			
		||||
You will see something like this:
 | 
			
		||||
## 1. Install Strapi and create a project
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
🚀 Creating your Strapi application.
 | 
			
		||||
- Navigate to your parent `Projects/` directory from your command line.
 | 
			
		||||
 | 
			
		||||
**Note:** In this tutorial, the example assumes a **Projects** folder on your **Desktop**. However, this is not required, and you may put your project where you want.
 | 
			
		||||
 | 
			
		||||
✅ Connected to the database
 | 
			
		||||
🏗  Application generation:
 | 
			
		||||
✔ Copying files ...
 | 
			
		||||
✔ Installing dependencies ...
 | 
			
		||||
✔ Building your admin UI ...
 | 
			
		||||
Path: `~/Desktop/Projects/`
 | 
			
		||||
 | 
			
		||||
👌 Your application was created at /Users/userName/Desktop/Projects/my-project.
 | 
			
		||||
 | 
			
		||||
⚡️ Starting your application...
 | 
			
		||||
 | 
			
		||||
> my-project@0.1.0 develop /Users/UserName/Desktop/Projects/my-project
 | 
			
		||||
> strapi develop
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This will open your default browser to the Welcome page for creating an admin user.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
Using the `--quickstart` flag will install Strapi using a [SQLite](https://www.sqlite.org/index.html) database. You may leave off the flag, but will need to follow the configuration steps for a different database. You will need to have your database choice already up and running prior to creating your project.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## 3. Create an admin user
 | 
			
		||||
 | 
			
		||||
The first user you create is the root user for your project. This user has all privileges and access rights. You will need to complete the following fields:
 | 
			
		||||
 | 
			
		||||
1. **Username**, create a username for login access to your project, eg. `paulbocuse`
 | 
			
		||||
2. **Password**, create a unique password for your project
 | 
			
		||||
3. **Email address**, this will be used for recovery
 | 
			
		||||
4. Check **Receive news**, this is optional but **recommended**
 | 
			
		||||
5. Click the **Ready to Start** button
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
Use **only one** of the following commands to create a new Strapi project:
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
After your admin user is registered, you will see the Strapi admin panel:
 | 
			
		||||
- Use **yarn** to install the Strapi project (**recommended**). [Install yarn with these docs](https://yarnpkg.com/lang/en/docs/install/)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
```bash
 | 
			
		||||
yarn create strapi-app my-project --quickstart
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 4. Create a new Content Type
 | 
			
		||||
**or**
 | 
			
		||||
 | 
			
		||||
**Content Types** are a collection of entered data represented by fields. For example, a **Content Type** called `restaurant` may be intended to display information regarding restaurants. A `restaurant` could have a `name`, a main `image`, a `description`, maybe even a link to the `restaurant menu`.
 | 
			
		||||
- Use **npm/npx** to install the Strapi project
 | 
			
		||||
 | 
			
		||||
Another example, could be a **Content Type** called `menu` which is made up of `menu items`. These could have a `name`, `description of the menu item`, an `image` and perhaps even a `list of ingredients`.
 | 
			
		||||
```bash
 | 
			
		||||
npx create-strapi-app my-project --quickstart
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
The command creates a Strapi project `my-project/` folder within your parent `Projects/` directory.
 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
More **Restaurant** themed **Content Types** can be seen in the Strapi demo site: [Strapi Foodadvisor](https://foodadvisor.strapi.io/).
 | 
			
		||||
When you create a new Quick Start(`--quickstart`) project, Strapi downloads the node modules and the Strapi files needed. Using `--quickstart` automatically completes an **additional** step of **building the administration panel** for Strapi and then **starting** Strapi for you. This opens the browser for you and brings you to the [Welcome](http://localhost:1337/admin/plugins/users-permissions/auth/register) page.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
At this point, your project is empty. You need to create a new **Content Type**.
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
You can replace the `my-project` name with any name you want. E.g., `yarn create strapi-app my-foodadvisor-project --quickstart` creates a folder `./Projects/my-foodadvisor-project`.
 | 
			
		||||
 | 
			
		||||
Go to the **Content Type Builder** plugin, located in the left menu: **PLUGINS** --> **Content Type Builder**.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
You will create two content types. You will create a content type called `Restaurant` and another called `Category`.
 | 
			
		||||
You see something like this. The output below indicates that your Strapi project is being downloaded and installed.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
```bash
 | 
			
		||||
yarn create v1.17.3
 | 
			
		||||
[1/4] 🔍  Resolving packages...
 | 
			
		||||
[2/4] 🚚  Fetching packages...
 | 
			
		||||
[3/4] 🔗  Linking dependencies...
 | 
			
		||||
[4/4] 🔨  Building fresh packages...
 | 
			
		||||
success Installed "create-strapi-app@3.0.0-beta.14" with binaries:
 | 
			
		||||
      - create-strapi-app
 | 
			
		||||
[#####################################################################] 71/71Creating a new Strapi application at /Users/paulbocuse/Desktop/Projects/my-project.
 | 
			
		||||
 | 
			
		||||
Creating a quickstart project.
 | 
			
		||||
Creating files.
 | 
			
		||||
Dependencies installed successfully.
 | 
			
		||||
 | 
			
		||||
Your application was created at /Users/paulbocuse/Desktop/Projects/my-project.
 | 
			
		||||
 | 
			
		||||
Available commands in your project:
 | 
			
		||||
 | 
			
		||||
  yarn develop
 | 
			
		||||
  Start Strapi in watch mode.
 | 
			
		||||
 | 
			
		||||
  yarn start
 | 
			
		||||
  Start Strapi without watch mode.
 | 
			
		||||
 | 
			
		||||
  yarn build
 | 
			
		||||
  Build Strapi admin panel.
 | 
			
		||||
 | 
			
		||||
  yarn strapi
 | 
			
		||||
  Display all available commands.
 | 
			
		||||
 | 
			
		||||
You can start by doing:
 | 
			
		||||
 | 
			
		||||
  cd /Users/paulbocuse/Desktop/Projects/my-project
 | 
			
		||||
  yarn develop
 | 
			
		||||
 | 
			
		||||
Running your Strapi application.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Next, you notice the following that builds your Strapi administration panel and automatically starts up Strapi:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
> my-project@0.1.0 develop /Users/paulbocuse/Desktop/Projects/my-project
 | 
			
		||||
> strapi develop
 | 
			
		||||
 | 
			
		||||
Building your admin UI with development configuration ...
 | 
			
		||||
 | 
			
		||||
✔ Webpack
 | 
			
		||||
  Compiled successfully in 52.21s
 | 
			
		||||
 | 
			
		||||
[2019-07-30T15:21:17.698Z] info File created: /Users/paulbocuse/Desktop/Projects/my-project/extensions/users-permissions/config/jwt.json
 | 
			
		||||
[2019-07-30T15:21:17.701Z] info The server is restarting
 | 
			
		||||
 | 
			
		||||
[2019-07-30T15:21:19.037Z] info Time: Tue Jul 30 2019 17:21:19 GMT+0200 (Central European Summer Time)
 | 
			
		||||
[2019-07-30T15:21:19.037Z] info Launched in: 910 ms
 | 
			
		||||
[2019-07-30T15:21:19.038Z] info Environment: development
 | 
			
		||||
[2019-07-30T15:21:19.038Z] info Process PID: 70615
 | 
			
		||||
[2019-07-30T15:21:19.038Z] info Version: 3.0.0-beta.14 (node v10.16.0)
 | 
			
		||||
[2019-07-30T15:21:19.038Z] info To shut down your server, press <CTRL> + C at any time
 | 
			
		||||
 | 
			
		||||
[2019-07-30T15:21:19.038Z] info ☄️  Admin panel: http://localhost:1337/admin
 | 
			
		||||
[2019-07-30T15:21:19.039Z] info ⚡️ Server: http://localhost:1337
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
Using the `--quickstart` flag installs Strapi using an [SQLite](https://www.sqlite.org/index.html) database. You may, at any time, leave off the **--flag**, but you need to follow a few configuration steps for your database choice. **You need to have your database choice installed and running locally before creating your project.**
 | 
			
		||||
 | 
			
		||||
**Note:** An **SQLite** database is an excellent database to use for prototyping and _developing_ Strapi projects. **SQLite** is a light database that ports effortlessly to the other relational databases (**MySQL**, **PostgreSQL**, and **MariaDB**). It is recommended to **develop** with SQLite and to use another relational database (MySQL, PostgreSQL or MariaDB) in production.
 | 
			
		||||
 | 
			
		||||
**Note:** If you would like to use **MongoDB** in production, you need to [install, run, and use MongoDB to develop your Strapi project (in development)](/3.0.0-beta.x/guides/databases.html#mongodb-installation).
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
You are now ready to create a new **Administrator** and new front-end **User**.
 | 
			
		||||
 | 
			
		||||
## 2. Create an Adminstrator and front-end User
 | 
			
		||||
 | 
			
		||||
The first step is to create an **Administrator** (or "root user") for your project. An **Administrator** has all administrator privileges and access rights. (You can read more about why **Administrators** and front-end **Users** are separate [here](https://blog.strapi.io/why-we-split-the-management-of-the-admin-users-and-end-users/).)
 | 
			
		||||
 | 
			
		||||
You need to complete the following fields:
 | 
			
		||||
 | 
			
		||||
- **Username**, create a username for login access to your project, e.g., `paulbocuse`
 | 
			
		||||
- **Password**, create a unique password for logging into your project
 | 
			
		||||
- **Email address**, this is used for recovery
 | 
			
		||||
- Check **Receive news**, this is optional but **recommended**
 | 
			
		||||
- Click the **Ready to Start** button
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
After your **Administrator** registration is complete, you see the Strapi _Administration Dashboard_:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
**Administrators** and front-end **Users** are separate roles.
 | 
			
		||||
 | 
			
		||||
**A.** An **Administrator** has access and rights to the Administration Dashboard (or backend) of Strapi. **Administrators** can, for example, add content, add plugins, and upload images.
 | 
			
		||||
 | 
			
		||||
**B.** A front-end **User** is someone who interacts with your project through the front-end. A front-end **User** can, for example, be an "Author" of an article, make a purchase, has an account, leaves a review, or leaves a comment.
 | 
			
		||||
 | 
			
		||||
Up until this point, you have created an **Administrator**, and so you next want to create a front-end **User**.
 | 
			
		||||
 | 
			
		||||
**Note:** It is not necessary to always create a front-end **User** for your **Administrators**; in this case, the **Administrator** is also a front-end **User** as an "Author" of content in the application.
 | 
			
		||||
 | 
			
		||||
- Click on `Users` located under **CONTENT TYPES** in the left-hand menu
 | 
			
		||||
- Click the blue **+ Add New User** button in the top right corner
 | 
			
		||||
- Next, complete the `Username`, `Email`, and `Password` fields
 | 
			
		||||
- Select `ON` for the **Confirmed** toggle field
 | 
			
		||||
- To the right, under **Role**, select `Authenticated`
 | 
			
		||||
- Save the new user by clicking the blue **Save** button (top right)
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You are now ready to create your first **Content Type**.
 | 
			
		||||
 | 
			
		||||
## 3. Create a new Content Type called "Restaurant"
 | 
			
		||||
 | 
			
		||||
**Content Types** are a collection of entered data represented by fields. For example, a **Content Type** called `Restaurant` may be intended to display information regarding restaurants. A `restaurant` **Content Type** could have fields that include a `name`, the main `image`, and a `description` - _at a minimum_. However, a `restaurant` could also have a `category` or multiple `categories`, and a `restaurant` could perhaps need to show `hoursofoperation`.
 | 
			
		||||
 | 
			
		||||
A **Content Type** can be considered a sort of _blueprint_ for the data created. In other words, a **Content Type** is the schema of the data structure.
 | 
			
		||||
 | 
			
		||||
The next section guides you through the steps needed for each of these above **Content Type** fields.
 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
Content Type `name` is always **singular**. For example, `restaurant` not `restaurants`. For example, `category` not `categories`.
 | 
			
		||||
Additional **Restaurant** themed **Content Types** and fields can be seen in the [FoodAdvisor demo site](https://foodadvisor.strapi.io/).
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### The Restaurant Content Type
 | 
			
		||||
 | 
			
		||||
The Restaurant Content Type will have a **String** field for the restaurant name, and a **Text** field (with a **WYSIWYG editor**) for the restaurant description.
 | 
			
		||||
- Go to the **Content Type Builder** plugin, located in the left menu: Under **PLUGINS**: --> **Content Type Builder**
 | 
			
		||||
 | 
			
		||||
1. Now add a new **Content Type**.
 | 
			
		||||
You are now able to see the three available **Content Types**. At this point, three Content Types are available `Permission`, `Role`, and `Users`.
 | 
			
		||||
 | 
			
		||||
Click the **"+ Add Content Type"** button.
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now enter a **Name** for your new **Content Type** (call this `restaurant`), and you can write `Restaurant Listings` for the **Description**.
 | 
			
		||||
You need to create a new **Content Type** for `Restaurants`.
 | 
			
		||||
 | 
			
		||||
Then click the **Save** button.
 | 
			
		||||
1. Complete these steps to **Add a Restaurant Content Type**:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
- Click the `+ Add A Content Type` link (under existing **CONTENT TYPES**)
 | 
			
		||||
- Enter a **Name** for your new **Content Type** (call this `restaurant`), and you can write `Restaurant Listings` for the **Description**
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||
2. Now you are ready to add the Content Type fields. Add a **String** field for the **Restaurant** name and a **Text** field for a **Restaurant description**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Next, click on the **String** field.
 | 
			
		||||
 | 
			
		||||
You should be under the **BASE SETTINGS** tab, in the **Name** field, type `Restaurant`. This will be the name of the restaurant.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now, click on the **ADVANCED SETTINGS** tab, check **Required field** and **Unique field**. This field is required for each restaurant and is not optional. Also, each restaurant is to have a unique name, so check the **Unique field** box. And then click the **Continue** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You are now ready to add the second field, **Text** for the restaurant description. Click the **+ Add New Field** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
From here, click the **Text** field.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You should be under the **BASE SETTINGS** tab, in the **Name** field, type `Description`. This will be the description of the restaurant.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now, click on the **ADVANCED SETTINGS** tab, check **Display as a WYSIWYG**. This field will provide a rich text editor.
 | 
			
		||||
 | 
			
		||||
And then click the **Continue** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Lastly, click the **Save** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Wait for Strapi to restart, and then continue to create the `Category` **Content Type**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### The Category Content Type
 | 
			
		||||
 | 
			
		||||
The Category Content Type will have a **String** field named `category`, and a **Relation field** with a **Many to Many** relationship.
 | 
			
		||||
 | 
			
		||||
Now add this second content type. Click the **+ Add Content Type** menu item.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now enter a name for this new **Content Type** (call this one `category`), and write a Description. We will write `Restaurant Categories` for the description. Then click the **Save** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now you are ready to add the content type fields. Next, add a **String** field for the Name and a **Relation Field** for creating a **Many to Many** relation between the Category Content Type and Restaurant Content Type.
 | 
			
		||||
 | 
			
		||||
Next, click on the **String** field.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You should be under the **BASE SETTINGS** tab, in the **Name** field, type `Catgeory`. This will be the name of the category.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now, click on the **ADVANCED SETTINGS** tab, check **Required field** and **Unique field**. This field is required for each category and is not optional. Also, each category to have a unique name, so check the **Unique field** box. And then click the **Continue** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You are now ready to add the second field, the **Relation** field for the **Many to Many relation** setting between Categories and Restaurants. Click the **+ Add New Field** button.
 | 
			
		||||
 | 
			
		||||
From here, click the **Relation** field.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
This will bring you to the **Add New Relation** screen. You will change two elements on this page for the **Relation** field.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
First, click on **Permission (Users-Permissions)** and change it to **Restaurant** (on the right side).
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
The second element to change is to click the **Many to Many** icon (in the middle). It should now read, **"Categories has and belongs to many Restaurants"**. Then click the **Save** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Lastly, you will save this **Content Type**. Click the **Save** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Wait for Strapi to restart.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
### Files structure
 | 
			
		||||
 | 
			
		||||
A new directory has been created in the `./Projects/my-project/` folder of your application which contains all the needed files related to your `restaurant` and `category` Content Types. (You may take a look at the [API structure documentation](../concepts/concepts.md#files-structure) for more information.)
 | 
			
		||||
 | 
			
		||||
## 5. Add content to each Content Type
 | 
			
		||||
 | 
			
		||||
Now you can add content to the new Content Types.
 | 
			
		||||
 | 
			
		||||
Click on **Restaurants** under the **CONTENT TYPES** menu in order to **Add New Restaurant**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Next click on the **+ Add New Restaurant** button (in the top right corner). Go ahead and type `Strapi Restaurant` in the **Name** field, with a description saying, `Strapi restaurant is a cosy restaurant delivering one of the very fastest and nicest dining experiences in the world, combining nods to tradition with fierce modernity, warmth with daring.` into the **Description** field. Then press the **Save** button..
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
When it is saved, you will see your restaurant listed in the entries. From here you can edit it or add a new restaurant.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You have **NOT** yet added a **Category** to the **Restaurant** that was created. You first have to add the actual Category items to the **Categories** content type.
 | 
			
		||||
 | 
			
		||||
You will then assign two Categories, `Convenient` and `Time Saving` to this restaurant.
 | 
			
		||||
 | 
			
		||||
Click on **Categories** under the **CONTENT TYPES** menu on the left.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Click the **+ Add New Category** button to add the first category **Convenient**. Type `Convenient` into the **Name** field. Next, you will see **Restaurants (0)** to the right. Select **Strapi Restaurant**, to add this category to the restaurant.
 | 
			
		||||
 | 
			
		||||
After selecting, **Restaurants (0)** to the right, it will change to **Restaurants (1)**. And then press the **Save** button.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You now see the Category listed. Click the **+ Add New Category** button to add the second category `Time Saving`.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Now let's add **Time Saving**. Type `Time Saving` into the **Category** field. Go ahead and save it **WITHOUT** adding it to the **Strapi Restaurant** field.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You return to the **Category** Content Type page. You see both categories listed. Both have been assigned to the **Restaurant** you created earlier.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
If you want to add Categories directly from the **Restaurants** Content Type, you simply click on the Restaurant and add, edit or change **EXISTING** categories. Otherwise, you can create and add new **Categories** from the **Category Content Type** as you did above.
 | 
			
		||||
 | 
			
		||||
Go ahead and add `Time Saving`, the `Strapi Restaurant`.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
The Content Type **Name** is always **singular**. For example, `restaurant` not `restaurants`.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## 6. Set roles and permissions
 | 
			
		||||
2. You are now at the **Field Selection** panel:
 | 
			
		||||
 | 
			
		||||
By default, Strapi publishes all **Content Types** with restricted permissions. Which means you have to explicitly give permissions to each **Content Type** you create. You are going to give **Public** API (or URL) access to the **Restaurant** Content Type.
 | 
			
		||||
You may add your first field, a **String** field for the **Restaurant** name.
 | 
			
		||||
 | 
			
		||||
Locate and click on the **Roles & Permissions** menu item under **PLUGINS** on the left menu. (The **Roles & Permissions** plugin can accomplish many tasks related to permissions. For now, focus on the **Public** role.)
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Next, click on the **Public** Role.
 | 
			
		||||
- Click on the `String` field
 | 
			
		||||
- In the **Name** field, type `name`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
From here, scroll down under **Permissions** and find **Restaurant** and **Category**. Click the checkbox next to **find** and **findone** for each of them. Scroll back to the top, and click the **Save** button.
 | 
			
		||||
- Click on the `ADVANCED SETTINGS` tab
 | 
			
		||||
- Check the `Required field` checkbox
 | 
			
		||||
- Check the `Unique field` checkbox
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
## 7. Consume the Content Type API
 | 
			
		||||
- Click the `+ Add Another Field` button
 | 
			
		||||
 | 
			
		||||
The project is accessible by following the `http://localhost:1337/` link. You will see the **'Welcome'** screen.
 | 
			
		||||
You are now ready to add the second field, a **Rich Text** field for the **Restaurant** description.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
What you want is the **Restaurant Content Type**. The route is `/restaurants`. In your browser, type `http://localhost:1337/restaurants`.
 | 
			
		||||
- Click the `Rich Text` field
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
- In the **Name** field, type `description`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `+ Add Another Field` button
 | 
			
		||||
 | 
			
		||||
You are now ready to add the third field, a **Media** field for the **Restaurant** thumbnail image.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Media` field
 | 
			
		||||
 | 
			
		||||
- In the **Name** field, type `image`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the **ADVANCED SETTINGS** tab
 | 
			
		||||
- Check the `Required field` checkbox
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||
Your new Content Type called **Restaurant** is ready to be **Saved**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button
 | 
			
		||||
 | 
			
		||||
- Wait for Strapi to restart
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
After Strapi has restarted, you are ready to continue to create the `Category` **Content Type**.
 | 
			
		||||
 | 
			
		||||
## 4. Create a new Content Type called "Category"
 | 
			
		||||
 | 
			
		||||
### The Category Content Type
 | 
			
		||||
 | 
			
		||||
The `Category` **Content Type** will have a **String** field named `category`, and a **Relation field** with a **Many to Many** relationship.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
1. Complete these steps to **add a Category Content Type**:
 | 
			
		||||
 | 
			
		||||
- Click the `+ Add A Content Type` link
 | 
			
		||||
- Enter a **Name** for your new **Content Type** (call this `category`)
 | 
			
		||||
- Enter `Restaurant Categories` for the **Description**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||
2. Now, you are ready to add fields to your **Category**:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `String` field
 | 
			
		||||
- In the **Name** field, type `name`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `ADVANCED SETTINGS` tab
 | 
			
		||||
- Check the `Required field` checkbox
 | 
			
		||||
- Check the `Unique field` checkbox
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `+ Add Another Field` button
 | 
			
		||||
 | 
			
		||||
You are now ready to add the second field, a **Relation** field for creating a **Many to Many** relationship between the **Category** and **Restaurant** Content Types.
 | 
			
		||||
 | 
			
		||||
- Click on the `Relation` field
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
This brings you to the **Add New Relation** screen.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on _right dropdown_ with `Permission (Users-Permissions)` and change it to `Restaurant`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Many to Many` icon (from the middle icon choices). It should now read, **"Categories has and belongs to many Restaurants"**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button
 | 
			
		||||
 | 
			
		||||
- Wait for Strapi to restart
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
After Strapi has restarted, you are ready to create a `Group and Repeatable Field` called **"Hours of Operations."**
 | 
			
		||||
 | 
			
		||||
## 5. Create a new Group, and Repeatable Field called, "Hours of Operation"
 | 
			
		||||
 | 
			
		||||
### The Hours of Operation Group
 | 
			
		||||
 | 
			
		||||
The `Restaurant` Content Type has a **Group** field named `Hours_of_operation`. This Group is **Repeatable** and for displaying the **Opening hours** and **Closing hours** of a **Restaurant**.
 | 
			
		||||
 | 
			
		||||
1. Complete these steps to **add a new Group**:
 | 
			
		||||
 | 
			
		||||
- Click the `+ Add A Group` link to add a new **Group**
 | 
			
		||||
- Enter a **Name** for your new **Group** (call this `hours_of_operation`), and you can write `Hours of Operation` for the **Description**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||
2. Now, you are ready to add fields to your **Group**:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `String` field
 | 
			
		||||
- In the **Name** field, type `day_interval`. This is to enter the **Day (or Days)** with **Hours of Operation**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `ADVANCED SETTINGS` tab
 | 
			
		||||
- Check the `Required field` checkbox
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `+ Add Another Field`
 | 
			
		||||
 | 
			
		||||
You are now ready to add a second field, another **String** field for the **Opening Hours**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `String` field
 | 
			
		||||
- In the **Name** field, type `opening_hours`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `+ Add Another Field` button
 | 
			
		||||
 | 
			
		||||
You are now ready to add a third field, another **String** field for the **Closing Hours**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `String` field
 | 
			
		||||
- In the **Name** field, type `closing_hours`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button
 | 
			
		||||
- Wait for Strapi to restart
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
After Strapi has restarted, you are ready to assign this **Hours_of_operation** group to the **Restaurant** Content Type.
 | 
			
		||||
 | 
			
		||||
**Note:** It would be possible to assign the **Hours_of_operation** group to another **Content Type**, let's say, a **Cafe** Content Type. You have the option to reuse this group across your application.
 | 
			
		||||
 | 
			
		||||
3. Next, you need to assign the **Hours_of_operation** Group to the **Restaurant** Content Type.
 | 
			
		||||
 | 
			
		||||
To access the **Hours_of_operation** Group from within the **Restaurant** Content Type, you need to **edit** the **Restaurant** Content Type in the **Content Type Builder**.
 | 
			
		||||
 | 
			
		||||
- If needed, navigate back to the **Content Type Builder**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `Restaurant` Content Type, under **CONTENT TYPES**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click one of the `+ Add Another Field` buttons, to add the **Group**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `Group` field
 | 
			
		||||
 | 
			
		||||
- Ensure `hours_of_operation` is displayed in the **Select a group** dropdown
 | 
			
		||||
- Provide a **name** for this group in the **Restaurant** Content Type. E.g., `restaurant_hours`
 | 
			
		||||
- Check the `Repeatable field` box
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `ADVANCED SETTINGS` tab
 | 
			
		||||
- Check the `Required field` checkbox
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Done` button
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button
 | 
			
		||||
 | 
			
		||||
- Wait for Strapi to restart
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
After Strapi has restarted, you are ready to continue to the next section where you customize the user-interface of your **Restaurant** Content Type.
 | 
			
		||||
 | 
			
		||||
4. Next, you edit the **View Settings** for the new **Hoursofoperation Group** from within the **Content Manager**.
 | 
			
		||||
 | 
			
		||||
You can _drag and drop_ fields into a different layout, as well as, _rename the labels_ as two examples of how you can customize the user interface for your **Content Types**.
 | 
			
		||||
 | 
			
		||||
- Navigate to and click on the `Content Manager`, under **PLUGINS** in the left-hand menu
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `Groups(1)` tab
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on `Hours_of_operation` to modify the **View Settings**
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Rearrange the fields and make them more user friendly. Grab the `opening_hours` and slide it next to `closing_hours`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Next, you will change the **field labels** to make them easier to understand:
 | 
			
		||||
 | 
			
		||||
- Click on the `day_interval` field
 | 
			
		||||
- Edit the **Label** to read, `Day (or Days)`
 | 
			
		||||
- Add a **Description**, `You can type in one day or a series of days to complete this field. E.g. "Tuesday" or "Tues - Wed"`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on the `opening_hours` field
 | 
			
		||||
- Edit the **Label** to read, `Opening Hours`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click on `closing_hours` field
 | 
			
		||||
- Edit the **Label** to read, `Closing Hours`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button, and then the `Confirm` button to save your settings
 | 
			
		||||
 | 
			
		||||
Your settings have now saved.
 | 
			
		||||
 | 
			
		||||
Whenever anyone enters in information for a **Restaurant**, the entry form is clear. With Strapi you can modify these and more settings to provide the best experience possible.
 | 
			
		||||
 | 
			
		||||
You are ready to start inputting actual content.
 | 
			
		||||
 | 
			
		||||
## 6. Manage and add content to a "Restaurant" Content Type
 | 
			
		||||
 | 
			
		||||
You are now ready to add some **Restaurants** and **Categories**.
 | 
			
		||||
 | 
			
		||||
1. You are now going to enter a new **Restaurant**
 | 
			
		||||
 | 
			
		||||
- Navigate to and click on the `Restaurants`, under **CONTENT TYPES** in the left-hand menu
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Next, click on the **+ Add new Restaurant** button (in the top right corner)
 | 
			
		||||
- Enter in the following information for your first **Restaurant** called **Biscotte Restaurant**
 | 
			
		||||
  - In the **Name** field, enter `Biscotte Restaurant`
 | 
			
		||||
  - In the **Description** field, enter `Welcome to Biscotte restaurant! Restaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers.`
 | 
			
		||||
  - Upload an **Image** to represent the **Restaurant**
 | 
			
		||||
 | 
			
		||||
**Note:** At this point, you would generally select the **Categories** for this **Restaurant**. You have not entered any **Categories**, so you do this step after entering this first **Restaurant**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Next scroll down to **RestaurantHours|(0)** and click the `+ ADD NEW ENTRY` button
 | 
			
		||||
  - In the **Create an Entry** section, enter the following details:
 | 
			
		||||
    - In the **Days (or Days)** field, enter `Sun - Mon`
 | 
			
		||||
    - In the **Opening Hours** field, enter `Closed`
 | 
			
		||||
    - **Skip** the **Closing Hours** field, as this **Restaurant** is closed all day
 | 
			
		||||
  - Click the `+ ADD NEW ENTRY` button to create another new entry
 | 
			
		||||
    - In the **Days (or Days)** field, enter `Tues - Fri`
 | 
			
		||||
    - In the **Opening Hours** field, enter `12:00`
 | 
			
		||||
    - In the **Closing Hours** field, enter `22:30`
 | 
			
		||||
  - Click the `+ ADD NEW ENTRY` button to create the last entry
 | 
			
		||||
    - In the **Days (or Days)** field, enter `Sat`
 | 
			
		||||
    - In the **Opening Hours** field, enter `11:30`
 | 
			
		||||
    - In the **Closing Hours** field, enter `16:00`
 | 
			
		||||
 | 
			
		||||
You have now entered in all the information necessary, for your first **Restaurant**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- **Scroll up** and click the `Save` button
 | 
			
		||||
 | 
			
		||||
Next, you need to enter in some **Categories** that can relate to the above and other **Restaurants**.
 | 
			
		||||
 | 
			
		||||
- Navigate to and click on the `Categories`, under **CONTENT TYPES** in the left-hand menu
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You are going to enter two **Categories**, but you could add as many **Categories** as you need to. Later, you can add additional **Categories** and assign them to existing and new **Restaurants**.
 | 
			
		||||
 | 
			
		||||
- Click on the `+ Add New Category` button
 | 
			
		||||
  - In the **Name** field, enter `French food`
 | 
			
		||||
  - In the **Restaurants(0)** dropdown, select `Biscotte Restaurant`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button
 | 
			
		||||
 | 
			
		||||
You now enter your second **Category**.
 | 
			
		||||
 | 
			
		||||
- Click on the `+ Add New Category` button
 | 
			
		||||
  - In the **Name** field, enter `Brunch`
 | 
			
		||||
  - In the **Restaurants(0)** dropdown, select `Biscotte Restaurant`
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Click the `Save` button
 | 
			
		||||
 | 
			
		||||
You have now entered your first **Restaurant** Content Type. You have also assigned two **Categories** to this **Restaurant**. Your next step is to set the **Roles and Permissions**.
 | 
			
		||||
 | 
			
		||||
## 7. Set Roles and Permissions
 | 
			
		||||
 | 
			
		||||
By default, Strapi publishes all **Content Types** with restricted permissions. Which means you have to explicitly give permissions to each **Content Type** you create. You are going to give **Public** API (or URL) access to both the **Restaurant** Content Type and **Category** Content Type.
 | 
			
		||||
 | 
			
		||||
- Click on the `Roles & Permissions` menu item, under **PLUGINS** in the left-hand-menu
 | 
			
		||||
- Locate and click on the **Roles & Permissions** menu item under **PLUGINS** on the left menu
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Next, click on the **Public** Role
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Next, scroll down under **Permissions** and locate the **Restaurant** and **Category** Content Types
 | 
			
		||||
- Click the checkbox for **find** and **findone** in the **Restaurant** Content Type
 | 
			
		||||
- Click the checkbox for **find** and **findone** in the **Category** Content Type
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- Scroll back to the top, and click the **Save** button
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
You have now opened the API and are ready to consume your content.
 | 
			
		||||
 | 
			
		||||
## 8. Consume the Content Type API
 | 
			
		||||
 | 
			
		||||
Each of your **Content Types** are accessible by following their automatically generated routes.
 | 
			
		||||
 | 
			
		||||
Both your **Restaurant** and **Category** Content Types can now be accessed:
 | 
			
		||||
 | 
			
		||||
- In your browser, follow `http://localhost:1337/restaurants` to return the data for the allowed **Find** value of your **Restaurant** Content Type
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
- In your browser, follow `http://localhost:1337/categories` to return the data for the allowed **Find** value of your **Category** Content Type
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
If you have incorrectly or not set permissions to your content type, you will get a **"403"** permission error. See the below example.
 | 
			
		||||
If you have incorrectly or not set permissions to your content type, you get a **"403"** permission error. See the below example.
 | 
			
		||||
 | 
			
		||||
Forbidden Access Looks like this:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: tip NOTE
 | 
			
		||||
 | 
			
		||||
If you would like to see the route of any specific **Content Type**, you need to navigate to the **Content Type** under the **Roles and Permissions** plugin and click the ⚙️ next to the value. On the right, you see the route:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: tip CONGRATULATIONS
 | 
			
		||||
👏 Congratulations, you have now completed the Strapi Quick Start Tutorial. Where to go next?
 | 
			
		||||
👏 Congratulations, you have now completed the **Strapi Getting Started Tutorial**. Where to go next?
 | 
			
		||||
 | 
			
		||||
- Learn how to use Strapi with React ([Gatsby](https://blog.strapi.io/building-a-static-website-using-gatsby-and-strapi) or [Next.js](https://blog.strapi.io/strapi-next-setup/)) or Vue.js ([Nuxt.js](https://blog.strapi.io/cooking-a-deliveroo-clone-with-nuxt-vue-js-graphql-strapi-and-stripe-setup-part-1-7/)).
 | 
			
		||||
- Read the [concepts](../concepts/concepts.html) and [articles](../articles/) to deep dive into Strapi.
 | 
			
		||||
- Get help on [StackOverflow](https://stackoverflow.com/questions/tagged/strapi).
 | 
			
		||||
- Read the [source code](https://github.com/strapi/strapi), [contribute](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) or [give a star](https://github.com/strapi/strapi) on GitHub.
 | 
			
		||||
- Follow us on [Twitter](https://twitter.com/strapijs) to get the latest news.
 | 
			
		||||
- [Join the vibrant and active Strapi community](https://slack.strapi.io) on Slack.
 | 
			
		||||
- Read the [concepts](../concepts/concepts.html) to deep dive into Strapi
 | 
			
		||||
- Get help on [StackOverflow](https://stackoverflow.com/questions/tagged/strapi)
 | 
			
		||||
- Read the [source code](https://github.com/strapi/strapi), [contribute](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) or [give a star](https://github.com/strapi/strapi) on GitHub
 | 
			
		||||
- Follow us on [Twitter](https://twitter.com/strapijs) to get the latest news
 | 
			
		||||
- [Join the vibrant and active Strapi community](https://slack.strapi.io) on Slack
 | 
			
		||||
  :::
 | 
			
		||||
 | 
			
		||||
@ -1,118 +1,117 @@
 | 
			
		||||
# Quick Start Guide
 | 
			
		||||
 | 
			
		||||
Get ready to get Strapi up and running in **less than 5 minutes** 🚀
 | 
			
		||||
Get ready to get Strapi up and running in **less than 5 minutes** 🚀.
 | 
			
		||||
 | 
			
		||||
<div class="video-container">
 | 
			
		||||
<iframe width="853" height="480"  src="https://www.youtube.com/embed/_qlLobVjd9k" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
 | 
			
		||||
<iframe width="853" height="480" src="https://www.youtube.com/embed/4m1wKzzfs-M" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
_For a step-by-step guide, please take a look at the [detailed tutorial](quick-start-tutorial.html)._
 | 
			
		||||
 | 
			
		||||
## 1. Install Strapi globally
 | 
			
		||||
(Before continuing, please make sure [Node.js and npm are properly installed](install-requirements.html) on your machine. You can [install the Yarn v1.2.0+ package here](https://yarnpkg.com/en/).)
 | 
			
		||||
 | 
			
		||||
Please make sure [Node.js and npm are properly installed](install-requirements.html) on your machine.
 | 
			
		||||
## 1. Install Strapi and Create a new project
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm install strapi@beta -g
 | 
			
		||||
yarn create strapi-app my-project --quickstart
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 2. Create a new project
 | 
			
		||||
**or**
 | 
			
		||||
 | 
			
		||||
- Use **npm/npx** to install the Strapi project.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
strapi new my-project --quickstart
 | 
			
		||||
npx create-strapi-app my-project --quickstart
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 3. Create an Administrator user
 | 
			
		||||
## 2. Create an Administrator user
 | 
			
		||||
 | 
			
		||||
Navigate to [**http://localhost:1337/admin**](http://localhost:1337/admin).
 | 
			
		||||
 | 
			
		||||
- Complete the form to create the first Administrator user.
 | 
			
		||||
- Complete the form to create the first **Administrator** user.
 | 
			
		||||
- Click **Ready to start**.
 | 
			
		||||
 | 
			
		||||
## 4. Create a new Content Type
 | 
			
		||||
## 3. Create a Restaurant Content Type
 | 
			
		||||
 | 
			
		||||
Navigate to [**PLUGINS** - **Content Type Builder**](http://localhost:1337/admin/plugins/content-type-builder), in the left-hand menu.
 | 
			
		||||
 | 
			
		||||
- Click the **"+ Add Content Type"** button.
 | 
			
		||||
- Enter `restaurant`.
 | 
			
		||||
- Click **"+ Add New Field"**
 | 
			
		||||
  - Click the **String** field.
 | 
			
		||||
  - Type `restaurant` under the **BASE SETTINGS** tab, in the **Name** field.
 | 
			
		||||
  - Check `Required field` and `Unique field` under the **ADVANCED SETTINGS** tab.
 | 
			
		||||
  - Click **Continue**.
 | 
			
		||||
- Click **"+ Add New Field"**
 | 
			
		||||
  - Click the **Text** field.
 | 
			
		||||
  - Type `description` under the **BASE SETTINGS** tab, in the **Name** field.
 | 
			
		||||
  - Click the **ADVANCED SETTINGS** tab, check **Display as a WYSIWYG**.
 | 
			
		||||
  - Click **Continue**.
 | 
			
		||||
- Click the **Save** button and wait for Strapi to restart.
 | 
			
		||||
- Click the **"+ Add Content Type"** link
 | 
			
		||||
- Enter `restaurant`, and click `Done`
 | 
			
		||||
- A window opens with fields options:
 | 
			
		||||
  - Click the **String** field
 | 
			
		||||
  - Type `name` in the **Name** field
 | 
			
		||||
  - Click over to the **ADVANCED SETTINGS** tab, and check the `Required field` and the `Unique field`
 | 
			
		||||
  - Click the **"+ Add New Field"** button
 | 
			
		||||
  - Click the **Rich Text** field
 | 
			
		||||
  - Type `description` under the **BASE SETTINGS** tab, in the **Name** field
 | 
			
		||||
  - Click `Done`
 | 
			
		||||
- Click the **Save** button and wait for Strapi to restart
 | 
			
		||||
 | 
			
		||||
## 5. Create another new Content type
 | 
			
		||||
## 4. Create a Category Content type
 | 
			
		||||
 | 
			
		||||
Navigate back to [**PLUGINS** - **Content Type Builder**](http://localhost:1337/admin/plugins/content-type-builder), in the left-hand menu.
 | 
			
		||||
 | 
			
		||||
- Click the **"+ Add Content Type"** button.
 | 
			
		||||
- Enter `category`.
 | 
			
		||||
- Click the **"+ Add Content Type"** link
 | 
			
		||||
- Enter `category`, and click `Done`
 | 
			
		||||
- A window opens with fields options:
 | 
			
		||||
  - Click the **String** field.
 | 
			
		||||
  - Type `category` under the **BASE SETTINGS** tab, in the **Name** field.
 | 
			
		||||
  - Check `Required field` and `Unique field` under the **ADVANCED SETTINGS** tab.
 | 
			
		||||
  - Click **Continue**.
 | 
			
		||||
- In the field options window:
 | 
			
		||||
  - Click the **Relation Field** field.
 | 
			
		||||
  - On the right side, click the **Permissions** dropdown and select, `Restaurant`.
 | 
			
		||||
  - In the center, select the icon the represents `many-to-many`. The text should read, `Categories has and belongs to Many Restaurants`.
 | 
			
		||||
  - Click **Save**.
 | 
			
		||||
- Click the **Save** button and wait for Strapi to restart.
 | 
			
		||||
  - Click the **String** field
 | 
			
		||||
  - Type `name` under the **BASE SETTINGS** tab, in the **Name** field
 | 
			
		||||
  - Click over to the **ADVANCED SETTINGS** tab, and check the `Required field` and the `Unique field`
 | 
			
		||||
  - Click the **"+ Add New Field"** button
 | 
			
		||||
  - Click the **Relation** field
 | 
			
		||||
  - On the right side, click the **Permissions** dropdown and select, `Restaurant`
 | 
			
		||||
  - In the center, select the icon that represents `many-to-many`. The text should read, `Categories has and belongs to many Restaurants`
 | 
			
		||||
  - Click `Done`
 | 
			
		||||
- Click the **Save** button and wait for Strapi to restart
 | 
			
		||||
 | 
			
		||||
## 6. Add content to "Restaurant" Content Type
 | 
			
		||||
## 5. Add content to "Restaurant" Content Type
 | 
			
		||||
 | 
			
		||||
Navigate to [**CONTENT TYPES** - **Restaurants**](http://localhost:1337/admin/plugins/content-manager/restaurant?source=content-manager).
 | 
			
		||||
Navigate to [**CONTENT TYPES** - **Restaurants**](http://localhost:1337/admin/plugins/content-manager/restaurant?source=content-manager), in the left-hand menu.
 | 
			
		||||
 | 
			
		||||
- Click on **+ Add New Restaurant** button. Type `Strapi Restaurant` in the **Restaurant** field. Type `Strapi restaurant is a cosy restaurant delivering one of the very fastest and nicest dining experiences in the world, combining nods to tradition with fierce modernity, warmth with daring.` into the **Description** field.
 | 
			
		||||
- Click on **+ Add New Restaurant** button. Type `Biscotte Restaurant` in the **Restaurant** field. Type `Welcome to Biscotte restaurant! Restaurant Biscotte offers a cuisine based on fresh, quality products, often local, organic when possible, and always produced by passionate producers.` into the **Description** field.
 | 
			
		||||
- Click **Save**.
 | 
			
		||||
 | 
			
		||||
You will see your restaurant listed in the entries.
 | 
			
		||||
 | 
			
		||||
## 7. Add categories to the "Category" Content Type
 | 
			
		||||
## 6. Add categories to the "Category" Content Type
 | 
			
		||||
 | 
			
		||||
Navigate to [**CONTENT TYPES** - **Categories**](http://localhost:1337/admin/plugins/content-manager/category?source=content-manager).
 | 
			
		||||
 | 
			
		||||
- Click on **+ Add New Category** button. Type `Convenient` in the **Category** field. Select `Strapi Restaurant`, on the right, from **Restaurant (0)**
 | 
			
		||||
- Click on **+ Add New Category** button. Type `French Food` in the **Category** field. Select `Biscotte Restaurant`, on the right, from **Restaurant (0)**
 | 
			
		||||
- Click **Save**.
 | 
			
		||||
 | 
			
		||||
You will see the **Convenient** category listed in the entries.
 | 
			
		||||
 | 
			
		||||
- Click on **+ Add New Category** button. Type `Time Saving` in the **Category** field. **DO NOT ADD IT HERE** to `Strapi Restaurant`.
 | 
			
		||||
- Click on **+ Add New Category** button. Type `Brunch` in the **Category** field. **DO NOT ADD IT HERE** to `Biscotte Restaurant`.
 | 
			
		||||
- Click **Save**.
 | 
			
		||||
 | 
			
		||||
You will see the **Time Saving** category listed in the entries.
 | 
			
		||||
You will see the **Brunch** category listed in the entries.
 | 
			
		||||
 | 
			
		||||
Navigate back to [**CONTENT TYPES** - **Restaurants**](http://localhost:1337/admin/plugins/content-manager/restaurant?source=content-manager).
 | 
			
		||||
 | 
			
		||||
- Click on `Strapi Restaurant`
 | 
			
		||||
- On the right, under **Categories(1)**, `select` the `Add an item...`, and add **Time Saving** as a category for this restaurant.
 | 
			
		||||
- Click on `Biscotte Restaurant`
 | 
			
		||||
- On the right, under **Categories(1)**, `select` the `Add an item...`, and add **Brunch** as a category for this restaurant, and click the **Save** button.
 | 
			
		||||
 | 
			
		||||
You have now seen **two different ways** to use the **relation** field type to add and connect relations between content types.
 | 
			
		||||
You have now seen **two different ways** to use the **relation** field type to add and connect relations between Content Types.
 | 
			
		||||
 | 
			
		||||
## 8. Set roles and permissions
 | 
			
		||||
## 7. Set Roles and Permissions
 | 
			
		||||
 | 
			
		||||
Navigate to [**PLUGINS** - **Roles & Permissions**](http://localhost:1337/admin/plugins/users-permissions/roles).
 | 
			
		||||
 | 
			
		||||
- Click the **Public** Role.
 | 
			
		||||
- Scroll down under **Permissions**, find **Restaurant**. Click the checkbox next to **find** and **findone**.
 | 
			
		||||
- Repeat and scroll down under **Permissions**, find **Category**. Click the checkbox next to **find** and **findone**.
 | 
			
		||||
- Repeat and find **Category**. Click the checkbox next to **find** and **findone**.
 | 
			
		||||
- Click **Save**.
 | 
			
		||||
 | 
			
		||||
## 9. Consume the Content Type's API
 | 
			
		||||
## 8. Consume the Content Type's API
 | 
			
		||||
 | 
			
		||||
Here we are! The list of **restaurants** is accessible at [`http://localhost:1337/restaurants`](http://localhost:1337/restaurants).
 | 
			
		||||
 | 
			
		||||
::: tip CONGRATULATIONS
 | 
			
		||||
👏 Congratulations, you have now completed the Strapi Quick Start. Where to go next?
 | 
			
		||||
👏 Congratulations, you have now completed the **Strapi Quick Start**. Where to go next?
 | 
			
		||||
 | 
			
		||||
- Learn how to use Strapi with React ([Gatsby](https://blog.strapi.io/building-a-static-website-using-gatsby-and-strapi) or [Next.js](https://blog.strapi.io/strapi-next-setup/)) or Vue.js ([Nuxt.js](https://blog.strapi.io/cooking-a-deliveroo-clone-with-nuxt-vue-js-graphql-strapi-and-stripe-setup-part-1-7/)).
 | 
			
		||||
- Read the [concepts](../concepts/concepts.html) and [articles](../articles/) to deep dive into Strapi.
 | 
			
		||||
- Read the [concepts](../concepts/concepts.html) and do the [Tutorial](/3.0.0-beta.x/getting-started/quick-start-tutorial.html) to deep dive into Strapi.
 | 
			
		||||
- Get help on [StackOverflow](https://stackoverflow.com/questions/tagged/strapi).
 | 
			
		||||
- Read the [source code](https://github.com/strapi/strapi), [contribute](https://github.com/strapi/strapi/blob/master/CONTRIBUTING.md) or [give a star](https://github.com/strapi/strapi) on GitHub.
 | 
			
		||||
- Follow us on [Twitter](https://twitter.com/strapijs) to get the latest news.
 | 
			
		||||
 | 
			
		||||
@ -38,8 +38,8 @@ If you are passing a number of configuration item values via environment variabl
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
{
 | 
			
		||||
  "host": "${process.env.APP_HOST || '127.0.0.1'}"
 | 
			
		||||
  "port": "${process.env.NODE_PORT || 1337}",
 | 
			
		||||
  "host": "${process.env.APP_HOST || '127.0.0.1'}",
 | 
			
		||||
  "port": "${process.env.NODE_PORT || 1337}"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -492,7 +492,7 @@ module.exports = {
 | 
			
		||||
    Query: {
 | 
			
		||||
      post: {
 | 
			
		||||
        description: 'Return a single post',
 | 
			
		||||
        policy: ['plugins.users-permissions.isAuthenticated', 'isOwner'], // Apply the 'isAuthenticated' policy of the `Users & Permissions` plugin, then the 'isOwner' policy before executing the resolver.
 | 
			
		||||
        policies: ['plugins.users-permissions.isAuthenticated', 'isOwner'], // Apply the 'isAuthenticated' policy of the `Users & Permissions` plugin, then the 'isOwner' policy before executing the resolver.
 | 
			
		||||
      },
 | 
			
		||||
      posts: {
 | 
			
		||||
        description: 'Return a list of posts', // Add a description to the query.
 | 
			
		||||
@ -504,7 +504,7 @@ module.exports = {
 | 
			
		||||
      },
 | 
			
		||||
      postsByTags: {
 | 
			
		||||
        description: 'Return the posts published by the author',
 | 
			
		||||
        resolverOf: 'Post.findByTags', // Will apply the same policy on the custom resolver than the controller's action `findByTags`.
 | 
			
		||||
        resolverOf: 'Post.findByTags', // Will apply the same policy on the custom resolver as the controller's action `findByTags`.
 | 
			
		||||
        resolver: (obj, options, ctx) => {
 | 
			
		||||
          // ctx is the context of the Koa request.
 | 
			
		||||
          await strapi.controllers.posts.findByTags(ctx);
 | 
			
		||||
@ -516,7 +516,7 @@ module.exports = {
 | 
			
		||||
    Mutation: {
 | 
			
		||||
      attachPostToAuthor: {
 | 
			
		||||
        description: 'Attach a post to an author',
 | 
			
		||||
        policy: ['plugins.users-permissions.isAuthenticated', 'isOwner'],
 | 
			
		||||
        policies: ['plugins.users-permissions.isAuthenticated', 'isOwner'],
 | 
			
		||||
        resolver: 'Post.attachToAuthor'
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
@ -677,7 +677,7 @@ module.exports = {
 | 
			
		||||
    Query: {
 | 
			
		||||
      posts: {
 | 
			
		||||
        description: 'Return a list of posts',
 | 
			
		||||
        policy: [
 | 
			
		||||
        policies: [
 | 
			
		||||
          'plugins.users-permissions.isAuthenticated',
 | 
			
		||||
          'isOwner',
 | 
			
		||||
          'global.logging',
 | 
			
		||||
@ -687,7 +687,10 @@ module.exports = {
 | 
			
		||||
    Mutation: {
 | 
			
		||||
      createPost: {
 | 
			
		||||
        description: 'Create a new post',
 | 
			
		||||
        policy: ['plugins.users-permissions.isAuthenticated', 'global.logging'],
 | 
			
		||||
        policies: [
 | 
			
		||||
          'plugins.users-permissions.isAuthenticated',
 | 
			
		||||
          'global.logging',
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
@ -782,7 +785,7 @@ module.exports = {
 | 
			
		||||
    Query: {
 | 
			
		||||
      posts: {
 | 
			
		||||
        description: 'Return a list of posts by author',
 | 
			
		||||
        resolverOf: 'Post.find', // Will apply the same policy on the custom resolver than the controller's action `find` located in `Post.js`.
 | 
			
		||||
        resolverOf: 'Post.find', // Will apply the same policy on the custom resolver as the controller's action `find` located in `Post.js`.
 | 
			
		||||
        resolver: (obj, options, context) => {
 | 
			
		||||
          // You can return a raw JSON object or a promise.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ The options key on the model-json states.
 | 
			
		||||
 | 
			
		||||
- `idAttribute`: This tells the model which attribute to expect as the unique identifier for each database row (typically an auto-incrementing primary key named 'id'). _Only valid for strapi-hook-bookshelf_
 | 
			
		||||
- `idAttributeType`: Data type of `idAttribute`, accepted list of value below. _Only valid for strapi-hook-bookshelf_
 | 
			
		||||
- `timestamps`: This tells the model which attributes to use for timestamps. Accepts either `boolean` or `Array` of strings where first element is create data and second element is update date. Default value when set to `true` for Bookshelf is `["created_at", "updated_at"]` and for MongoDB is `["createdAt", "updatedAt"]`.
 | 
			
		||||
- `timestamps`: This tells the model which attributes to use for timestamps. Accepts either `boolean` or `Array` of strings where first element is create date and second element is update date. Default value when set to `true` for Bookshelf is `["created_at", "updated_at"]` and for MongoDB is `["createdAt", "updatedAt"]`.
 | 
			
		||||
- `uuid` : Boolean to enable UUID support on MySQL, you will need to set the `idAttributeType` to `uuid` as well and install the `bookshelf-uuid` package. To load the package you can see [this example](../configurations/configurations.md#bookshelf-mongoose).
 | 
			
		||||
 | 
			
		||||
## Define the attributes
 | 
			
		||||
 | 
			
		||||
@ -32,7 +32,7 @@ Then run either `yarn install` or `npm install`.
 | 
			
		||||
 | 
			
		||||
## Building your administration panel
 | 
			
		||||
 | 
			
		||||
This new release introduces changes to the administration panel than require rebuild it.
 | 
			
		||||
This new release introduces changes to the administration panel that require a rebuild.
 | 
			
		||||
 | 
			
		||||
Start by deleting your current build:
 | 
			
		||||
 | 
			
		||||
@ -52,9 +52,9 @@ npm run build
 | 
			
		||||
 | 
			
		||||
### Wysiwyg
 | 
			
		||||
 | 
			
		||||
Wysiwyg was previously an option of the `text` type that was stored in the database. When deploying to production for the first time you had to select again the option in the interface.
 | 
			
		||||
Wysiwyg was previously an option of the `text` type that was stored in the database. When deploying to production for the first time you had to re-select the option in the interface.
 | 
			
		||||
 | 
			
		||||
To improve make sure a field stays the same when deploying we introduced the `richtext` type. This type is equivalent to the previous `text` type with `wysiwyg` option enabled.
 | 
			
		||||
To make sure a Wysiwyg field stays the same when deploying, we introduced the `richtext` type. This type is equivalent to the previous `text` type with `wysiwyg` option enabled.
 | 
			
		||||
 | 
			
		||||
**Before**:
 | 
			
		||||
 | 
			
		||||
@ -119,7 +119,7 @@ Keep in mind that if you are running custom ORM queries with Bookshelf or Mongoo
 | 
			
		||||
 | 
			
		||||
### Bootstrap function
 | 
			
		||||
 | 
			
		||||
The function exported in `config/functions/bootstrap.js` previsouly received a callback. This is not the case anymore. You can either use an async function, return a promise or simply run a synchronous function.
 | 
			
		||||
The function exported in `config/functions/bootstrap.js` previously received a callback. This is not the case anymore. You can either use an async function, return a promise or simply run a synchronous function.
 | 
			
		||||
 | 
			
		||||
**Before**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7665
									
								
								docs/yarn.lock
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -17,6 +17,10 @@
 | 
			
		||||
    "restaurant": {
 | 
			
		||||
      "model": "restaurant",
 | 
			
		||||
      "via": "menu"
 | 
			
		||||
    },
 | 
			
		||||
    "menusections": {
 | 
			
		||||
      "collection": "menusection",
 | 
			
		||||
      "via": "menu"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -21,7 +21,12 @@
 | 
			
		||||
    },
 | 
			
		||||
    "dishes": {
 | 
			
		||||
      "group": "dish",
 | 
			
		||||
      "type": "group"
 | 
			
		||||
      "type": "group",
 | 
			
		||||
      "repeatable": true
 | 
			
		||||
    },
 | 
			
		||||
    "menu": {
 | 
			
		||||
      "model": "menu",
 | 
			
		||||
      "via": "menusections"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -7,7 +7,10 @@
 | 
			
		||||
  },
 | 
			
		||||
  "options": {
 | 
			
		||||
    "increments": true,
 | 
			
		||||
    "timestamps": ["created_at", "updated_at"],
 | 
			
		||||
    "timestamps": [
 | 
			
		||||
      "created_at",
 | 
			
		||||
      "updated_at"
 | 
			
		||||
    ],
 | 
			
		||||
    "comment": ""
 | 
			
		||||
  },
 | 
			
		||||
  "attributes": {
 | 
			
		||||
@ -22,6 +25,11 @@
 | 
			
		||||
      "via": "related",
 | 
			
		||||
      "plugin": "upload"
 | 
			
		||||
    },
 | 
			
		||||
    "images": {
 | 
			
		||||
      "collection": "file",
 | 
			
		||||
      "via": "related",
 | 
			
		||||
      "plugin": "upload"
 | 
			
		||||
    },
 | 
			
		||||
    "menu": {
 | 
			
		||||
      "model": "menu",
 | 
			
		||||
      "via": "restaurant"
 | 
			
		||||
@ -29,11 +37,14 @@
 | 
			
		||||
    "categories": {
 | 
			
		||||
      "collection": "category"
 | 
			
		||||
    },
 | 
			
		||||
    "address": {
 | 
			
		||||
      "model": "address"
 | 
			
		||||
    },
 | 
			
		||||
    "price_range": {
 | 
			
		||||
      "enum": ["very_cheap", "cheap", "average", "expensive", "very_expensive"],
 | 
			
		||||
      "enum": [
 | 
			
		||||
        "very_cheap",
 | 
			
		||||
        "cheap",
 | 
			
		||||
        "average",
 | 
			
		||||
        "expensive",
 | 
			
		||||
        "very_expensive"
 | 
			
		||||
      ],
 | 
			
		||||
      "type": "enumeration"
 | 
			
		||||
    },
 | 
			
		||||
    "description": {
 | 
			
		||||
@ -57,6 +68,9 @@
 | 
			
		||||
      "required": true,
 | 
			
		||||
      "repeatable": true,
 | 
			
		||||
      "type": "group"
 | 
			
		||||
    },
 | 
			
		||||
    "address": {
 | 
			
		||||
      "model": "address"
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@ -14,6 +14,12 @@
 | 
			
		||||
    },
 | 
			
		||||
    "price": {
 | 
			
		||||
      "type": "float"
 | 
			
		||||
    },
 | 
			
		||||
    "picture": {
 | 
			
		||||
      "model": "file",
 | 
			
		||||
      "via": "related",
 | 
			
		||||
      "plugin": "upload",
 | 
			
		||||
      "required": false
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "getstarted",
 | 
			
		||||
  "private": true,
 | 
			
		||||
  "version": "3.0.0-beta.16",
 | 
			
		||||
  "version": "3.0.0-beta.16.5",
 | 
			
		||||
  "description": "A Strapi application.",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "develop": "strapi develop",
 | 
			
		||||
@ -15,23 +15,23 @@
 | 
			
		||||
    "mysql": "^2.17.1",
 | 
			
		||||
    "pg": "^7.10.0",
 | 
			
		||||
    "sqlite3": "^4.0.6",
 | 
			
		||||
    "strapi": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-admin": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-hook-bookshelf": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-hook-knex": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-hook-mongoose": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-middleware-views": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-content-manager": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-content-type-builder": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-documentation": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-email": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-graphql": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-settings-manager": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-upload": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-plugin-users-permissions": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-provider-email-mailgun": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-provider-upload-aws-s3": "3.0.0-beta.16",
 | 
			
		||||
    "strapi-utils": "3.0.0-beta.16"
 | 
			
		||||
    "strapi": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-admin": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-hook-bookshelf": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-hook-knex": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-hook-mongoose": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-middleware-views": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-content-manager": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-content-type-builder": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-documentation": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-email": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-graphql": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-settings-manager": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-upload": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-plugin-users-permissions": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-provider-email-mailgun": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-provider-upload-aws-s3": "3.0.0-beta.16.5",
 | 
			
		||||
    "strapi-utils": "3.0.0-beta.16.5"
 | 
			
		||||
  },
 | 
			
		||||
  "strapi": {
 | 
			
		||||
    "uuid": "getstarted"
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
  "version": "3.0.0-beta.16",
 | 
			
		||||
  "version": "3.0.0-beta.16.5",
 | 
			
		||||
  "packages": [
 | 
			
		||||
    "packages/*",
 | 
			
		||||
    "examples/*"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										16
									
								
								package.json
									
									
									
									
									
								
							
							
						
						@ -3,25 +3,25 @@
 | 
			
		||||
  "dependencies": {},
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@testing-library/jest-dom": "^4.0.0",
 | 
			
		||||
    "@testing-library/react": "^9.1.1",
 | 
			
		||||
    "@testing-library/react-hooks": "^2.0.1",
 | 
			
		||||
    "babel-eslint": "^10.0.1",
 | 
			
		||||
    "@testing-library/react": "^9.1.0",
 | 
			
		||||
    "@testing-library/react-hooks": "^2.0.0",
 | 
			
		||||
    "babel-eslint": "^10.0.0",
 | 
			
		||||
    "cross-env": "^5.2.0",
 | 
			
		||||
    "cypress": "3.1.2",
 | 
			
		||||
    "enzyme": "^3.9.0",
 | 
			
		||||
    "enzyme-adapter-react-16": "^1.12.1",
 | 
			
		||||
    "eslint": "^5.16.0",
 | 
			
		||||
    "eslint-config-prettier": "^6.0.0",
 | 
			
		||||
    "eslint": "^6.3.0",
 | 
			
		||||
    "eslint-config-prettier": "^6.2.0",
 | 
			
		||||
    "eslint-plugin-react": "^7.14.0",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^1.6.1",
 | 
			
		||||
    "eslint-plugin-redux-saga": "^1.0.0",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^2.0.0",
 | 
			
		||||
    "eslint-plugin-redux-saga": "^1.1.0",
 | 
			
		||||
    "execa": "^1.0.0",
 | 
			
		||||
    "husky": "^3.0.0",
 | 
			
		||||
    "istanbul": "~0.4.2",
 | 
			
		||||
    "jest": "^24.5.0",
 | 
			
		||||
    "jest-cli": "^24.5.0",
 | 
			
		||||
    "lerna": "^3.13.1",
 | 
			
		||||
    "lint-staged": "^9.1.0",
 | 
			
		||||
    "lint-staged": "^9.2.0",
 | 
			
		||||
    "npm-run-all": "^4.1.5",
 | 
			
		||||
    "prettier": "^1.18.2",
 | 
			
		||||
    "react-test-renderer": "^16.9.0",
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "create-strapi-app",
 | 
			
		||||
  "version": "3.0.0-beta.16",
 | 
			
		||||
  "version": "3.0.0-beta.16.5",
 | 
			
		||||
  "description": "Generate a new Strapi application.",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "homepage": "http://strapi.io",
 | 
			
		||||
@ -21,7 +21,7 @@
 | 
			
		||||
  ],
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "commander": "^2.20.0",
 | 
			
		||||
    "strapi-generate-new": "3.0.0-beta.16"
 | 
			
		||||
    "strapi-generate-new": "3.0.0-beta.16.5"
 | 
			
		||||
  },
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "echo \"no tests yet\""
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,6 @@ import {
 | 
			
		||||
  pluginLoaded,
 | 
			
		||||
  unfreezeApp,
 | 
			
		||||
  updatePlugin,
 | 
			
		||||
  getAppPluginsSucceeded,
 | 
			
		||||
} from './containers/App/actions';
 | 
			
		||||
import { showNotification } from './containers/NotificationProvider/actions';
 | 
			
		||||
 | 
			
		||||
@ -54,8 +53,6 @@ const { dispatch } = store;
 | 
			
		||||
const MOUNT_NODE =
 | 
			
		||||
  document.getElementById('app') || document.createElement('div');
 | 
			
		||||
 | 
			
		||||
dispatch(getAppPluginsSucceeded(Object.keys(plugins)));
 | 
			
		||||
 | 
			
		||||
Object.keys(plugins).forEach(plugin => {
 | 
			
		||||
  const currentPlugin = plugins[plugin];
 | 
			
		||||
 | 
			
		||||
@ -180,7 +177,7 @@ if (NODE_ENV !== 'test') {
 | 
			
		||||
          import('intl/locale-data/jsonp/en.js'),
 | 
			
		||||
          import('intl/locale-data/jsonp/de.js'),
 | 
			
		||||
        ])
 | 
			
		||||
      ) // eslint-disable-line prettier/prettier
 | 
			
		||||
      )
 | 
			
		||||
      .then(() => render(translationMessages))
 | 
			
		||||
      .catch(err => {
 | 
			
		||||
        throw err;
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
<svg width="647" height="240" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -12)" fill="none" fill-rule="evenodd" opacity=".5"><rect stroke="#E3E3E5" stroke-width="2" x="91" y="99" width="8" height="8" rx="4"/><rect stroke="#E3E3E5" stroke-width="2" x="21" y="149" width="4" height="4" rx="2"/><rect stroke="#E3E3E5" stroke-width="2" x="111" y="249" width="2" height="2" rx="1"/><rect fill="#F0F0F3" y="228" width="12" height="12" rx="5"/><rect stroke="#E3E3E5" stroke-width="2" x="141" y="179" width="10" height="10" rx="5"/><rect stroke="#E3E3E5" stroke-width="2" x="531" y="189" width="12" height="12" rx="6"/><rect stroke="#E3E3E5" stroke-width="2" x="641" y="129" width="5" height="5" rx="2.5"/><rect stroke="#E3E3E5" stroke-width="2" x="441" y="229" width="3" height="3" rx="1.5"/><rect fill="#F0F0F3" x="520" y="88" width="10" height="10" rx="5"/><rect stroke="#E3E3E5" stroke-width="2" x="431" y="19" width="4" height="4" rx="2"/><rect fill="#F0F0F3" x="220" y="208" width="9" height="9" rx="4.5"/><path d="M570.002 4.805c0 .115-.068.24-.204.375l-2.836 2.766.672 3.907c.005.037.008.089.008.156a.46.46 0 0 1-.082.278.276.276 0 0 1-.238.113.632.632 0 0 1-.313-.094l-3.508-1.844-3.508 1.844a.664.664 0 0 1-.313.094c-.11 0-.191-.038-.246-.113a.46.46 0 0 1-.082-.278c0-.03.005-.083.015-.156l.672-3.907-2.844-2.766c-.13-.14-.195-.265-.195-.375 0-.192.146-.312.438-.36l3.922-.57L563.118.32c.099-.213.227-.32.383-.32s.284.107.383.32l1.758 3.555 3.922.57c.292.048.438.168.438.36zM417 156.696c0 .088-.052.184-.156.288l-2.182 2.128.517 3.005a.909.909 0 0 1 .006.12.354.354 0 0 1-.063.213.212.212 0 0 1-.183.087.486.486 0 0 1-.24-.072L412 161.047l-2.698 1.418a.51.51 0 0 1-.24.072c-.085 0-.148-.029-.19-.087a.354.354 0 0 1-.063-.213c0-.024.004-.064.012-.12l.517-3.005-2.188-2.128c-.1-.108-.15-.204-.15-.288 0-.148.112-.24.337-.277l3.016-.438 1.353-2.735c.076-.164.174-.246.294-.246s.218.082.294.246l1.353 2.735 3.016.438c.225.037.337.129.337.277zM177 36.696c0 .088-.052.184-.156.288l-2.182 2.128.517 3.005a.909.909 0 0 1 .006.12.354.354 0 0 1-.063.213.212.212 0 0 1-.183.087.486.486 0 0 1-.24-.072L172 41.047l-2.698 1.418a.51.51 0 0 1-.24.072c-.085 0-.148-.029-.19-.087a.354.354 0 0 1-.063-.213c0-.024.004-.064.012-.12l.517-3.005-2.188-2.128c-.1-.108-.15-.204-.15-.288 0-.148.112-.24.337-.277l3.016-.438 1.353-2.735c.076-.164.174-.246.294-.246s.218.082.294.246l1.353 2.735 3.016.438c.225.037.337.129.337.277z" fill-rule="nonzero" fill="#E3E3E5"/></g></svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 2.4 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								packages/strapi-admin/admin/src/assets/images/logo_strapi.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 1.8 KiB  | 
@ -1,95 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright@React-FullStory (https://github.com/cereallarceny/react-fullstory)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
const canUseDOM = !!(
 | 
			
		||||
  typeof window !== 'undefined' &&
 | 
			
		||||
  window.document &&
 | 
			
		||||
  window.document.createElement
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const getWindowFullStory = () => window[window['_fs_namespace']];
 | 
			
		||||
 | 
			
		||||
class FullStory extends React.Component {
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
 | 
			
		||||
    window['_fs_debug'] = false;
 | 
			
		||||
    window['_fs_host'] = 'fullstory.com';
 | 
			
		||||
    window['_fs_org'] = props.org;
 | 
			
		||||
    window['_fs_namespace'] = 'FS';
 | 
			
		||||
    (function(m,n,e,t,l,o,g,y) {
 | 
			
		||||
      if (e in m) {
 | 
			
		||||
        if(m.console && m.console.log) { 
 | 
			
		||||
          m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');
 | 
			
		||||
        } 
 | 
			
		||||
        
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      g = m[e]= function(a,b,s) {
 | 
			
		||||
        g.q ? g.q.push([a,b,s]) : g._api(a,b,s);
 | 
			
		||||
      };
 | 
			
		||||
      g.q=[];
 | 
			
		||||
      o = n.createElement(t);
 | 
			
		||||
      o.async = 1;
 | 
			
		||||
      o.src = `https://${window._fs_host}/s/fs.js`;
 | 
			
		||||
      y = n.getElementsByTagName(t)[0];
 | 
			
		||||
      y.parentNode.insertBefore(o,y);
 | 
			
		||||
      g.identify = function(i,v,s) {
 | 
			
		||||
        g(l,{ uid:i },s);
 | 
			
		||||
 | 
			
		||||
        if (v) { 
 | 
			
		||||
          g(l,v,s);
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
      g.setUserVars = function(v,s) {
 | 
			
		||||
        g(l,v,s);
 | 
			
		||||
      };
 | 
			
		||||
      g.event = function(i,v,s) {
 | 
			
		||||
        g('event',{ n:i,p:v },s);
 | 
			
		||||
      };
 | 
			
		||||
      g.shutdown = function() {
 | 
			
		||||
        g("rec",!1);
 | 
			
		||||
      };
 | 
			
		||||
      g.restart = function() {
 | 
			
		||||
        g("rec",!0);
 | 
			
		||||
      };
 | 
			
		||||
      g.consent = function(a) {
 | 
			
		||||
        g("consent",!arguments.length||a);
 | 
			
		||||
      };
 | 
			
		||||
      g.identifyAccount = function(i,v) { 
 | 
			
		||||
        o = 'account';
 | 
			
		||||
        v = v||{};
 | 
			
		||||
        v.acctId = i;
 | 
			
		||||
        g(o,v);
 | 
			
		||||
      };
 | 
			
		||||
      g.clearUserCookie = function() {};
 | 
			
		||||
    })(window, document, window['_fs_namespace'], 'script', 'user');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  shouldComponentUpdate() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentWillUnmount() {
 | 
			
		||||
    if (!canUseDOM || !getWindowFullStory()) return false;
 | 
			
		||||
 | 
			
		||||
    getWindowFullStory().shutdown();
 | 
			
		||||
 | 
			
		||||
    delete getWindowFullStory();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FullStory.propTypes = {
 | 
			
		||||
  org: PropTypes.string.isRequired,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default FullStory;
 | 
			
		||||
							
								
								
									
										122
									
								
								packages/strapi-admin/admin/src/components/Logout/components.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,122 @@
 | 
			
		||||
import styled from 'styled-components';
 | 
			
		||||
 | 
			
		||||
const Wrapper = styled.div`
 | 
			
		||||
  position: relative;
 | 
			
		||||
  min-width: 19rem;
 | 
			
		||||
  -webkit-font-smoothing: antialiased;
 | 
			
		||||
  > div {
 | 
			
		||||
    height: 6rem;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    line-height: 5.8rem;
 | 
			
		||||
    z-index: 999;
 | 
			
		||||
    > button {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      padding-right: 20px;
 | 
			
		||||
      background: transparent;
 | 
			
		||||
      border: none;
 | 
			
		||||
      border-radius: 0;
 | 
			
		||||
      color: #333740;
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      font-weight: 500;
 | 
			
		||||
      text-align: right;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      transition: background 0.2s ease-out;
 | 
			
		||||
 | 
			
		||||
      &:hover,
 | 
			
		||||
      &:focus,
 | 
			
		||||
      &:active {
 | 
			
		||||
        color: #333740;
 | 
			
		||||
        background-color: #fafafb !important;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      > i {
 | 
			
		||||
        margin-left: 10px;
 | 
			
		||||
        transition: transform 0.3s ease-out;
 | 
			
		||||
 | 
			
		||||
        &[alt='true'] {
 | 
			
		||||
          transform: rotateX(180deg);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &:after {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: -1px;
 | 
			
		||||
    top: calc(50% - 10px);
 | 
			
		||||
    content: '';
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    border-left: 1px solid #f3f4f4;
 | 
			
		||||
    transition: opacity 0.2s ease-out;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &:hover:after {
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .dropDownContent {
 | 
			
		||||
    left: auto !important;
 | 
			
		||||
    min-width: 100% !important;
 | 
			
		||||
    margin: 0 !important;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    line-height: 1.8rem;
 | 
			
		||||
    border: none !important;
 | 
			
		||||
    border-top-left-radius: 0 !important;
 | 
			
		||||
    border-top-right-radius: 0 !important;
 | 
			
		||||
    font-size: 14px;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    box-shadow: 0 1px 4px 0px rgba(40, 42, 49, 0.05);
 | 
			
		||||
 | 
			
		||||
    &:before {
 | 
			
		||||
      content: '';
 | 
			
		||||
      position: absolute;
 | 
			
		||||
      top: -3px;
 | 
			
		||||
      left: -1px;
 | 
			
		||||
      width: calc(100% + 1px);
 | 
			
		||||
      height: 3px;
 | 
			
		||||
      box-shadow: 0 1px 2px 0 rgba(40, 42, 49, 0.16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > button {
 | 
			
		||||
      height: 40px;
 | 
			
		||||
      padding: 0px 15px;
 | 
			
		||||
      line-height: 40px;
 | 
			
		||||
      &:hover,
 | 
			
		||||
      &:focus,
 | 
			
		||||
      &:active {
 | 
			
		||||
        background-color: #fafafb !important;
 | 
			
		||||
        border-radius: 0px;
 | 
			
		||||
        cursor: pointer;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > button {
 | 
			
		||||
      height: 44px;
 | 
			
		||||
      line-height: 48px;
 | 
			
		||||
      &:hover,
 | 
			
		||||
      &:active {
 | 
			
		||||
        color: #333740;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    > button:last-child {
 | 
			
		||||
      color: #f75b1d;
 | 
			
		||||
      > i {
 | 
			
		||||
        margin-left: 10px;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .item {
 | 
			
		||||
    &:active {
 | 
			
		||||
      color: black;
 | 
			
		||||
    }
 | 
			
		||||
    &:hover {
 | 
			
		||||
      background-color: #fafafb !important;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
export default Wrapper;
 | 
			
		||||
@ -5,7 +5,7 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { useState } from 'react';
 | 
			
		||||
import { FormattedMessage } from 'react-intl';
 | 
			
		||||
import { withRouter } from 'react-router-dom';
 | 
			
		||||
import { get } from 'lodash';
 | 
			
		||||
@ -16,63 +16,53 @@ import {
 | 
			
		||||
  DropdownToggle,
 | 
			
		||||
} from 'reactstrap';
 | 
			
		||||
import { auth } from 'strapi-helper-plugin';
 | 
			
		||||
import Wrapper from './components';
 | 
			
		||||
 | 
			
		||||
import styles from './styles.scss';
 | 
			
		||||
const Logout = ({ history: { push } }) => {
 | 
			
		||||
  const [isOpen, setIsOpen] = useState(false);
 | 
			
		||||
  const toggle = () => setIsOpen(prev => !prev);
 | 
			
		||||
  const handleGoTo = () => {
 | 
			
		||||
    const id = get(auth.getUserInfo(), 'id');
 | 
			
		||||
 | 
			
		||||
class Logout extends React.Component {
 | 
			
		||||
  // eslint-disable-line react/prefer-stateless-function
 | 
			
		||||
  state = { isOpen: false };
 | 
			
		||||
 | 
			
		||||
  handleGoTo = () => {
 | 
			
		||||
    const id = get(auth.getUserInfo(), 'id') || get(auth.getUserInfo(), '_id');
 | 
			
		||||
    this.props.history.push({
 | 
			
		||||
    push({
 | 
			
		||||
      pathname: `/plugins/content-manager/administrator/${id}`,
 | 
			
		||||
      search:
 | 
			
		||||
        '?redirectUrl=/plugins/content-manager/administrator/?page=0&limit=0&sort=id&source=admin',
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleGoToAdministrator = () => {
 | 
			
		||||
    this.props.history.push({
 | 
			
		||||
  const handleGoToAdministrator = () => {
 | 
			
		||||
    push({
 | 
			
		||||
      pathname: '/plugins/content-manager/administrator',
 | 
			
		||||
      search: '?source=admin',
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleLogout = () => {
 | 
			
		||||
  const handleLogout = () => {
 | 
			
		||||
    auth.clearAppStorage();
 | 
			
		||||
    this.props.history.push('/plugins/users-permissions/auth/login');
 | 
			
		||||
    push('/auth/login');
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  toggle = () => this.setState({ isOpen: !this.state.isOpen });
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return (
 | 
			
		||||
      <div className={styles.logout}>
 | 
			
		||||
        <ButtonDropdown isOpen={this.state.isOpen} toggle={this.toggle}>
 | 
			
		||||
          <DropdownToggle>
 | 
			
		||||
            {get(auth.getUserInfo(), 'username')}
 | 
			
		||||
            <i className="fa fa-caret-down" alt={`${this.state.isOpen}`} />
 | 
			
		||||
          </DropdownToggle>
 | 
			
		||||
          <DropdownMenu className={styles.dropDownContent}>
 | 
			
		||||
            <DropdownItem onClick={this.handleGoTo} className={styles.item}>
 | 
			
		||||
              <FormattedMessage id="app.components.Logout.profile" />
 | 
			
		||||
            </DropdownItem>
 | 
			
		||||
            <DropdownItem
 | 
			
		||||
              onClick={this.handleGoToAdministrator}
 | 
			
		||||
              className={styles.item}
 | 
			
		||||
            >
 | 
			
		||||
              <FormattedMessage id="app.components.Logout.admin" />
 | 
			
		||||
            </DropdownItem>
 | 
			
		||||
            <DropdownItem onClick={this.handleLogout}>
 | 
			
		||||
              <FormattedMessage id="app.components.Logout.logout" />
 | 
			
		||||
              <i className="fa fa-sign-out" />
 | 
			
		||||
            </DropdownItem>
 | 
			
		||||
          </DropdownMenu>
 | 
			
		||||
        </ButtonDropdown>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
  return (
 | 
			
		||||
    <Wrapper>
 | 
			
		||||
      <ButtonDropdown isOpen={isOpen} toggle={toggle}>
 | 
			
		||||
        <DropdownToggle>
 | 
			
		||||
          {get(auth.getUserInfo(), 'username')}
 | 
			
		||||
          <i className="fa fa-caret-down" alt={`${isOpen}`} />
 | 
			
		||||
        </DropdownToggle>
 | 
			
		||||
        <DropdownMenu className="dropDownContent">
 | 
			
		||||
          <DropdownItem onClick={handleGoTo} className="item">
 | 
			
		||||
            <FormattedMessage id="app.components.Logout.profile" />
 | 
			
		||||
          </DropdownItem>
 | 
			
		||||
          <DropdownItem onClick={handleGoToAdministrator} className="item">
 | 
			
		||||
            <FormattedMessage id="app.components.Logout.admin" />
 | 
			
		||||
          </DropdownItem>
 | 
			
		||||
          <DropdownItem onClick={handleLogout}>
 | 
			
		||||
            <FormattedMessage id="app.components.Logout.logout" />
 | 
			
		||||
            <i className="fa fa-sign-out" />
 | 
			
		||||
          </DropdownItem>
 | 
			
		||||
        </DropdownMenu>
 | 
			
		||||
      </ButtonDropdown>
 | 
			
		||||
    </Wrapper>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default withRouter(Logout);
 | 
			
		||||
 | 
			
		||||
@ -1,113 +0,0 @@
 | 
			
		||||
.logout {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  min-width: 19rem;
 | 
			
		||||
  -webkit-font-smoothing: antialiased;
 | 
			
		||||
  > div {
 | 
			
		||||
    height: 6rem;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    line-height: 5.8rem;
 | 
			
		||||
    z-index: 999;
 | 
			
		||||
    > button {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      padding-right: 20px;
 | 
			
		||||
      background: transparent;
 | 
			
		||||
      border: none;
 | 
			
		||||
      border-radius: 0;
 | 
			
		||||
      color: #333740;
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      font-weight: 500;
 | 
			
		||||
      text-align: right;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      transition: background .2s ease-out;
 | 
			
		||||
 | 
			
		||||
      &:hover, &:focus, &:active {
 | 
			
		||||
        color: #333740;
 | 
			
		||||
        background-color: #FAFAFB !important;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      > i {
 | 
			
		||||
        margin-left: 10px;
 | 
			
		||||
        transition: transform .3s ease-out;
 | 
			
		||||
 | 
			
		||||
        &[alt="true"] {
 | 
			
		||||
          transform: rotateX(180deg);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &:after {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    right: -1px;
 | 
			
		||||
    top: calc(50% - 10px);
 | 
			
		||||
    content: '';
 | 
			
		||||
    display: inline-block;
 | 
			
		||||
    vertical-align: middle;
 | 
			
		||||
    height: 20px;
 | 
			
		||||
    border-left: 1px solid #F3F4F4;
 | 
			
		||||
    transition: opacity .2s ease-out;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  &:hover:after {
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
.dropDownContent {
 | 
			
		||||
  left: auto !important;
 | 
			
		||||
  min-width: 100%!important;
 | 
			
		||||
  margin: 0 !important;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  line-height: 1.8rem;
 | 
			
		||||
  border: none!important;
 | 
			
		||||
  border-top-left-radius: 0!important;
 | 
			
		||||
  border-top-right-radius: 0!important;
 | 
			
		||||
  font-size: 14px;
 | 
			
		||||
  overflow: hidden;
 | 
			
		||||
  box-shadow: 0 1px 4px 0px rgba(40, 42, 49, 0.05);
 | 
			
		||||
 | 
			
		||||
  &:before{
 | 
			
		||||
    content: '';
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    top: -3px;
 | 
			
		||||
    left: -1px;
 | 
			
		||||
    width: calc(100% + 1px);
 | 
			
		||||
    height: 3px;
 | 
			
		||||
    box-shadow: 0 1px 2px 0 rgba(40, 42, 49, 0.16);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > button {
 | 
			
		||||
    height: 40px;
 | 
			
		||||
    padding: 0px 15px;
 | 
			
		||||
    line-height: 40px;
 | 
			
		||||
    &:hover, &:focus, &:active {
 | 
			
		||||
      background-color: #FAFAFB!important;
 | 
			
		||||
      border-radius: 0px;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > button {
 | 
			
		||||
    height: 44px;
 | 
			
		||||
    line-height: 48px;
 | 
			
		||||
    &:hover, &:active {
 | 
			
		||||
      color: #333740;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  > button:last-child {
 | 
			
		||||
    color: #F75B1D;
 | 
			
		||||
    > i {
 | 
			
		||||
      margin-left: 10px
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.item {
 | 
			
		||||
  &:active {
 | 
			
		||||
    color: black;
 | 
			
		||||
  }
 | 
			
		||||
  &:hover {
 | 
			
		||||
    background-color: #FAFAFB !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,11 @@
 | 
			
		||||
import styled from 'styled-components';
 | 
			
		||||
 | 
			
		||||
const NavTopRightWrapper = styled.div`
 | 
			
		||||
  position: fixed;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  right: 0;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  z-index: 1050;
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
export default NavTopRightWrapper;
 | 
			
		||||
@ -0,0 +1,18 @@
 | 
			
		||||
import React, { memo } from 'react';
 | 
			
		||||
import Helmet from 'react-helmet';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
import favicon from '../../favicon.ico';
 | 
			
		||||
 | 
			
		||||
const PageTitle = ({ title }) => (
 | 
			
		||||
  <Helmet
 | 
			
		||||
    title={title}
 | 
			
		||||
    link={[{ rel: 'icon', type: 'image/png', href: favicon }]}
 | 
			
		||||
  />
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
PageTitle.propTypes = {
 | 
			
		||||
  title: PropTypes.string.isRequired,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default memo(PageTitle);
 | 
			
		||||
@ -43,7 +43,7 @@ class Row extends React.Component {
 | 
			
		||||
 | 
			
		||||
    const settingsPath =
 | 
			
		||||
      name === 'content-manager'
 | 
			
		||||
        ? '/plugins/content-manager/ctm-configurations'
 | 
			
		||||
        ? '/plugins/content-manager/ctm-configurations/models'
 | 
			
		||||
        : `/plugins/${name}/configurations/${currentEnvironment}`;
 | 
			
		||||
 | 
			
		||||
    const icons = [];
 | 
			
		||||
 | 
			
		||||
@ -1,24 +0,0 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import PropTypes from 'prop-types';
 | 
			
		||||
 | 
			
		||||
const style = {
 | 
			
		||||
  position: 'fixed',
 | 
			
		||||
  top: '0',
 | 
			
		||||
  right: '0',
 | 
			
		||||
  display: 'flex',
 | 
			
		||||
  zIndex: '1050',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function NavWrapper({ children }) {
 | 
			
		||||
  return <div style={style}>{children}</div>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NavWrapper.propTypes = {
 | 
			
		||||
  children: PropTypes.node,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
NavWrapper.defaultProps = {
 | 
			
		||||
  children: null,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default NavWrapper;
 | 
			
		||||
@ -4,20 +4,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  EMIT_EVENT,
 | 
			
		||||
  GET_INIT_DATA,
 | 
			
		||||
  GET_INIT_DATA_SUCCEEDED,
 | 
			
		||||
  GET_SECURED_DATA,
 | 
			
		||||
  GET_SECURED_DATA_SUCCEEDED,
 | 
			
		||||
  HIDE_LEFT_MENU,
 | 
			
		||||
  HIDE_LOGOUT,
 | 
			
		||||
  SET_APP_ERROR,
 | 
			
		||||
  SET_APP_SECURED,
 | 
			
		||||
  SHOW_LEFT_MENU,
 | 
			
		||||
  SHOW_LOGOUT,
 | 
			
		||||
  UNSET_APP_SECURED,
 | 
			
		||||
} from './constants';
 | 
			
		||||
import { EMIT_EVENT, SET_APP_ERROR } from './constants';
 | 
			
		||||
 | 
			
		||||
export function emitEvent(event, properties) {
 | 
			
		||||
  return {
 | 
			
		||||
@ -27,70 +14,8 @@ export function emitEvent(event, properties) {
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getInitData() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: GET_INIT_DATA,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getInitDataSucceeded(data) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: GET_INIT_DATA_SUCCEEDED,
 | 
			
		||||
    data,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getSecuredData() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: GET_SECURED_DATA,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getSecuredDataSucceeded(data) {
 | 
			
		||||
  return {
 | 
			
		||||
    type: GET_SECURED_DATA_SUCCEEDED,
 | 
			
		||||
    data,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function hideLeftMenu() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: HIDE_LEFT_MENU,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function hideLogout() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: HIDE_LOGOUT,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function setAppError() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: SET_APP_ERROR,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function setAppSecured() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: SET_APP_SECURED,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function showLeftMenu() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: SHOW_LEFT_MENU,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function showLogout() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: SHOW_LOGOUT,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function unsetAppSecured() {
 | 
			
		||||
  return {
 | 
			
		||||
    type: UNSET_APP_SECURED,
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,16 +5,4 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
export const EMIT_EVENT = 'app/Admin/EMIT_EVENT';
 | 
			
		||||
export const GET_INIT_DATA = 'StrapiAdmin/Admin/GET_INIT_DATA';
 | 
			
		||||
export const GET_INIT_DATA_SUCCEEDED =
 | 
			
		||||
  'StrapiAdmin/Admin/GET_INIT_DATA_SUCCEEDED';
 | 
			
		||||
export const GET_SECURED_DATA = 'StrapiAdmin/Admin/GET_SECURED_DATA';
 | 
			
		||||
export const GET_SECURED_DATA_SUCCEEDED =
 | 
			
		||||
  'StrapiAdmin/Admin/GET_SECURED_DATA_SUCCEEDED';
 | 
			
		||||
export const HIDE_LEFT_MENU = 'StrapiAdmin/Admin/HIDE_LEFT_MENU';
 | 
			
		||||
export const HIDE_LOGOUT = 'StrapiAdmin/Admin/HIDE_LOGOUT';
 | 
			
		||||
export const SET_APP_ERROR = 'StrapiAdmin/Admin/SET_APP_ERROR';
 | 
			
		||||
export const SET_APP_SECURED = 'StrapiAdmin/Admin/SET_APP_SECURED';
 | 
			
		||||
export const SHOW_LEFT_MENU = 'StrapiAdmin/Admin/SHOW_LEFT_MENU';
 | 
			
		||||
export const SHOW_LOGOUT = 'StrapiAdmin/Admin/SHOW_LOGOUT';
 | 
			
		||||
export const UNSET_APP_SECURED = 'StrapiAdmin/Admin/UNSET_APP_SECURED';
 | 
			
		||||
 | 
			
		||||
@ -10,17 +10,14 @@ import { connect } from 'react-redux';
 | 
			
		||||
import { createStructuredSelector } from 'reselect';
 | 
			
		||||
import { bindActionCreators, compose } from 'redux';
 | 
			
		||||
import { Switch, Route } from 'react-router-dom';
 | 
			
		||||
 | 
			
		||||
import { isEmpty } from 'lodash';
 | 
			
		||||
// Components from strapi-helper-plugin
 | 
			
		||||
import {
 | 
			
		||||
  LoadingIndicatorPage,
 | 
			
		||||
  OverlayBlocker,
 | 
			
		||||
  injectHooks,
 | 
			
		||||
} from 'strapi-helper-plugin';
 | 
			
		||||
import { LoadingIndicatorPage, OverlayBlocker } from 'strapi-helper-plugin';
 | 
			
		||||
import { SHOW_TUTORIALS } from '../../config';
 | 
			
		||||
 | 
			
		||||
import Header from '../../components/Header/index';
 | 
			
		||||
import Logout from '../../components/Logout';
 | 
			
		||||
import NavTopRightWrapper from '../../components/NavTopRightWrapper';
 | 
			
		||||
 | 
			
		||||
import ComingSoonPage from '../ComingSoonPage';
 | 
			
		||||
import LeftMenu from '../LeftMenu';
 | 
			
		||||
@ -41,78 +38,28 @@ import makeSelecApp from '../App/selectors';
 | 
			
		||||
 | 
			
		||||
import injectSaga from '../../utils/injectSaga';
 | 
			
		||||
import injectReducer from '../../utils/injectReducer';
 | 
			
		||||
import difference from './utils/difference';
 | 
			
		||||
 | 
			
		||||
import localeToggleReducer from '../LocaleToggle/reducer';
 | 
			
		||||
import {
 | 
			
		||||
  resetLocaleDefaultClassName,
 | 
			
		||||
  setLocaleCustomClassName,
 | 
			
		||||
} from '../LocaleToggle/actions';
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  emitEvent,
 | 
			
		||||
  getInitData,
 | 
			
		||||
  getSecuredData,
 | 
			
		||||
  hideLeftMenu,
 | 
			
		||||
  hideLogout,
 | 
			
		||||
  setAppError,
 | 
			
		||||
  setAppSecured,
 | 
			
		||||
  showLeftMenu,
 | 
			
		||||
  showLogout,
 | 
			
		||||
  unsetAppSecured,
 | 
			
		||||
} from './actions';
 | 
			
		||||
import { emitEvent, setAppError } from './actions';
 | 
			
		||||
import makeSelectAdmin from './selectors';
 | 
			
		||||
import reducer from './reducer';
 | 
			
		||||
import saga from './saga';
 | 
			
		||||
 | 
			
		||||
import NavTopRightWrapper from './NavTopRightWrapper';
 | 
			
		||||
 | 
			
		||||
import styles from './styles.scss';
 | 
			
		||||
 | 
			
		||||
export class Admin extends React.Component {
 | 
			
		||||
  // eslint-disable-line react/prefer-stateless-function
 | 
			
		||||
  state = { shouldSecureAfterAllPluginsAreMounted: true };
 | 
			
		||||
 | 
			
		||||
  getChildContext = () => ({
 | 
			
		||||
    emitEvent: this.props.emitEvent,
 | 
			
		||||
    currentEnvironment: this.props.admin.currentEnvironment,
 | 
			
		||||
    currentEnvironment: this.props.global.currentEnvironment,
 | 
			
		||||
    disableGlobalOverlayBlocker: this.props.disableGlobalOverlayBlocker,
 | 
			
		||||
    enableGlobalOverlayBlocker: this.props.enableGlobalOverlayBlocker,
 | 
			
		||||
    plugins: this.props.global.plugins,
 | 
			
		||||
    updatePlugin: this.props.updatePlugin,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    /* istanbul ignore next */
 | 
			
		||||
    // Retrieve the main settings of the application
 | 
			
		||||
    this.props.getInitData();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* istanbul ignore next */
 | 
			
		||||
  componentDidUpdate(prevProps) {
 | 
			
		||||
    const {
 | 
			
		||||
      admin: { didGetSecuredData, isLoading, isSecured },
 | 
			
		||||
      getHook,
 | 
			
		||||
      getSecuredData,
 | 
			
		||||
      location: { pathname },
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (!isLoading && this.state.shouldSecureAfterAllPluginsAreMounted) {
 | 
			
		||||
      if (!this.hasApluginNotReady(this.props)) {
 | 
			
		||||
        getHook('willSecure');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (prevProps.location.pathname !== pathname) {
 | 
			
		||||
      getHook('willSecure');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (prevProps.admin.isSecured !== isSecured && isSecured) {
 | 
			
		||||
      getSecuredData();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (prevProps.admin.didGetSecuredData !== didGetSecuredData) {
 | 
			
		||||
      getHook('didGetSecuredData');
 | 
			
		||||
    }
 | 
			
		||||
  shouldComponentUpdate(prevProps) {
 | 
			
		||||
    return !isEmpty(difference(prevProps, this.props));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* istanbul ignore next */
 | 
			
		||||
@ -129,16 +76,6 @@ export class Admin extends React.Component {
 | 
			
		||||
    this.props.setAppError();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getContentWrapperStyle = () => {
 | 
			
		||||
    const {
 | 
			
		||||
      admin: { showMenu },
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    return showMenu
 | 
			
		||||
      ? { main: {}, sub: styles.content }
 | 
			
		||||
      : { main: { width: '100%' }, sub: styles.wrapper };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  hasApluginNotReady = props => {
 | 
			
		||||
    const {
 | 
			
		||||
      global: { plugins },
 | 
			
		||||
@ -150,36 +87,14 @@ export class Admin extends React.Component {
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  helpers = {
 | 
			
		||||
    hideLeftMenu: this.props.hideLeftMenu,
 | 
			
		||||
    hideLogout: this.props.hideLogout,
 | 
			
		||||
    setAppSecured: this.props.setAppSecured,
 | 
			
		||||
    showLeftMenu: this.props.showLeftMenu,
 | 
			
		||||
    showLogout: this.props.showLogout,
 | 
			
		||||
    unsetAppSecured: this.props.unsetAppSecured,
 | 
			
		||||
    updatePlugin: this.props.updatePlugin,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  isAcceptingTracking = () => {
 | 
			
		||||
    const {
 | 
			
		||||
      admin: { uuid },
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    return !!uuid;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Display the app loader until the app is ready
 | 
			
		||||
   * @returns {Boolean}
 | 
			
		||||
   */
 | 
			
		||||
  showLoader = () => {
 | 
			
		||||
    const {
 | 
			
		||||
      global: { isAppLoading },
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (isAppLoading) {
 | 
			
		||||
      return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.hasApluginNotReady(this.props);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -212,14 +127,15 @@ export class Admin extends React.Component {
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      admin: { isLoading, showLogoutComponent, showMenu, strapiVersion },
 | 
			
		||||
      global: { blockApp, overlayBlockerData, plugins, showGlobalAppBlocker },
 | 
			
		||||
      global: {
 | 
			
		||||
        blockApp,
 | 
			
		||||
        overlayBlockerData,
 | 
			
		||||
        plugins,
 | 
			
		||||
        showGlobalAppBlocker,
 | 
			
		||||
        strapiVersion,
 | 
			
		||||
      },
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    if (isLoading) {
 | 
			
		||||
      return <LoadingIndicatorPage />;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // We need the admin data in order to make the initializers work
 | 
			
		||||
    if (this.showLoader()) {
 | 
			
		||||
      return (
 | 
			
		||||
@ -232,18 +148,15 @@ export class Admin extends React.Component {
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <div className={styles.adminPage}>
 | 
			
		||||
        {showMenu && <LeftMenu version={strapiVersion} plugins={plugins} />}
 | 
			
		||||
        <LeftMenu version={strapiVersion} plugins={plugins} />
 | 
			
		||||
        <NavTopRightWrapper>
 | 
			
		||||
          {/* Injection zone not ready yet */}
 | 
			
		||||
          {showLogoutComponent && <Logout />}
 | 
			
		||||
          <Logout />
 | 
			
		||||
          <LocaleToggle isLogged />
 | 
			
		||||
        </NavTopRightWrapper>
 | 
			
		||||
        <div
 | 
			
		||||
          className={styles.adminPageRightWrapper}
 | 
			
		||||
          style={this.getContentWrapperStyle().main}
 | 
			
		||||
        >
 | 
			
		||||
          {showMenu ? <Header /> : ''}
 | 
			
		||||
          <div className={this.getContentWrapperStyle().sub}>
 | 
			
		||||
        <div className={styles.adminPageRightWrapper}>
 | 
			
		||||
          <Header />
 | 
			
		||||
          <div className={styles.content}>
 | 
			
		||||
            <Switch>
 | 
			
		||||
              <Route
 | 
			
		||||
                path="/"
 | 
			
		||||
@ -255,7 +168,11 @@ export class Admin extends React.Component {
 | 
			
		||||
                render={this.renderPluginDispatcher}
 | 
			
		||||
              />
 | 
			
		||||
              <Route path="/plugins" component={ComingSoonPage} />
 | 
			
		||||
              <Route path="/list-plugins" component={ListPluginsPage} exact />
 | 
			
		||||
              <Route
 | 
			
		||||
                path="/list-plugins"
 | 
			
		||||
                render={props => this.renderRoute(props, ListPluginsPage)}
 | 
			
		||||
                exact
 | 
			
		||||
              />
 | 
			
		||||
              <Route
 | 
			
		||||
                path="/marketplace"
 | 
			
		||||
                render={this.renderMarketPlace}
 | 
			
		||||
@ -272,7 +189,7 @@ export class Admin extends React.Component {
 | 
			
		||||
          isOpen={blockApp && showGlobalAppBlocker}
 | 
			
		||||
          {...overlayBlockerData}
 | 
			
		||||
        />
 | 
			
		||||
        {showLogoutComponent && SHOW_TUTORIALS && <Onboarding />}
 | 
			
		||||
        {SHOW_TUTORIALS && <Onboarding />}
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
@ -289,41 +206,21 @@ Admin.childContextTypes = {
 | 
			
		||||
 | 
			
		||||
Admin.propTypes = {
 | 
			
		||||
  admin: PropTypes.shape({
 | 
			
		||||
    autoReload: PropTypes.bool,
 | 
			
		||||
    appError: PropTypes.bool,
 | 
			
		||||
    currentEnvironment: PropTypes.string,
 | 
			
		||||
    didGetSecuredData: PropTypes.bool,
 | 
			
		||||
    isLoading: PropTypes.bool,
 | 
			
		||||
    isSecured: PropTypes.bool,
 | 
			
		||||
    layout: PropTypes.object,
 | 
			
		||||
    showLogoutComponent: PropTypes.bool,
 | 
			
		||||
    showMenu: PropTypes.bool,
 | 
			
		||||
    strapiVersion: PropTypes.string,
 | 
			
		||||
    uuid: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
 | 
			
		||||
  }).isRequired,
 | 
			
		||||
  disableGlobalOverlayBlocker: PropTypes.func.isRequired,
 | 
			
		||||
  emitEvent: PropTypes.func.isRequired,
 | 
			
		||||
  enableGlobalOverlayBlocker: PropTypes.func.isRequired,
 | 
			
		||||
  getHook: PropTypes.func.isRequired,
 | 
			
		||||
  getInitData: PropTypes.func.isRequired,
 | 
			
		||||
  getSecuredData: PropTypes.func.isRequired,
 | 
			
		||||
  global: PropTypes.shape({
 | 
			
		||||
    appPlugins: PropTypes.array,
 | 
			
		||||
    blockApp: PropTypes.bool,
 | 
			
		||||
    currentEnvironment: PropTypes.string,
 | 
			
		||||
    overlayBlockerData: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
 | 
			
		||||
    isAppLoading: PropTypes.bool,
 | 
			
		||||
    plugins: PropTypes.object,
 | 
			
		||||
    showGlobalAppBlocker: PropTypes.bool,
 | 
			
		||||
    strapiVersion: PropTypes.string,
 | 
			
		||||
  }).isRequired,
 | 
			
		||||
  hideLeftMenu: PropTypes.func.isRequired,
 | 
			
		||||
  hideLogout: PropTypes.func.isRequired,
 | 
			
		||||
  location: PropTypes.object.isRequired,
 | 
			
		||||
  resetLocaleDefaultClassName: PropTypes.func.isRequired,
 | 
			
		||||
  setAppError: PropTypes.func.isRequired,
 | 
			
		||||
  setAppSecured: PropTypes.func.isRequired,
 | 
			
		||||
  showLeftMenu: PropTypes.func.isRequired,
 | 
			
		||||
  showLogout: PropTypes.func.isRequired,
 | 
			
		||||
  unsetAppSecured: PropTypes.func.isRequired,
 | 
			
		||||
  updatePlugin: PropTypes.func.isRequired,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -338,17 +235,7 @@ export function mapDispatchToProps(dispatch) {
 | 
			
		||||
      disableGlobalOverlayBlocker,
 | 
			
		||||
      emitEvent,
 | 
			
		||||
      enableGlobalOverlayBlocker,
 | 
			
		||||
      getInitData,
 | 
			
		||||
      getSecuredData,
 | 
			
		||||
      hideLeftMenu,
 | 
			
		||||
      hideLogout,
 | 
			
		||||
      resetLocaleDefaultClassName,
 | 
			
		||||
      setAppError,
 | 
			
		||||
      setAppSecured,
 | 
			
		||||
      setLocaleCustomClassName,
 | 
			
		||||
      showLeftMenu,
 | 
			
		||||
      showLogout,
 | 
			
		||||
      unsetAppSecured,
 | 
			
		||||
      updatePlugin,
 | 
			
		||||
    },
 | 
			
		||||
    dispatch
 | 
			
		||||
@ -361,16 +248,9 @@ const withConnect = connect(
 | 
			
		||||
);
 | 
			
		||||
const withReducer = injectReducer({ key: 'admin', reducer });
 | 
			
		||||
const withSaga = injectSaga({ key: 'admin', saga });
 | 
			
		||||
const withLocaleToggleReducer = injectReducer({
 | 
			
		||||
  key: 'localeToggle',
 | 
			
		||||
  reducer: localeToggleReducer,
 | 
			
		||||
});
 | 
			
		||||
const withHooks = injectHooks({ key: 'admin' });
 | 
			
		||||
 | 
			
		||||
export default compose(
 | 
			
		||||
  withReducer,
 | 
			
		||||
  withLocaleToggleReducer,
 | 
			
		||||
  withSaga,
 | 
			
		||||
  withConnect,
 | 
			
		||||
  withHooks
 | 
			
		||||
  withConnect
 | 
			
		||||
)(Admin);
 | 
			
		||||
 | 
			
		||||
@ -4,69 +4,17 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import { fromJS, Map } from 'immutable';
 | 
			
		||||
import {
 | 
			
		||||
  GET_INIT_DATA_SUCCEEDED,
 | 
			
		||||
  GET_SECURED_DATA_SUCCEEDED,
 | 
			
		||||
  HIDE_LEFT_MENU,
 | 
			
		||||
  HIDE_LOGOUT,
 | 
			
		||||
  SET_APP_ERROR,
 | 
			
		||||
  SET_APP_SECURED,
 | 
			
		||||
  SHOW_LEFT_MENU,
 | 
			
		||||
  SHOW_LOGOUT,
 | 
			
		||||
  UNSET_APP_SECURED,
 | 
			
		||||
} from './constants';
 | 
			
		||||
import { fromJS } from 'immutable';
 | 
			
		||||
import { SET_APP_ERROR } from './constants';
 | 
			
		||||
 | 
			
		||||
const initialState = fromJS({
 | 
			
		||||
  autoReload: false,
 | 
			
		||||
  appError: false,
 | 
			
		||||
  currentEnvironment: 'development',
 | 
			
		||||
  didGetSecuredData: false,
 | 
			
		||||
  isLoading: true,
 | 
			
		||||
  isSecured: false,
 | 
			
		||||
  layout: Map({}),
 | 
			
		||||
  // NOTE: This should be the models and our stuffs
 | 
			
		||||
  // Since this api is not implemented yet I just set this vague key ATM
 | 
			
		||||
  securedData: {},
 | 
			
		||||
  showMenu: true,
 | 
			
		||||
  showLogoutComponent: false,
 | 
			
		||||
  strapiVersion: '3',
 | 
			
		||||
  uuid: false,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function adminReducer(state = initialState, action) {
 | 
			
		||||
  switch (action.type) {
 | 
			
		||||
    case GET_INIT_DATA_SUCCEEDED: {
 | 
			
		||||
      const {
 | 
			
		||||
        data: { autoReload, currentEnvironment, layout, strapiVersion, uuid },
 | 
			
		||||
      } = action;
 | 
			
		||||
 | 
			
		||||
      return state
 | 
			
		||||
        .update('autoReload', () => autoReload)
 | 
			
		||||
        .update('currentEnvironment', () => currentEnvironment)
 | 
			
		||||
        .update('isLoading', () => false)
 | 
			
		||||
        .update('layout', () => Map(layout))
 | 
			
		||||
        .update('strapiVersion', () => strapiVersion)
 | 
			
		||||
        .update('uuid', () => uuid);
 | 
			
		||||
    }
 | 
			
		||||
    case GET_SECURED_DATA_SUCCEEDED:
 | 
			
		||||
      return state
 | 
			
		||||
        .update('didGetSecuredData', v => !v)
 | 
			
		||||
        .update('securedData', () => action.data);
 | 
			
		||||
    case HIDE_LEFT_MENU:
 | 
			
		||||
      return state.update('showMenu', () => false);
 | 
			
		||||
    case HIDE_LOGOUT:
 | 
			
		||||
      return state.update('showLogoutComponent', () => false);
 | 
			
		||||
    case SET_APP_ERROR:
 | 
			
		||||
      return state.update('appError', () => true);
 | 
			
		||||
    case SET_APP_SECURED:
 | 
			
		||||
      return state.update('isSecured', () => true);
 | 
			
		||||
    case SHOW_LEFT_MENU:
 | 
			
		||||
      return state.update('showMenu', () => true);
 | 
			
		||||
    case SHOW_LOGOUT:
 | 
			
		||||
      return state.update('showLogoutComponent', () => true);
 | 
			
		||||
    case UNSET_APP_SECURED:
 | 
			
		||||
      return state.update('isSecured', () => false);
 | 
			
		||||
    default:
 | 
			
		||||
      return state;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -1,10 +1,7 @@
 | 
			
		||||
/* eslint-disable */
 | 
			
		||||
import { all, fork, call, put, select, takeLatest } from 'redux-saga/effects';
 | 
			
		||||
import { request } from 'strapi-helper-plugin';
 | 
			
		||||
 | 
			
		||||
import { getInitDataSucceeded, getSecuredDataSucceeded } from './actions';
 | 
			
		||||
import { EMIT_EVENT, GET_INIT_DATA, GET_SECURED_DATA } from './constants';
 | 
			
		||||
import { makeSelectUuid } from './selectors';
 | 
			
		||||
import { EMIT_EVENT } from './constants';
 | 
			
		||||
import { makeSelectUuid } from '../App/selectors';
 | 
			
		||||
 | 
			
		||||
export function* emitter(action) {
 | 
			
		||||
  try {
 | 
			
		||||
@ -30,55 +27,7 @@ export function* emitter(action) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function* getData() {
 | 
			
		||||
  try {
 | 
			
		||||
    const endPoints = [
 | 
			
		||||
      'gaConfig',
 | 
			
		||||
      'strapiVersion',
 | 
			
		||||
      'currentEnvironment',
 | 
			
		||||
      'layout',
 | 
			
		||||
    ];
 | 
			
		||||
    const [
 | 
			
		||||
      { uuid },
 | 
			
		||||
      { strapiVersion },
 | 
			
		||||
      { autoReload, currentEnvironment },
 | 
			
		||||
      { layout },
 | 
			
		||||
    ] = yield all(
 | 
			
		||||
      endPoints.map(endPoint =>
 | 
			
		||||
        call(request, `/admin/${endPoint}`, { method: 'GET' })
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    yield put(
 | 
			
		||||
      getInitDataSucceeded({
 | 
			
		||||
        autoReload,
 | 
			
		||||
        uuid,
 | 
			
		||||
        strapiVersion,
 | 
			
		||||
        currentEnvironment,
 | 
			
		||||
        layout,
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    strapi.notification.error('notification.error');
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* istanbul ignore next */
 | 
			
		||||
export function* getSecuredData() {
 | 
			
		||||
  try {
 | 
			
		||||
    const data = {};
 | 
			
		||||
 | 
			
		||||
    yield put(getSecuredDataSucceeded(data));
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log(err); // eslint-disable-line no-console
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Individual exports for testing
 | 
			
		||||
export default function* defaultSaga() {
 | 
			
		||||
  yield all([
 | 
			
		||||
    fork(takeLatest, EMIT_EVENT, emitter),
 | 
			
		||||
    fork(takeLatest, GET_INIT_DATA, getData),
 | 
			
		||||
    fork(takeLatest, GET_SECURED_DATA, getSecuredData),
 | 
			
		||||
  ]);
 | 
			
		||||
  yield all([fork(takeLatest, EMIT_EVENT, emitter)]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||