Migrate netlify-cms-www site into this repo (#860)

* Add frontmatter to docs files (prep to move)

* Move docs into position for website migration

* Migrate website from netlify-cms-www

Some modifications, including most of the changes in https://github.com/netlify/netlify-cms-www/pull/58 (previously reverted).

Also updated the readme and added hugo-bin for quicker onboarding of new docs contributors.

* Remove netlify.toml

This allows separate build commands for cms-demo and netlifycms.org.

* Remove website/netlify.toml

May re-add later, but it's not doing anything for now.

* Remove unused content file
This commit is contained in:
Jessica Parsons
2017-12-04 16:42:20 -08:00
committed by GitHub
parent 8e529ee7cc
commit 155f40e5e4
85 changed files with 9616 additions and 18 deletions

6
website/site/config.yaml Executable file
View File

@ -0,0 +1,6 @@
baseurl: "/"
languageCode: "en-us"
title: "Netlify CMS | Open-Source Content Management System"
disable404: true
pluralizeListTitles: false
metaDataFormat: "yaml"

0
website/site/content/.keep Executable file
View File

View File

@ -0,0 +1,65 @@
---
title: Architecture
position: 90
---
# 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,97 @@
---
title: Authentication & Backends
position: 25
---
# Authentication & Backends
Netlify CMS stores content in your GitHub repository. (GitLab and Bitbucket coming soon!) In order for this to work, you need to authenticate with GitHub, and that requires a server. We have a few options for handling this.
## Git Gateway with Netlify Identity
[Git Gateway](https://github.com/netlify/git-gateway) is a Netlify open source project that allows you to add editors to your site CMS without giving them direct push access to your GitHub repository. [Netlify Identity](https://www.netlify.com/docs/identity/) service handles the authentication and provides a simple interface for user management. The Netlify CMS [Test Drive](/test-drive/) is a working example of this backend.
To use it in your own project, follow these steps:
1. Head over to the [Netlify Identity docs](https://www.netlify.com/docs/identity) and follow the
steps to get started.
2. Add the following lines to your `config.yml` file:
``` yaml
backend:
name: git-gateway
accept_roles: #optional - accepts all users if left out
- admin
- editor
```
3. Optionally, you can assign roles to users in your Netlify dashboard, and then limit which
roles can access the CMS by defining the `accept_roles` field in the `config.yml` example above.
Otherwise `accept_roles` can be left out, and all Netlify Identity users on your site will have access.
## Git Gateway without Netlify
You can use [Git Gateway](https://github.com/netlify/git-gateway) without Netlify by setting up your own Git Gateway server and connecting it with your own instance of [GoTrue](https://www.gotrueapi.org) (the open source microservice that powers Netlify Identity), or with any other identity service that can issue JSON Web Tokens (JWT).
To configure in Netlify CMS, use the same `backend` settings in your `config.yml` file as described in Step 2 of the [Git Gateway with Netlify Identity](#git-gateway-with-netlify-identity) instructions above.
## GitHub Backend
The GitHub backend allows CMS users to log in directly with their GitHub account. Note that the
user's GitHub account must have push access to your content repository for this to work.
Because Github [requires a
server](https://github.com/netlify/netlify-cms/issues/663#issuecomment-335023723) for
authentication, Netlify facilitates basic GitHub authentication.
To enable it:
1. Follow the authentication provider setup steps in the [Netlify
docs](https://www.netlify.com/docs/authentication-providers/#using-an-authentication-provider).
2. Add the following lines to your `config.yml` file:
``` yaml
backend:
name: github
repo: owner-name/repo-name # Path to your Github repository
```
### External OAuth Clients
If you would like to facilitate your own OAuth authentication rather than use Netlify's service, you
can use one of the community-maintained projects below. Feel free to [submit a pull request](https://github.com/netlify/netlify-cms/blob/master/CONTRIBUTING.md) if you'd like to add yours!
| Author | Supported Git hosts | Languages | Link |
|----------------------------------------|---------------------------|-----------|------------------------------------------------------------------------------|
| [@vencax](https://github.com/vencax) | GitHub, GitHub Enterprise | Node.js | [Repo](https://github.com/vencax/netlify-cms-github-oauth-provider) |
| [@igk1972](https://github.com/igk1972) | GitHub, GitHub Enterprise | Go | [Repo](https://github.com/igk1972/netlify-cms-oauth-provider-go) |
Check each project's documentation for instructions on how to configure it.
## Bitbucket and GitLab Support
Netlify CMS is meant to be platform agnostic, so were always looking to expand the ecosystem and
find new ways to use it. Check out our active PRs in progress for
[Bitbucket](https://github.com/netlify/netlify-cms/pull/525) and
[Gitlab](https://github.com/netlify/netlify-cms/pull/517) backends.
Git Gateway could also be modified to support these Git hosts. If you're interested, you can file an
issue (or a pull request!) in the [git-gateway repo](https://github.com/netlify/git-gateway).
## Options
Both `git-gateway` and `github` backends allow some additional optional fields for certain use
cases. A full reference is below. Note that these are properties of the `backend` field, and should
be nested under that field.
| Field | Default | Description |
|----------------|-------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `repo` | none | **Required** for `github` backend; ignored by `git-gateway`. Follows the pattern `[org-or-username]/[repo-name]`. |
| `accept_roles` | none | `git-gateway` only. Limits CMS access to your defined array of user roles. Omitting this field gives access to all registered users. |
| `branch` | `master` | The branch where published content is stored. All CMS commits and PRs are made to this branch. |
| `api_root` | `https://api.github.com` (ignored for `git-gateway` backend) | The API endpoint. Only necessary in certain cases, like with GitHub Enterprise. |
| `site_domain` | `location.hostname` (or `cms.netlify.com` when on `localhost`) | Sets the `site_id` query param sent to the API endpoint. Non-Netlify auth setups will often need to set this for local development to work properly. |
| `base_url` | `https://api.netlify.com` | OAuth client URL for the `github` backend. **Required** when using an external OAuth server with the `github` backend. |

View File

@ -0,0 +1,14 @@
---
title: Contributing
position: 100
---
# 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,199 @@
---
title: Custom Previews
position: 50
---
# 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>
}
}
```

View File

@ -0,0 +1,44 @@
---
title: Editorial Workflow
position: 40
---
# 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.

View File

@ -0,0 +1,13 @@
---
title: Examples
position: 110
---
# 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/)

View File

@ -0,0 +1,117 @@
---
title: Extending Widgets
position: 60
---
# 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)

View File

@ -0,0 +1,107 @@
---
title: Introduction
position: 0
---
# 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.

View File

@ -0,0 +1,235 @@
---
title: Quick Start
position: 20
---
# 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`
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!

View File

@ -0,0 +1,33 @@
---
title: Test Drive
position: 10
---
# 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).

View File

@ -0,0 +1,59 @@
---
title: Validation
position: 70
---
# 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.

View File

@ -0,0 +1,132 @@
---
title: Widgets
position: 30
---
# 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)!
### Select Widget
The select widget allows you to pick a string value from a drop down menu
```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: Align Content, name: align, widget: select, options: ['left', 'center', 'right']}
```
### 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
```
### Date Widget / DateTime Widget
The date and datetime widgets provide date or datetime pickers when the widget is active. The resulting date string can be formatted (uses Moment.js), and accepts a default value. The initial value will be the current date unless `false` or an empty string are provided as the default value.
The following field configuration properties are specific to fields using the date or datetime widget:
Property | Accepted Values | Description
--- | --- | ---
`format` | string | format string uses Moment.js [tokens](https://momentjs.com/docs/#/parsing/string-format/)
`default` | boolean, string | can be a date string, or else an empty string - defaults to current date

View File

@ -0,0 +1,29 @@
---
title: Community
layout: community
url: /community
headline: Be a part of building the CMS of the future.
subhead: We're serious about being community driven, so everyone is welcome to join our open, bi-weekly planning sessions.
primarycta: "[Register on Eventbrite →](https://www.eventbrite.com/e/netlify-cms-planning-session-bi-weekly-tickets-35794058994)"
upcomingevent:
hook: The next development planning session is on
howitworks: Every other week we have public development planning sessions. They're web based, last about an hour, and are geared toward contributors and those interested in contributing. Sessions currently take place every other Wednesday, 9am - 10am PT.
howtojoin: |
**On the web:**
1. https://bluejeans.com/278173690
**By phone:**
1. [+1.408.740.7256](tel:+14087407256) (United States)
[+1.408.317.9253](tel:+14083179253) (Alternate number)
(http://bluejeans.com/numbers)
2. Enter Meeting ID: 278173690
---

0
website/site/data/.keep Executable file
View File

View File

@ -0,0 +1,13 @@
---
styleoverrides: '/docs.css'
headline: Netlify builds, deploys, and hosts your front end.
bottomcta:
hook: Want to get started quick?
btns:
- type: primary
btntext: View our Templates
linksto: https://app.netlify.com/signup/templates
- type: secondary
btntext: Read our Tutorials
linksto: /tags/tutorial/
---

View File

@ -0,0 +1,13 @@
---
footer:
hook: |
Host faster.</br>
Manage easier.</br>
Smile bigger.
headline: "Netlify is your one-stop web dev solution."
body: "Vestibulum rutrum quam vitae fringilla tincidunt. Suspendisse nec tortor urna. Ut laoreet sodales nisi, quis iaculis nulla iaculis vitae. Donec sagittis faucibu."
cta:
secondarycta:
copy: "Try it now FREE"
link: "/intro"
---

View File

@ -0,0 +1,50 @@
---
hero:
headline: An open-source CMS for your Git workflow
subhead: |
- Content management in a single-page app
- Built in React.js with extensible components
- Integrate with any build tool
- Plug in to any static site generator
ctas: |
- [Take it for a test drive →](/docs/test-drive)
productvideo:
thumbnail: "/img/hero-graphic.jpg"
mp4: "/img/demo.mp4"
ogg: "/img/demo.ogg"
webm: "/img/demo.webm"
thanksdevs: "**Thanks to all our contributors.**Keep shooting for the stars with Netlify CMS."
collab:
hook: "Integrate the roles of developers, writers, and editors."
body: "Writers can focus on writing. Editors can approve content and publish with ease. Developers can use their favorite tools and libraries. **Netlify CMS keeps all of their contributions together, in Git.**"
graphicpath: "/img/collab.svg"
featureshook: "Designed with developers in mind - its in our DNA."
features:
- feature: "Fast, web-based UI"
description: "Built with React, featuring rich-text editing, real-time preview, and drag-and-drop media uploads."
- feature: "Platform agnostic"
description: "Works with most static site generators for sites stored in GitHub."
- feature: "Easy installation"
description: "Add two files to your site, include or link to the JS, and add your custom configuration."
- feature: "Modern authentication"
description: "Connect with Netflify's authentication service or roll your own, using GitHub and JSON web tokens."
- feature: "Flexible content types"
description: "Specify an unlimited number of content types with custom fields."
- feature: "Fully extensible"
description: "Create custom-styled previews, UI widgets, and editor plugins."
featuresgraphic: "/img/helix.svg"
inspiration: |
Netlify CMS is part of a long evolution in content management for the web. Even in the budding realm of the [JAMstack](https://www.jamstack.org) and [static site generators](https://www.staticgen.com), developers have a variety [“headless” CMS](http://www.headlesscms.org) options to choose from.
Proprietary services like Contentful and DatoCMS provide the ease and polish of a concierge provider, while simple open source projects like Prose and Google Drive CMS provide targeted functionality for limited use cases. We love that these other projects help advance the modern web, and we believe there will always be a place for them in the JAMstack ecosystem.
With Netlify CMS, we hope to carve a different niche. We aim to do for the JAMstack what WordPress did for dynamic sites back in the day. In other words, we want to build a CMS that is open-source but fully-featured and production-ready, thats as easy to customize as it is to use, and that developers and content editors can build a community around.
[Help us make it happen!](/docs/contributor-guide)
---

View File

@ -0,0 +1,16 @@
notifications:
- loud: true
message: >-
Register to join us online for our next community dev meeting, every other
Wednesday at 9am-10am PT!
published: false
title: Netlify CMS Development Planning Sessions Promo
url: >-
https://www.eventbrite.com/e/netlify-cms-planning-session-bi-weekly-tickets-35794058994
- loud: true
message: >-
We have a community on Gitter - join now to ask questions and discuss the
project with other devs!
published: true
title: Gitter shoutout
url: 'https://gitter.im/netlify/netlifycms'

View File

@ -0,0 +1,35 @@
{{ partial "header" . }}
<div class="docs page">
<div class="container">
<aside id="sidebar" class="sidebar">
<nav class="docs-nav" id="docs-nav">
{{ range sort .Data.Pages "Params.position" }}
<a href="{{ .Permalink }}" class="nav-link {{ if eq .Params.position 0 }}active{{ end }}">{{ .Title }}</a>
{{ end }}
</nav>
<div class="mobile docs-nav">
<select class="btn-primary" id="mobile-docs-nav">
<option value="" selected="selected">Select A Topic</option>
{{ range sort .Data.Pages "Params.position" }}
<option value="{{ .Permalink }}" class="nav-link {{ if eq .Params.position 0 }}active{{ end }}">{{ .Title }}</option>
{{ end }}
</select>
</div>
</aside>
<article class="docs-content" id="docs-content">
{{ range sort .Data.Pages "Params.position" }}
{{ if eq .Params.position 0 }}
<div>
<p>
{{ partial "edit-link" . }}
</p>
{{ .Content }}
</div>
{{ end }}
{{ end }}
</article>
</div>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,30 @@
{{ partial "header" . }}
{{ $activePage := .Params.position }}
<div class="docs detail page">
<div class="container">
<aside id="sidebar" class="sidebar">
<nav class="docs-nav" id="docs-nav">
{{ $subjects := where .Site.Pages "Section" "docs" }}
{{ range sort $subjects "Params.position" }}
<a href="{{ .Permalink }}" class="nav-link {{ if eq .Params.position $activePage }}active{{ end }}">{{ .Title }}</a>
{{ end }}
</nav>
<div class="mobile docs-nav">
<select class="btn-primary" id="mobile-docs-nav">
<option value="" selected="selected">Select A Topic</option>
{{ $subjects := where .Site.Pages "Section" "docs" }}
{{ range sort $subjects "Params.position" }}
<option value="{{ .Permalink }}" class="nav-link {{ if eq .Params.position $activePage }}active{{ end }}">{{ .Title }}</option>
{{ end }}
</select>
</div>
</aside>
<article class="docs-content" id="docs-content">
{{ partial "edit-link" . }}
{{ .Content }}
</article>
</div>
</div>
{{ partial "footer" . }}

69
website/site/layouts/index.html Executable file
View File

@ -0,0 +1,69 @@
{{ partial "header" . }}
<div class="landing page">
<section class="hero">
<div class="contained">
<div class="hero-copy">
<h1 class="headline">{{ .Site.Data.landing.hero.headline | markdownify }}</h1>
<h2 class="subhead">{{ .Site.Data.landing.hero.subhead | markdownify }}</h2>
<h3 class="ctas">{{ .Site.Data.landing.hero.ctas | markdownify }}</h3>
<p class="mooseheads">
<strong>As seen on...</strong>
<img src="/img/smashing-logo.svg"/>
</p>
</div>
<div class="hero-graphic">
<img src="/img/demo.gif" class="responsive"/>
<!--<p class="thanks-devs">{{ .Site.Data.landing.thanksdevs | markdownify }}</p>-->
</div>
</div>
</section>
<section class="collab">
<div class="container">
<h1>{{ .Site.Data.landing.collab.hook | markdownify }}</h1>
<div class="row">
<div class="collab-graphic">
<img src="{{ .Site.Data.landing.collab.graphicpath }}" class="responsive"/>
</div>
<div class="collab-copy">
<p>{{ .Site.Data.landing.collab.body | markdownify }}</p>
</div>
</div>
</div>
</section>
<section class="features">
<div class="container">
<h1>{{ .Site.Data.landing.featureshook | markdownify }}</h1>
{{ $featuresgraphic := .Site.Data.landing.featuresgraphic }}
<div class="features-column">
{{ range $index, $id := .Site.Data.landing.features }}
<div class="feature">
<h3>{{ .feature | markdownify }}</h3>
<p>{{ .description | markdownify }}</p>
</div>
{{ if eq $index 2 }}
</div>
<div class="features-graphic">
<img src="{{ $featuresgraphic }}" class="responsive"/>
</div>
<div class="features-column">
{{ end }}
{{ end }}
</div>
</div>
</section>
<section class="inspiration">
<div class="container">
<h1>Our Inspiration</h1>
<p>{{ .Site.Data.landing.inspiration | markdownify }}</p>
</div>
</section>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,38 @@
{{ partial "header" . }}
<div class="community page">
<section class="hero">
<div class="contained">
<div class="hero-copy">
<h1 class="headline">{{ .Params.headline | markdownify }}</h1>
<h2 class="subhead">{{ .Params.subhead | markdownify }}</h2>
<h3 class="ctas"><ul><li>{{ .Params.primarycta | markdownify }}</li></ul></h3>
</div>
<div class="calendar-cta">
<h2>{{ .Params.upcomingevent.hook }}</h2>
<div class="calendar">
<div class="month"></div>
<div class="day"></div>
</div>
<h2><strong></strong> at <strong></strong></h2>
<div class="cal-cta">{{ .Params.primarycta | markdownify }}</div>
</div>
</div>
</section>
<section class="how-it-works clearfix">
<div class="contained">
<div class="half">
<h4 class="section-label">How it works</h4>
<p>{{ .Params.howitworks | markdownify }}</p>
<h4 class="section-label">How to join</h4>
<p>{{ .Params.howtojoin | markdownify }}</p>
</div>
</div>
</section>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,10 @@
<a class="edit-this-page" href="https://github.com/netlify/netlify-cms/blob/master/website/site/content/{{ .File.Path }}">
<svg version="1.1" id="pencil" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="14px" height="14px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path d="M398.875,248.875L172.578,475.187l-22.625-22.625L376.25,226.265L398.875,248.875z M308.375,158.39L82.063,384.687
l45.266,45.25L353.625,203.64L308.375,158.39z M263.094,113.125L36.828,339.437l22.625,22.625L285.75,135.765L263.094,113.125z
M308.375,67.875L285.719,90.5L421.5,226.265l22.625-22.625L308.375,67.875z M376.25,0L331,45.25l135.75,135.766L512,135.781
L376.25,0z M32,453.5V480h26.5L32,453.5 M0,376.25L135.766,512H0V376.25L0,376.25z"/>
</svg>
Edit this page
</a>

View File

@ -0,0 +1,25 @@
<footer>
<p class="contained"><a href="https://github.com/netlify/netlify-cms/blob/master/LICENSE" class="text-link">Distributed under MIT License</a> ·
<a href="https://github.com/netlify/netlify-cms/blob/master/CODE_OF_CONDUCT.md" class="text-link">Code of Conduct</a></p>
</footer>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<script src="/jquery.scrollTo.min.js"></script>
<script src="/jquery.localScroll.min.js"></script>
<script src="/prism.js"></script>
{{ end }}
{{ if eq .Title "Community" }}
<script src="/moment.min.js"></script>
{{ end }}
<script src="/app.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.js"></script>
<script type="text/javascript"> docsearch({
apiKey: '08d03dc80862e84c70c5a1e769b13019',
indexName: 'netlifycms',
inputSelector: '.algolia-search',
debug: false // Set debug to true if you want to inspect the dropdown
});
</script>
</body>
</html>

View File

@ -0,0 +1,40 @@
<!doctype html>
<html>
<head>
<title>{{ .Site.Title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="apple-touch-icon" sizes="180x180" href="/img/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" href="/img/favicon/favicon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/img/favicon/favicon-16x16.png" sizes="16x16">
<link rel="manifest" href="/img/favicon/manifest.json">
<link rel="mask-icon" href="/img/favicon/safari-pinned-tab.svg" color="#96cf05">
<meta name="apple-mobile-web-app-title" content="NetlifyCMS">
<meta name="application-name" content="NetlifyCMS">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="/css/main.css"/>
{{ if or (eq .Section "docs") (eq .Title "Docs") }}
<link rel="stylesheet" href="/css/lib/prism.css"/>
{{ end }}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/docsearch.js/2/docsearch.min.css" />
</head>
<body>
{{ range .Site.Data.notifications }}
{{ range . }}
{{ if .published }}
<a href="{{ .url }}" class="notification {{ if .loud }}notification-loud{{end}}">{{ .message }}</a>
{{ end }}
{{ end }}
{{ end }}
<header id="header" {{ if or (eq .Section "docs") (eq .Title "Docs") }}class="docs"{{ end }}>
<div class="contained">
<a href="/" class="logo"><img src="/img/netlify-cms-logo.svg"/></a>
<a class="nav-link docs-link" href="/docs">Docs</a>
<a class="nav-link contributing-link" href="/docs/contributor-guide">Contributing</a>
<a class="nav-link" href="/community">Community</a>
<a class="utility-input">
<img src="/img/search.svg" />
<input type="search" placeholder="Search the docs" class="algolia-search"/>
</a>
<a href="https://github.com/netlify/netlify-cms" class="btn-secondary small github-btn "><img src="/img/github.svg"/> Fork me on GitHub</a>
</div>
</header>

0
website/site/static/.keep Executable file
View File

View File

@ -0,0 +1 @@
/docs/custom-authentication /docs/authentication-backends 301

View File

@ -0,0 +1,35 @@
backend:
name: github
repo: netlify/netlify-cms
publish_mode: editorial_workflow
media_folder: "website/site/static/img" # Folder where user uploaded files should go
public_folder: "img"
collections: # A list of collections the CMS should be able to edit
- name: "docs" # Used in routes, ie.: /admin/collections/:slug/edit
label: "Docs" # Used in the UI, ie.: "New Post"
folder: "website/site/content/docs" # 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: "Position", name: "position", widget: "number"}
- {label: "Body", name: "body", widget: "markdown"}
- name: notifications
label: Notifications
files:
- name: notifications
label: Notifications
file: website/site/data/notifications.yml
description: Site-top notifications - publish one at a time
fields:
- name: notifications
label: Notifications
widget: list
fields:
- {label: Title, name: title, widget: string, tagname: h1}
- {label: Published, name: published, widget: boolean}
- {label: Loud, name: loud, widget: boolean}
- {label: Message, name: message, widget: text}
- {label: URL, name: url}

View File

@ -0,0 +1,16 @@
<!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 stylesheets from your site here -->
<link rel="stylesheet" href="https://unpkg.com/netlify-cms@^0.7/dist/cms.css" />
<!-- Include a CMS specific stylesheet here -->
</head>
<body>
<script src="https://unpkg.com/netlify-cms@^0.7/dist/cms.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) 2007-2016 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* @author Ariel Flesler
* @version 1.4.0
*/
;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var g=location.href.replace(/#.*/,'');var h=$.localScroll=function(a){$('body').localScroll(a)};h.defaults={duration:1000,axis:'y',event:'click',stop:true,target:window};$.fn.localScroll=function(a){a=$.extend({},h.defaults,a);if(a.hash&&location.hash){if(a.target)window.scrollTo(0,0);scroll(0,location,a)}return a.lazy?this.on(a.event,'a,area',function(e){if(filter.call(this)){scroll(e,this,a)}}):this.find('a,area').filter(filter).bind(a.event,function(e){scroll(e,this,a)}).end().end();function filter(){return!!this.href&&!!this.hash&&this.href.replace(this.hash,'')===g&&(!a.filter||$(this).is(a.filter))}};h.hash=function(){};function scroll(e,a,b){var c=a.hash.slice(1),elem=document.getElementById(c)||document.getElementsByName(c)[0];if(!elem)return;if(e)e.preventDefault();var d=$(b.target);if(b.lock&&d.is(':animated')||b.onBefore&&b.onBefore(e,elem,d)===false)return;if(b.stop){d.stop(true)}if(b.hash){var f=elem.id===c?'id':'name',$a=$('<a> </a>').attr(f,c).css({position:'absolute',top:$(window).scrollTop(),left:$(window).scrollLeft()});elem[f]='';$('body').prepend($a);location.hash=a.hash;$a.remove();elem[f]=c}d.scrollTo(elem,b).trigger('notify.serialScroll',[elem])}return h}));

View File

@ -0,0 +1,7 @@
/**
* Copyright (c) 2007-2015 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
* Licensed under MIT
* @author Ariel Flesler
* @version 2.1.0
*/
;(function(l){'use strict';l(['jquery'],function($){var k=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};k.defaults={axis:'xy',duration:0,limit:true};function isWin(a){return!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!==-1}$.fn.scrollTo=function(f,g,h){if(typeof g==='object'){h=g;g=0}if(typeof h==='function'){h={onAfter:h}}if(f==='max'){f=9e9}h=$.extend({},k.defaults,h);g=g||h.duration;var j=h.queue&&h.axis.length>1;if(j){g/=2}h.offset=both(h.offset);h.over=both(h.over);return this.each(function(){if(f===null)return;var d=isWin(this),elem=d?this.contentWindow||window:this,$elem=$(elem),targ=f,attr={},toff;switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=d?$(targ):$(targ,elem);if(!targ.length)return;case'object':if(targ.is||targ.style){toff=(targ=$(targ)).offset()}}var e=$.isFunction(h.offset)&&h.offset(elem,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a==='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,prev=$elem[key](),max=k.max(elem,a);if(toff){attr[key]=toff[pos]+(d?0:prev-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b),10)||0;attr[key]-=parseInt(targ.css('border'+b+'Width'),10)||0}attr[key]+=e[pos]||0;if(h.over[pos]){attr[key]+=targ[a==='x'?'width':'height']()*h.over[pos]}}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)==='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key])){attr[key]=attr[key]<=0?0:Math.min(attr[key],max)}if(!i&&h.axis.length>1){if(prev===attr[key]){attr={}}else if(j){animate(h.onAfterFirst);attr={}}}});animate(h.onAfter);function animate(a){var b=$.extend({},h,{queue:true,duration:g,complete:a&&function(){a.call(elem,targ,h)}});$elem.animate(attr,b)}})};k.max=function(a,b){var c=b==='x'?'Width':'Height',scroll='scroll'+c;if(!isWin(a))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,doc=a.ownerDocument||a.document,html=doc.documentElement,body=doc.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}$.Tween.propHooks.scrollLeft=$.Tween.propHooks.scrollTop={get:function(t){return $(t.elem)[t.prop]()},set:function(t){var a=this.get(t);if(t.options.interrupt&&t._last&&t._last!==a){return $(t.elem).stop()}var b=Math.round(t.now);if(a!==b){$(t.elem)[t.prop](b);t._last=this.get(t)}}};return k})}(typeof define==='function'&&define.amd?define:function(a,b){'use strict';if(typeof module!=='undefined'&&module.exports){module.exports=b(require('jquery'))}else{b(jQuery)}}));

7
website/site/static/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long