Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
martinjagodic committed May 30, 2023
2 parents 538b01d + a7ea882 commit 618d1c9
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 31 deletions.
37 changes: 25 additions & 12 deletions website/content/docs/add-to-your-site.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ admin
└ config.yml
```

The first file, `admin/index.html`, is the entry point for the Decap CMS admin interface. This means that users navigate to `yoursite.com/admin/` to access it. On the code side, it's a basic HTML starter page that loads the Decap CMS JavaScript file. The second file, `admin/config.yml`, is the heart of your Decap CMS installation, and a bit more complex. The [Configuration](#configuration) section covers the details.
The first file, `admin/index.html`, is the entry point for the Decap CMS admin interface. This means that users navigate to `yoursite.com/admin/` to access it. On the code side, it's a basic HTML starter page that loads the Decap CMS JavaScript file.

In this example, we pull the `admin/index.html` file from a public CDN.
The second file, `admin/config.yml`, is the heart of your Decap CMS installation, and a bit more complex. The [Configuration](#configuration) section covers the details.

In this example, we pull the `admin/index.html` file from a public CDN.

```html
<!doctype html>
Expand All @@ -60,7 +62,9 @@ In the code above the `script` is loaded from the `unpkg` CDN. Should there be a

### Installing with npm

You can also use Decap CMS as an npm module. Wherever you import Decap CMS, it automatically runs, taking over the current page. Make sure the script that imports it only runs on your CMS page. First install the package and save it to your project:
You can also use Decap CMS as an npm module. Wherever you import Decap CMS, it automatically runs, taking over the current page. Make sure the script that imports it only runs on your CMS page.

First install the package and save it to your project:

```bash
npm install netlify-cms-app --save
Expand Down Expand Up @@ -98,7 +102,7 @@ The configuration above specifies your backend protocol and your publication bra

### Editorial Workflow

**Note:** Editorial workflow works with GitHub repositories, and support for GitLab and Bitbucket is [in beta](/docs/beta-features/#gitlab-and-bitbucket-editorial-workflow-support).
**Note:** Editorial workflow works with GitHub repositories. Support for GitLab and Bitbucket is [in beta](/docs/beta-features/#gitlab-and-bitbucket-editorial-workflow-support).

By default, saving a post in the CMS interface pushes a commit directly to the publication branch specified in `backend`. However, you also have the option to enable the [Editorial Workflow](../configuration-options/#publish-mode), which adds an interface for drafting, reviewing, and approving posts. To do this, add the following line to your Decap CMS `config.yml`:

Expand All @@ -118,17 +122,17 @@ media_folder: "images/uploads" # Media files will be stored in the repo under im

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:
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
# These lines should *not* be indented
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 are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which is relative to the file where it's called. For this reason, we usually start the path at the site root, using the opening `/`.
The configuration above adds a new setting: `public_folder`. Whereas `media_folder` specifies where uploaded files are saved in the repo, `public_folder` indicates where they are found in the published site. Image `src` attributes use this path, which 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, Decap CMS defaults to the same value as `media_folder`, adding an opening `/` if one is not included.*
*__Note:__ If `public_folder` is not set, Decap CMS defaults to the same value as `media_folder`, adding an opening `/` if one is not included.*

### Collections

Expand All @@ -148,7 +152,7 @@ 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 in your NetlifyCMS `config.yml` file:
Given this example, our `collections` settings would look like this in your Decap CMS `config.yml` file:

```yaml
collections:
Expand Down Expand Up @@ -188,7 +192,7 @@ Let's break that down:
</tr>
<tr>
<td><code>slug</code></td>
<td>Template for filenames. <code>{{year}}</code>, <code>{{month}}</code>, and <code>{{day}}</code> pulls 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>Template for filenames. <code>{{year}}</code>, <code>{{month}}</code>, and <code>{{day}}</code> pulls 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>
Expand Down Expand Up @@ -243,7 +247,7 @@ Netlify's Identity and Git Gateway services allow you to manage CMS admin users

### 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, add the following script tag in two places:
With the backend configured 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, add the following script tag in two places:

```html
<script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
Expand All @@ -267,7 +271,7 @@ When a user logs in with the Netlify Identity widget, an access token directs to
</script>
```

Note: This example script requires modern JavaScript and does not work on IE11. For legacy browser support, use function expressions (`function () {}`) in place of the arrow functions (`() => {}`), or use a transpiler such as [Babel](https://babeljs.io/).
**Note:** This example script requires modern JavaScript and does not work on IE11. For legacy browser support, use function expressions (`function () {}`) in place of the arrow functions (`() => {}`), or use a transpiler such as [Babel](https://babeljs.io/).

## Accessing the CMS

Expand All @@ -277,6 +281,15 @@ If you set your registration preference to "Invite only," invite yourself (and a

If you left your site registration open, or for return visits after confirming an email invitation, access your site's CMS at `yoursite.com/admin/`.

**Note:** No matter where you access Decap CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Decap CMS config.yml file. This means that content fetched in the admin UI matches the content in the repository, which may be different from your locally running site. It also means that content saved using the admin UI saves directly to the hosted repository, even if you're running the UI locally or in staging.
---
**Note:** No matter where you access Decap CMS — whether running locally, in a staging environment, or in your published site — it always fetches and commits files in your hosted repository (for example, on GitHub), on the branch you configured in your Decap CMS `config.yml` file.

This means:

- Content fetched in the admin UI matches the content in the repository, which may be different from your locally running site.

- Content saved using the admin UI saves directly to the hosted repository, even if you're running the UI locally or in staging.

---

Happy posting!
40 changes: 21 additions & 19 deletions website/content/docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,43 @@ Decap CMS is a React application, using Redux for state management with immutabl

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.
* Each `collection` represents a group of entries. This might be similar entries which share the same structure, or 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.
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).
- **Auth:** Keeps track of the logged state and the current user.

**Collections:** List of available collections, their fields and metadata information.
- **Config:** Holds the environment configuration (backend type, available collections, and fields).

**Entries:** Entries for each field.
- **Collections:** List of available collections, their fields, and metadata information.

**EntryDraft:** Reused for each entry that is edited or created. It holds the entry's temporary data until it's persisted on the backend.
- **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.

## 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.
- **`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 path.
- **`getAsset`:** Selects a single `AssetProxy` object for the given path.

## Value Objects
**AssetProxy:** AssetProxy is a Value Object that holds information regarding an asset file (for example, an image), whether it's persisted online or held locally in cache.
**`AssetProxy`:** `AssetProxy` is a Value Object that holds information regarding an asset file (for example, an image), 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.
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).
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.
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:
Expand All @@ -55,19 +57,19 @@ For either updating an existing entry or creating a new one, the `EntryEditor` i
#### Widget components implementation
The control component receives one (1) callback as a prop: `onChange`.

* 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): Should be invoked with an `AssetProxy` value object if the field accepts file uploads for media (images, for example). `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.
* **`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): Should be invoked with an `AssetProxy` value object if the field accepts file uploads for media (images, for example). `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.
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.
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.


## Editorial Workflow 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.
Furthermore, all editorial workflow state is managed in Redux; there's an `actions/editorialWorkflow.js` file and a `reducers/editorialWorkflow.js` file.

### About metadata

Expand Down

0 comments on commit 618d1c9

Please sign in to comment.