Revert "remove internal docs"

This reverts commit 8cfe2c549dc350bb7650c1b0321813c5f17ce2d8.
This commit is contained in:
Shawn Erquhart 2017-11-27 14:22:11 -05:00
parent 8cfe2c549d
commit 8bd48e164b
16 changed files with 977 additions and 22 deletions

View File

@ -5,26 +5,6 @@
A CMS for static site generators. Give non-technical users a simple way to edit
and add content to any site built with a static site generator.
## Documentation
You can find the Netlify CMS documentation [on the website](https://netlifycms.org/docs).
It is divided into several sections:
* [Introduction](https://netlifycms.org/docs/intro)
* [Test Drive](https://netlifycms.org/docs/test-drive)
* [Quick Start](https://netlifycms.org/docs/quick-start)
* [Widgets](https://netlifycms.org/docs/widgets)
* [Editorial Workflow](https://netlifycms.org/docs/editorial-workflow)
* [Custom Previews](https://netlifycms.org/docs/customization)
* [Extending Widgets](https://netlifycms.org/docs/extending)
* [Validation](https://netlifycms.org/docs/validation)
* [Auth & Backends](https://netlifycms.org/docs/auth-backends)
* [Architecture](https://netlifycms.org/docs/architecture)
* [Contributing](https://netlifycms.org/docs/contributor-guide)
* [Examples](https://netlifycms.org/docs/examples)
You can improve it by sending pull requests to [this repository](https://github.com/netlify/netlify-cms-www).
## How it works
Netlify CMS is a single-page app that you pull into the `/admin` part of your site.
@ -37,14 +17,14 @@ tweak the main layout of the CMS a bit to fit your own site.
When a user navigates to `/admin` they'll be prompted to login, and once authenticated
they'll be able to create new content or edit existing content.
Read more about Netlify CMS [Core Concepts](https://netlifycms.org/docs/intro.md).
Read more about Netlify CMS [Core Concepts](docs/intro.md).
# Installation and Configuration
The Netlify CMS can be used in two different ways.
* A Quick and easy install, that just requires you to create a single HTML file and a configuration file. All the CMS Javascript and CSS are loaded from a CDN.
To learn more about this installation method, refer to the [Quick Start Guide](https://netlifycms.org/docs/quick-start.md)
To learn more about this installation method, refer to the [Quick Start Guide](docs/quick-start.md)
* A complete, more complex install, that gives you more flexibility but requires that you use a static site builder with a build system that supports npm packages.
# Community

60
docs/architecture.md Executable file
View File

@ -0,0 +1,60 @@
# Technical Architecture
Netlify CMS is a React application, using Redux for state management with immutable data structures (immutable.js).
The core abstractions for content editing are `collections`, `entries` and `widgets`.
Each `collection` represents a collection of entries. This can either be a collection of similar entries with the same structure, or a set of entries where each has its own structure.
The structure of an entry is defined as a series of fields, each with a `name`, a `label`, and a `widget` .
The `widget` determines the UI widget that the content editor will use when editing this field of an entry, as well as how the content of the field is presented in the editing preview.
Entries are loaded and persisted through a `backend` that will typically represent a `git` repository.
## State shape / reducers
**Auth:** Keeps track of the logged state and the current user.
**Config:** Holds the environment configuration (backend type, available collections and fields).
**Collections** List of available collections, their fields and metadata information.
**Entries:** Entries for each field.
**EntryDraft:** Reused for each entry that is edited or created. It holds the entry's temporary data until it's persisted on the backend.
**Medias:** Keeps references to all media files uploaded by the user during the current session.
## Selectors
Selectors are functions defined within reducers used to compute derived data from the Redux store. The available selectors are:
**selectEntry:** Selects a single entry, given the collection and a slug.
**selectEntries:** Selects all entries for a given collection.
**getAsset:** Selects a single AssetProxy object for the given URI.
## Value Objects
**AssetProxy:** AssetProxy is a Value Object that holds information regarding an asset file (such as an image, for example), whether it's persisted online or held locally in cache.
For a file persisted online, the AssetProxy only keeps information about its URI. For local files, the AssetProxy will keep a reference to the actual File object while generating the expected final URIs and on-demand blobs for local preview.
The AssetProxy object can be used directly inside a media tag (such as `<img>`), as it will always return something that can be used by the media tag to render correctly (either the URI for the online file or a single-use blob).
## Components structure and Workflows
Components are separated into two main categories: Container components and Presentational components.
### Entry Editing
For either updating an existing entry or creating a new one, the `EntryEditor` is used and the flow is the same:
* When mounted, the `EntryPage` container component dispatches the `createDraft` action, setting the `entryDraft` state to a blank state (in case of a new entry) or to a copy of the selected entry (in case of an edit).
* The `EntryPage` will also render widgets for each field type in the given entry.
* Widgets are used for editing entry fields. There are different widgets for different field types, and they are always defined in a pair containing a `control` and a `preview` component. The control component is responsible for presenting the user with the appropriate interface for manipulating the current field value, while the preview component is responsible for displaying the value with the appropriate styling.
#### Widget components implementation
The control component receives three (3) callbacks as props: `onChange`, `onAddAsset`, and `onRemoveAsset`.
* onChange (required): Should be called when the users changes the current value. It will ultimately end up updating the EntryDraft object in the Redux Store, thus updating the preview component.
* onAddAsset & onRemoveAsset (optionals): If the field accepts file uploads for media (images, for example), these callbacks should be invoked with a `AssetProxy` value object. `onAddAsset` will get the current media stored in the Redux state tree while `onRemoveAsset` will remove it. AssetProxy objects are stored in the `Medias` object and referenced in the `EntryDraft` object on the state tree.
Both control and preview widgets receive a `getAsset` selector via props. Displaying the media (or its URI) for the user should always be done via `getAsset`, as it returns an AssetProxy that can return the correct value for both medias already persisted on the server and cached media not yet uploaded.
The actual persistence of the content and medias inserted into the control component is delegated to the backend implementation. The backend will be called with the updated values and a list of assetProxy objects for each field of the entry, and should return a promise that can resolve into the persisted entry object and the list of the persisted media URIs.

View File

@ -0,0 +1,9 @@
# Welcome, contributors!
We're hoping that Netlify CMS will do for the [JAMstack](https://www.jamstack.org) what WordPress did for dynamic sites back in the day. We know we can't do that without building a thriving community of contributors and users, and we'd love to have you join us.
While we work on building this page (and you can help!), here are some links with more information about getting involved:
* [Project Milestones](https://github.com/netlify/netlify-cms/milestones)
* [Code of Conduct](https://github.com/netlify/netlify-cms/blob/master/CODE_OF_CONDUCT.md)
* [Setup instructions and Contribution Guidelines](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md)

View File

@ -0,0 +1,33 @@
# Custom Authentication
Netlify CMS is meant to be platform agnostic, so we're always looking to expand the ecosystem and find new ways to use it. Below is a list of currently submitted OAuth providers - feel free to [submit a pull request](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md) if you'd like to add yours!
## External OAuth Clients:
| Author | Supported Git hosts | Languages | Link |
|------------|---------------------------|-----------|---------------------------------------------------------------------|
| @vencax | GitHub, GitHub Enterprise | Node.js | [Repo](https://github.com/vencax/netlify-cms-github-oauth-provider) |
Check each project's readme for instructions on how to configure it.
## Configuration
CMS configuration properties that affect authentication, including some optional properties that aren't mentioned elsewhere in the docs, are explained below:
```yaml
backend:
# REQUIRED CONFIG
name: github
repo: user/repository
# OPTIONAL CONFIG
# Note: no trailing slashes on URLs
api_root: https://github.some.domain.com/api/v3
site_domain: static.site.url.com
base_url: https://auth.server.url.com
```
* **name:** name of the auth provider, varies by implementation. `github` when using GitHub auth, even with a third party auth client.
* **repo:** repo where content is to be stored.
* **api_root (optional):** the API endpoint. Defaults to `https://api.github.com` when used with the `github` provider. Only necessary in certain cases, e.g., when using with GitHub Enterprise.
* **site_domain (optional):** sets `site_id` query param sent to API endpoint. Defaults to `location.hostname`, minus any port, or `cms.netlify.com` on localhost so that auth "just works" during local development. Sites with custom authentication will often need to set this for local development to work properly.
* **base_url (optional):** OAuth client URL, defaults to `https://api.netlify.com` as a convenience. This is **required** when using an external OAuth server.

194
docs/customization.md Normal file
View File

@ -0,0 +1,194 @@
# Customizing the Preview Pane
The NetlifyCMS exposes a `window.CMS` global object that you can use to register custom widgets, previews and editor plugins. The available customization methods are:
* **registerPreviewStyle:** Register a custom stylesheet to use on the preview pane.
* **registerPreviewTemplate:** Registers a template for a collection.
Explore the [NetlifyCMS GitHub example](https://github.com/netlify/netlify-cms/blob/9ced3f16c8bcc3d1a36773b126842d023c589eaf/example/index.html#L90-L91), a working example you can review on GitHub.
### React Components inline interaction
NetlifyCMS is a collection of React components and exposes two constructs globally to allow you to create components inline: createClass and h (alias for React.createElement).
## `registerPreviewStyle`
Register a custom stylesheet to use on the preview pane.
```js
CMS.registerPreviewStyle(file);
```
**Params:**
* **file:** css file path
**Example:**
```html
// index.html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
CMS.registerPreviewStyle("/example.css");
</script>
```
```css
/* example.css */
html,
body {
color: #444;
font-size: 14px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
body {
padding: 20px;
}
```
## `registerPreviewTemplate`
Registers a template for a collection.
`CMS.registerPreviewTemplate(collection, react_component);`
**Params:**
* collection: The name of the collection which this preview component will be used for.
* react_component: A React component that renders the collection data. Four props will be passed to your component during render:
* entry: Immutable collection containing the entry data.
* widgetFor: Returns the appropriate widget preview component for a given field.
* [widgetsFor](#lists-and-objects): Returns an array of objects with widgets and associated field data. For use with list and object type entries.
* getAsset: Returns the correct filePath or in-memory preview for uploaded images.
**Example:**
```html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
var PostPreview = createClass({
render: function() {
var entry = this.props.entry;
var image = entry.getIn(['data', 'image']);
var bg = this.props.getAsset(image);
return h('div', {},
h('h1', {}, entry.getIn(['data', 'title'])),
h('img', {src: bg.toString()}),
h('div', {"className": "text"}, this.props.widgetFor('body'))
);
}
});
CMS.registerPreviewTemplate("posts", PostPreview);
</script>
```
### Lists and Objects
The API for accessing the individual fields of list- and object-type entries is similar to the API
for accessing fields in standard entries, but there are a few key differences. Access to these
nested fields is facilitated through the `widgetsFor` function, which is passed to the preview
template component during render.
**Note**: as is often the case with the NetlifyCMS API, arrays and objects are created with
Immutable.js. If some of the methods that we use are unfamiliar, such as `getIn`, check out
[their docs](https://facebook.github.io/immutable-js/docs/#/) to get a better understanding.
**List Example:**
```html
<script>
var AuthorsPreview = createClass({
// For list fields, the widgetFor function returns an array of objects
// that you can map over in your template. If our field is a list of
// authors containing two entries, with fields `name` and `description`,
// the return value of `widgetsFor` would look like this:
//
// [{
// data: { name: 'Mathias', description: 'Co-Founder'},
// widgets: { name: (<WidgetComponent>), description: (WidgetComponent>)}
// },
// {
// data: { name: 'Chris', description: 'Co-Founder'},
// widgets: { name: (<WidgetComponent>), description: (WidgetComponent>)}
// }]
//
// Templating would look something like this:
render: function() {
return h('div', {},
// This is a static header that would only be rendered once for the entire list
h('h1', {}, 'Authors'),
// Here we provide a simple mapping function that will be applied to each
// object in the array of authors
this.props.widgetsFor('authors').map(function(author, index) {
return h('div', {key: index},
h('hr', {}),
h('strong', {}, author.getIn(['data', 'name'])),
author.getIn(['widgets', 'description'])
);
})
);
}
});
CMS.registerPreviewTemplate("authors", AuthorsPreview);
</script>
```
**Object Example:**
```html
<script>
var GeneralPreview = createClass({
// Object fields are simpler than lists - instead of `widgetsFor` returning
// an array of objects, it returns a single object. Accessing the shape of
// that object is the same as the shape of objects returned for list fields:
//
// {
// data: { front_limit: 0, author: 'Chris' },
// widgets: { front_limit: (<WidgetComponent>), author: (WidgetComponent>)}
// }
render: function() {
var entry = this.props.entry;
var title = entry.getIn(['data', 'site_title']);
var posts = entry.getIn(['data', 'posts']);
return h('div', {},
h('h1', {}, title),
h('dl', {},
h('dt', {}, 'Posts on Frontpage'),
h('dd', {}, this.props.widgetsFor('posts').getIn(['widgets', 'front_limit']) || 0),
h('dt', {}, 'Default Author'),
h('dd', {}, this.props.widgetsFor('posts').getIn(['data', 'author']) || 'None'),
)
);
}
});
CMS.registerPreviewTemplate("general", GeneralPreview);
</script>
```
### Accessing Metadata
Preview Components also receive an additional prop: `fieldsMetaData`. It contains aditional information (besides the plain plain textual value of each field) that can be useful for preview purposes.
For example, the Relation widget passes the whole selected relation data in `fieldsMetaData`.
```js
export default class ArticlePreview extends React.Component {
render() {
const {entry, fieldsMetaData} = this.props;
const author = fieldsMetaData.getIn(['authors', data.author]);
return <article><h2>{ entry.getIn(['data', 'title']) }</h2>
{author &&<AuthorBio author={author.toJS()}/>}
</article>
}
}
```

39
docs/editorial-workflow.md Executable file
View File

@ -0,0 +1,39 @@
# Editorial Workflow
## Overview
By default, all entries created or edited in the Netlify CMS are committed directly into the main repository branch.
Alternatively, you can enable an optional "Editorial Workflow" mode that allows for more control over the content publishing phases. All unpublished entries will be arranged in a board according to their status, and they can be further reviewed and edited before going live.
![Editorial workflow](https://cloud.githubusercontent.com/assets/33676/19452442/d10d9002-948f-11e6-9463-06955b6c15c8.png)
From a technical perspective, the workflow translates editor UI actions into common Git commands:
Actions in Netlify UI ... | Perform these Git actions
--- | ---
Save draft | Commits to a new branch, and opens a pull request
Edit draft | Pushes another commit to the draft branch/pull request
Approve and publish draft | Merges pull request and deletes branch
## Adding to your site
To enable the editorial workflow, add this line to your `admin/config.yml` file:
``` yaml
publish_mode: editorial_workflow
```
There are no other configuration options right now. There are always three possible statuses, and new branch names are created according to the pattern `cms/collectionName-entrySlug`.
## About metadata
Netlify CMS embraces the idea of Git-as-backend for storing metadata. The first time it runs with the editorial_workflow setup, it creates a new ref called `meta/_netlify_cms`, pointing to an empty, orphan tree.
Actual data are stored in individual `json` files committed to this tree.
## Implementation
Instead of adding logic to `CollectionPage` and `EntryPage`, the Editorial Workflow is implemented as Higher Order Components, adding UI and dispatching additional actions.
Furthermore, all editorial workflow state is managed in Redux - there's an `actions/editorialWorkflow.js` file and a `reducers/editorialWorkflow.js` file.

8
docs/examples.md Normal file
View File

@ -0,0 +1,8 @@
# Examples
Do you have a great example? Submit a pull request to this page.
Name | Tools | Type | Example | More info |
--- | --- | --- | --- | ---
This Developing Journey | middleman | blog | [briandouglas.me](https://briandouglas.me) | [blog post](https://deploy-preview-496--www.netlify.com/blog/2017/04/18/blog-with-middleman-and-the-netlifycms/)
JAMstack Recipes | Hugo, Azure | demo | [jamstack-cms.netlify.com](http://jamstack-cms.netlify.com) | [blog post](http://conductofcode.io/post/managing-content-for-a-jamstack-site-with-netlify-cms/)

113
docs/extending.md Executable file
View File

@ -0,0 +1,113 @@
# Extending With Widgets
The NetlifyCMS exposes an `window.CMS` global object that you can use to register custom widgets, previews, and editor plugins. The available widget extension methods are:
* **registerWidget:** lets you register a custom widget.
* **registerEditorComponent:** lets you add a block component to the Markdown editor.
### Writing React Components inline
The `registerWidget` requires you to provide a React component. If you have a build process in place for your project, it is possible to integrate with this build process.
However, although possible, it may be cumbersome or even impractical to add a React build phase. For this reason, NetlifyCMS exposes two constructs globally to allow you to create components inline: createClass and h (alias for React.createElement).
## `registerWidget`
Register a custom widget.
```js
CMS.registerWidget(name, control, \[preview\])
```
**Params:**
Param | Type | Description
--- | --- | ---
`name` | string | Widget name, allows this widget to be used via the field `widget` property in config
`control` | React.Component \| string | <ul><li>React component that renders the control, receives the following props: <ul><li>**value:** Current field value</li><li>**onChange:** Callback function to update the field value</li></ul></li><li>Name of a registered widget whose control should be used (includes built in widgets).</li></ul>
[`preview`] | React.Component, optional | Renders the widget preview, receives the following props: <ul><li>**value:** Current preview value</li><li>**field:** Immutable map of current field configuration</li><li>**metadata:** Immutable map of any available metadata for the current field</li><li>**getAsset:** Function for retrieving an asset url for image/file fields</li><li>**entry:** Immutable Map of all entry data</li><li>**fieldsMetaData:** Immutable map of metadata from all fields.</li></ul>
* **field:** The field type that this widget will be used for.
* **control:** A React component that renders the editing interface for this field. Two props will be passed:
* **value:** The current value for this field.
* **onChange:** Callback function to update the field value.
* **preview (optional):** A React component that renders the preview of how the content will look. A `value` prop will be passed to this component.
**Example:**
```html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
var CategoriesControl = createClass({
handleChange: function(e) {
this.props.onChange(e.target.value.split(',').map((e) => e.trim()));
},
render: function() {
var value = this.props.value;
return h('input', { type: 'text', value: value ? value.join(', ') : '', onChange: this.handleChange });
}
});
var CategoriesPreview = createClass({
render: function() {
return h('ul', {},
this.props.value.map(function(val, index) {
return h('li', {key: index}, val);
})
);
}
});
CMS.registerWidget('categories', CategoriesControl, CategoriesPreview);
</script>
```
## `registerEditorComponent`
Register a block level component for the Markdown editor:
CMS.registerEditorComponent(definition)
**Params**
* **definition:** The component definition; must specify: id, label, fields, patterns, fromBlock, toBlock, toPreview
**Example:**
```html
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
<script>
CMS.registerEditorComponent({
// Internal id of the component
id: "youtube",
// Visible label
label: "Youtube",
// Fields the user need to fill out when adding an instance of the component
fields: [{name: 'id', label: 'Youtube Video ID', widget: 'string'}],
// Pattern to identify a block as being an instance of this component
pattern: /youtube (\S+)\s/,
// Function to extract data elements from the regexp match
fromBlock: function(match) {
return {
id: match[1]
};
},
// Function to create a text block from an instance of this component
toBlock: function(obj) {
return 'youtube ' + obj.id;
},
// Preview output for this component. Can either be a string or a React component
// (component gives better render performance)
toPreview: function(obj) {
return (
'<img src="http://img.youtube.com/vi/' + obj.id + '/maxresdefault.jpg" alt="Youtube Video"/>'
);
}
});
</script>
```
**Result:**
![youtube-widget](/img/youtube-widget.png)

103
docs/intro.md Executable file
View File

@ -0,0 +1,103 @@
# Introduction
Netlify CMS is a Content Management System for static sites, allowing collaborators to create, edit, review, and publish content without writing code or dealing with version control. It brings the ease of WordPress-style editing to the simplicity and speed of static sites.
At its core, Netlify CMS is an open-source React app that acts as a wrapper for the Git workflow, using the GitHub API. This provides many advantages, including:
* **Fast, web-based UI:** with rich-text editing, real-time preview, and drag-and-drop media uploads.
* **Platform agnostic:** works with most static site generators.
* **Easy installation:** add two files to your site and hook up the backend by including in your build process or linking to our CDN.
* **Modern authentication:** using GitHub and JSON web tokens.
* **Flexible content types:** specify an unlimited number of content types with custom fields.
* **Fully extensible:** create custom-styled previews, UI widgets, and editor plugins.
# Core Concepts
## The Admin Interface
The admin interface is a single-page app with the entry point stored in a static `/admin` folder you add to the root of your site. You can include it with a simple `index.html` file that loads the necessary CSS and JS files from a CDN:
``` html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.css" />
</head>
<body>
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
</body>
</html>
```
The JS is also available via npm and can be integrated into your regular build process.
### Editorial Workflow
Netlify CMS has an optional [editorial workflow](/docs/editorial-workflow) that translates common Git commands into familiar language in a simple UI:
Actions in Netlify UI ... | Perform these Git actions
--- | ---
Save draft | Commits to a new branch, and opens a pull request
Edit draft | Pushes another commit to the draft branch/pull request
Approve and publish draft | Merges pull request and deletes branch
## Configuration
All configuration is handled in a single `config.yml` file, also stored in the `/admin` folder. A simple version might look like this:
``` yaml
backend:
name: github
repo: owner/repo # Path to your Github repository
branch: master # Branch to update (master by default)
media_folder: "img/uploads" # Folder where user uploaded files should go
collections: # A list of collections the CMS should be able to edit
- name: "post" # Used in routes, e.g., /admin/collections/:slug/edit
label: "Post" # Used in the UI, e.g., "New Post"
folder: "_posts" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
fields: # The fields each document in this collection have
- {label: "Title", name: "title", widget: "string", tagname: "h1"}
- {label: "Body", name: "body", widget: "markdown"}
- {label: "Foo", name: "foo", widget: "foo"}
- {label: "Publish Date", name: "date", widget: "datetime"}
```
### Backend
Because Netlify CMS is a wrapper for the GitHub API, the "backend" is a repo stored on GitHub. *(Using a different Git host? File a [feature request](https://github.com/netlify/netlify-cms/issues), or [help us add it](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md)!)* For authentication, you can connect to GitHub using Netlifys [Authentication Provider feature](https://www.netlify.com/docs/authentication-providers), or you can roll your own.
### Collections
All content managed by Netlify CMS is organized in Collections (groups of files), such as:
* blog posts
* portfolio samples
* product listings
* podcast episodes
You point to where the files are stored, and specify the fields that define them. The `body` field typically stores the main text of a file, while any other fields are included at the top of the document in the front matter. They can be required, optional, or hidden, and can have preset defaults.
### Widgets
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in [widgets](/docs/widgets).
## Customization
Netlify CMS exposes a `window.CMS` global object that you can use to register custom widgets, previews, and editor plugins. The available methods are:
* `registerPreviewStyle`: register a custom stylesheet to match the editor preview pane to your site style.
* `registerPreviewTemplate`: registers a template to determine how fields are displayed in the preview, customizable for each collection.
* `registerWidget`: registers a custom widget.
* `registerEditorComponent`: adds a block component to the Markdown editor.

233
docs/quick-start.md Executable file
View File

@ -0,0 +1,233 @@
# Quick Start
Netlify CMS is adaptable to a wide variety of projects. The only inflexible requirement is that your site content must be written in markdown, JSON, YAML, or TOML files, stored in a repo on [GitHub](https://github.com/). (If you're partial to another Git hosting service, check out the PRs in progress for [GitLab](https://github.com/netlify/netlify-cms/pull/517) and [Bitbucket](https://github.com/netlify/netlify-cms/pull/525) support.)
In this guide, we're going to assume you're using a [static site generator](https://www.staticgen.com/), like Jekyll, Hugo, Hexo, or Gatsby.
## App File Structure
All Netlify CMS files are contained in a static `admin` folder, stored at the root of your published site. Where you store this folder in the source files depends on your static site generator. Here's the the static file location for a few of the most popular static site generators:
These generators ... | store static files in
--- | ---
Jekyll, GitBook | `/` (project root)
Hugo, Gatsby* | `/static`
Hexo, Middleman | `/source`
Spike | `/views`
Notes:
- Gatsby treats the `static` folder more strictly and will not render the admin page as the other generators. You will have to make a [page component](https://www.gatsbyjs.org/docs/building-with-components/) containing the necessary scripts of the Netlify CMS app in the admin page. However, the `config.yml` file (described below) can be placed in the `static` folder as with other generators.
If your generator isn't listed here, you can check its documentation, or as a shortcut, look in your project for a `css` or `images` folder. The contents of folders like that are usually processed as static files, so it's likely you can store your `admin` folder next to those. (When you've found the location, feel free to add it to these docs by [filing a pull request](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md)!)
Inside the `admin` folder, you'll create two files:
```
admin
├ index.html
└ config.yml
```
The first file, `admin/index.html`, is the entry point for the Netlify CMS admin interface. This means that users can navigate to `yoursite.com/admin` to access it. On the code side, it's a basic HTML starter page that loads the necessary CSS and JavaScript files. In this example, we pull those files from a public CDN:
``` html
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Content Manager</title>
<!-- Include the styles for the Netlify CMS UI, after your own styles -->
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.css" />
</head>
<body>
<!-- Include the script that builds the page and powers Netlify CMS -->
<script src="https://unpkg.com/netlify-cms@^0.7.0/dist/cms.js"></script>
</body>
</html>
```
The second file, `admin/config.yml`, is the heart of your Netlify CMS installation, and a bit more complex. The next section covers the details.
## Configuration
Configuration will be different for every site, so we'll break it down into parts. All code snippets in this section will be added to your `admin/config.yml` file.
### Backend
Because we're using GitHub and Netlify for our hosting and authentication, backend configuration is fairly strightforward. You can start your `config.yml` file with these lines:
``` yaml
backend:
name: git-gateway
branch: master # Branch to update (optional; defaults to master)
```
These lines specify your backend protocol and your publication branch. Git Gateway is an open source API that acts as a proxy between authenticated users of your site and your site repo. (We'll get to the details of that in the [Authentication section](#authentication) below.) If you leave out the `branch` declaration, it will default to `master`.
### Editorial Workflow
By default, saving a post in the CMS interface will push a commit directly to the publication branch specified in `backend`. However, you also have the option to enable the [Editorial Workflow](/docs/editorial-workflow), which adds an interface for drafting, reviewing, and approving posts. To do this, add the following line to your `config.yml`:
``` yaml
publish_mode: editorial_workflow
```
### Media and Public Folders
Netlify CMS allows users to upload images directly within the editor. For this to work, the CMS needs to know where to save them. If you already have an `images` folder in your project, you could use its path, possibly creating an `uploads` sub-folder, for example:
``` yaml
media_folder: "images/uploads" # Media files will be stored in the repo under images/uploads
```
If you're creating a new folder for uploaded media, you'll need to know where your static site generator expects static files. You can refer to the paths outlined above in [App File Structure](#app-file-structure), and put your media folder in the same location where you put the `admin` folder.
Note that the`media_folder` file path is relative to the project root, so the example above would work for Jekyll, GitBook, or any other generator that stores static files at the project root. However, it would not work for Hugo, Hexo, Middleman or others that store static files in a subfolder. Here's an example that could work for a Hugo site:
``` yaml
media_folder: "static/images/uploads" # Media files will be stored in the repo under static/images/uploads
public_folder: "/images/uploads" # The src attribute for uploaded media will begin with /images/uploads
```
The configuration above adds a new setting, `public_folder`. While `media_folder` specifies where uploaded files will be saved in the repo, `public_folder` indicates where they can be found in the published site. This path is used in image `src` attributes and is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
*If `public_folder` is not set, Netlify CMS will default to the same value as `media_folder`, adding an opening `/` if one is not included.*
### Collections
Collections define the structure for the different content types on your static site. Since every site is different, the `collections` settings will be very different from one site to the next.
Let's say your site has a blog, with the posts stored in `_posts/blog`, and files saved in a date-title format, like `1999-12-31-lets-party.md`. Each post begins with settings in yaml-formatted front matter, like so:
``` yaml
---
layout: blog
title: "Let's Party"
date: 1999-12-31 11:59:59 -0800
thumbnail: "/images/prince.jpg"
rating: 5
---
This is the post body, where I write about our last chance to party before the Y2K bug destroys us all.
```
Given this example, our `collections` settings would look like this:
``` yaml
collections:
- name: "blog" # Used in routes, e.g., /admin/collections/blog
label: "Blog" # Used in the UI
folder: "_posts/blog" # The path to the folder where the documents are stored
create: true # Allow users to create new documents in this collection
slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
fields: # The fields for each document, usually in front matter
- {label: "Layout", name: "layout", widget: "hidden", default: "blog"}
- {label: "Title", name: "title", widget: "string"}
- {label: "Publish Date", name: "date", widget: "datetime"}
- {label: "Featured Image", name: "thumbnail", widget: "image"}
- {label: "Rating (scale of 1-5)", name: "rating", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"}
```
Let's break that down:
<table>
<tr>
<td><code>name</code></td>
<td>Post type identifier, used in routes. Must be unique.</td>
</tr>
<tr>
<td><code>label</code></td>
<td>What the post type will be called in the admin UI.</td>
</tr>
<tr>
<td><code>folder</code></td>
<td>Where files of this type are stored, relative to the repo root.</td>
</tr>
<tr>
<td><code>create</code></td>
<td>Set to <code>true</code> to allow users to create new files in this collection.
</td>
</tr>
<tr>
<td><code>slug</code></td>
<td>Template for filenames. <code>{{year}}</code>, <code>{{month}}</code>, and <code>{{day}}</code> will pull from the post's <code>date</code> field or save date. <code>{{slug}}</code> is a url-safe version of the post's <code>title</code>. Default is simply <code>{{slug}}</code>.
</td>
</tr>
<tr>
<td><code>fields</code></td>
<td>Fields listed here are shown as fields in the content editor, then saved as front matter at the beginning of the document (except for <code>body</code>, which follows the front matter). Each field contains the following properties:
<ul>
<li><code>label</code>: Field label in the editor UI.</li>
<li><code>name</code>: Field name in the document front matter.</li>
<li><code>widget</code>: Determines UI style and value data type (details below).</li>
<li><code>default</code> (optional): Sets a default value for the field.</li>
</ul>
</td>
</tr>
</table>
As described above, the `widget` property specifies a built-in or custom UI widget for a given field. When a content editor enters a value into a widget, that value will be saved in the document front matter as the value for the `name` specified for that field. A full listing of available widgets can be found in the [Widgets doc](/docs/widgets).
Based on this example, you can go through the post types in your site and add the appropriate settings to your `config.yml` file. Each post type should be listed as a separate node under the `collections` field.
### Filter
The entries for any collection can be filtered based on the value of a single field. The example collection below would only show post entries with the value "en" in the language field.
``` yaml
collections:
- name: "posts"
label: "Post"
folder: "_posts"
filter:
field: language
value: en
fields:
- {label: "Language", name: "language"}
```
## Authentication
Now that you have your Netlify CMS files in place and configured, all that's left is to enable authentication. There are [many ways to do this](/docs/custom-authentication) (with or without deploying to Netlify), but this example uses Netlify because it's one of the quickest ways to get started.
### Setup on Netlify
Netlify offers a built-in authentication service called Identity. In order to use it, you'll need to connect your site repo with Netlify. Netlify has published a general [Step-by-Step Guide](https://www.netlify.com/blog/2016/10/27/a-step-by-step-guide-deploying-a-static-site-or-single-page-app/) for this, along with detailed guides for many popular static site generators, including [Jekyll](https://www.netlify.com/blog/2015/10/28/a-step-by-step-guide-jekyll-3.0-on-netlify/), [Hugo](https://www.netlify.com/blog/2016/09/21/a-step-by-step-guide-victor-hugo-on-netlify/), [Hexo](https://www.netlify.com/blog/2015/10/26/a-step-by-step-guide-hexo-on-netlify/), [Middleman](https://www.netlify.com/blog/2015/10/01/a-step-by-step-guide-middleman-on-netlify/), [Gatsby](https://www.netlify.com/blog/2016/02/24/a-step-by-step-guide-gatsby-on-netlify/) and more.
### Enable Identity and Git Gateway
Netlify's Identity and Git Gateway services allow you to manage CMS admin users for your site without requiring them to have GitHub accounts or commit access on your repo. From your site dashboard on Netlify:
1. Go to **Settings > Identity**, and select **Enable Identity service**.
2. Under **Registration preferences**, select **Open** or **Invite only**. In most cases, you'll want only invited users to access your CMS, but if you're just experimenting, you can leave it open for convenience.
3. If you'd like to allow one-click login with services like Google and GitHub, check the boxes next to the services you'd like to use, under **External providers**.
4. Scroll down to **Services > Git Gateway**, and click **Enable Git Gateway**. This will authenticate with GitHub and generate a GitHub API access token. In this case, we're leaving the **Roles** field blank, which means any logged in user may access the CMS. For information on changing this, check the [Netlify Identity documentation](https://www.netlify.com/docs/identity/).
### Add the Netlify Identity Widget
With the backend set to handle authentication, now you need a frontend interface to connect to it. The open source Netlify Identity Widget is a drop-in widget made for just this purpose. To include the widget in your site, you'll need to add the following script tag in two places:
```html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
```
You'll need to add this to the `<head>` of your CMS index page at `/admin/index.html`, as well as the `<head>` of your site's main index page. Depending on how your site generator is set up, this may mean you need to add it to the default template, or to a "partial" or "include" template. If you can find where the site stylesheet is linked, that's probably the right place. Alternatively, you can include the script in your site using Netlify's [Script Injection](https://www.netlify.com/docs/inject-analytics-snippets/) feature.
When a user logs in with the Netlify Identity widget, they will be directed to the site homepage with an access token. In order to complete the login and get back to the CMS, we'll need to redirect the user back to the `/admin` path. To do this, add the following script before the closing `body` tag of your site's main index page:
```html
<script>
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
window.netlifyIdentity.on("login", () => {
document.location.href = "/admin/";
});
}
});
}
</script>
```
Note: This example script requires modern JavaScript and will not work on IE11. For legacy browser support, use function expressions (`function () {}`) in place of the arrow functions (`() => {}`), or use a transpiler like [Babel](https://babeljs.io/).
## Accessing the CMS
Your site CMS is now fully configured and ready for login!
If you set your registration preference to "Invite only," you'll need to invite yourself (and anyone else you choose) as a site user. To do this, select the **Identity** tab from your site dashboard, and then select the **Invite users** button. Invited users will receive an email invitation with a confirmation link. Clicking the link will take you to your site with a login prompt.
If you left your site registration open, or for return visits after comfirming an email invitation, you can access your site's CMS at `yoursite.com/admin`.
Happy posting!

28
docs/test-drive.md Normal file
View File

@ -0,0 +1,28 @@
# Take a test drive
Netlify CMS can run in any frontend web environment, but the quickest way to try it out is by running it on a pre-configured starter site with Netlify. Our example here is the [Kaldi coffee company template](https://github.com/netlify-templates/one-click-hugo-cms). Use the button below to build and deploy your own copy of the repository:
[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/netlify-templates/one-click-hugo-cms&stack=cms)
After clicking that button, youll authenticate with GitHub and choose a repository name. Netlify will then automatically create a repository in your GitHub account with a copy of the files from the template. Next, it will build and deploy the new site on Netlify, bringing you to the site dashboard when the build is complete. Next, youll need to set up Netlify's [Identity](https://www.netlify.com/docs/identity) service to authorize users to log in to the CMS.
## Adding users
1. From the Netlify site dashboard, select the **Identity** tab, and you'll find that there are no users yet. By default, this site is set to accept users by invitation only, and even the site owner needs to be invited.
2. Select **Invite users**, enter your email address, and select Send.
3. Check your email for the invitation. It will be sent from `no-reply@netlify.com`.
![Sample email subject line: You've been invited to join radiologist-amanda-53841.netlify.com](/img/email-subject.png?raw=true)
4. Click the link to accept the invite, and youll be directed to your new site, with a prompt to create a password.
!["Complete your signup" modal on the Kaldi coffee site](/img/create-password.png?raw=true)
5. Enter a password, sign in, and youll be directed straight to the CMS!
Try adding and editing posts, or changing the content of the Products page. When you save, the changes will be pushed immediately to GitHub, triggering a build on Netlify, and updating the content on your site.
## More paths to explore
- If youd like to learn more about how it all works, check out the [Intro](/docs/intro).
- To see how to integrate Netlify CMS into an existing project, go to the [Quick Start](/docs/quick-start).
- If youd like to change how users log in to your site, read up on [Netlify Identity service](https://www.netlify.com/docs/identity).

54
docs/validation.md Executable file
View File

@ -0,0 +1,54 @@
# Collection Field Validation
## Available validations to use on `config.yml`:
- Presence: By default all widgets are required, unless specified in the config. Example:
`- {label: "Subtitle", name: "subtitle", widget: "string", required: false}`
- Pattern: Field configuration can specify a regex pattern with the appropriate error message. Example:
`- {label: "Title", name: "title", widget: "string", pattern: ['.{10,}', "Should have more than 10 characters"] }`
## Advanced Guide (For widget authors)
The widget control can optionally implement an `isValid` method to perform custom validations, in addition to presence and pattern. The `isValid` method will be automatically called, and it can return either a boolean value, an object with an error message or a promise. Examples:
**Boolean**
No errors:
```javascript
isValid = () => {
// Do internal validation
return true;
};
```
Existing error:
```javascript
isValid = () => {
// Do internal validation
return false;
};
```
**Object with `error` (useful for returning custom error messages)**
Existing error:
```javascript
isValid = () => {
// Do internal validation
return { error: 'Your error message.' };
};
```
**Promise**
You can also return a promise from `isValid`. While the promise is pending, the widget will be marked as "in error". When the promise resolves, the error is automatically cleared.
```javascript
isValid = () => {
return this.existingPromise;
};
```
Note: Do not create a promise inside `isValid` - `isValid` is called right before trying to persist. This means that even if a previous promise was already resolved, when the user hits 'save', `isValid` will be called again. If it returns a new promise, it will be immediately marked as "in error" until the new promise resolves.

101
docs/widgets.md Normal file
View File

@ -0,0 +1,101 @@
# Configuring your site
## Widgets
Widgets define the data type and interface for entry fields. Netlify CMS comes with several built-in widgets, including:
| Name | UI | Data Type |
| -------- | ---------------------------------- | ---------------------------------------------------|
| `string` | text input | string |
| `boolean` | toggle switch | boolean |
| `text` | textarea input | string (multiline) |
| `number` | number input | number |
| `markdown` | rich text editor | string (markdown) |
| `datetime` | date picker | string (ISO date) |
| `select` | select input (dropdown) | string |
| `image` | file picker w/ drag-and-drop | image file |
| `file` | file picker w/ drag-and-drop | file |
| `hidden` | none | string |
| `object` | group of other widgets | Immutable Map containing field values |
| `list` | repeatable group of other widgets | Immutable List of objects containing field values |
| `relation` | text input w/ suggestions dropdown | value of `valueField` in related entry (see below) |
Were always adding new widgets, and you can also [create your own](/docs/extending)!
### List Widget
The list widget allows you to map a user-provided string with a comma delimiter into a list. Consider the following example that also demonstrates how to set default values:
```yaml
collections:
- name: posts
label: Post
folder: "_posts"
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
create: true
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Body, name: body, widget: markdown}
- {label: Categories, name: categories, widget: list}
- {label: Tags, name: tags, widget: list, default: ['term_1', 'term_2']}
```
Lists of objects are supported as well and require a nested field list.
```yaml
collections:
- name: posts
label: Post
folder: "_posts"
slug: "{{year}}-{{month}}-{{day}}-{{slug}}"
create: true
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Body, name: body, widget: markdown}
- name: authors
label: Authors
widget: list
fields:
- {label: Name, name: name, widget: string}
- {label: Description, name: description, widget: markdown}
```
### Relation Widget
The relation widget allows you to reference an existing entry from within the entry you're editing. It provides a search input with a list of entries from the collection you're referencing, and the list automatically updates with matched entries based on what you've typed.
The following field configuration properties are specific to fields using the relation widget:
Property | Accepted Values | Description
--- | --- | ---
`collection` | string | name of the collection being referenced
`searchFields` | list | one or more names of fields in the referenced colleciton to search for the typed value
`valueField` | string | name a field from the referenced collection whose value will be stored for the relation
`name` | text input | string
Let's say we have a "posts" collection and an "authors" collection, and we want to select an author for each post - our config might look something like this:
```yaml
collections:
- name: authors
label: Authors
folder: "authors"
create: true
fields:
- {name: name, label: Name}
- {name: twitterHandle, label: "Twitter Handle"}
- {name: bio, label: Bio, widget: text}
- name: posts
label: Posts
folder: "posts"
create: true
fields:
- {name: title, label: Title}
- {name: body, label: Body, widget: markdown}
- name: author
label: Author
widget: relation
collection: authors
searchFields: [name, twitterHandle]
valueField: name
```

BIN
img/create-password.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

BIN
img/email-subject.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
img/github-oauth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB