<commit_before>## Overview

The `apostrophe-workflow` module adds powerful workflow and localization capabilities to Apostrophe. As a workflow system it provides for a draft version of every document, so that changes do not immediately "go live." As a localization system it provides for documents to exist in several locales, allowing for easy internationalization.

We'll begin with the steps needed simply to add workflow to your project. Then we'll examine the changes needed for localization (also known as i18n or internationalization).

- [Before getting started](#user-content-before-getting-started)
  * [Adding `parkedId` to your parked pages](#user-content-adding-parkedid-to-your-parked-pages)
  * [Adding workflow to your database](#user-content-adding-workflow-to-your-database)
- [Using the workflow feature](#user-content-using-the-workflow-feature)
  * ["Why am I asked to commit so many things?"](#user-content-why-am-i-asked-to-commit-so-many-things)
  * [Workflow for pieces](#user-content-workflow-for-pieces)
  * [Workflow for page settings](#user-content-workflow-for-page-settings)
- [Using the localization feature](#user-content-using-the-localization-feature)
  * [Private locales and default locale](#user-content-private-locales-and-default-locale)
  * [Document structure for locales](#user-content-document-structure-for-locales)
  * [Setting the `lang` attribute](#user-content-setting-the-lang-attribute)
  * [Tags and localization: we recommend using joins instead](#user-content-tags-and-localization-we-recommend-using-joins-instead)
  * [Building a locale picker on the front end](#user-content-building-a-locale-picker-on-the-front-end)
  * [Exporting between locales](#user-content-exporting-between-locales)
    + [Not all patches can be exported](#user-content-not-all-patches-can-be-exported)
  * [Forcing exports](#user-content-forcing-exports)
  * [Forcing export of one widget](#user-content-forcing-export-of-one-widget)
  * [Switching locales via custom hostnames and/or prefixes](#user-content-switching-locales-via-custom-hostnames-and-or-prefixes)
    + [Adding prefixes to an existing database](#user-content-adding-prefixes-to-an-existing-database)
    + [If you only care about subdomains](#user-content-if-you-only-care-about-subdomains)
    + [If you only care about prefixes](#user-content-if-you-only-care-about-prefixes)
    + [One login across all hostnames](#user-content-one-login-across-all-hostnames)
    + [Locale-specific stylesheets](#user-content-locale-specific-stylesheets)
  * [Excluding certain types and properties from workflow](#user-content-excluding-certain-types-and-properties-from-workflow)
- [Workflow with permissions: limiting who can do what](#user-content-workflow-with-permissions-limiting-who-can-do-what)
  * [Setting up for permissions: enabling group management](#user-content-setting-up-for-permissions-enabling-group-management)
    + [Removing the legacy groups](#user-content-removing-the-legacy-groups)
  * [An overview of permissions](#user-content-an-overview-of-permissions)
    + [The "Editor" permission](#user-content-the-editor-permission)
    + [The "Admin: All" permission](#user-content-the-admin-all-permission)
    + ["View Private Locales"](#user-content-view-private-locales)
    + ["Upload and Crop"](#user-content-upload-and-crop)
    + [The "Admin: Global" permission](#user-content-the-admin-global-permission)
    + [The "Edit: Global" permission (do not use)](#user-content-the-edit-global-permission-do-not-use)
    + ["Admin: Pages": total control of pages](#user-content-admin-pages--total-control-of-pages)
    + ["Edit: Pages": candidates to edit pages](#user-content-edit-pages--candidates-to-edit-pages)
    + ["Admin: Article": total control of articles](#user-content-admin-article--total-control-of-articles)
    + ["Edit: Article": creating and editing their own articles](#user-content-edit-article--creating-and-editing-their-own-articles)
    + ["Admin: Image": total control of articles](#user-content-admin-image--total-control-of-articles)
    + ["Edit: Image": creating and editing their own images](#user-content-edit-image--creating-and-editing-their-own-images)
    + ["Edit: File": creating and editing their own files](#user-content-edit-file--creating-and-editing-their-own-files)
  * [Locales for permissions](#user-content-locales-for-permissions)
  * [Permissions tutorial](#user-content-permissions-tutorial)
    + [Creating the fr-editors group](#user-content-creating-the-fr-editors-group)
    + [Creating an "fr-editor" user](#user-content-creating-an-fr-editor-user)
    + [Creating the "fr-committers" group](#user-content-creating-the-fr-committers-group)
    + [Creating an "fr-comitter" user](#user-content-creating-an-fr-comitter-user)
    + [Granting editing permissions on the home page](#user-content-granting-editing-permissions-on-the-home-page)
    + [Working with the "fr-editors" account](#user-content-working-with-the-fr-editors-account)
    + [Working with the "fr-committers" account](#user-content-working-with-the-fr-committers-account)
    + [Exporting](#user-content-exporting)
    + [Accessing newly created pages in other locales](#user-content-accessing-newly-created-pages-in-other-locales)
- [Other developer concerns](#user-content-other-developer-concerns)
  * [Aliasing the module](#user-content-aliasing-the-module)
  * [Previewing piece types without an index page](#user-content-previewing-piece-types-without-an-index-page)
  * [Command line tasks and workflow](#user-content-command-line-tasks-and-workflow)
    + [Using the `-workflow-locale` option](#user-content-using-the--workflow-locale-option)
  * [Setting the current locale programmatically](#user-content-setting-the-current-locale-programmatically)
  * [Direct MongoDB access and workflow](#user-content-direct-mongodb-access-and-workflow)
  * [`setPropertiesAcrossLocales`: modifying a document programmatically across locales](#user-content-setpropertiesacrosslocales--modifying-a-document-programmatically-across-locales)
  * [Writing safe `afterInsert` and `docAfterInsert` handlers, etc.](#user-content-writing-safe-afterinsert-and-docafterinsert-handlers-etc)
    + [Recognizing inserts due to localization](#user-content-recognizing-inserts-due-to-localization)
    + [Always finish the job before continuing](#user-content-always-finish-the-job-before-continuing)
- [Technical approach](#user-content-technical-approach)
  * [Use of jsondiffpatch](#user-content-use-of-jsondiffpatch)
  * [Patching and exporting of widgets](#user-content-patching-and-exporting-of-widgets)
- [Legacy tasks](#user-content-legacy-tasks)
  * [Cleaning up duplicate homepages](#user-content-cleaning-up-duplicate-homepages)

## Before getting started

Start by installing apostrophe-workflow.

```
npm install -save apostrophe-workflow
```

Then configure the `apostrophe-workflow` module in `app.js` with the rest of your modules. We'll start with a simple configuration just providing workflow:

```
'apostrophe-workflow': {
  // IMPORTANT: if you follow the examples below,
  // be sure to set this so the templates work
  alias: 'workflow'
}
```

### Adding `parkedId` to your parked pages

If you are using the `park` option with `apostrophe-pages`, and you have not already set a unique `parkedId` property for each page specified for that option, **do so before you start using workflow.** This will address problems that otherwise occur if slugs change due to locale prefixes.

### Adding workflow to your database

Odds are, you already have a database. Either from an existing project, or for a new one, since Apostrophe creates the database on the very first run. So, follow these steps to add workflow to your database.

1. **FOR EXISTING PROJECTS, BACK UP YOUR DATABASE,** In case you decide this module is not for you. Currently there is no command to stop using workflow once you start.

You should initially experiment with this module with a *local* copy of your site, not your live production content.

You can back up your database easily with the `mongodump` command line utility.

**Once you add this module and restart your apostrophe process,** all of your documents will exist in both draft and live versions. Editors will be able to the draft version while in "draft" mode. Everyone else, and editors in "live" mode, will see the live version and will not be able to edit it directly. The only way to make new content live is to "commit" the changes that have been made to the document.

If you have not added an admin user yet, you can do it now in the usual way:

```
# If you do not have preconfigured groups, add a group too
node app apostrophe-groups:add admin admin
# Now add the user
node app apostrophe-users:add admin admin
```

2. **FOR EXISTING PROJECTS, YOU MUST ADD LOCALES TO EXISTING DOCUMENTS.** This is NOT automatic for existing projects, you must run this task on a one-time basis:

```
node app apostrophe-workflow:add-missing-locales -live
```

## Using the workflow feature

This basic configuration provides you with a live/draft toggle on the page (lower left corner). Editing is not possible in the live mode. You will not see most types of pieces in the admin bar, and you will not see editing controls on the page. This is normal.

In the draft mode, editing works in a familiar way. Your changes are constantly saved, just like before, but they are only saved to the draft version of the document.

When you are satisfied, click "submit" to submit your work for review by someone else, or "commit" to commit it to the live version of the page yourself.

If work is submitted that you have permission to edit, you can view a list of those pages and pieces via the "Workflow" admin bar menu.

### "Why am I asked to commit so many things?"

When you click "Commit" on the page, all of the documents that make up what you see on the page need to be committed in order to go live on the site. That includes the images in a slideshow, the blog posts in a blog widget, and so on. It may seem like a lot of work the first time. Just remember that you won't be asked to commit them again unless their drafts have been updated.

### Workflow for pieces

"Pieces," like blog posts or events, work just like before. However, just make sure you enter "draft" mode; until you do that most piece types won't show up on the admin bar, because you can only edit the draft version directly.

When you are finished editing a piece, use the "workflow" menu in the upper right corner of the dialog box to select "submit" or "commit."

### Workflow for page settings

Workflow also applies to page settings, such as the title. You can easily toggle between displaying the draft and live versions of the title while in the page settings dialog box. And, you can submit or commit via the workflow dropdown menu in the upper right corner of the dialog box.

## Using the localization feature

To enable localization, configure more than one locale in `app.js`:<commit_msg>two dashes for `--live`<commit_after>## Overview

The `apostrophe-workflow` module adds powerful workflow and localization capabilities to Apostrophe. As a workflow system it provides for a draft version of every document, so that changes do not immediately "go live." As a localization system it provides for documents to exist in several locales, allowing for easy internationalization.

We'll begin with the steps needed simply to add workflow to your project. Then we'll examine the changes needed for localization (also known as i18n or internationalization).

- [Before getting started](#user-content-before-getting-started)
  * [Adding `parkedId` to your parked pages](#user-content-adding-parkedid-to-your-parked-pages)
  * [Adding workflow to your database](#user-content-adding-workflow-to-your-database)
- [Using the workflow feature](#user-content-using-the-workflow-feature)
  * ["Why am I asked to commit so many things?"](#user-content-why-am-i-asked-to-commit-so-many-things)
  * [Workflow for pieces](#user-content-workflow-for-pieces)
  * [Workflow for page settings](#user-content-workflow-for-page-settings)
- [Using the localization feature](#user-content-using-the-localization-feature)
  * [Private locales and default locale](#user-content-private-locales-and-default-locale)
  * [Document structure for locales](#user-content-document-structure-for-locales)
  * [Setting the `lang` attribute](#user-content-setting-the-lang-attribute)
  * [Tags and localization: we recommend using joins instead](#user-content-tags-and-localization-we-recommend-using-joins-instead)
  * [Building a locale picker on the front end](#user-content-building-a-locale-picker-on-the-front-end)
  * [Exporting between locales](#user-content-exporting-between-locales)
    + [Not all patches can be exported](#user-content-not-all-patches-can-be-exported)
  * [Forcing exports](#user-content-forcing-exports)
  * [Forcing export of one widget](#user-content-forcing-export-of-one-widget)
  * [Switching locales via custom hostnames and/or prefixes](#user-content-switching-locales-via-custom-hostnames-and-or-prefixes)
    + [Adding prefixes to an existing database](#user-content-adding-prefixes-to-an-existing-database)
    + [If you only care about subdomains](#user-content-if-you-only-care-about-subdomains)
    + [If you only care about prefixes](#user-content-if-you-only-care-about-prefixes)
    + [One login across all hostnames](#user-content-one-login-across-all-hostnames)
    + [Locale-specific stylesheets](#user-content-locale-specific-stylesheets)
  * [Excluding certain types and properties from workflow](#user-content-excluding-certain-types-and-properties-from-workflow)
- [Workflow with permissions: limiting who can do what](#user-content-workflow-with-permissions-limiting-who-can-do-what)
  * [Setting up for permissions: enabling group management](#user-content-setting-up-for-permissions-enabling-group-management)
    + [Removing the legacy groups](#user-content-removing-the-legacy-groups)
  * [An overview of permissions](#user-content-an-overview-of-permissions)
    + [The "Editor" permission](#user-content-the-editor-permission)
    + [The "Admin: All" permission](#user-content-the-admin-all-permission)
    + ["View Private Locales"](#user-content-view-private-locales)
    + ["Upload and Crop"](#user-content-upload-and-crop)
    + [The "Admin: Global" permission](#user-content-the-admin-global-permission)
    + [The "Edit: Global" permission (do not use)](#user-content-the-edit-global-permission-do-not-use)
    + ["Admin: Pages": total control of pages](#user-content-admin-pages--total-control-of-pages)
    + ["Edit: Pages": candidates to edit pages](#user-content-edit-pages--candidates-to-edit-pages)
    + ["Admin: Article": total control of articles](#user-content-admin-article--total-control-of-articles)
    + ["Edit: Article": creating and editing their own articles](#user-content-edit-article--creating-and-editing-their-own-articles)
    + ["Admin: Image": total control of articles](#user-content-admin-image--total-control-of-articles)
    + ["Edit: Image": creating and editing their own images](#user-content-edit-image--creating-and-editing-their-own-images)
    + ["Edit: File": creating and editing their own files](#user-content-edit-file--creating-and-editing-their-own-files)
  * [Locales for permissions](#user-content-locales-for-permissions)
  * [Permissions tutorial](#user-content-permissions-tutorial)
    + [Creating the fr-editors group](#user-content-creating-the-fr-editors-group)
    + [Creating an "fr-editor" user](#user-content-creating-an-fr-editor-user)
    + [Creating the "fr-committers" group](#user-content-creating-the-fr-committers-group)
    + [Creating an "fr-comitter" user](#user-content-creating-an-fr-comitter-user)
    + [Granting editing permissions on the home page](#user-content-granting-editing-permissions-on-the-home-page)
    + [Working with the "fr-editors" account](#user-content-working-with-the-fr-editors-account)
    + [Working with the "fr-committers" account](#user-content-working-with-the-fr-committers-account)
    + [Exporting](#user-content-exporting)
    + [Accessing newly created pages in other locales](#user-content-accessing-newly-created-pages-in-other-locales)
- [Other developer concerns](#user-content-other-developer-concerns)
  * [Aliasing the module](#user-content-aliasing-the-module)
  * [Previewing piece types without an index page](#user-content-previewing-piece-types-without-an-index-page)
  * [Command line tasks and workflow](#user-content-command-line-tasks-and-workflow)
    + [Using the `-workflow-locale` option](#user-content-using-the--workflow-locale-option)
  * [Setting the current locale programmatically](#user-content-setting-the-current-locale-programmatically)
  * [Direct MongoDB access and workflow](#user-content-direct-mongodb-access-and-workflow)
  * [`setPropertiesAcrossLocales`: modifying a document programmatically across locales](#user-content-setpropertiesacrosslocales--modifying-a-document-programmatically-across-locales)
  * [Writing safe `afterInsert` and `docAfterInsert` handlers, etc.](#user-content-writing-safe-afterinsert-and-docafterinsert-handlers-etc)
    + [Recognizing inserts due to localization](#user-content-recognizing-inserts-due-to-localization)
    + [Always finish the job before continuing](#user-content-always-finish-the-job-before-continuing)
- [Technical approach](#user-content-technical-approach)
  * [Use of jsondiffpatch](#user-content-use-of-jsondiffpatch)
  * [Patching and exporting of widgets](#user-content-patching-and-exporting-of-widgets)
- [Legacy tasks](#user-content-legacy-tasks)
  * [Cleaning up duplicate homepages](#user-content-cleaning-up-duplicate-homepages)

## Before getting started

Start by installing apostrophe-workflow.

```
npm install -save apostrophe-workflow
```

Then configure the `apostrophe-workflow` module in `app.js` with the rest of your modules. We'll start with a simple configuration just providing workflow:

```
'apostrophe-workflow': {
  // IMPORTANT: if you follow the examples below,
  // be sure to set this so the templates work
  alias: 'workflow'
}
```

### Adding `parkedId` to your parked pages

If you are using the `park` option with `apostrophe-pages`, and you have not already set a unique `parkedId` property for each page specified for that option, **do so before you start using workflow.** This will address problems that otherwise occur if slugs change due to locale prefixes.

### Adding workflow to your database

Odds are, you already have a database. Either from an existing project, or for a new one, since Apostrophe creates the database on the very first run. So, follow these steps to add workflow to your database.

1. **FOR EXISTING PROJECTS, BACK UP YOUR DATABASE,** In case you decide this module is not for you. Currently there is no command to stop using workflow once you start.

You should initially experiment with this module with a *local* copy of your site, not your live production content.

You can back up your database easily with the `mongodump` command line utility.

**Once you add this module and restart your apostrophe process,** all of your documents will exist in both draft and live versions. Editors will be able to the draft version while in "draft" mode. Everyone else, and editors in "live" mode, will see the live version and will not be able to edit it directly. The only way to make new content live is to "commit" the changes that have been made to the document.

If you have not added an admin user yet, you can do it now in the usual way:

```
# If you do not have preconfigured groups, add a group too
node app apostrophe-groups:add admin admin
# Now add the user
node app apostrophe-users:add admin admin
```

2. **FOR EXISTING PROJECTS, YOU MUST ADD LOCALES TO EXISTING DOCUMENTS.** This is NOT automatic for existing projects, you must run this task on a one-time basis:

```
node app apostrophe-workflow:add-missing-locales --live
```

## Using the workflow feature

This basic configuration provides you with a live/draft toggle on the page (lower left corner). Editing is not possible in the live mode. You will not see most types of pieces in the admin bar, and you will not see editing controls on the page. This is normal.

In the draft mode, editing works in a familiar way. Your changes are constantly saved, just like before, but they are only saved to the draft version of the document.

When you are satisfied, click "submit" to submit your work for review by someone else, or "commit" to commit it to the live version of the page yourself.

If work is submitted that you have permission to edit, you can view a list of those pages and pieces via the "Workflow" admin bar menu.

### "Why am I asked to commit so many things?"

When you click "Commit" on the page, all of the documents that make up what you see on the page need to be committed in order to go live on the site. That includes the images in a slideshow, the blog posts in a blog widget, and so on. It may seem like a lot of work the first time. Just remember that you won't be asked to commit them again unless their drafts have been updated.

### Workflow for pieces

"Pieces," like blog posts or events, work just like before. However, just make sure you enter "draft" mode; until you do that most piece types won't show up on the admin bar, because you can only edit the draft version directly.

When you are finished editing a piece, use the "workflow" menu in the upper right corner of the dialog box to select "submit" or "commit."

### Workflow for page settings

Workflow also applies to page settings, such as the title. You can easily toggle between displaying the draft and live versions of the title while in the page settings dialog box. And, you can submit or commit via the workflow dropdown menu in the upper right corner of the dialog box.

## Using the localization feature

To enable localization, configure more than one locale in `app.js`:
{'fraction_non_alphanumeric': 0.10517014817164348, 'fraction_numerical': 0.000362499433594635, 'mean_word_length': 4.651728553137004, 'pattern_counts': {'":': 14, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3414026', 'n_tokens_mistral': 6454, 'n_tokens_neox': 6067, 'n_words': 2266}