+ }
+}
+
+BitCoinRate.propTypes = {
+ bitcoinRateReceived: PropTypes.func,
+ rate: PropTypes.number,
+};
+
+const mapStateToProps = state => ({ rate: state.bitcoinRate });
+
+export default connect(mapStateToProps, {
+ bitcoinRateReceived: bitcoinRateReceivedAction,
+})(BitCoinRate);
+```
+
+In order to put the rate passed to `bitcoinRateReceived()` into the Redux store, you'll need a reducer:
+
+```jsx
+// in src/rateReducer.js
+import { BITCOIN_RATE_RECEIVED } from './bitcoinRateReceived';
+
+export default (previousState = 0, { type, payload }) => {
+ if (type === BITCOIN_RATE_RECEIVED) {
+ return payload.rate;
+ }
+ return previousState;
+}
+```
+
+Now the question is: How can you put this reducer in the `` app? Simple: use the `customReducers` props:
+
+
+```jsx
+// in src/App.js
+import React from 'react';
+import { Admin } from 'react-admin';
+
+import rate from './rateReducer';
+
+const App = () => (
+
+ ...
+
+);
+
+export default App;
+```
+
+
+**Tip**: You can avoid storing data in the Redux state by storing data in a component state instead. It's much less complicated to deal with, and more performant, too. Use the global state only when you really need to.
+
+## List Bulk Actions
+
+Almost everything we saw before is true for custom `List` bulk actions too, with the following few differences:
+
+* They receive the following props: `resource`, `selectedIds` and `filterValues`
+* They do not receive the current record in the `record` prop as there are many of them.
+* They must render as a material-ui [`MenuItem`](http://www.material-ui.com/#/components/menu).
+
+You can find a complete example in the `List` documentation, in the [`bulk-actions`](/List.html#bulk-actions) section.
+
+## Conclusion
+
+Which style should you choose for your own action buttons?
+
+The first version (with `fetch`) is perfectly fine, and if you're not into unit testing your components, or decoupling side effects from pure functions, then you can stick with it without problem.
+
+On the other hand, if you want to promote reusability, separation of concerns, adhere to react-admin's coding standards, and if you know enough Redux and Saga, use the final version.
diff --git a/docs/admin-component.md b/docs/admin-component.md
new file mode 100644
index 0000000..2d9e9c3
--- /dev/null
+++ b/docs/admin-component.md
@@ -0,0 +1,557 @@
+---
+id: admin-component
+title:
+---
+
+The `` component creates an application with its own state, routing, and controller logic. `` requires only a `dataProvider` prop, and at least one child `` to work:
+
+```jsx
+// in src/App.js
+import React from 'react';
+
+import { Admin, Resource } from 'react-admin';
+import simpleRestProvider from 'ra-data-simple-rest';
+
+import { PostList } from './posts';
+
+const App = () => (
+
+
+
+);
+
+export default App;
+```
+
+Here are all the props accepted by the component:
+
+* [`dataProvider`](#dataprovider)
+* [`title`](#title)
+* [`dashboard`](#dashboard)
+* [`catchAll`](#catchall)
+* [`menu`](#menu) (deprecated)
+* [`theme`](#theme)
+* [`appLayout`](#applayout)
+* [`customReducers`](#customreducers)
+* [`customSagas`](#customsagas)
+* [`customRoutes`](#customroutes)
+* [`authProvider`](#authprovider)
+* [`loginPage`](#loginpage)
+* [`logoutButton`](#logoutbutton)
+* [`locale`](#internationalization)
+* [`messages`](#internationalization)
+* [`initialState`](#initialstate)
+* [`history`](#history)
+
+## `dataProvider`
+
+The only required prop, it must be a function returning a promise, with the following signature:
+
+```jsx
+/**
+ * Query a data provider and return a promise for a response
+ *
+ * @example
+ * dataProvider(GET_ONE, 'posts', { id: 123 })
+ * => new Promise(resolve => resolve({ id: 123, title: "hello, world" }))
+ *
+ * @param {string} type Request type, e.g GET_LIST
+ * @param {string} resource Resource name, e.g. "posts"
+ * @param {Object} payload Request parameters. Depends on the action type
+ * @returns {Promise} the Promise for a response
+ */
+const dataProvider = (type, resource, params) => new Promise();
+```
+
+The `dataProvider` is also the ideal place to add custom HTTP headers, authentication, etc. The [Data Providers Chapter](./DataProviders.html) of the documentation lists available data providers, and explains how to build your own.
+
+## `title`
+
+By default, the header of an admin app uses 'React Admin' as the main app title. It's probably the first thing you'll want to customize. The `title` prop serves exactly that purpose.
+
+```jsx
+const App = () => (
+
+ // ...
+
+);
+```
+
+## `dashboard`
+
+By default, the homepage of an an admin app is the `list` of the first child ``. But you can also specify a custom component instead. To fit in the general design, use Material UI's `` component, and react-admin's `` component:
+
+```jsx
+// in src/Dashboard.js
+import React from 'react';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import { ViewTitle } from 'react-admin';
+export default () => (
+
+
+ Lorem ipsum sic dolor amet...
+
+);
+```
+
+```jsx
+// in src/App.js
+import Dashboard from './Dashboard';
+
+const App = () => (
+
+ // ...
+
+);
+```
+
+
+
+**Tip**: Adding the `` component will also allow the header to be displayed in mobile resolutions.
+
+## `catchAll`
+
+When users type URLs that don't match any of the children `` components, they see a default "Not Found" page.
+
+
+
+You can customize this page to use the component of your choice by passing it as the `catchAll` prop. To fit in the general design, use Material UI's `` component, and react-admin's `` component:
+
+```jsx
+// in src/NotFound.js
+import React from 'react';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import { ViewTitle } from 'react-admin';
+
+export default () => (
+
+
+
+
404: Page not found
+
+
+);
+```
+
+```jsx
+// in src/App.js
+import NotFound from './NotFound';
+
+const App = () => (
+
+ // ...
+
+);
+```
+
+**Tip**: If your custom `catchAll` component contains react-router `` components, this allows you to register new routes displayed within the react-admin layout easily. Note that these routes will match *after* all the react-admin resource routes have been tested. To add custom routes *before* the react-admin ones, and therefore override the default resource routes, use the [`customRoutes` prop](#customroutes) instead.
+
+## `menu`
+
+**Tip**: This prop is deprecated. To override the menu component, use a [custom layout](#appLayout) instead.
+
+React-admin uses the list of `` components passed as children of `` to build a menu to each resource with a `list` component.
+
+If you want to add or remove menu items, for instance to link to non-resources pages, you can create your own menu component:
+
+```jsx
+// in src/Menu.js
+import React, { createElement } from 'react';
+import { connect } from 'react-redux';
+import { MenuItemLink, getResources } from 'react-admin';
+import { withRouter } from 'react-router-dom';
+import LabelIcon from '@material-ui/icons/Label';
+
+import Responsive from '../layout/Responsive';
+
+const Menu = ({ resources, onMenuClick, logout }) => (
+
+);
+
+const mapStateToProps = state => ({
+ resources: getResources(state),
+});
+
+export default withRouter(connect(mapStateToProps)(Menu));
+```
+
+**Tip**: Note the `MenuItemLink` component. It must be used to avoid unwanted side effects in mobile views. It supports a custom text and icon (which must be a material-ui ``).
+
+**Tip**: Note that we include the `logout` item only on small devices. Indeed, the `logout` button is already displayed in the AppBar on larger devices.
+
+**Tip**: Note that we use React Router [`withRouter`](https://reacttraining.com/react-router/web/api/withRouter) Higher Order Component and that it is used **before** Redux [`connect](https://github.com/reactjs/react-redux/blob/master/docs/api.html#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options). This is required if you want the active menu item to be highlighted.
+
+Then, pass it to the `` component as the `menu` prop:
+
+```jsx
+// in src/App.js
+import Menu from './Menu';
+
+const App = () => (
+
+ // ...
+
+);
+```
+
+See the [Theming documentation](./Theming.html#using-a-custom-menu) for more details.
+
+## `theme`
+
+Material UI supports [theming](http://www.material-ui.com/#/customization/themes). This lets you customize the look and feel of an admin by overriding fonts, colors, and spacing. You can provide a custom material ui theme by using the `theme` prop:
+
+```jsx
+import { createMuiTheme } from '@material-ui/core/styles';
+
+const theme = createMuiTheme({
+ palette: {
+ type: 'dark', // Switching the dark mode on is a single property value change.
+ },
+});
+
+const App = () => (
+
+ // ...
+
+);
+```
+
+
+
+For more details on predefined themes and custom themes, refer to the [Material UI Customization documentation](https://material-ui.com/customization/themes/).
+
+## `appLayout`
+
+If you want to deeply customize the app header, the menu, or the notifications, the best way is to provide a custom layout component. It must contain a `{children}` placeholder, where react-admin will render the resources. If you use material UI fields and inputs, it should contain a `` element. And finally, if you want to show the spinner in the app header when the app fetches data in the background, the Layout should connect to the redux store.
+
+Use the [default layout](https://github.com/marmelab/react-admin/blob/master/packages/ra-ui-materialui/src/layout/Layout.js) as a starting point, and check [the Theming documentation](./Theming.html#using-a-custom-layout) for examples.
+
+```jsx
+// in src/App.js
+import MyLayout from './MyLayout';
+
+const App = () => (
+
+ // ...
+
+);
+```
+
+Your custom layout can simply extend the default `` component if you only want to override the appBar, the menu, or the notification component. For instance:
+
+```jsx
+// in src/MyLayout.js
+import { Layout } from 'react-admin';
+import MyAppBar from './MyAppBar';
+import MyMenu from './MyMenu';
+import MyNotification from './MyNotification';
+
+const MyLayout = (props) => ;
+
+export default MyLayout;
+```
+
+For more details on custom layouts, check [the Theming documentation](./Theming.html#using-a-custom-layout).
+
+## `customReducers`
+
+The `` app uses [Redux](http://redux.js.org/) to manage state. The state has the following keys:
+
+```jsx
+{
+ admin: { /*...*/ }, // used by react-admin
+ form: { /*...*/ }, // used by redux-form
+ routing: { /*...*/ }, // used by react-router-redux
+}
+```
+
+If your components dispatch custom actions, you probably need to register your own reducers to update the state with these actions. Let's imagine that you want to keep the bitcoin exchange rate inside the `bitcoinRate` key in the state. You probably have a reducer looking like the following:
+
+```jsx
+// in src/bitcoinRateReducer.js
+export default (previousState = 0, { type, payload }) => {
+ if (type === 'BITCOIN_RATE_RECEIVED') {
+ return payload.rate;
+ }
+ return previousState;
+}
+```
+
+To register this reducer in the `` app, simply pass it in the `customReducers` prop:
+
+{% raw %}
+```jsx
+// in src/App.js
+import React from 'react';
+import { Admin } from 'react-admin';
+
+import bitcoinRateReducer from './bitcoinRateReducer';
+
+const App = () => (
+
+ ...
+
+);
+
+export default App;
+```
+{% endraw %}
+
+Now the state will look like:
+
+```jsx
+{
+ admin: { /*...*/ }, // used by react-admin
+ form: { /*...*/ }, // used by redux-form
+ routing: { /*...*/ }, // used by react-router-redux
+ bitcoinRate: 123, // managed by rateReducer
+}
+```
+
+## `customSagas`
+
+The `` app uses [redux-saga](https://github.com/redux-saga/redux-saga) to handle side effects (AJAX calls, notifications, redirections, etc).
+
+If your components dispatch custom actions, you probably need to register your own side effects as sagas. Let's imagine that you want to show a notification whenever the `BITCOIN_RATE_RECEIVED` action is dispatched. You probably have a saga looking like the following:
+
+```jsx
+// in src/bitcoinSaga.js
+import { put, takeEvery } from 'redux-saga/effects';
+import { showNotification } from 'react-admin';
+
+export default function* bitcoinSaga() {
+ yield takeEvery('BITCOIN_RATE_RECEIVED', function* () {
+ yield put(showNotification('Bitcoin rate updated'));
+ })
+}
+```
+
+To register this saga in the `` app, simply pass it in the `customSagas` prop:
+
+```jsx
+// in src/App.js
+import React from 'react';
+import { Admin } from 'react-admin';
+
+import bitcoinSaga from './bitcoinSaga';
+
+const App = () => (
+
+ ...
+
+);
+
+export default App;
+```
+
+## `customRoutes`
+
+To register your own routes, create a module returning a list of [react-router](https://github.com/ReactTraining/react-router) `` component:
+
+```jsx
+// in src/customRoutes.js
+import React from 'react';
+import { Route } from 'react-router-dom';
+import Foo from './Foo';
+import Bar from './Bar';
+import Baz from './Baz';
+
+export default [
+ ,
+ ,
+ ,
+];
+```
+
+Then, pass this array as `customRoutes` prop in the `` component:
+
+```jsx
+// in src/App.js
+import React from 'react';
+import { Admin } from 'react-admin';
+
+import customRoutes from './customRoutes';
+
+const App = () => (
+
+ ...
+
+);
+
+export default App;
+```
+
+Now, when a user browses to `/foo` or `/bar`, the components you defined will appear in the main part of the screen.
+When a user browses to `/baz`, the component will appear outside of the defined Layout, leaving you the freedom
+to design the screen the way you want.
+
+**Tip**: It's up to you to create a [custom menu](#menu) entry, or custom buttons, to lead to your custom pages.
+
+**Tip**: Your custom pages take precedence over react-admin's own routes. That means that `customRoutes` lets you override any route you want! If you want to add routes *after* all the react-admin routes, use the [`catchAll` prop](#catchall) instead.
+
+**Tip**: To look like other react-admin pages, your custom pages should have the following structure:
+
+```jsx
+// in src/Foo.js
+import React from 'react';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import { ViewTitle } from 'react-admin';
+
+const Foo = () => (
+
+
+
+ ...
+
+
+));
+
+export default Foo;
+```
+
+## `authProvider`
+
+The `authProvider` prop expect a function returning a Promise, to control the application authentication strategy:
+
+```jsx
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
+
+const authProvider(type, params) {
+ // type can be any of AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, and AUTH_CHECK
+ // ...
+ return Promise.resolve();
+};
+
+const App = () => (
+
+ ...
+
+);
+```
+
+The [Authentication documentation](./Authentication.html) explains how to implement these functions in detail.
+
+## `loginPage`
+
+If you want to customize the Login page, or switch to another authentication strategy than a username/password form, pass a component of your own as the `loginPage` prop. React-admin will display this component whenever the `/login` route is called.
+
+```jsx
+import MyLoginPage from './MyLoginPage';
+
+const App = () => (
+
+ ...
+
+);
+```
+
+See The [Authentication documentation](./Authentication.html#customizing-the-login-and-logout-components) for more details.
+
+## `logoutButton`
+
+If you customize the `loginPage`, you probably need to override the `logoutButton`, too - because they share the authentication strategy.
+
+```jsx
+import MyLoginPage from './MyLoginPage';
+import MyLogoutButton from './MyLogoutButton';
+
+const App = () => (
+
+ ...
+
+);
+```
+
+## `initialState`
+
+The `initialState` prop lets you pass preloaded state to Redux. See the [Redux Documentation](http://redux.js.org/docs/api/createStore.html#createstorereducer-preloadedstate-enhancer) for more details.
+
+## `history`
+
+By default, react-admin creates URLs using a hash sign (e.g. "myadmin.acme.com/#/posts/123"). The hash portion of the URL (i.e. `#/posts/123` in the example) contains the main application route. This strategy has the benefit of working without a server, and with legacy web browsers. But you may want to use another routing strategy, e.g. to allow server-side rendering.
+
+You can create your own `history` function (compatible with [the `history` npm package](https://github.com/reacttraining/history)), and pass it to the `` component to override the default history strategy. For instance, to use `browserHistory`:
+
+```js
+import createHistory from 'history/createBrowserHistory';
+
+const history = createHistory();
+
+const App = () => (
+
+ ...
+
+);
+```
+
+## Internationalization
+
+The `locale` and `messages` props let you translate the GUI. The [Translation Documentation](./Translation.html) details this process.
+
+## Declaring resources at runtime
+
+You might want to dynamically define the resources when the app starts. The `` component accepts a function as its child and this function can return a Promise. If you also defined an `authProvider`, the function will receive the result of a call to `authProvider` with the `AUTH_GET_PERMISSIONS` type (you can read more about this in the [Authorization](./Authorization.html) chapter).
+
+For instance, getting the resource from an API might look like:
+
+```js
+import React from 'react';
+
+import { Admin, Resource } from 'react-admin';
+import simpleRestProvider from 'ra-data-simple-rest';
+
+import { PostList } from './posts';
+import { CommentList } from './comments';
+
+const knownResources = [
+ ,
+ ,
+];
+
+const fetchResources = permissions =>
+ fetch('https://myapi/resources', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(permissions),
+ })
+ .then(response => response.json())
+ .then(json => knownResources.filter(resource => json.resources.includes(resource.props.name)));
+
+const App = () => (
+
+ {fetchResources}
+
+);
+```
+
+## Using react-admin without `` and ``
+
+Using `` and `` is completely optional. If you feel like bootstrapping a redux app yourself, it's totally possible. Head to [Including in another app](./CustomApp.html) for a detailed how-to.
diff --git a/docs/authentication.md b/docs/authentication.md
new file mode 100644
index 0000000..acad617
--- /dev/null
+++ b/docs/authentication.md
@@ -0,0 +1,348 @@
+---
+id: authentication
+title: Authentication
+---
+
+
+
+React-admin lets you secure your admin app with the authentication strategy of your choice. Since there are many different possible strategies (Basic Auth, JWT, OAuth, etc.), react-admin simply provides hooks to execute your own authentication code.
+
+By default, an react-admin app doesn't require authentication. But if the REST API ever returns a 401 (Unauthorized) or a 403 (Forbidden) response, then the user is redirected to the `/login` route. You have nothing to do - it's already built in.
+
+## Configuring the Auth Provider
+
+By default, the `/login` route renders a special component called `Login`, which displays a login form asking for username and password.
+
+
+
+What this form does upon submission depends on the `authProvider` prop of the `` component. This function receives authentication requests `(type, params)`, and should return a Promise. `Login` calls `authProvider` with the `AUTH_LOGIN` type, and `{ login, password }` as parameters. It's the ideal place to authenticate the user, and store their credentials.
+
+For instance, to query an authentication route via HTTPS and store the credentials (a token) in local storage, configure `authProvider` as follows:
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN } from 'react-admin';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ const { username, password } = params;
+ const request = new Request('https://mydomain.com/authenticate', {
+ method: 'POST',
+ body: JSON.stringify({ username, password }),
+ headers: new Headers({ 'Content-Type': 'application/json' }),
+ })
+ return fetch(request)
+ .then(response => {
+ if (response.status < 200 || response.status >= 300) {
+ throw new Error(response.statusText);
+ }
+ return response.json();
+ })
+ .then(({ token }) => {
+ localStorage.setItem('token', token);
+ });
+ }
+ return Promise.resolve();
+}
+```
+
+**Tip**: It's a good idea to store credentials in `localStorage`, to avoid reconnection when opening a new browser tab. But this makes your application [open to XSS attacks](http://www.redotheweb.com/2015/11/09/api-security.html), so you'd better double down on security, and add an `httpOnly` cookie on the server side, too.
+
+Then, pass this client to the `` component:
+
+```jsx
+// in src/App.js
+import authProvider from './authProvider';
+
+const App = () => (
+
+ ...
+
+);
+```
+
+Upon receiving a 403 response, the admin app shows the Login page. `authProvider` is now called when the user submits the login form. Once the promise resolves, the login form redirects to the previous page, or to the admin index if the user just arrived.
+
+## Sending Credentials to the API
+
+To use the credentials when calling a data provider, you have to tweak, this time, the `dataProvider` function. As explained in the [Data providers documentation](DataProviders.md#adding-custom-headers), `simpleRestProvider` and `jsonServerProvider` take an `httpClient` as second parameter. That's the place where you can change request headers, cookies, etc.
+
+For instance, to pass the token obtained during login as an `Authorization` header, configure the Data Provider as follows:
+
+```jsx
+import { fetchUtils, Admin, Resource } from 'react-admin';
+import simpleRestProvider from 'ra-data-simple-rest';
+
+const httpClient = (url, options = {}) => {
+ if (!options.headers) {
+ options.headers = new Headers({ Accept: 'application/json' });
+ }
+ const token = localStorage.getItem('token');
+ options.headers.set('Authorization', `Bearer ${token}`);
+ return fetchUtils.fetchJson(url, options);
+}
+const dataProvider = simpleRestProvider('http://localhost:3000', httpClient);
+
+const App = () => (
+
+ ...
+
+);
+```
+
+If you have a custom REST client, don't forget to add credentials yourself.
+
+## Adding a Logout Button
+
+If you provide an `authProvider` prop to ``, react-admin displays a logout button in the top bar (or in the menu on mobile). When the user clicks on the logout button, this calls the `authProvider` with the `AUTH_LOGOUT` type and removes potentially sensitive data from the redux store. When resolved, the user gets redirected to the login page.
+
+For instance, to remove the token from local storage upon logout:
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT } from 'react-admin';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ // ...
+ }
+ if (type === AUTH_LOGOUT) {
+ localStorage.removeItem('token');
+ return Promise.resolve();
+ }
+ return Promise.resolve();
+};
+```
+
+
+
+The `authProvider` is also a good place to notify the authentication API that the user credentials are no longer valid after logout.
+
+## Catching Authentication Errors On The API
+
+Even though a user may be authenticated on the client-side, their credentials may no longer be valid server-side (e.g. if the token is only valid for a couple weeks). In that case, the API usually answers to all REST requests with an error code 401 or 403 - but what about *your* API?
+
+Fortunately, each time the API returns an error, the `authProvider` is called with the `AUTH_ERROR` type. Once again, it's up to you to decide which HTTP status codes should let the user continue (by returning a resolved promise) or log them out (by returning a rejected promise).
+
+For instance, to redirect the user to the login page for both 401 and 403 codes:
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR } from 'react-admin';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ // ...
+ }
+ if (type === AUTH_LOGOUT) {
+ // ...
+ }
+ if (type === AUTH_ERROR) {
+ const status = params.status;
+ if (status === 401 || status === 403) {
+ localStorage.removeItem('token');
+ return Promise.reject();
+ }
+ return Promise.resolve();
+ }
+ return Promise.resolve();
+};
+```
+
+## Checking Credentials During Navigation
+
+Redirecting to the login page whenever a REST response uses a 401 status code is usually not enough, because react-admin keeps data on the client side, and could display stale data while contacting the server - even after the credentials are no longer valid.
+
+Fortunately, each time the user navigates, react-admin calls the `authProvider` with the `AUTH_CHECK` type, so it's the ideal place to check for credentials.
+
+For instance, to check for the existence of the token in local storage:
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ // ...
+ }
+ if (type === AUTH_LOGOUT) {
+ // ...
+ }
+ if (type === AUTH_ERROR) {
+ // ...
+ }
+ if (type === AUTH_CHECK) {
+ return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
+ }
+ return Promise.reject('Unkown method');
+};
+```
+
+If the promise is rejected, react-admin redirects by default to the `/login` page. You can override where to redirect the user by passing an argument with a `redirectTo` property to the rejected promise:
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ // ...
+ }
+ if (type === AUTH_LOGOUT) {
+ // ...
+ }
+ if (type === AUTH_ERROR) {
+ // ...
+ }
+ if (type === AUTH_CHECK) {
+ return localStorage.getItem('token') ? Promise.resolve() : Promise.reject({ redirectTo: '/no-access' });
+ }
+ return Promise.reject('Unkown method');
+};
+```
+
+**Tip**: For the `AUTH_CHECK` call, the `params` argument contains the `resource` name, so you can implement different checks for different resources:
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ // ...
+ }
+ if (type === AUTH_LOGOUT) {
+ // ...
+ }
+ if (type === AUTH_ERROR) {
+ // ...
+ }
+ if (type === AUTH_CHECK) {
+ const { resource } = params;
+ if (resource === 'posts') {
+ // check credentials for the posts resource
+ }
+ if (resource === 'comments') {
+ // check credentials for the comments resource
+ }
+ }
+ return Promise.reject('Unkown method');
+};
+```
+
+**Tip**: The `authProvider` can only be called with `AUTH_LOGIN`, `AUTH_LOGOUT`, `AUTH_ERROR`, or `AUTH_CHECK`; that's why the final return is a rejected promise.
+
+## Customizing The Login and Logout Components
+
+Using `authProvider` and `checkCredentials` is enough to implement a full-featured authorization system if the authentication relies on a username and password.
+
+But what if you want to use an email instead of a username? What if you want to use a Single-Sign-On (SSO) with a third-party authentication service? What if you want to use two-factor authentication?
+
+For all these cases, it's up to you to implement your own `LoginPage` component, which will be displayed under the `/login` route instead of the default username/password form, and your own `LogoutButton` component, which will be displayed in the sidebar. Pass both these components to the `` component:
+
+**Tip**: Use the `userLogin` and `userLogout` actions in your custom `Login` and `Logout` components.
+
+```jsx
+// in src/MyLoginPage.js
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { userLogin } from 'react-admin';
+
+class MyLoginPage extends Component {
+ submit = (e) => {
+ e.preventDefault();
+ // gather your data/credentials here
+ const credentials = { };
+
+ // Dispatch the userLogin action (injected by connect)
+ this.props.userLogin(credentials);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+};
+
+export default connect(undefined, { userLogin })(MyLoginPage);
+
+// in src/MyLogoutButton.js
+import React from 'react';
+import { connect } from 'react-redux';
+import { Responsive, userLogout } from 'react-admin';
+import MenuItem from '@material-ui/core/MenuItem';
+import Button from '@material-ui/core/Button';
+import ExitIcon from '@material-ui/icons/PowerSettingsNew';
+
+const MyLogoutButton = ({ userLogout, ...rest }) => (
+
+ Logout
+
+ }
+ medium={
+
+ }
+ />
+);
+export default connect(undefined, { userLogout: userLogout() })(MyLogoutButton);
+
+// in src/App.js
+import MyLoginPage from './MyLoginPage';
+import MyLogoutButton from './MyLogoutButton';
+
+const App = () => (
+
+ ...
+
+);
+```
+
+## Restricting Access To A Custom Page
+
+If you add [custom pages](./Actions.md), of if you [create an admin app from scratch](./CustomApp.md), you may need to secure access to pages manually. That's the purpose of the `` component, that you can use as a decorator for your own components.
+
+
+```jsx
+// in src/MyPage.js
+import { withRouter } from 'react-router-dom';
+import { Authenticated } from 'react-admin';
+
+const MyPage = ({ location }) => (
+
+
+ ...
+
+
+);
+
+export default withRouter(MyPage);
+```
+
+
+The `` component calls the `authProvider` function with `AUTH_CHECK` and `authParams`. If the response is a fulfilled promise, the child component is rendered. If the response is a rejected promise, `` redirects to the login form. Upon successful login, the user is redirected to the initial location (that's why it's necessary to get the location from the router).
+
+
+## Redirect After Logout
+
+By default, react-admin redirects the user to '/login' after they log out. This can be changed by passing the url to redirect to as parameter to the `userLogout()` action creator when you `connect` the `MyLogoutButton` component:
+
+```diff
+// in src/MyLogoutButton.js
+// ...
+- export default connect(undefined, { userLogout: userLogout() })(MyLogoutButton);
++ export default connect(undefined, { userLogout: userLogout('/') })(MyLogoutButton);
+```
diff --git a/docs/authorization.md b/docs/authorization.md
new file mode 100644
index 0000000..a6a275d
--- /dev/null
+++ b/docs/authorization.md
@@ -0,0 +1,303 @@
+---
+id: authorization
+title: Authorization
+---
+
+Some applications may require to determine what level of access a particular authenticated user should have to secured resources. Since there are many different possible strategies (single role, multiple roles or rights, etc.), react-admin simply provides hooks to execute your own authorization code.
+
+By default, a react-admin app doesn't require authorization. However, if needed, it will rely on the `authProvider` introduced in the [Authentication](./Authentication.html) section.
+
+## Configuring the Auth Provider
+
+A call to the `authProvider` with the `AUTH_GET_PERMISSIONS` type will be made each time a component requires to check the user's permissions.
+
+Following is an example where the `authProvider` stores the user's role upon authentication, and returns it when called for a permissions check:
+
+
+```jsx
+// in src/authProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_GET_PERMISSIONS } from 'react-admin';
+import decodeJwt from 'jwt-decode';
+
+export default (type, params) => {
+ if (type === AUTH_LOGIN) {
+ const { username, password } = params;
+ const request = new Request('https://mydomain.com/authenticate', {
+ method: 'POST',
+ body: JSON.stringify({ username, password }),
+ headers: new Headers({ 'Content-Type': 'application/json' }),
+ })
+ return fetch(request)
+ .then(response => {
+ if (response.status < 200 || response.status >= 300) {
+ throw new Error(response.statusText);
+ }
+ return response.json();
+ })
+ .then(({ token }) => {
+ const decodedToken = decodeJwt(token);
+ localStorage.setItem('token', token);
+ localStorage.setItem('role', decodedToken.role);
+ });
+ }
+ if (type === AUTH_LOGOUT) {
+ localStorage.removeItem('token');
+ localStorage.removeItem('role');
+ return Promise.resolve();
+ }
+ if (type === AUTH_ERROR) {
+ // ...
+ }
+ if (type === AUTH_CHECK) {
+ return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
+ }
+ if (type === AUTH_GET_PERMISSIONS) {
+ const role = localStorage.getItem('role');
+ return role ? Promise.resolve(role) : Promise.reject();
+ }
+ return Promise.reject('Unkown method');
+};
+```
+
+
+## Restricting Access to Resources or Views
+
+It's possible to restrict access to resources or their views inside the `Admin` component. To do so, you must specify a function as the `Admin` only child. This function will be called with the permissions returned by the `authProvider`.
+
+
+```jsx
+
+ {permissions => [
+ // Restrict access to the edit and remove views to admin only
+ ,
+ // Only include the categories resource for admin users
+ permissions === 'admin'
+ ?
+ : null,
+ ]}
+
+```
+
+
+Note that the function returns an array of React elements. This is required to avoid having to wrap them in a container element which would prevent the `Admin` from working.
+
+**Tip** Even if that's possible, be careful when completely excluding a resource (like with the `categories` resource in this example) as it will prevent you to reference them in the other resource views, too.
+
+## Restricting Access to Fields and Inputs
+
+You might want to display some fields or inputs only to users with specific permissions. Those permissions are retrieved for each route and will provided to your component as a `permissions` prop.
+
+Each route will call the `authProvider` with the `AUTH_GET_PERMISSIONS` type and some parameters including the current location and route parameters. It's up to you to return whatever you need to check inside your component such as the user's role, etc.
+
+Here's an example inside a `Create` view with a `SimpleForm` and a custom `Toolbar`:
+
+
+```jsx
+const UserCreateToolbar = ({ permissions, ...props }) =>
+
+
+ {permissions === 'admin' &&
+ }
+ ;
+
+export const UserCreate = ({ permissions, ...props }) =>
+
+ }
+ defaultValue={{ role: 'user' }}
+ >
+
+ {permissions === 'admin' &&
+ }
+
+ ;
+```
+
+
+**Tip** Note how the `permissions` prop is passed down to the custom `toolbar` component.
+
+This also works inside an `Edition` view with a `TabbedForm`, and you can hide a `FormTab` completely:
+
+
+```jsx
+export const UserEdit = ({ permissions, ...props }) =>
+ } {...props}>
+
+
+ {permissions === 'admin' && }
+
+
+ {permissions === 'admin' &&
+
+
+ }
+
+ ;
+```
+
+
+What about the `List` view, the `DataGrid`, `SimpleList` and `Filter` components? It works there, too.
+
+
+```jsx
+const UserFilter = ({ permissions, ...props }) =>
+
+
+
+ {permissions === 'admin' ? : null}
+ ;
+
+export const UserList = ({ permissions, ...props }) =>
+ }
+ sort={{ field: 'name', order: 'ASC' }}
+ >
+ record.name}
+ secondaryText={record =>
+ permissions === 'admin' ? record.role : null}
+ />
+ }
+ medium={
+
+
+
+ {permissions === 'admin' && }
+ {permissions === 'admin' && }
+
+
+ }
+ />
+ ;
+```
+
+
+**Tip** Note how the `permissions` prop is passed down to the custom `filters` component.
+
+## Restricting Access to Content Inside a Dashboard
+
+The component provided as a [`dashboard`]('./Admin.md#dashboard) will receive the permissions in its props too:
+
+
+```jsx
+// in src/Dashboard.js
+import React from 'react';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import { ViewTitle } from 'react-admin';
+
+export default ({ permissions }) => (
+
+
+ Lorem ipsum sic dolor amet...
+ {permissions === 'admin'
+ ? Sensitive data
+ : null
+ }
+
+);
+```
+
+
+## Restricting Access to Content Inside Custom Pages
+
+You might want to check user permissions inside a [custom pages](./Admin.md#customroutes). You'll have to use the `WithPermissions` component for that. It will ensure the user is authenticated then call the `authProvider` with the `AUTH_GET_PERMISSIONS` type and the `authParams` you specify:
+
+
+```jsx
+// in src/MyPage.js
+import React from 'react';
+import Card from '@material-ui/core/Card';
+import CardContent from '@material-ui/core/CardContent';
+import { ViewTitle, WithPermissions } from 'react-admin';
+import { withRouter } from 'react-router-dom';
+
+const MyPage = ({ permissions }) => (
+
+
+ Lorem ipsum sic dolor amet...
+ {permissions === 'admin'
+ ? Sensitive data
+ : null
+ }
+
+)
+const MyPageWithPermissions = ({ location, match }) => (
+ }
+ />
+);
+
+export default MyPageWithPermissions;
+
+// in src/customRoutes.js
+import React from 'react';
+import { Route } from 'react-router-dom';
+import Foo from './Foo';
+import Bar from './Bar';
+import Baz from './Baz';
+import MyPageWithPermissions from './MyPage';
+
+export default [
+ ,
+ ,
+ ,
+ ,
+];
+```
+
+
+## Restricting Access to Content in Custom Menu
+
+What if you want to check the permissions inside a [custom menu](./Admin.html#menu) ? Much like getting permissions inside a custom page, you'll have to use the `WithPermissions` component:
+
+
+```jsx
+// in src/myMenu.js
+import React from 'react';
+import { connect } from 'react-redux';
+import { MenuItemLink, WithPermissions } from 'react-admin';
+
+const Menu = ({ onMenuClick, logout, permissions }) => (
+
+);
+```
+
diff --git a/docs/creat-edit-view-components.md b/docs/creat-edit-view-components.md
new file mode 100644
index 0000000..25ef643
--- /dev/null
+++ b/docs/creat-edit-view-components.md
@@ -0,0 +1,699 @@
+---
+layout: creat-edit-view-components
+title: and Views
+---
+
+The Create and Edit views both display a form, initialized with an empty record (for the Create view) or with a record fetched from the API (for the Edit view). The `` and `` components then delegate the actual rendering of the form to a form component - usually ``. This form component uses its children ([``](./Inputs.md) components) to render each form input.
+
+
+
+
+
+## The `Create` and `Edit` components
+
+The `` and `` components render the page title and actions, and fetch the record from the data provider. They are not responsible for rendering the actual form - that's the job of their child component (usually ``), to which they pass the `record` as prop.
+
+Here are all the props accepted by the `` and `` components:
+
+* [`title`](#page-title)
+* [`actions`](#actions)
+
+Here is the minimal code necessary to display a form to create and edit comments:
+
+```jsx
+// in src/App.js
+import React from 'react';
+import { Admin, Resource } from 'react-admin';
+import jsonServerProvider from 'ra-data-json-server';
+
+import { PostCreate, PostEdit } from './posts';
+
+const App = () => (
+
+
+
+);
+
+export default App;
+
+// in src/posts.js
+import React from 'react';
+import { Create, Edit, SimpleForm, DisabledInput, TextInput, DateInput, LongTextInput, ReferenceManyField, Datagrid, TextField, DateField, EditButton } from 'react-admin';
+import RichTextInput from 'ra-input-rich-text';
+
+export const PostCreate = (props) => (
+
+
+
+
+
+
+
+
+);
+
+export const PostEdit = (props) => (
+ } {...props}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+```
+
+That's enough to display the post edit form:
+
+
+
+**Tip**: You might find it cumbersome to repeat the same input components for both the `` and the `` view. In practice, these two views almost never have exactly the same form inputs. For instance, in the previous snippet, the `` views shows related comments to the current post, which makes no sense for a new post. Having two separate sets of input components for the two views is therefore a deliberate choice. However, if you have the same set of input components, export them as a custom Form component to avoid repetition.
+
+ `` accepts a `record` prop, to initialize the form based on an value object.
+
+### Page Title
+
+By default, the title for the Create view is "Create [resource_name]", and the title for the Edit view is "Edit [resource_name] #[record_id]".
+
+You can customize this title by specifying a custom `title` prop:
+
+```jsx
+export const PostEdit = (props) => (
+
+ ...
+
+);
+```
+
+More interestingly, you can pass a component as `title`. React-admin clones this component and, in the ``, injects the current `record`. This allows to customize the title according to the current record:
+
+```jsx
+const PostTitle = ({ record }) => {
+ return Post {record ? `"${record.title}"` : ''};
+};
+export const PostEdit = (props) => (
+ } {...props}>
+ ...
+
+);
+```
+
+### Actions
+
+You can replace the list of default actions by your own element using the `actions` prop:
+
+```jsx
+import Button from '@material-ui/core/Button';
+import {
+ CardActions,
+ ListButton,
+ ShowButton,
+ DeleteButton,
+ RefreshButton,
+} from 'react-admin';
+
+const PostEditActions = ({ basePath, data, resource }) => (
+
+
+
+
+
+ {/* Add your custom actions */}
+
+
+);
+
+export const PostEdit = (props) => (
+ } {...props}>
+ ...
+
+);
+```
+
+Using a custom `EditActions` component also allow to remove the `` if you want to prevent deletions from the admin.
+
+## Prefilling a `Create` Record
+
+By default, the `` view starts with an empty `record`. You can pass a custom `record` object to start with preset values:
+
+```jsx
+const commentDefaultValue = { nb_views: 0 };
+export const CommentCreate = (props) => (
+
+
+
+
+
+
+
+);
+```
+
+While using the `record` to set default values works here, it doesn't work with ``. So it's recommended to use [the `defaultValue` prop in the Form component](#default-values) instead.
+
+However, there is a valid use case for presetting the `record` prop: to prepopulate a record based on a related record. For instance, in a `PostShow` component, you may want to display a button to create a comment related to the current post, that would lead to a `CommentCreate` page where the `post_id` is preset.
+
+To enable this, you must first update the `CommentCreate` component to read the record from the `location` object (which is injected by react-router):
+
+```diff
+const commentDefaultValue = { nb_views: 0 };
+-export const CommentCreate = (props) => (
++export const CommentCreate = ({ location, ...props}) => (
+-
++
+
+
+
+
+
+
+);
+```
+
+To set this `location.state`, you have to create a link or a button using react-router's `` component:
+
+```jsx
+// in PostShow.js
+import Button from '@material-ui/core/Button';
+import { Link } from 'react-router-dom';
+
+const CreateRelatedCommentButton = ({ record }) => (
+
+);
+
+export default PostShow = props => (
+
+
+ ...
+
+
+
+)
+```
+
+**Tip**: To style the button with the main color from the material-ui theme, use the `Link` component from the `react-admin` package rather than the one from `react-router`.
+
+## The `SimpleForm` component
+
+The `` component receives the `record` as prop from its parent component. It is responsible for rendering the actual form. It is also responsible for validating the form data. Finally, it receives a `handleSubmit` function as prop, to be called with the updated record as argument when the user submits the form.
+
+The `` renders its child components line by line (within `
` components). It uses `redux-form`.
+
+
+
+By default the `` submits the form when the user presses `ENTER`. If you want
+to change this behaviour you can pass `false` for the `submitOnEnter` property, and the user will only be able to submit by pressing the save button. This can be useful e.g. if you have an input widget using `ENTER` for a special function.
+
+Here are all the props accepted by the `` component:
+
+* [`defautValue`](#default-values)
+* [`validate`](#validation)
+* [`submitOnEnter`](#submit-on-enter)
+* [`redirect`](#redirection-after-submission)
+* [`toolbar`](#toolbar)
+* `save`: The function invoked when the form is submitted. This is passed automatically by `react-admin` when the form component is used inside `Create` and `Edit` components.
+* `saving`: A boolean indicating whether a save operation is ongoing. This is passed automatically by `react-admin` when the form component is used inside `Create` and `Edit` components.
+* `form`: The name of the [`redux-form`](https://redux-form.com/7.4.2/docs/api/reduxform.md/#-code-form-string-code-required-). It defaults to `record-form` and should only be modified when using the `SimpleForm` outside of a `Create` or `Edit` component.
+
+```jsx
+export const PostCreate = (props) => (
+
+
+
+
+
+
+
+);
+```
+
+## The `TabbedForm` component
+
+Just like ``, `` receives the `record` prop, renders the actual form, and handles form validation on submit. However, the `` component renders inputs grouped by tab. The tabs are set by using `` components, which expect a `label` and an `icon` prop.
+
+
+
+By default the `` submits the form when the user presses `ENTER`, if you want
+to change this behaviour you can pass `false` for the `submitOnEnter` property.
+
+Here are all the props accepted by the `` component:
+
+* [`defautValue`](#default-values)
+* [`validate`](#validation)
+* [`submitOnEnter`](#submit-on-enter)
+* [`redirect`](#redirection-after-submission)
+* [`toolbar`](#toolbar)
+* `save`: The function invoked when the form is submitted. This is passed automatically by `react-admin` when the form component is used inside `Create` and `Edit` components.
+* `saving`: A boolean indicating whether a save operation is ongoing. This is passed automatically by `react-admin` when the form component is used inside `Create` and `Edit` components.
+* `form`: The name of the [`redux-form`](https://redux-form.com/7.4.2/docs/api/reduxform.md/#-code-form-string-code-required-). It defaults to `record-form` and should only be modified when using the `TabbedForm` outside of a `Create` or `Edit` component.
+
+
+```jsx
+import { TabbedForm, FormTab } from 'react-admin'
+
+export const PostEdit = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+```
+
+## Default Values
+
+To define default values, you can add a `defaultValue` prop to form components (``, ``, etc.), or add a `defaultValue` to individual input components. Let's see each of these options.
+
+### Global Default Value
+
+The value of the form `defaultValue` prop can be an object, or a function returning an object, specifying default values for the created record. For instance:
+
+```jsx
+const postDefaultValue = { created_at: new Date(), nb_views: 0 };
+export const PostCreate = (props) => (
+
+
+
+
+
+
+
+);
+```
+
+**Tip**: You can include properties in the form `defaultValue` that are not listed as input components, like the `created_at` property in the previous example.
+
+### Per Input Default Value
+
+Alternatively, you can specify a `defaultValue` prop directly in `` components. Just like for form-level default values, an input-level default value can be a scalar, or a function returning a scalar. React-admin will merge the input default values with the form default value (input > form):
+
+```jsx
+export const PostCreate = (props) => (
+
+
+ uuid()}/>
+
+
+
+
+
+);
+```
+
+## Validation
+
+React-admin relies on [redux-form](http://redux-form.com/) for the validation.
+
+To validate values submitted by a form, you can add a `validate` prop to the form component, to individual inputs, or even mix both approaches.
+
+### Global Validation
+
+The value of the form `validate` prop must be a function taking the record as input, and returning an object with error messages indexed by field. For instance:
+
+```jsx
+const validateUserCreation = (values) => {
+ const errors = {};
+ if (!values.firstName) {
+ errors.firstName = ['The firstName is required'];
+ }
+ if (!values.age) {
+ errors.age = ['The age is required'];
+ } else if (values.age < 18) {
+ errors.age = ['Must be over 18'];
+ }
+ return errors
+};
+
+export const UserCreate = (props) => (
+
+
+
+
+
+
+);
+```
+
+**Tip**: The props you pass to `` and `` end up as `reduxForm()` parameters. This means that, in addition to `validate`, you can also pass `warn` or `asyncValidate` functions. Read the [`reduxForm()` documentation](http://redux-form.com/6.5.0/docs/api/ReduxForm.md/) for details.
+
+### Per Input Validation: Function Validator
+
+Alternatively, you can specify a `validate` prop directly in `` components, taking either a function, or an array of functions. These functions should return `undefined` when there is no error, or an error string.
+
+```jsx
+const required = (message = 'Required') =>
+ value => value ? undefined : message;
+const maxLength = (max, message = 'Too short') =>
+ value => value && value.length > max ? message : undefined;
+const number = (message = 'Must be a number') =>
+ value => value && isNaN(Number(value)) ? message : undefined;
+const minValue = (min, message = 'Too small') =>
+ value => value && value < min ? message : undefined;
+
+const ageValidation = (value, allValues) => {
+ if (!value) {
+ return 'The age is required';
+ }
+ if (value < 18) {
+ return 'Must be over 18';
+ }
+ return [];
+}
+
+const validateFirstName = [required(), maxLength(15)];
+const validateAge = [required(), number(), ageValidation];
+
+export const UserCreate = (props) => (
+
+
+
+
+
+
+);
+```
+
+React-admin will combine all the input-level functions into a single function looking just like the previous one.
+
+Input validation functions receive the current field value, and the values of all fields of the current record. This allows for complex validation scenarios (e.g. validate that two passwords are the same).
+
+**Tip**: Validator functions receive the form `props` as third parameter, including the `translate` function. This lets you build internationalized validators:
+
+```jsx
+const required = (message = 'myroot.validation.required') =>
+ (value, allValues, props) => value ? undefined : props.translate(message);
+```
+
+**Tip**: Make sure to define validation functions or array of functions in a variable, instead of defining them directly in JSX. This can result in a new function or array at every render, and trigger infinite rerender.
+
+
+```jsx
+const validateStock = [required(), number(), minValue(0)];
+
+export const ProductEdit = ({ ...props }) => (
+
+
+ ...
+ {/* do this */}
+
+ {/* don't do that */}
+
+ ...
+
+
+);
+```
+
+
+**Tip**: The props of your Input components are passed to a redux-form `` component. So in addition to `validate`, you can also use `warn`.
+
+**Tip**: You can use *both* Form validation and input validation.
+
+### Built-in Field Validators
+
+React-admin already bundles a few validator functions, that you can just require, and use as input-level validators:
+
+* `required(message)` if the field is mandatory,
+* `minValue(min, message)` to specify a minimum value for integers,
+* `maxValue(max, message)` to specify a maximum value for integers,
+* `minLength(min, message)` to specify a minimum length for strings,
+* `maxLength(max, message)` to specify a maximum length for strings,
+* `number(message)` to check that the input is a valid number,
+* `email(message)` to check that the input is a valid email address,
+* `regex(pattern, message)` to validate that the input matches a regex,
+* `choices(list, message)` to validate that the input is within a given list,
+
+Example usage:
+
+```jsx
+import {
+ required,
+ minLength,
+ maxLength,
+ minValue,
+ maxValue,
+ number,
+ regex,
+ email,
+ choices
+} from 'react-admin';
+
+const validateFirstName = [required(), minLength(2), maxLength(15)];
+const validateEmail = email();
+const validateAge = [number(), minValue(18)];
+const validateZipCode = regex(/^\d{5}$/, 'Must be a valid Zip Code');
+const validateSex = choices(['m', 'f'], 'Must be Male or Female');
+
+export const UserCreate = (props) => (
+
+
+
+
+
+
+
+
+
+);
+```
+
+**Tip**: If you pass a function as a message, react-admin calls this function with `{ args, value, values,translate, ...props }` as argument. For instance:
+
+```jsx
+const message = ({ translate }) => translate('myroot.validation.email_invalid');
+const validateEmail = email(message);
+```
+
+## Submit On Enter
+
+By default, pressing `ENTER` in any of the form fields submits the form - this is the expected behavior in most cases. However, some of your custom input components (e.g. Google Maps widget) may have special handlers for the `ENTER` key. In that case, to disable the automated form submission on enter, set the `submitOnEnter` prop of the form component to `false`:
+
+```jsx
+export const PostEdit = (props) => (
+
+
+ ...
+
+
+);
+```
+
+## Redirection After Submission
+
+By default:
+
+- Submitting the form in the `` view redirects to the `` view
+- Submitting the form in the `` view redirects to the `` view
+
+You can customize the redirection by setting the `redirect` prop of the form component. Possible values are "edit", "show", "list", and `false` to disable redirection. You may also specify a custom path such as `/my-custom-route`. For instance, to redirect to the `` view after edition:
+
+```jsx
+export const PostEdit = (props) => (
+
+
+ ...
+
+
+);
+```
+
+You can also pass a custom route (e.g. "/home") or a function as `redirect` prop value. For example, if you want to redirect to a page related to the current object:
+
+```jsx
+// redirect to the related Author show page
+const redirect = (basePath, id, data) => `/author/${data.author_id}/show`;
+
+export const PostEdit = (props) => {
+
+
+ ...
+
+
+);
+```
+
+This affects both the submit button, and the form submission when the user presses `ENTER` in one of the form fields.
+
+## Toolbar
+
+At the bottom of the form, the toolbar displays the submit button. You can override this component by setting the `toolbar` prop, to display the buttons of your choice.
+
+The most common use case is to display two submit buttons in the `` view:
+
+- one that creates and redirects to the `` view of the new resource, and
+- one that redirects to a blank `` view after creation (allowing bulk creation)
+
+
+
+For that use case, use the `` component with a custom `redirect` prop:
+
+```jsx
+import { Edit, SimpleForm, SaveButton, Toolbar } from 'react-admin';
+
+const PostCreateToolbar = props => (
+
+
+
+
+);
+
+export const PostEdit = (props) => (
+
+ } redirect="show">
+ ...
+
+
+);
+```
+
+Here are the props received by the `Toolbar` component when passed as the `toolbar` prop of the `SimpleForm` or `TabbedForm` components:
+
+* `handleSubmitWithRedirect`: The function to call in order to submit the form. It accepts a single parameter overriding the form's default redirect.
+* `invalid`: A boolean indicating whether the form is invalid
+* `pristine`: A boolean indicating whether the form is pristine (eg: no inputs have been changed yet)
+* `redirect`: The default form's redirect
+* `saving`: A boolean indicating whether a save operation is ongoing.
+* `submitOnEnter`: A boolean indicating whether the form should be submitted when pressing `enter`
+
+**Tip**: Use react-admin's `` component instead of material-ui's `` component. The former builds up on the latter, and adds support for an alternative mobile layout (and is therefore responsive).
+
+**Tip**: Don't forget to also set the `redirect` prop of the Form component to handle submission by the `ENTER` key.
+
+## Customizing Input Container Styles
+
+The input components are wrapped inside a `div` to ensure a good looking form by default. You can pass a `formClassName` prop to the input components to customize the style of this `div`. For example, here is how to display two inputs on the same line:
+
+
+```jsx
+const styles = {
+ inlineBlock: { display: 'inline-flex', marginRight: '1rem' },
+};
+export const UserEdit = withStyles(styles)(({ classes, ...props }) => (
+
+
+
+
+ {/* This input will be display below the two first ones */}
+
+
+
+```
+
+
+## Displaying Fields or Inputs depending on the user permissions
+
+You might want to display some fields, inputs or filters only to users with specific permissions. Those permissions are retrieved for each route and will provided to your component as a `permissions` prop.
+
+Each route will call the `authProvider` with the `AUTH_GET_PERMISSIONS` type and some parameters including the current location and route parameters. It's up to you to return whatever you need to check inside your component such as the user's role, etc.
+
+Here's an example inside a `Create` view with a `SimpleForm` and a custom `Toolbar`:
+
+
+```jsx
+const UserCreateToolbar = ({ permissions, ...props }) =>
+
+
+ {permissions === 'admin' &&
+ }
+ ;
+
+export const UserCreate = ({ permissions, ...props }) =>
+
+ }
+ defaultValue={{ role: 'user' }}
+ >
+
+ {permissions === 'admin' &&
+ }
+
+ ;
+```
+
+
+**Tip**: Note how the `permissions` prop is passed down to the custom `toolbar` component.
+
+This also works inside an `Edition` view with a `TabbedForm`, and you can hide a `FormTab` completely:
+
+
+```jsx
+export const UserEdit = ({ permissions, ...props }) =>
+ } {...props}>
+
+
+ {permissions === 'admin' && }
+
+
+ {permissions === 'admin' &&
+
+
+ }
+
+ ;
+```
+
diff --git a/docs/custom-app.md b/docs/custom-app.md
new file mode 100644
index 0000000..f83b58b
--- /dev/null
+++ b/docs/custom-app.md
@@ -0,0 +1,110 @@
+---
+id: custom-app
+title: Including the Admin in Another App
+---
+
+The `` tag is a great shortcut got be up and running with react-admin in minutes. However, in many cases, you will want to embed the admin in another application, or customize the admin deeply. Fortunately, you can do all the work that `` does on any React application.
+
+Beware that you need to know about [redux](http://redux.js.org/), [react-router](https://github.com/reactjs/react-router), and [redux-saga](https://github.com/yelouafi/redux-saga) to go further.
+
+**Tip**: Before going for the Custom App route, explore all the options of [the `` component](./Admin.md). They allow you to add custom routes, custom reducers, custom sagas, and customize the layout.
+
+Here is the main code for bootstrapping a barebones react-admin application with 3 resources: `posts`, `comments`, and `users`:
+
+```jsx
+// in src/App.js
+import React from 'react';
+import PropTypes from 'prop-types';
+import { render } from 'react-dom';
+
+// redux, react-router, redux-form, saga, and material-ui
+// form the 'kernel' on which react-admin runs
+import { combineReducers, createStore, compose, applyMiddleware } from 'redux';
+import { Provider } from 'react-redux';
+import createHistory from 'history/createHashHistory';
+import { Switch, Route } from 'react-router-dom';
+import { ConnectedRouter, routerReducer, routerMiddleware } from 'react-router-redux';
+import { reducer as formReducer } from 'redux-form';
+import createSagaMiddleware from 'redux-saga';
+import { MuiThemeProvider } from '@material-ui/core/styles';
+import AppBar from '@material-ui/core/AppBar';
+import Toolbar from '@material-ui/core/Toolbar';
+import Typography from '@material-ui/core/Typography';
+
+// prebuilt react-admin features
+import {
+ adminReducer,
+ i18nReducer,
+ adminSaga,
+ TranslationProvider,
+} from 'react-admin';
+import restProvider from 'ra-data-simple-rest';
+import defaultMessages from 'ra-language-english';
+
+// your app components
+import Dashboard from './Dashboard';
+import { PostList, PostCreate, PostEdit, PostShow } from './Post';
+import { CommentList, CommentEdit, CommentCreate } from './Comment';
+import { UserList, UserEdit, UserCreate } from './User';
+// your app labels
+import messages from './i18n';
+
+// create a Redux app
+const reducer = combineReducers({
+ admin: adminReducer,
+ i18n: i18nReducer('en', messages['en']),
+ form: formReducer,
+ routing: routerReducer,
+});
+const sagaMiddleware = createSagaMiddleware();
+const history = createHistory();
+const store = createStore(reducer, undefined, compose(
+ applyMiddleware(sagaMiddleware, routerMiddleware(history)),
+ window.devToolsExtension ? window.devToolsExtension() : f => f,
+));
+
+// side effects
+const dataProvider = restProvider('http://path.to.my.api/');
+const authProvider = () => Promise.resolve();
+const i18nProvider = locale => {
+ if (locale !== 'en') {
+ return messages[locale];
+ }
+ return defaultMessages;
+};
+sagaMiddleware.run(adminSaga(dataProvider, authProvider, i18nProvider));
+
+// bootstrap redux and the routes
+const App = () => (
+
+
+
+
+
+
+
+ My admin
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+
+
+);
+```
+
+This application has no sidebar, no theming, no [auth control](./Authentication.md#restricting-access-to-a-custom-page) - it's up to you to add these. From there on, you can customize pretty much anything you want.
diff --git a/docs/data-providers.md b/docs/data-providers.md
new file mode 100644
index 0000000..4ec355d
--- /dev/null
+++ b/docs/data-providers.md
@@ -0,0 +1,664 @@
+---
+id: data-providers
+title: Data Providers
+---
+
+React-admin can communicate with any API, whether it uses REST, GraphQL, or even SOAP, regardless of the dialect it uses. For REST servers, it can be [JSON API](http://jsonapi.org/), [HAL](http://stateless.co/hal_specification.html), [OData](http://www.odata.org/) or a custom dialect. The only thing react-admin needs is a Data Provider function. This is the place to translate data queries to HTTP requests, and HTTP responses to data responses.
+
+
+
+The `dataProvider` parameter of the `` component must be a function with the following signature:
+
+```jsx
+/**
+ * Query a data provider and return a promise for a response
+ *
+ * @example
+ * dataProvider(GET_ONE, 'posts', { id: 123 })
+ * => Promise.resolve({ data: { id: 123, title: "hello, world" } })
+ *
+ * @param {string} type Request type, e.g GET_LIST
+ * @param {string} resource Resource name, e.g. "posts"
+ * @param {Object} payload Request parameters. Depends on the action type
+ * @returns {Promise} the Promise for a response
+ */
+const dataProvider = (type, resource, params) => new Promise();
+```
+
+You can find a Data Provider example implementation in [`packages/ra-data-simple-rest/src/index.js`](https://github.com/marmelab/react-admin/blob/master/packages/ra-data-simple-rest/src/index.js);
+
+The `dataProvider` is also the ideal place to add custom HTTP headers, authentication, etc.
+
+## Available Providers
+
+The react-admin project includes 4 Data Providers:
+
+* Simple REST: [marmelab/ra-data-simple-rest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-simple-rest) ([read more below](#simple-rest)). It serves mostly as an example. Incidentally, it is compatible with the [FakeRest](https://github.com/marmelab/FakeRest) API.
+* **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). Great for prototyping an admin over a yet-to-be-developed REST API.
+* [Graphcool](https://www.graph.cool/): [marmelab/ra-data-graphcool](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphcool). A provider for GraphQL servers following the Graphcool convention. Incidentally, this package builds up on [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql), which lets you develop providers for other GraphQL conventions.
+* Local JSON: [marmelab/ra-data-fakerest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-fakerest). Based on a local object, it doesn't even use HTTP. Use it for testing purposes.
+
+You can find Data Providers for various backends in third-party repositories:
+
+* **[DynamoDb](https://github.com/abiglobalhealth/aor-dynamodb-client)**: [abiglobalhealth/aor-dynamodb-client](https://github.com/abiglobalhealth/aor-dynamodb-client)
+* **[Epilogue](https://github.com/dchester/epilogue)**: [dunghuynh/aor-epilogue-client](https://github.com/dunghuynh/aor-epilogue-client)
+* **[Feathersjs](http://www.feathersjs.com/)**: [josx/aor-feathers-client](https://github.com/josx/aor-feathers-client)
+* **[Firebase](https://firebase.google.com/)**: [sidferreira/aor-firebase-client](https://github.com/sidferreira/aor-firebase-client)
+* **[JSON API](http://jsonapi.org/)**: [moonlight-labs/aor-jsonapi-client](https://github.com/moonlight-labs/aor-jsonapi-client)
+* **[Loopback](http://loopback.io/)**: [kimkha/aor-loopback](https://github.com/kimkha/aor-loopback)
+* **[Parse Server](https://github.com/ParsePlatform/parse-server)**: [leperone/aor-parseserver-client](https://github.com/leperone/aor-parseserver-client)
+* **[PostgREST](http://postgrest.com/en/v0.4/)**: [tomberek/aor-postgrest-client](https://github.com/tomberek/aor-postgrest-client)
+* **[Xmysql](https://github.com/o1lab/xmysql)**: [soaserele/aor-xmysql](https://github.com/soaserele/aor-xmysql)
+
+If you've written a Data Provider for another backend, and open-sourced it, please help complete this list with your package.
+
+## Usage
+
+As an example, let's focus on the Simple REST data provider. It fits REST APIs using simple GET parameters for filters and sorting.
+
+Install the `ra-data-simple-rest` package to use this provider.
+
+```sh
+npm install ra-data-simple-rest
+```
+
+Then, initialize the provider with the RESt backend URL, and pass the result to the `dataProvider` prop of the `` component:
+
+```jsx
+// in src/App.js
+import React from 'react';
+import { Admin, Resource } from 'react-admin';
+import simpleRestProvider from 'ra-data-simple-rest';
+
+import { PostList } from './posts';
+
+const App = () => (
+
+
+
+);
+
+export default App;
+```
+
+Here is how this provider maps request types to API calls:
+
+| Request type | API calls
+|----------------------|----------------------------------------------------------------
+| `GET_LIST` | `GET http://my.api.url/posts?sort=['title','ASC']&range=[0, 24]&filter={title:'bar'}`
+| `GET_ONE` | `GET http://my.api.url/posts/123`
+| `CREATE` | `POST http://my.api.url/posts/123`
+| `UPDATE` | `PUT http://my.api.url/posts/123`
+| `UPDATE_MANY` | Multiple calls to `PUT http://my.api.url/posts/123`
+| `DELETE` | `DELETE http://my.api.url/posts/123`
+| `DELETE_MANY` | Multiple calls to `DELETE http://my.api.url/posts/123`
+| `GET_MANY` | `GET http://my.api.url/posts?filter={ids:[123,456,789]}`
+| `GET_MANY_REFERENCE` | `GET http://my.api.url/posts?filter={author_id:345}`
+
+**Note**: The simple REST client expects the API to include a `Content-Range` header in the response to `GET_LIST` calls. The value must be the total number of resources in the collection. This allows react-admin to know how many pages of resources there are in total, and build the pagination controls.
+
+```
+Content-Range: posts 0-24/319
+```
+
+If your API is on another domain as the JS code, you'll need to whitelist this header with an `Access-Control-Expose-Headers` [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) header.
+
+```
+Access-Control-Expose-Headers: Content-Range
+```
+
+## Adding Custom Headers
+
+The `simpleRestProvider` function accepts an HTTP client function as second argument. By default, it uses react-admin's `fetchUtils.fetchJson()` as HTTP client. It's similar to HTML5 `fetch()`, except it handles JSON decoding and HTTP error codes automatically.
+
+That means that if you need to add custom headers to your requests, you can just *wrap* the `fetchJson()` call inside your own function:
+
+```jsx
+import { fetchUtils, Admin, Resource } from 'react-admin';
+import simpleRestProvider from 'ra-data-simple-rest';
+
+const httpClient = (url, options = {}) => {
+ if (!options.headers) {
+ options.headers = new Headers({ Accept: 'application/json' });
+ }
+ // add your own headers here
+ options.headers.set('X-Custom-Header', 'foobar');
+ return fetchUtils.fetchJson(url, options);
+}
+const dataProvider = simpleRestProvider('http://path.to.my.api/', httpClient);
+
+const App = () => (
+
+
+
+);
+```
+
+Now all the requests to the REST API will contain the `X-Custom-Header: foobar` header.
+
+**Tip**: The most common usage of custom headers is for authentication. `fetchJson` has built-on support for the `Authorization` token header:
+
+```jsx
+const httpClient = (url, options = {}) => {
+ options.user = {
+ authenticated: true,
+ token: 'SRTRDFVESGNJYTUKTYTHRG'
+ }
+ return fetchUtils.fetchJson(url, options);
+}
+const dataProvider = simpleRestProvider('http://path.to.my.api/', httpClient);
+```
+
+Now all the requests to the REST API will contain the `Authorization: SRTRDFVESGNJYTUKTYTHRG` header.
+
+## Decorating your Data Provider (Example of File Upload)
+
+Instead of writing your own Data Provider, you can enhance the capabilities of an existing data provider. You can even restrict the customization on a given resource.
+
+For instance, if you want to use upload components (such as `` one), you can decorate the provider the following way:
+
+```jsx
+// in addUploadFeature.js
+/**
+ * Convert a `File` object returned by the upload input into a base 64 string.
+ * That's not the most optimized way to store images in production, but it's
+ * enough to illustrate the idea of data provider decoration.
+ */
+const convertFileToBase64 = file => new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file.rawFile);
+
+ reader.onload = () => resolve(reader.result);
+ reader.onerror = reject;
+});
+
+/**
+ * For posts update only, convert uploaded image in base 64 and attach it to
+ * the `picture` sent property, with `src` and `title` attributes.
+ */
+const addUploadFeature = requestHandler => (type, resource, params) => {
+ if (type === 'UPDATE' && resource === 'posts') {
+ if (params.data.pictures && params.data.pictures.length) {
+ // only freshly dropped pictures are instance of File
+ const formerPictures = params.data.pictures.filter(p.rawFile => !(p instanceof File));
+ const newPictures = params.data.pictures.filter(p.rawFile => p instanceof File);
+
+ return Promise.all(newPictures.map(convertFileToBase64))
+ .then(base64Pictures => base64Pictures.map(picture64 => ({
+ src: picture64,
+ title: `${params.data.title}`,
+ })))
+ .then(transformedNewPictures => requestHandler(type, resource, {
+ ...params,
+ data: {
+ ...params.data,
+ pictures: [...transformedNewPictures, ...formerPictures],
+ },
+ }));
+ }
+ }
+ // for other request types and reources, fall back to the defautl request handler
+ return requestHandler(type, resource, params);
+};
+
+export default addUploadFeature;
+```
+
+To enhance a provider with the upload feature, compose `addUploadFeature` function with the data provider function:
+
+```jsx
+import simpleRestProvider from 'ra-data-simple-rest';
+import addUploadFeature from './addUploadFeature';
+
+const dataProvider = simpleRestProvider('http://path.to.my.api/');
+const uploadCapableDataProvider = addUploadFeature(dataProvider);
+
+const App = () => (
+
+
+
+);
+```
+
+## Writing Your Own Data Provider
+
+Quite often, there is no Data Provider that suits you API - either in the core providers, or in the third-party providers. In such cases, you'll have to write your own Data Provider.
+
+A Data Provider is a function that receives a request, and returns a promise for a response. Both the request and the response format are standardized.
+
+```jsx
+/**
+ * Query a data provider and return a promise for a response
+ *
+ * @example
+ * dataProvider(GET_ONE, 'posts', { id: 123 })
+ * => Promise.resolve({ data: { id: 123, title: "hello, world" } })
+ *
+ * @param {string} type Request type, e.g GET_LIST
+ * @param {string} resource Resource name, e.g. "posts"
+ * @param {Object} payload Request parameters. Depends on the action type
+ * @returns {Promise} the Promise for a response
+ */
+const dataProvider = (type, resource, params) => new Promise();
+```
+
+When you write a Data provider, your job is to route requests to your API backend(s), then transform their response to match the format returned by the Data Provider.
+
+### Request Format
+
+Data queries require a *type* (e.g. `GET_ONE`), a *resource* (e.g. 'posts') and a set of *parameters*.
+
+*Tip*: In comparison, HTTP requests require a *verb* (e.g. 'GET'), an *url* (e.g. 'http://myapi.com/posts'), a list of *headers* (like `Content-Type`) and a *body*.
+
+Possible types are:
+
+Type | Usage | Params format
+-------------------- | ------------------------------------------------|-------------------------------
+`GET_LIST` | Search for resources | `{ pagination: { page: {int} , perPage: {int} }, sort: { field: {string}, order: {string} }, filter: {Object} }`
+`GET_ONE` | Read a single resource, by id | `{ id: {mixed} }`
+`CREATE` | Create a single resource | `{ data: {Object} }`
+`UPDATE` | Update a single resource | `{ id: {mixed}, data: {Object}, previousData: {Object} }`
+`UPDATE_MANY` | Update multiple resources | `{ ids: {mixed[]}, data: {Object} }`
+`DELETE` | Delete a single resource | `{ id: {mixed}, previousData: {Object} }`
+`DELETE_MANY` | Delete multiple resources | `{ ids: {mixed[]} }`
+`GET_MANY` | Read a list of resource, by ids | `{ ids: {mixed[]} }`
+`GET_MANY_REFERENCE` | Read a list of resources related to another one | `{ target: {string}, id: {mixed}, pagination: { page: {int} , perPage: {int} }, sort: { field: {string}, order: {string} }, filter: {Object} }`
+
+Here are several examples of how react-admin can call the Data Provider with these types:
+
+```jsx
+dataProvider(GET_LIST, 'posts', {
+ pagination: { page: 1, perPage: 5 },
+ sort: { field: 'title', order: 'ASC' },
+ filter: { author_id: 12 },
+});
+dataProvider(GET_ONE, 'posts', { id: 123 });
+dataProvider(CREATE, 'posts', { data: { title: "hello, world" } });
+dataProvider(UPDATE, 'posts', {
+ id: 123,
+ data: { title: "hello, world!" },
+ previousData: { title: "previous title" }
+});
+dataProvider(UPDATE_MANY, 'posts', {
+ ids: [123, 234],
+ data: { views: 0 },
+});
+dataProvider(DELETE, 'posts', {
+ id: 123,
+ previousData: { title: "hello, world" }
+});
+dataProvider(DELETE_MANY, 'posts', { ids: [123, 234] });
+dataProvider(GET_MANY, 'posts', { ids: [123, 124, 125] });
+dataProvider(GET_MANY_REFERENCE, 'comments', {
+ target: 'post_id',
+ id: 123,
+ sort: { field: 'created_at', order: 'DESC' }
+});
+```
+
+### Example Request Processing
+
+Let's say that you want to map the Data Provider requests to a REST backend, like so:
+
+ * `GET_LIST => GET http://path.to.my.api/posts?sort=["title","ASC"]&range=[0, 24]&filter={"author_id":12}`
+ * `GET_ONE => GET http://path.to.my.api/posts/123`
+ * `CREATE => POST http://path.to.my.api/posts`
+ * `UPDATE => PUT http://path.to.my.api/posts/123`
+ * `UPDATE_MANY => PUT http://path.to.my.api/posts?filter={"ids":[123,124,125]}`
+ * `DELETE => DELETE http://path.to.my.api/posts/123`
+ * `DELETE_MANY => DELETE http://path.to.my.api/posts?filter={"ids":[123,124,125]}`
+ * `GET_MANY => GET http://path.to.my.api/posts?filter={"ids":[123,124,125]}`
+ * `GET_MANY_REFERENCE => GET http://path.to.my.api/comments?sort=["created_at","DESC"]&range=[0, 24]&filter={"post_id":123}`
+
+Data Providers often use a `switch` statement, and finish by a call to `fetch()`. Here is an example implementation:
+
+```js
+// in myRestProvider.js
+import { stringify } from 'query-string';
+import {
+ GET_LIST,
+ GET_ONE,
+ CREATE,
+ UPDATE,
+ DELETE,
+ GET_MANY,
+ GET_MANY_REFERENCE,
+} from 'react-admin';
+
+const apiUrl = 'http://path.to.my.api/';
+
+/**
+ * Maps react-admin queries to my REST API
+ *
+ * @param {string} type Request type, e.g GET_LIST
+ * @param {string} resource Resource name, e.g. "posts"
+ * @param {Object} payload Request parameters. Depends on the request type
+ * @returns {Promise} the Promise for a data response
+ */
+export default (type, resource, params) => {
+ let url = '';
+ const options = {
+ headers : new Headers({
+ Accept: 'application/json',
+ }),
+ };
+ switch (type) {
+ case GET_LIST: {
+ const { page, perPage } = params.pagination;
+ const { field, order } = params.sort;
+ const query = {
+ sort: JSON.stringify([field, order]),
+ range: JSON.stringify([
+ (page - 1) * perPage,
+ page * perPage - 1,
+ ]),
+ filter: JSON.stringify(params.filter),
+ };
+ url = `${apiUrl}/${resource}?${stringify(query)}`;
+ break;
+ }
+ case GET_ONE:
+ url = `${apiUrl}/${resource}/${params.id}`;
+ break;
+ case CREATE:
+ url = `${apiUrl}/${resource}`;
+ options.method = 'POST';
+ options.body = JSON.stringify(params.data);
+ break;
+ case UPDATE:
+ url = `${apiUrl}/${resource}/${params.id}`;
+ options.method = 'PUT';
+ options.body = JSON.stringify(params.data);
+ break;
+ case UPDATE_MANY:
+ const query = {
+ filter: JSON.stringify({ id: params.ids }),
+ };
+ url = `${apiUrl}/${resource}?${stringify(query)}`;
+ options.method = 'PATCH';
+ options.body = JSON.stringify(params.data);
+ break;
+ case DELETE:
+ url = `${apiUrl}/${resource}/${params.id}`;
+ options.method = 'DELETE';
+ break;
+ case DELETE_MANY:
+ const query = {
+ filter: JSON.stringify({ id: params.ids }),
+ };
+ url = `${apiUrl}/${resource}?${stringify(query)}`;
+ options.method = 'DELETE';
+ break;
+ case GET_MANY: {
+ const query = {
+ filter: JSON.stringify({ id: params.ids }),
+ };
+ url = `${apiUrl}/${resource}?${stringify(query)}`;
+ break;
+ }
+ case GET_MANY_REFERENCE: {
+ const { page, perPage } = params.pagination;
+ const { field, order } = params.sort;
+ const query = {
+ sort: JSON.stringify([field, order]),
+ range: JSON.stringify([
+ (page - 1) * perPage,
+ page * perPage - 1,
+ ]),
+ filter: JSON.stringify({
+ ...params.filter,
+ [params.target]: params.id,
+ }),
+ };
+ url = `${apiUrl}/${resource}?${stringify(query)}`;
+ break;
+ }
+ default:
+ throw new Error(`Unsupported Data Provider request type ${type}`);
+ }
+
+ return fetch(url, options)
+ .then(res => res.json())
+ .then(response =>
+ /* Convert HTTP Response to Data Provider Response */
+ /* Covered in the next section */
+ );
+};
+```
+
+### Response Format
+
+React-admin expects responses from Data Providers to be objects with a `data` property. The data format depends on the request type.
+
+Request Type | Response format
+-------------------- | ----------------
+`GET_LIST` | `{ data: {Record[]}, total: {int} }`
+`GET_ONE` | `{ data: {Record} }`
+`CREATE` | `{ data: {Record} }`
+`UPDATE` | `{ data: {Record} }`
+`UPDATE_MANY` | `{ data: {mixed[]} }` The ids which have been updated
+`DELETE` | `{ data: {Record} }`
+`DELETE_MANY` | `{ data: {mixed[]} }` The ids which have been deleted
+`GET_MANY` | `{ data: {Record[]} }`
+`GET_MANY_REFERENCE` | `{ data: {Record[]}, total: {int} }`
+
+A `{Record}` is an object literal with at least an `id` property, e.g. `{ id: 123, title: "hello, world" }`.
+
+Building up on the previous example, here are example responses matching the format expected by react-admin:
+
+```jsx
+dataProvider(GET_LIST, 'posts', {
+ pagination: { page: 1, perPage: 5 },
+ sort: { field: 'title', order: 'ASC' },
+ filter: { author_id: 12 },
+})
+.then(response => console.log(response));
+// {
+// data: [
+// { id: 126, title: "allo?", author_id: 12 },
+// { id: 127, title: "bien le bonjour", author_id: 12 },
+// { id: 124, title: "good day sunshine", author_id: 12 },
+// { id: 123, title: "hello, world", author_id: 12 },
+// { id: 125, title: "howdy partner", author_id: 12 },
+// ],
+// total: 27
+// }
+
+dataProvider(GET_ONE, 'posts', { id: 123 })
+.then(response => console.log(response));
+// {
+// data: { id: 123, title: "hello, world" }
+// }
+
+dataProvider(CREATE, 'posts', { data: { title: "hello, world" } })
+.then(response => console.log(response));
+// {
+// data: { id: 450, title: "hello, world" }
+// }
+
+dataProvider(UPDATE, 'posts', {
+ id: 123,
+ data: { title: "hello, world!" },
+ previousData: { title: "previous title" }
+})
+.then(response => console.log(response));
+// {
+// data: { id: 123, title: "hello, world!" }
+// }
+
+dataProvider(UPDATE_MANY, 'posts', {
+ ids: [123, 234],
+ data: { views: 0 },
+})
+.then(response => console.log(response));
+// {
+// data: [123, 234]
+// }
+
+dataProvider(DELETE, 'posts', {
+ id: 123,
+ previousData: { title: "hello, world!" }
+})
+.then(response => console.log(response));
+// {
+// data: { id: 123, title: "hello, world" }
+// }
+
+dataProvider(DELETE_MANY, 'posts', { ids: [123, 234] })
+.then(response => console.log(response));
+// {
+// data: [123, 234]
+// }
+
+dataProvider(GET_MANY, 'posts', { ids: [123, 124, 125] })
+.then(response => console.log(response));
+// {
+// data: [
+// { id: 123, title: "hello, world" },
+// { id: 124, title: "good day sunshise" },
+// { id: 125, title: "howdy partner" },
+// ]
+// }
+
+dataProvider(GET_MANY_REFERENCE, 'comments', {
+ target: 'post_id',
+ id: 123,
+ sort: { field: 'created_at', order: 'DESC' }
+});
+.then(response => console.log(response));
+// {
+// data: [
+// { id: 667, title: "I agree", post_id: 123 },
+// { id: 895, title: "I don't agree", post_id: 123 },
+// ],
+// total: 2,
+// }
+```
+
+### Example Response Processing
+
+Let's continue with the REST backend example. This backend returns responses as follows:
+
+```
+GET http://path.to.my.api/posts?sort=['title','ASC']&range=[0, 4]&filter={author_id:12}
+Content-Range: posts 0-4/27
+[
+ { "id": 126, "title": "allo?", "author_id": 12 },
+ { "id": 127, "title": "bien le bonjour", "author_id": 12 },
+ { "id": 124, "title": "good day sunshine", "author_id": 12 },
+ { "id": 123, "title": "hello, world", "author_id": 12 },
+ { "id": 125, "title": "howdy partner", "author_id": 12 }
+]
+
+GET http://path.to.my.api/posts/123
+{ "id": 123, "title": "hello, world", "author_id": 12 }
+
+POST http://path.to.my.api/posts
+{ "id": 123, "title": "hello, world", "author_id": 12 }
+
+PUT http://path.to.my.api/posts/123
+{ "id": 123, "title": "hello, world", "author_id": 12 }
+
+PATCH http://path.to.my.api/posts?filter={ids:[123,124,125]}
+[123, 124, 125]
+
+DELETE http://path.to.my.api/posts/123
+{ "id": 123, "title": "hello, world", "author_id": 12 }
+
+DELETE http://path.to.my.api/posts?filter={ids:[123,124,125]}
+[123, 124, 125]
+
+GET http://path.to.my.api/posts?filter={ids:[123,124,125]}
+[
+ { "id": 123, "title": "hello, world", "author_id": 12 },
+ { "id": 124, "title": "good day sunshine", "author_id": 12 },
+ { "id": 125, "title": "howdy partner", "author_id": 12 }
+]
+
+GET http://path.to.my.api/comments?sort=['created_at','DESC']&range=[0, 24]&filter={post_id:123}
+Content-Range: comments 0-1/2
+[
+ { "id": 667, "title": "I agree", "post_id": 123 },
+ { "id": 895, "title": "I don't agree", "post_id": 123 }
+]
+```
+
+The Data Provider must therefore transform the response from the API backend to the expected response format.
+
+```js
+// in myRestProvider.js
+import { stringify } from 'query-string';
+import {
+ GET_LIST,
+ GET_ONE,
+ CREATE,
+ UPDATE,
+ UPDATE_MANY,
+ DELETE,
+ DELETE_MANY,
+ GET_MANY,
+ GET_MANY_REFERENCE,
+} from 'react-admin';
+
+const apiUrl = 'http://path.to.my.api/';
+
+/**
+ * Maps react-admin queries to my REST API
+ *
+ * @param {string} type Request type, e.g GET_LIST
+ * @param {string} resource Resource name, e.g. "posts"
+ * @param {Object} payload Request parameters. Depends on the request type
+ * @returns {Promise} the Promise for a data response
+ */
+export default (type, resource, params) => {
+ let url = '';
+ const options = {
+ headers : new Headers({
+ Accept: 'application/json',
+ }),
+ };
+ switch (type) {
+ /* Prepare url and options as above */
+ }
+
+ let headers;
+ return fetch(url, options)
+ .then(res => {
+ headers = res.headers;
+ return res.json();
+ })
+ .then(json => {
+ switch (type) {
+ case GET_LIST:
+ case GET_MANY_REFERENCE:
+ if (!headers.has('content-range')) {
+ throw new Error(
+ 'The Content-Range header is missing in the HTTP Response. The simple REST data provider expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare Content-Range in the Access-Control-Expose-Headers header?'
+ );
+ }
+ return {
+ data: json,
+ total: parseInt(
+ headers
+ .get('content-range')
+ .split('/')
+ .pop(),
+ 10
+ ),
+ };
+ case CREATE:
+ return { data: { ...params.data, id: json.id } };
+ default:
+ return { data: json };
+ }
+ });
+};
+```
+
+### Error Format
+
+When the API backend returns an error, the Data Provider should `throw` an `Error` object. This object should contain a `status` property with the HTTP response code (404, 500, etc.). React-admin inspects this error code, and uses it for [authentication](./Authentication.md) (in case of 401 or 403 errors). Besides, react-admin displays the error `message` on screen in a temporary notification.
+
+### Example implementation
+
+Check the code from the [simple REST client](https://github.com/marmelab/react-admin/blob/master/packages/ra-data-simple-rest/src/index.js): it's a good starting point for a custom Data Provider implementation.
diff --git a/docs/ecosystem.md b/docs/ecosystem.md
new file mode 100644
index 0000000..eb2a839
--- /dev/null
+++ b/docs/ecosystem.md
@@ -0,0 +1,41 @@
+---
+id: ecosystem
+title: Ecosystem
+---
+
+- [Inputs and Fields](#inputs-and-fields)
+- [Translations](#translations)
+- [Data Providers](#data-providers)
+- [Miscellaneous](#miscellaneous)
+
+## Inputs and Fields
+
+- [vascofg/react-admin-color-input](https://github.com/vascofg/react-admin-color-input): a color input using [React Color](http://casesandberg.github.io/react-color/), a collection of color pickers.
+- [LoicMahieu/aor-tinymce-input](https://github.com/LoicMahieu/aor-tinymce-input): a TinyMCE component, useful for editing HTML
+- [vascofg/react-admin-date-inputs](https://github.com/vascofg/react-admin-date-inputs): a collection of Date Inputs, based on [material-ui-pickers](https://material-ui-pickers.firebaseapp.com/)
+
+## Translations
+
+See the [translation](./Translation.md#available-locales) page.
+
+## Data Providers
+
+* **[Epilogue](https://github.com/dchester/epilogue)**: [dunghuynh/aor-epilogue-client](https://github.com/dunghuynh/aor-epilogue-client)
+* **[Feathersjs](http://www.feathersjs.com/)**: [josx/aor-feathers-client](https://github.com/josx/aor-feathers-client)
+* **[Firebase](https://firebase.google.com/)**: [sidferreira/aor-firebase-client](https://github.com/sidferreira/aor-firebase-client)
+* **[GraphQL](http://graphql.org/)**: [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](http://www.apollodata.com/))
+* **[GraphCool](http://www.graph.cool/)**: [marmelab/ra-data-graphcool](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](http://www.apollodata.com/))
+* **[Hydra](http://www.hydra-cg.com/) / [JSON-LD](https://json-ld.org/)**: [api-platform/admin/hydra](https://github.com/api-platform/admin/blob/master/src/hydra/hydraClient.js)
+* **[JSON API](http://jsonapi.org/)**: [moonlight-labs/aor-jsonapi-client](https://github.com/moonlight-labs/aor-jsonapi-client)
+* **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/ra-data-json-server).
+* Local JSON: [marmelab/ra-data-fakerest](https://github.com/marmelab/ra-data-fakerest)
+* **[Loopback](http://loopback.io/)**: [kimkha/aor-loopback](https://github.com/kimkha/aor-loopback)
+* **[Parse Server](https://github.com/ParsePlatform/parse-server)**: [leperone/aor-parseserver-client](https://github.com/leperone/aor-parseserver-client)
+* **[PostgREST](http://postgrest.com/en/v0.4/)**: [tomberek/aor-postgrest-client](https://github.com/tomberek/aor-postgrest-client)
+* Simple REST: [marmelab/ra-data-simple-rest](https://github.com/marmelab/ra-data-simple-rest).
+
+## Miscellaneous
+
+- [api-platform/admin](https://api-platform.com/docs/admin): create a fully featured admin using React Admin for API supporting the [Hydra Core Vocabulary](http://www.hydra-cg.com/), including but not limited to APIs created using the [API Platform framework](https://api-platform.com)
+- [marmelab/ra-realtime](https://github.com/marmelab/react-admin/tree/master/packages/ra-realtime): enable realtime updates
+- [zifnab87/ra-component-factory](https://github.com/zifnab87/ra-component-factory): centralized configuration of immutability/visibility of fields/menu-links/action buttons, easy re-ordering of fields/properties and tab reorganization based on permission roles
diff --git a/docs/faq.md b/docs/faq.md
new file mode 100644
index 0000000..cffbb3a
--- /dev/null
+++ b/docs/faq.md
@@ -0,0 +1,98 @@
+---
+id: faq
+title: FAQ
+---
+
+- [Can I have custom identifiers/primary keys for my resources?](#can-i-have-custom-identifiersprimary-keys-for-my-resources)
+- [I get warning about unique key for child in array](#i-get-warning-about-unique-key-for-child-in-array)
+- [A form with validation freezes when rendering](#a-form-with-validation-freezes-when-rendering)
+- [How can I customize the UI depending on the user permissions?](#how-can-i-customize-the-ui-depending-on-the-user-permissions)
+- [How can I customize forms depending on its inputs values?](#how-can-i-customize-forms-depending-on-its-inputs-values)
+
+## Can I have custom identifiers/primary keys for my resources?
+
+React-admin requires that each resource has an `id` field to identify it. If your API uses a different name for the primary key, you have to map that name to `id` in a custom [dataProvider](./DataProviders.md). For instance, to use a field named `_id` as identifier:
+
+```js
+const convertHTTPResponse = (response, type, resource, params) => {
+ const { headers, json } = response;
+ switch (type) {
+ case GET_LIST:
+ return {
+ data: json.map(resource => ({ ...resource, id: resource._id }) ),
+ total: parseInt(headers.get('content-range').split('/').pop(), 10),
+ };
+ case UPDATE:
+ case DELETE:
+ case GET_ONE:
+ return { ...json, id: json._id };
+ case CREATE:
+ return { ...params.data, id: json._id };
+ default:
+ return json;
+ }
+};
+```
+
+## I get warning about unique key for child in array
+
+When displaying a `Datagrid` component, you get the following warning:
+
+> Warning: Each child in an array or iterator should have a unique "key" prop.
+> Check the render method of `DatagridBody`.
+
+This is most probably because the resource does not have an `id` property as expected by react-admin. See the previous FAQ to see how to resolve this: [Can I have custom identifiers/primary keys for my resources?](#can-i-have-custom-identifiersprimary-keys-for-my-resources)
+
+## A form with validation freezes when rendering
+
+You're probably using validator factories directly in the render method of your component:
+
+```jsx
+export const CommentEdit = ({ ...props }) => (
+
+
+
+
+
+
+
+);
+```
+
+Avoid calling functions directly inside the render method:
+
+```jsx
+const validateMinLength = minLength(10);
+
+export const CommentEdit = ({ ...props }) => (
+
+
+
+
+
+
+
+);
+```
+
+This is related to [redux-form](https://github.com/erikras/redux-form/issues/3288).
+
+## How can I customize the UI depending on the user permissions?
+
+Some fairly common use cases which may be dependent on the user permissions:
+
+- Specific views
+- Having parts of a view (fields, inputs) differents for specific users
+- Hiding or displaying menu items
+
+For all those cases, you can use the [aor-permissions](https://github.com/marmelab/aor-permissions) addon.
+
+## How can I customize forms depending on its inputs values?
+
+Some use cases:
+
+- Show/hide some inputs if another input has a value
+- Show/hide some inputs if another input has a specific value
+- Show/hide some inputs if the current form values matches specific constraints
+
+For all those cases, you can use the [aor-dependent-input](https://github.com/marmelab/aor-dependent-input) addon.
diff --git a/docs/field-components.md b/docs/field-components.md
new file mode 100644
index 0000000..f15a439
--- /dev/null
+++ b/docs/field-components.md
@@ -0,0 +1,1010 @@
+---
+layout: field-components
+title: Components
+---
+
+A `Field` component displays a given property of a REST resource. Such components are used in the `List` view, but you can also use them in the `Edit` and `Create` views for read-only fields. The most usual of all field components is ``:
+
+```jsx
+// in src/posts.js
+import React from 'react';
+import { List, Datagrid, TextField } from 'react-admin';
+
+export const PostList = (props) => (
+
+
+
+
+
+
+
+);
+```
+
+All field components accept the following attributes:
+
+* `source`: Property name of your entity to view/edit. This attribute is required.
+* `label`: Used as a table header of an input label. Defaults to the `source` when omitted.
+* `sortable`: Should the list be sortable using `source` attribute? Defaults to `true`.
+* `className`: A class name (usually generated by JSS) to customize the look and feel of the field element itself
+* `cellClassName`: A class name (usually generated by JSS) to customize the look and feel of the field container (e.g. the `
` in a datagrid).
+* `headerClassName`: A class name (usually generated by JSS) to customize the look and feel of the field header (e.g. the `
` in a datagrid).
+* `addLabel`: Defines the visibility of the label when the field is not in a datagrid. Default value is ```true```.
+* `textAlign`: Defines the text alignment inside a cell. Supports `left` (the default) and `right`.
+
+```jsx
+
+```
+
+**Tip**: You can use field components inside the `Edit` or `Show` views, too:
+
+```jsx
+export const PostShow = ({ ...props }) => (
+
+
+
+
+
+);
+```
+
+**Tip**: If you display a record with a complex structure, you can use a path with dot separators as the `source` attribute. For instance, if the API returns the following 'book' record:
+
+```jsx
+{
+ id: 1234,
+ title: 'War and Peace',
+ author: {
+ firstName: 'Leo',
+ lastName: 'Tolstoi'
+ }
+}
+```
+
+Then you can display the author first name as follows:
+
+```jsx
+
+```
+
+**Tip**: If you want to format a field according to the value, use a higher-order component to do conditional formatting, as described in the [Theming documentation](./Theming.md#conditional-formatting).
+
+**Tip**: If your interface has to support multiple languages, don't use the `label` prop, and put the localized labels in a dictionary instead. See the [Translation documentation](./Translation.md#translating-resource-and-field-names) for details.
+
+## `ArrayField` Component
+
+Display a collection using `` child components.
+
+Ideal for embedded arrays of objects, e.g. `tags` and `backlinks` in the following `post` object:
+
+```js
+{
+ id: 123
+ tags: [
+ { name: 'foo' },
+ { name: 'bar' }
+ ],
+ backlinks: [
+ {
+ date: '2012-08-10T00:00:00.000Z',
+ url: 'http://example.com/foo/bar.html',
+ },
+ {
+ date: '2012-08-14T00:00:00.000Z',
+ url: 'https://blog.johndoe.com/2012/08/12/foobar.html',
+ }
+ ]
+}
+```
+
+The child must be an iterator component (like `` or ``).
+
+Here is how to display all the backlinks of the current post as a ``
+
+```jsx
+
+
+
+
+
+
+```
+
+And here is how to display all the tags of the current post as `` components:
+
+```jsx
+
+
+
+
+
+```
+
+**Tip**: If you need to render a collection in a custom way, it's often simpler to write your own component:
+
+```jsx
+const TagsField = ({ record }) => (
+
+ {record.tags.map(item => (
+
{item.name}
+ ))}
+
+)
+TagsField.defaultProps = { addLabel: true };
+```
+
+## `BooleanField` Component
+
+Displays a boolean value as a check.
+
+```jsx
+import { BooleanField } from 'react-admin';
+
+
+```
+
+
+
+## `ChipField` Component
+
+Displays a value inside a ["Chip"](http://www.material-ui.com/#/components/chip), which is Material UI's term for a label.
+
+```jsx
+import { ChipField } from 'react-admin';
+
+
+```
+
+
+
+This field type is especially useful for one to many relationships, e.g. to display a list of books for a given author:
+
+```jsx
+import { ChipField, SingleFieldList, ReferenceManyField } from 'react-admin';
+
+
+
+
+
+
+```
+
+## `DateField` Component
+
+Displays a date or datetime using the browser locale (thanks to `Date.toLocaleDateString()` and `Date.toLocaleString()`).
+
+```jsx
+import { DateField } from 'react-admin';
+
+
+```
+
+This component accepts a `showTime` attribute (false by default) to force the display of time in addition to date. It uses `Intl.DateTimeFormat()` if available, passing the `locales` and `options` props as arguments. If Intl is not available, it ignores the `locales` and `options` props.
+
+```jsx
+
+// renders the record { id: 1234, publication_date: new Date('2017-04-23') } as
+4/23/2017
+
+
+// renders the record { id: 1234, publication_date: new Date('2017-04-23 23:05') } as
+4/23/2017, 11:05:00 PM
+
+
+// renders the record { id: 1234, publication_date: new Date('2017-04-23') } as
+Sunday, April 23, 2017
+
+
+// renders the record { id: 1234, publication_date: new Date('2017-04-23') } as
+23/04/2017
+
+
+// renders the record { id: 1234, publication_date: new Date('2017-04-23') } as
+4/23/2017
+```
+
+See [Intl.DateTimeformat documentation](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/toLocaleDateString) for the `options` prop syntax.
+
+**Tip**: If you need more formatting options than what `Intl.DateTimeformat` can provide, build your own field component leveraging a third-party library like [moment.js](http://momentjs.com/).
+
+## `EmailField` Component
+
+`` displays an email as a `` link.
+
+```jsx
+import { EmailField } from 'react-admin';
+
+
+```
+
+## `FunctionField` Component
+
+If you need a special function to render a field, `` is the perfect match. It passes the `record` to a `render` function supplied by the developer. For instance, to display the full name of a `user` record based on `first_name` and `last_name` properties:
+
+```jsx
+import { FunctionField } from 'react-admin'
+
+ `${record.first_name} ${record.last_name}`} />
+```
+
+**Tip**: Technically, you can omit the `source` and `sortBy` properties for the `` since you provide the render function. However, providing a `source` or a `sortBy` will allow the datagrid to make the column sortable, since when a user clicks on a column, the datagrid uses these properties to sort. Should you provide both, `sortBy` will override `source` for sorting the column.
+
+## `ImageField` Component
+
+If you need to display an image provided by your API, you can use the `` component:
+
+```jsx
+import { ImageField } from 'react-admin';
+
+
+```
+
+This field is also generally used within an [](http://marmelab.com/react-admin/Inputs.md#imageinput) component to display preview.
+
+The optional `title` prop points to the picture title property, used for both `alt` and `title` attributes. It can either be an hard-written string, or a path within your JSON object:
+
+```jsx
+// { picture: { url: 'cover.jpg', title: 'Larry Cover (French pun intended)' } }
+
+// Title would be "picture.title", hence "Larry Cover (French pun intended)"
+
+
+// Title would be "Picture", as "Picture" is not a path in previous given object
+
+```
+
+If passed value is an existing path within your JSON object, then it uses the object attribute. Otherwise, it considers its value as an hard-written title.
+
+
+If the record actually contains an array of images in its property defined by the `source` prop, the `src` prop will be needed to determine the `src` value of the images, for example:
+
+```js
+// This is the record
+{
+ pictures: [
+ { url: 'image1.jpg', desc: 'First image' },
+ { url: 'image2.jpg', desc: 'Second image' },
+ ],
+}
+
+
+```
+
+## `FileField` Component
+
+If you need to display a file provided by your API, you can use the `` component:
+
+```jsx
+import { FileField } from 'react-admin';
+
+
+```
+
+This field is also generally used within an [](http://marmelab.com/react-admin/Inputs.md#fileinput) component to display preview.
+
+The optional `title` prop points to the file title property, used for `title` attributes. It can either be an hard-written string, or a path within your JSON object:
+
+```jsx
+// { file: { url: 'doc.pdf', title: 'Presentation' } }
+
+// Title would be "file.title", hence "Presentation"
+
+
+// Title would be "File", as "File" is not a path in previous given object
+
+```
+
+If passed value is an existing path within your JSON object, then it uses the object attribute. Otherwise, it considers its value as an hard-written title.
+
+If the record actually contains an array of files in its property defined by the `source` prop, the `src` prop will be needed to determine the `href` value of the links, for example:
+
+```js
+// This is the record
+{
+ files: [
+ { url: 'image1.jpg', desc: 'First image' },
+ { url: 'image2.jpg', desc: 'Second image' },
+ ],
+}
+
+
+```
+
+You can optionally set the `target` prop to choose which window will the link try to open in.
+
+```jsx
+// Will make the file open in new window
+
+```
+
+## `NumberField` Component
+
+Displays a number formatted according to the browser locale, right aligned.
+
+Uses `Intl.NumberFormat()` if available, passing the `locales` and `options` props as arguments. This allows perfect display of decimals, currencies, percentage, etc.
+
+If Intl is not available, it outputs number as is (and ignores the `locales` and `options` props).
+
+
+```jsx
+import { NumberField } from 'react-admin';
+
+
+// renders the record { id: 1234, score: 567 } as
+567
+
+
+// renders the record { id: 1234, score: 567.3567458569 } as
+567.35
+
+
+// renders the record { id: 1234, share: 0.2545 } as
+25%
+
+
+// renders the record { id: 1234, price: 25.99 } as
+$25.99
+
+
+// renders the record { id: 1234, price: 25.99 } as
+25,99 $US
+
+
+// renders the record { id: 1234, score: 567 } as
+567
+```
+
+See [Intl.Numberformat documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString) for the `options` prop syntax.
+
+**Tip**: If you need more formatting options than what `Intl.Numberformat` can provide, build your own field component leveraging a third-party library like [numeral.js](http://numeraljs.com/).
+
+**Tip**: When used in a `Show` view, the right alignment may look weird. Disable it by resetting the `style` attribute:
+
+```jsx
+import { NumberField } from 'react-admin';
+
+
+```
+
+## `SelectField` Component
+
+When you need to display an enumerated field, `` maps the value to a string.
+
+For instance, if the `gender` field can take values "M" and "F", here is how to display it as "Male" or "Female":
+
+```jsx
+import { SelectField } from 'react-admin';
+
+
+```
+
+By default, the text is built by
+
+- finding a choice where the 'id' property equals the field value
+- using the 'name' property an the option text
+
+**Warning**: This component name may conflict with material-ui's [``](http://www.material-ui.com/#/components/select-field) if you import both.
+
+You can also customize the properties to use for the lookup value and text, thanks to the 'optionValue' and 'optionText' attributes.
+
+```jsx
+const choices = [
+ { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' },
+ { _id: 456, full_name: 'Jane Austen', sex: 'F' },
+];
+
+```
+
+`optionText` also accepts a function, so you can shape the option text at will:
+
+```jsx
+const choices = [
+ { id: 123, first_name: 'Leo', last_name: 'Tolstoi' },
+ { id: 456, first_name: 'Jane', last_name: 'Austen' },
+];
+const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`;
+
+```
+
+`optionText` also accepts a React Element, that will be cloned and receive the related choice as the `record` prop. You can use Field components there.
+
+```jsx
+const choices = [
+ { id: 123, first_name: 'Leo', last_name: 'Tolstoi' },
+ { id: 456, first_name: 'Jane', last_name: 'Austen' },
+];
+const FullNameField = ({ record }) => {record.first_name} {record.last_name};
+}/>
+```
+
+The current choice is translated by default, so you can use translation identifiers as choices:
+
+```jsx
+const choices = [
+ { id: 'M', name: 'myroot.gender.male' },
+ { id: 'F', name: 'myroot.gender.female' },
+];
+```
+
+However, in some cases (e.g. inside a ``), you may not want the choice to be translated. In that case, set the `translateChoice` prop to false.
+
+```jsx
+
+```
+
+**Tip**: sets `translateChoice` to false by default.
+
+## `ReferenceField` Component
+
+This component fetches a single referenced record (using the `GET_MANY` REST method), and displays one field of this record. That's why a `` must always have a child ``.
+
+For instance, here is how to fetch the `post` related to `comment` records, and display the `title` for each:
+
+```jsx
+import React from 'react';
+import { List, Datagrid, ReferenceField, TextField } from 'react-admin';
+
+export const PostList = (props) => (
+
+
+
+
+
+
+
+
+);
+```
+
+With this configuration, `` wraps the user's name in a link to the related user `` page.
+
+
+
+`` accepts a `reference` attribute, which specifies the resource to fetch for the related record. Also, you can use any `Field` component as child.
+
+**Note**: You **must** add a `` for the reference resource - react-admin needs it to fetch the reference data. You *can* omit the `list` prop in this reference if you want to hide it in the sidebar menu.
+
+```jsx
+
+
+
+
+```
+
+To change the link from the `` page to the `` page, set the `linkType` prop to "show".
+
+```jsx
+
+
+
+```
+
+By default, `` is sorted by its `source`. To specify another attribute to sort by, set the `sortBy` prop to the according attribute's name.
+
+```jsx
+
+
+
+```
+
+You can also prevent `` from adding link to children by setting `linkType` to `false`.
+
+```jsx
+// No link
+
+
+
+```
+
+**Tip**: React-admin uses `CRUD_GET_ONE_REFERENCE` action to accumulate and deduplicate the ids of the referenced records to make *one* `GET_MANY` call for the entire list, instead of n `GET_ONE` calls. So for instance, if the API returns the following list of comments:
+
+```jsx
+[
+ {
+ id: 123,
+ body: 'Totally agree',
+ post_id: 789,
+ },
+ {
+ id: 124,
+ title: 'You are right my friend',
+ post_id: 789
+ },
+ {
+ id: 125,
+ title: 'Not sure about this one',
+ post_id: 735
+ }
+]
+```
+
+Then react-admin renders the `` with a loader for the ``, fetches the API for the related posts in one call (`GET http://path.to.my.api/posts?ids=[789,735]`), and re-renders the list once the data arrives. This accelerates the rendering, and minimizes network load.
+
+## `ReferenceManyField` Component
+
+This component fetches a list of referenced records by reverse lookup of the current `record.id` in other resource (using the `GET_MANY_REFERENCE` REST method). The field name of the current record's id in the other resource is specified by the required `target` field. The result is then passed to an iterator component (like `` or ``). The iterator component usually has one or more child `` components.
+
+For instance, here is how to fetch the `comments` related to a `post` record by matching `comment.post_id` to `post.id`, and then display the `author.name` for each, in a ``:
+
+```jsx
+import React from 'react';
+import { List, Datagrid, ChipField, ReferenceManyField, SingleFieldList, TextField } from 'react-admin';
+
+export const PostList = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+);
+```
+
+
+
+`` accepts a `reference` attribute, which specifies the resource to fetch for the related record. It also accepts a `source` attribute which define the field containing the value to look for in the `target` field of the referenced resource. By default this is the `id` of the resource (`post.id` in the previous example).
+
+**Note**: You **must** add a `` for the reference resource - react-admin needs it to fetch the reference data. You *can* omit the `list` prop in this reference if you want to hide it in the sidebar menu.
+
+You can use a `` instead of a `` - but not inside another ``! This is useful if you want to display a read-only list of related records. For instance, if you want to show the `comments` related to a `post` in the post's `` view:
+
+```jsx
+import React from 'react';
+import { Edit, Datagrid, SimpleForm, DisabledInput, DateField, EditButton, ReferenceManyField, TextField, TextInput } from 'react-admin';
+
+export const PostEdit = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+```
+
+
+
+By default, react-admin restricts the possible values to 25. You can change this limit by setting the `perPage` prop.
+
+```jsx
+
+ ...
+
+```
+
+By default, it orders the possible values by id desc. You can change this order by setting the `sort` prop (an object with `field` and `order` properties).
+
+```jsx
+
+ ...
+
+```
+
+Also, you can filter the query used to populate the possible values. Use the `filter` prop for that.
+
+```jsx
+
+ ...
+
+```
+
+## `ReferenceArrayField` Component
+
+Use `` to display an list of reference values based on an array of foreign keys.
+
+For instance, if a post has many tags, a post resource may look like:
+
+```js
+{
+ id: 1234,
+ title: 'Lorem Ipsum',
+ tag_ids: [1, 23, 4]
+}
+```
+
+Where `[1, 23, 4]` refer to ids of `tag` resources.
+
+`` can fetch the `tag` resources related to this `post` resource by matching `post.tag_ids` to `tag.id`. `` would issue an HTTP request looking like:
+
+```
+http://myapi.com/tags?id=[1,23,4]
+```
+
+**Tip**: `` fetches the related resources using the `GET_MANY` REST method, so the actual HTTP request depends on your REST client.
+
+Once it receives the related resources, `` passes them to its child component using the `ids` and `data` props, so the child must be an iterator component (like `` or ``). The iterator component usually has one or more child `` components.
+
+Here is how to fetch the list of tags for each post in a `PostList`, and display the `name` for each `tag` in a ``:
+
+```jsx
+import React from 'react';
+import { List, Datagrid, ChipField, ReferenceArrayField, SingleFieldList, TextField } from 'react-admin';
+
+export const PostList = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+);
+```
+
+**Note**: You **must** add a `` component for the reference resource to your `` component, because react-admin needs it to fetch the reference data. You can omit the `list` prop in this Resource if you don't want to show an entry for it in the sidebar menu.
+
+```jsx
+export const App = () => (
+
+
+ // <= this one is compulsory
+
+);
+```
+
+In an Edit of Show view, you can combine `` with `` to display a related resources in a table. For instance, to display more details about the tags related to a post in the `PostShow` view:
+
+```jsx
+import React from 'react';
+import { Show, SimpleShowLayout, TextField, ReferenceArrayField, Datagrid, ShowButton } from 'react-admin';
+
+export const PostShow = (props) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+```
+
+## `RichTextField` Component
+
+This component displays some HTML content. The content is "rich" (i.e. unescaped) by default.
+
+```jsx
+import { RichTextField } from 'react-admin';
+
+
+```
+
+
+
+The `stripTags` attribute (`false` by default) allows you to remove any HTML markup, preventing some display glitches (which is especially useful in list views).
+
+```jsx
+import { RichTextField } from 'react-admin';
+
+
+```
+
+## `TextField` Component
+
+The most simple as all fields, `` simply displays the record property as plain text.
+
+```jsx
+import { TextField } from 'react-admin';
+
+
+```
+
+## `UrlField` Component
+
+`` displays an url in an `< a href="">` tag.
+
+```jsx
+import { UrlField } from 'react-admin';
+
+
+```
+
+## Styling Fields
+
+All field components accept a `className` prop, allowing you to customize their style to your liking. We advise you to use the Material UI styling solution, JSS, to generate those classes. See their [documentation](https://material-ui.com/customization/css-in-js/#api) about that.
+
+
+```jsx
+import { withStyles } from '@material-ui/core/styles';
+
+const styles = {
+ price: { color: 'purple' },
+};
+
+const PriceField = withStyles(styles)(({ classes, ...props }) => (
+
+));
+
+export const ProductList = (props) => (
+
+
+
+
+
+);
+
+// renders in the datagrid as
+