Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

apostrophe-workflow

apostrophecms352MIT2.40.3

Workflow, approvals, localization and internationalization for ApostropheCMS

apostrophe, apostrophecms, apostrophe-cms, content management, content management system, cms, workflow, localization, editorial

readme

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

Note: Apostrophe Workflow requires Node.js 10+.

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. We will also turn on the very handy "Manage Workflow" dialog box by turning on the apostrophe-workflow-modified-documents module, which comes bundled inside apostrophe-workflow. You do not have to separately install it.

'apostrophe-workflow': {
  // IMPORTANT: if you follow the examples below,
  // be sure to set this so the templates work
  alias: 'workflow',
  // Recommended to save database space. You can still
  // export explicitly between locales
  replicateAcrossLocales: false
},
'apostrophe-workflow-modified-documents': {}

Turning off automatic replication across locales

For historical reasons, in the default configuration, documents automatically replicate between locales (languages), starting out in the trash in other locales until they are made active there.

Our own clients have found this wastes a lot of database space. So, set replicateAcrossLocales: false as shown above. Users can still export documents between locales as you'll see later on.

If you find this useful but need to occasionally make an exception and just completely copy a locale to another once, you can use the apostrophe-workflow:replicate-locale command line task, with --from and --to options, to do that on a one-time basis. If you have already done work in the to locale, we recommend running apostrophe-attachments:recompute-all-doc-references afterwards, to ensure attachments do not lose track of whether it is time to disable access.

Did you already start a large project with replicated documents? You can use the apostrophe-workflow:dereplicate command line task to remove documents from all other locales if they are outside the trash in only one locale. We recommend backing up your database first.

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, we recommend backing up your database first, to make it easier to change your mind. However, there is an apostrophe-workflow:remove task available if you choose to remove workflow later.

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
  1. 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.

You can commit documents in bulk, too. The easiest way is via the "Manage Workflow" dialog box, accessed via the "Manage" option on the "Workflow" dropdown menu. If you don't see it, switch to draft mode first. If you still don't see it, make sure you enabled the apostrophe-workflow-modified-documents module as noted above in the installation instructions. Once you have access to this dialog box, you can use the "Select All" checkbox and the "Batch Commit" operation to expedite things.

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:

'apostrophe-workflow': {
  locales: [
    {
      name: 'default',
      label: 'Default',
      private: true,
      children: [
        {
          name: 'en-gb',
          label: 'England'
        },
        {
          name: 'en-us',
          label: 'United States'
        },
        {
          name: 'fr',
          label: 'France'
        }
      ]
    },
  ],
  defaultLocale: 'en-gb',
  // IMPORTANT: if you follow the examples below,
  // be sure to set this
  alias: 'workflow'
}

If you have worked with localization before you will recognize locale names like en-gb. These are arbitrary; you may choose any name. However, if you plan to use URL prefixes to distinguish between locales (see below), you must choose a hyphenated, lower-case name without punctuation. So we suggest that you always do that.

Private locales and default locale

What about the default locale? What does private do? Private locales cannot actually be accessed by the public. Although it isn't mandatory, we recommend setting up a private default locale for the "master copy" of your content, written in your team's most familiar language, and then exporting content to child locales.

If you use private locales, you must give the "view private locales" permission to any Apostrophe groups that should be able to see those locales when logged in. This is a simple permission that can be granted to a group via the "Groups" option on the admin bar or, if you are using hardcoded groups, via the "groups" option to the apostrophe-users module (the latter requires a restart to push the new permission). If you're using workflow, it's probably a good idea to comment out the groups option, which allows you to manage your groups through Apostrophe's interface instead.

Note that if you do not have a locale named default, you must set the defaultLocale option to the name of a locale you do have. If the default locale is private, a public locale should be set as defaultLocale instead (e.g., 'en-gb' above). Also note that if you started out with no locales for simple workflow, Apostrophe already created a default locale implicitly. Leaving that out of your locale configuration would give you no way to access the existing content.

The parent-child relationship between locales is just a convenience for quickly exporting content to them, as you'll see below. You can nest children as many levels deep as you wish.

Document structure for locales

When you restart your Apostrophe node process or run a command line task such as `apostrophe-migrations:migrate, Apostrophe will automatically add the new locales to all of the existing docs in the database.

You may also do this explicitly:

node app apostrophe-workflow:add-missing-locales

By default, docs copied to new locales via this task will be considered trash in all live locales, until an editor chooses to commit them. If you prefer that that they be immediately live everywhere, use this command instead:

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

Now access the site as an administrator. You will be able to click on the current locale name to switch to other locales.

Every document automatically exists in all locales, however it may or may not be published or in the trash in any given locale. This is useful since it allows you to have pages that are "only for France," for instance.

Note that a single document may have a different slug in different locales. The slugs may also be the same, but you'll typically want to enable locale-specific prefixes, locale-specific domain names or a combination of the two as described below.

Setting the lang attribute

Multilingual websites should set the lang attribute of the html element appropriately.

Apostrophe currently ships with an outerLayoutBase template that includes a locale block, which can be used to easily override the lang attribute of the page. And this module ships with a helper function to set lang for you.

So just write this in your lib/modules/apostrophe-templates/views/layout.html template, or outerLayout.html if you have one:

{% block locale %}{{ apos.modules['apostrophe-workflow'].lang() }}{% endblock %}

By default, this helper converts a string like en-gb to en, and leaves a string like fr alone.

If this is not sufficient for your needs, you may set the lang property when configuring each locale, and that value will be output directly.

Tags and localization: we recommend using joins instead

Tags in Apostrophe follow the typical MongoDB approach of a simple array property containing strings. They are localized like other fields. Thus if they are used to select content for display it is important to be consistent when translating tags to a particular locale.

When working with localization it may be preferable to avoid tags in favor of joins. A joinByOne or joinByArray relationship can be used to relate a document to various "categories," which are localized documents in their own right and therefore behave consistently across locales. apostrophe-workflow will ensure that exported joins referencing a category in one locale are correctly adjusted to point to the equivalent category in the other locale.

Building a locale picker on the front end

Here's how to code a locale picker on the front end:

{# Typically in `layout.html` #}
<ul>
  {% for localization in apos.workflow.localizations() %}
    <li><a href="{{ localization._url | build({ workflowLocale: localization.workflowLocale }) }}">{{ localization.label }}</a></li>
  {% endfor %}
</ul>

This ul will populate with localized links to the current context page or piece. If the page or piece is unpublished or considered trash in a locale, there won't be a link for that locale.

If you use localization.label as shown here, you'll see the labels that you set when configuring your locales.

If you use localization.title instead, you'll see the title of the individual piece or page as it was translated or localized for that locale. The former is usually less confusing.

This code:

| build({ workflowLocale: localization.workflowLocale })

May be omitted if you are using the subdomains feature or the prefixes feature. If you are using neither, then a query parameter is necessary as the slugs could be the same across locales. However the query parameter is automatically removed after the new locale is stored in the user's session.

Exporting between locales

After committing a change, you will be invited to export that change to other locales. If you do so, it is applied as a "patch" to the other locale's draft (it is not made live right away).

This allows for editors fluent in the other locale to complete any necessary translation tasks before finally committing the changes for that locale.

If you find this option appears too frequently

Do you export rarely? If so, you may want to set the exportAfterCommit option of the apostrophe-workflow module to false, or set disableExportAfterCommit to true. The latter is useful if you are using the apostrophe-override-options module to creaqte an editable boolean field for this purpose.

If you do so, the "export" dialog box will not appear right after every commit. Instead the user must choose to access it. The option can be found on the "workflow" dropdown menu, accessed via "Page Settings" or via the editing dialog box for any piece type, including "global."

Not all patches can be exported

If the page has been altered greatly in structure, for example if the rich text widget on a page has been removed and replaced, making it effectively a separate widget altogether, then an edit to that widget will not take effect as a patch. It is a best practice to initially create all content in a "default" locale and then export it to others.

Forcing exports

If you need to, you can force an export of a document so that it is copied directly to the draft version in other locales. To do that, choose "Force Export" from the dialog box for the piece in question, or from the "Page Settings" dialog box for a page.

Even a forced export only alters the draft version of the other locales. Work must still be reviewed and committed there.

Forcing export of one widget

You can also force the export of a single widget. You can do that via the new export button, displayed along with the up and down arrows, edit pencil and trash icon. This will always push that widget to the draft version of the document in other locales, as long as it can be found there.

Switching locales via custom hostnames and/or prefixes

You'll want URLs to be different between locales so that there is no ambiguity when a user shares them.

You can do so by setting the hostnames and/or prefixes options. Notice that these are separate from the main locales array. This is done to make it easier to differentiate the hostnames between development, staging and production environments using a data/local.js file that is present only in the proper environment. Apostrophe merges the contents of that file with your main app.js Apostrophe configuration using _.merge(), which works best with objects and properties.

Notice that a hostname is specified for every locale, and if a hostname is shared by two or more locales, all of those locales must specify prefixes.

There does not have to be any similarity between the hostnames. They can be completely different.

Two or more locales may have the same prefix, as long as they have different hostnames.

Two or more locales may share a hostname, as long as no more than one does not have a prefix. That is, you may have a "fallback" locale for a hostname with no prefix configured, but you can't have more than one.

If one of several locales sharing a hostname has no prefix, you should review the addApiCalls option, to avoid situations where Apostrophe assumes accesses to a private API implemented by your site should count as a switch to the unprefixed locale.

Example

    'apostrophe-workflow': {
      hostnames: {
        'fr': 'exemple.fr',
        'default': 'example.com',
        'us': 'example.com',
        'us-en': 'example.com',
        'us-es': 'example.com'
      },
      prefixes: {
        'us': '/us',
        'us-en': '/en',
        'us-es': '/es',
        // We don't need prefixes for fr because
        // that hostname is not shared with other
        // locales
      },
      locales: [
        {
          name: 'default',
          label: 'Default',
          private: true,
          children: [
            {
              name: 'fr'
            },
            {
              name: 'us',
              private: true,
              children: [
                {
                  name: 'us-en'
                },
                {
                  name: 'us-es'
                }
              ]
            }
          ]
        }
      ],
      defaultLocale: 'default',
      // IMPORTANT: if you follow the examples below,
      // be sure to set this
      alias: 'workflow'
    }

If all of the locales that share a hostname have a prefix, you can still configure one to be the default, in which case an access to the hostname with no prefix will redirect to that locale:

    defaultLocalesByHostname: {
      // 'en-us' and 'en-gb' are both mapped to example.com, with
      // separate prefixes, but 'en-us' is the default
      'example.com': 'en-us'
    }

This will issue a 302 (temporary) direct status code. You can override that if you wish:

    missingPrefixRedirectStatusCode: 301

We recommend doing this only after some experience with your configuration, as permanent redirects may be cached by browsers for a long time.

Default locales for individual hostnames

In addition to the global defaultLocale option, you can set up a defaultLocalesByHostname object to map hostnames to default locales. This is useful if the host has several prefixes but a request arrives with no prefix. If the global default locale is not the best choice for yourcompany.us, you can map it, for instance, to en-us:

hostnames: {
  'en-us': 'yourcompany.us',
  'es-us': 'yourcompany.us',
  'default': 'yourcompany.com'
  // ... and other unrelated hostnames ...
},
prefixes: {
  'en-us': '/en',
  'es-us': '/es'
},
defaultLocalesByHostname: {
  'yourcompany.us': 'en-us'
},
defaultLocale: 'default'

Using addApiCalls: when a locale that shares a hostname has no prefix

When one of the locales sharing a hostname has no prefix, Apostrophe needs your help to distinguish between page URLs that should switch to that locale and API calls that should rely on the locale setting already in the user's session.

By default Apostrophe knows that any URL starting with /modules should not change the locale. Neither should /login or /logout, or anything with a file extension, to avoid accidental locale switches due to missing assets.

You can add additional rules like these by passing an array of them as the addApiCalls option to the module:

  addApiCalls: [
    // simple string match
    '/my/private/api',
    // minimatch glob match, just one folder level
    '/my/apis/*',
    // regular expression
    /^\/nothing\/past\/here\/.*$/
  ]

Adding prefixes to an existing database

Prefixes are automatically added to page slugs when Apostrophe is restarted or you run a command line task, such as apostrophe-migrations:migrate.

You can also request this explicitly:

node app apostrophe-workflow:add-locale-prefixes

This is a one-time action. Prefixes are automatically added to new pages and when editing the "page settings" of old ones.

If you change or remove the prefix for a locale, the change will take place for existing pages the next time you restart the site or run a task.

The editor may appear to allow removing the prefix from the slug, but it is always restored on save.

If you only care about subdomains

As a convenience, if all of your locales use subdomains which match the name of the locale, you may set subdomains: true and skip the hostnames option.

If you only care about prefixes

Similarly, if all of your locales use prefixes which match the name of the locale, you may set prefixes: true rather than passing an object that spells out the prefixes for each locale.

Of course, if you use the hostnames or subdomains option, your front end proxy must actually be configured to forward traffic for those hostnames.

One login across all hostnames

The workflow module provides single sign-on across all of the hostnames, provided that you use the locale picker provided by Apostrophe's editing interface to switch between them. The user's session cookie is transferred to the other hostname as part of following that link.

Locale-specific stylesheets

Basic support for locale-specific stylesheets is provided. You may, if you wish, specify a stylesheet name for a locale. The primary purpose of such a stylesheet is to define font face imports and other global items, so that the regular LESS CSS build of Apostrophe can then use a consistent font-family setting for all locales but will in fact receive the correct actual font.

You may also define a defaultStylesheet to be pushed to all locales that do not specify a stylesheet. This is useful in cases where a single @font-face or @import declaration will serve for almost all locales, but should be overridden completely for a few specific locales to avoid redundant downloads.

'apostrophe-workflow': {
  locales: [
    {
      name: 'en-gb',
      label: 'England'
    },
    {
      name: 'en-us',
      label: 'United States'
    },
    {
      name: 'cn',
      label: 'China',
      stylesheet: 'cn'
    }
  ],
  defaultStylesheet: 'default'
  // other options...
}

Since we specified stylesheet: 'cn' for the cn locale, the project-level file /lib/modules/apostrophe-workflow/public/css/cn.css will be served to the browser. For all other locales, /lib/modules/apostrophe-workflow/public/sss/default.css will be served, because defaultStylesheet was also set.

It will be delivered via a special Express route and the URL will target that route. The URL will be unique to the current asset generation and locale.

The file will not be part of a minified, CDN-delivered asset bundle. However, site-relative URLs (those beginning with /) will be rewritten to account for Apostrophe's prefix option and/or the use of an asset bundle in a CDN. Other types of relative URLs currently are not supported here. If you need to reference assets in the public folder of a module, use /modules/modulename, prefixing modulename with my- if it is in the project level public folder of an Apostrophe core or npm module.

This file will be pushed via a separate link element in the head, prior to the main, LESS-compiled spreadsheet.

This file currently WILL NOT be compiled with LESS, and it MAY NOT set LESS variables for other stylesheets to honor. Again, its primary purpose is to declare font face imports in a way that does not require excessive imports that are not needed in other locales.

Parked pages with localized slugs

In apostrophe-pages configuration, you can have parked pages created automatically on server startup. The usual way is to configure a slug string for each parked page. If you need localized slugs, there will be a slug object instead:

park: [
  {
    slug: '/',
    published: true,
    _defaults: {
      title: 'Home',
      type: 'home'
    },
    _children: [
      {
        slug: {
         'en': '/products-en',
         'fr': '/produits',
         '_default': '/products'
        },
        _defaults: {
          type: 'product-page',
          title: 'Product'
        },
        published: true,
        parkedId: 'products'
      },
    ]
  }
]

The slug keys must match workflow locales. Note the _default property for any locales not enumerated in the slug object. It is mandatory if at least one locale from apostrophe-workflow is not mentioned. Also, the homepage ('/') cannot be localized this way because prefixes can be configured in apostrophe-workflow.

Prefixes in localized slugs

They will be automatically added to the slugs defined for parked pages, even if they are localized.

For example:

// app.js
require('apostrophe')({
  shortName: 'test',
  modules: {
    'apostrophe-workflow': {
      prefixes: {
        'en': '/en',
        'fr': '/fr',
        'de': '/de'
      }
    },
    'apostrophe-pages': {
      types: [
        {
          name: 'home',
          label: 'Home'
        },
        {
          name: 'product-page',
          label: 'Product'
        }
      ],
      park: [
        {
          slug: '/',
          published: true,
          _defaults: {
            title: 'Home',
            type: 'home'
          },
          _children: [
            {
              slug: {
              'en': '/products-en',
              'fr': '/produits',
              '_default': '/products'
              },
              _defaults: {
                type: 'product-page',
                title: 'Product'
              },
              published: true,
              parkedId: 'products'
            }
          ]
        }
      ]
    },
    product: {
      extend: 'apostrophe-pieces',
      name: 'product',
      label: 'Product'
    },
    'product-pages': {
      extend: 'apostrophe-pieces-pages'
    }
  }
});

With this configuration, the product page URLs created at server startup will be :

  • for the en locale: /en/products-en
  • for the fr locale: /fr/produits
  • for the de locale: /de/products

Workflow with permissions: limiting who can do what

The workflow module supports permissions. This tutorial breaks down how to go about setting up a site with permissions and then creating permissions groups for particular locales. We'll then add new users to each of those groups and experiment with what they can and can't do.

These features are helpful when a large team manages a site together. If your team is small and everyone might potentially work on everything, you might not choose to use these features.

Setting up for permissions: enabling group management

First, make sure to remove the groups option in the apostrophe-users module or comment it out:

// groups: [
//   {
//     title: 'admin',
//     permissions: [ 'admin' ]
//   }
// ],

Next, using the command line, create the admin group and the admin user:

cd /app
# Add group 'admin' with permission 'admin'.
node app.js apostrophe-groups:add admin admin
# Add user 'admin', who is a member of group 'admin'.
node app.js apostrophe-users:add admin admin

Now launch your site.

Removing the legacy groups

If you set up the site with the typical admin, guest and editor groups, but your plan is to give out permissions for specific locales to specific groups of people, you may wish to remove the editor group. You can do that via the "groups" button in the admin bar. Removing the guest group is optional; some find it useful for simple intranet pages.

Do not remove the admin group. You need it to log in with full privileges.

An overview of permissions

Permissions are an important issue when working with workflow and locales. First we'll review all of the permissions you're sure to have questions about. Then we'll look at selecting locales for each permission and what that allows us to do.

The "Editor" permission

If you give this permission to a group, members of the group can create and edit their own pieces of any type, except for admin-only types like users and groups. In addition they are candidates to edit pages, but only if they are given permission explicitly for that page. If you do not need to distinguish between permissions for one piece type and another, this can be convenient.

The "Admin: All" permission

Do not give this permission to a group unless you want them to have total control, including making more users and giving groups more permissions.

This permission does not present a choice of locales, because it provides total control of the website.

"View Private Locales"

This permission restricts access to locales that are marked with private: true. These are the locales that the general public cannot access. Often a default locale is the parent of all other locales and the public cannot see it. You should generally give this permission to any group that has editing privileges on the site.

"Upload and Crop"

This permission is required to upload attachments to the site. You should generally give it to any group that has editing privileges on the site.

The "Admin: Global" permission

This refers to the shared "global" document that is often used for shared headers and footers that appear on every page of a site. If you wish a group to be able to edit this, give them the "Admin: Global" permission.

The "Edit: Global" permission (do not use)

The "Edit: Global" permission exists because the global document is technically a piece, but will be hidden in the interface soon. Users can't create their own new "global" doc, so this permission is not useful. See "Admin: Global" instead.

"Admin: Pages": total control of pages

If you give this permission to a group, members of the group can edit all of the pages on the site, subject to locale restrictions, as we'll see in a moment.

"Edit: Pages": candidates to edit pages

If you give this permission to a group, members of the group are candidates to edit pages, but only if they are given permission explicitly for that page. They can also create subpages at that point, and will have permission to edit those as well.

"Admin: Article": total control of articles

Users with this permission have complete control of articles (blog post pieces, as configured in the sandbox project).

"Edit: Article": creating and editing their own articles

Users with this permission can create and edit their own articles. They usually cannot edit anyone else's, unless custom edit permissions for pieces are specifically enabled (see the pieces module documentation).

"Admin: Image": total control of articles

Users with this permission have complete control of images (the image pieces that the apostrophe-images widget displays).

"Edit: Image": creating and editing their own images

Users with this permission can add and edit their own images on the site. They cannot typically edit anyone else's, although it is possible to enable custom edit permissions for pieces (see the pieces module documentation). You will usually want to give any group with editing permissions access to edit images.

"Edit: File": creating and editing their own files

Just like "Edit: Images", but for files such as PDFs, typically used with the apostrophe-files widget.

Locales for permissions

After you check the box for a permission, you will be presented with a choice of locales. There is a dropdown menu for each one.

If you leave it set to "none," then members of the group cannot perform that action for content in that locale.

If you set it to "edit," then members of the group can perform that action for draft content in that locale, but cannot commit the content (make it live).

If you set it to "commit," then members of the group can both edit the draft and commit it and make it live.

"Admin: All" and a few other permissions, like "view private locales" and "upload and crop," do not present a choice of locales because they are not locale-specific.

If you are only using this module for workflow and have not set up multiple locales, you will still need to set the dropdown for the "default" locale to "edit" or "commit" for each permission.

Permissions tutorial

This tutorial assumes you have configured a default parent locale and en and fr child locales. We also assume you are working with our sandbox project, which has the blog module configured with the label "Articles."

Our goal is to enable a certain group of people to edit, but not commit, the en locale, and another group of people to commit their changes, making them live. That second group should also be able to export those changes as new drafts in the fr locale.

Creating the fr-editors group

Log in as the admin user. Click on the admin bar. Click "Groups."

Click "Add New Group" and give it the name "fr-editors".

Now click on the "Info" tab and begin selecting permissions.

We recommend you check these boxes:

  • View Private Locales
  • Upload and Crop
  • Edit: Page
  • Edit: Image
  • Edit: File
  • Edit: Article

After you check each of the last four, you will see dropdowns allowing you to pick a level of control for each locale. For "fr," pick "Edit." Leave the rest set to "None."

Creating an "fr-editor" user

Next, click on "Users" in the admin bar. Add an "fr-editor" user. Make them a member of the "fr-editors" group by clicking the "Browse" button for "Groups." It works just like editing any other relationship in Apostrophe.

Save the user and move on to the next step.

Creating the "fr-committers" group

Now we'll want a group with permission to commit changes to "fr," and also export them, as drafts, to the "en" locale to explore that feature.

Follow the procedure to create a group again, name it "fr-committers," and click the "Info" tab to edit permissions.

This time, check these boxes:

  • View Private Locales
  • Upload and Crop
  • Admin: Pages
  • Admin: Image
  • Admin: File
  • Admin: Article

For each one, the list of locales will appear again. For "fr", pick "commit." For "en", pick "edit."

"Why Admin: rather than Edit: this time?" Because this allows us to edit pieces that were created by other people. It also gives us access to all of the pages on the site for the specified locales. If you don't want this — if you want to be more restrictive, and give out permission page by page to this group — you can choose "Edit: Pages." Conversely, you can specify "Admin: Pages" for the "fr-editors" group if you wish to skip giving out permissions to them page by page.

Creating an "fr-comitter" user

Next, click on "Users" in the admin bar. Add an "fr-committer" user. Much like before, make them a member of the "fr-committers" group by clicking the "Browse" button for "Groups."

Save the user and move on to the next step.

Granting editing permissions on the home page

Now, as the admin user, switch from "Live" to "Draft." Then click "Page Menu" and "Page Settings." Now click the "Permissions" tab.

Here you can change the view and edit permissions of the home page. For "These Groups can Edit," click "Browse" and pick both groups.

When the option appears, set "Apply to Subpages" to "Yes." This will perform a one-time change of the permissions of all of the descendants of the home page. If you skip this step, you are giving out permissions on the homepage only, not its subpages.

Now save your work. Permissions for the home page have been pushed to the two new groups.

Working with the "fr-editors" account

Next, log out, or use an incognito window, separate browser, or separate user identity in Chrome.

Now log in as "fr-editor".

Unless fr is the default locale, you won't have any editing privileges right away. That's because we only gave them out for the fr locale. Click "Locales" and pick "fr".

Now you'll see edit buttons on the home page and you can edit it normally. You can also click "Submit" to request review. But, you can't commit the page.

Similarly, when you edit "Articles" via the admin bar, you can submit them, but you cannot commit them. So, you can't make changes live on your own.

Working with the "fr-committers" account

Now use another browser identity, or log out, and log back in as "fr-committer".

Again, you'll need to switch to "draft" mode and also switch to the "fr" locale before you see editing capabilities.

This time, you'll notice a "commit" button on the home page. And, you'll find that when you edit pieces via the admin bar, you have a "commit" option (accessed via the "workflow" dropdown in the editing dialog box for each piece).

Commit changes to make them live for the home page, and you'll see that as a logged-out site visitor you are now able to see them, provided that you have implemented a way for logged-out users to switch to the fr locale.

Exporting

After you commit a change, such as on the home page, you'll be offered the usual option to export the change. And, as "fr-committer", you will be able to check the box to export to "en" (English). However, if any other locales are present, you will not be able to check those boxes. That's because we did not give the "fr-committers" group "edit" access to those locales for pages.

Accessing newly created pages in other locales

Often a page or piece is created solely for use in a single locale. Technically, the page or piece exists in every locale. However, to avoid clutter, it is initially "trash" in other locales. So how do we make those pieces and pages live in other locales when we wish to?

For pieces, it is straightforward to switch locales, click to edit that type of piece, and pick "Yes" from the "Trash" dropdown to see the pieces that are currently considered trash. Just click to edit the piece in question, and change "Trash" to "No."

For pages, it is almost as straightforward. Click on "Pages" in the admin bar to access the "reorganize" view. Here you can locate a page in the trash at any level. Just locate its parent page, then click on the trash can displayed at that level in the tree to open the trash and find the page you want.

As with pieces, change "Trash" to "No." The "Trash" field will be located right after the "Published" field. When you click save, the page will be live in this locale.

Removing workflow from a project

It is possible to remove workflow from a project. If you want localization and/or an approval process, you want to keep it. But if you don't want either of those things anymore, read on.

WHEN YOU REMOVE WORKFLOW, ALL CONTENT IS DELETED FOREVER, EXCEPT FOR THE ONE LOCALE YOU CHOOSE TO KEEP, AND EITHER THE DRAFT OR THE LIVE CONTENT, WHICHEVER YOU CHOOSE TO KEEP. We strongly recommend backing up your database with mongodump before you remove workflow.

This command will remove workflow from a project without locales (a project which has a "commit" button, but no language picker):

# Keep the live content, DISCARD the draft content
node app apostrophe-workflow:remove --live

You may also specify --draft. You may NOT specify both draft and live.

This command will remove workflow from a project with locales. ONLY THE en LOCALE IS KEPT IN THIS EXAMPLE. EVERYTHING ELSE IS DELETED:

# Keep ONLY the "draft" content fron the "en" locale
node app apostrophe-workflow:remove --locale=en --draft

"Why does it work this way?" If there is no workflow module to interpret URLs across locales, or determine whether you are in draft or live mode, then there is no way to serve more than one home page, etc. If you want to keep these features, you must keep the workflow module.

Removing workflow in production

Some notes on removing workflow from a site that is already in production:

  1. Always back up the database first (mongodump).
  2. Plan for downtime. You need to shut the site down while running the apostrophe-workflow:remove task so that it does not attempt to reinsert workflow-related things.
  3. Run the task on the server while the site is shut down.
  4. Redeploy your site with the apostrophe-workflow module removed from the configuration.

Other developer concerns

Aliasing the module

By default, optional modules like apostrophe-workflow do not have an alias. That means you can't just type apos.workflow to access them.

However, in the suggested examples above, we assume you have done this when enabling the module:

'apostrophe-workflow': {
  locales: [
    {
      name: 'en'
    }
  ],
  defaultLocale: 'en',
  // IMPORTANT: if you follow the examples below,
  // be sure to set this
  alias: 'workflow'
}

If you are using that alias for another module in your project, all of the examples above will still work. Just replace any references to apos.workflow with a different alias and configure that alias for the module.

Excluding certain types and properties from workflow

You may have piece types and individual document properties that should not be subject to workflow.

For instance, the apostrophe-user and apostrophe-group piece types are automatically excluded from workflow, because they power login on the site, have permissions associated with them and are generally not intended to be displayed as frontend content.

To exclude additional types, set the excludeTypes option:

'apostrophe-workflow': {
  excludeTypes: [ 'my-type-name' ]
}

Note that my-type-name will be singular, it matches the name option of your pieces module, it is not the module name.

You may also want to exclude individual properties. If you have a property of your pieces which only makes sense for the live locales and should not be translated either, such as a hit counter field, you will not want workflow to constantly present the "commit" button based on that difference between draft and live.

To exclude a property, write:

'apostrophe-workflow': {
  excludeProperties: [ 'hitCounter' ]
}

The property is excluded for all doc types. Use a name that is unambiguous for such properties.

Managing the pages tree without workflow

With the normal apostrophe-workflow behavior, when you move a page in the page tree modal, it only moves the draft one. Later, when committing, it will move the live one too. The draft document is moved relatively to a target (it can be before, after or inside), the live one is moved relatively to the live version of this target.

In some cases, when moving too much nested pages without committing each one before, it can generate mismatches between draft and live pages trees and lead to confusion.

To avoid this behavior and move draft and live pages at the same time, you can pass this flag to you workflow config:

'apostrophe-workflow': {
  autoCommitPageMoves: true,
}

:warning: This will only commit the new position in the tree of a moved page. It will not commit any other properties of the page, such as the slug or title.

Previewing piece types without an index page

The preview iframe displayed by the commit and history review modals works with regular pages and also with pieces that can be displayed on a page via a pieces index page, such as a blog.

For other doc types, or pieces that will never have an index page, you may optionally implement a workflowPreview method. Here is the implementation for apostrophe-images:

self.workflowPreview = function(req, before, after) {
  return self.render(req, 'workflowPreview', { image: after });
};

And the workflowPreview.html template:

<img
  class="apos-workflow-preview-image"
  src="{{ apos.attachments.url(data.image.attachment, { size: 'one-half' }) }}"
/>

If you do not supply an implementation, a message indicating that no preview is available will be displayed. A list of modified fields will still be offered to help the user understand what has changed.

Command line tasks and workflow

Using the --workflow-locale option

By default, command line tasks that use Apostrophe's find, insert and update methods see and modify the content of the default locale (not the draft version of it).

You can change this by adding the --workflow-locale option to your command line:

node app my-module:my-task --workflow-locale=en
node app my-module:my-task --workflow-locale=en-draft

Note that you must add the -draft suffix if you want to target draft content, not live content.

Setting the current locale programmatically

You can also choose a locale programmatically when creating a req object for use in a task.

Here's a way to get an "admin" req object that can do anything and "sees" docs in the default locale:

self.apos.tasks.getReq({ locale: 'fr' })

And here's a way to get an "anonymous" req object that can only see what the logged-out public sees:

self.apos.tasks.getAnonReq({ locale: 'fr' })

As usual, these can be used with any Apostrophe method that expects a req object.

Direct MongoDB access and workflow

Code that bypasses Apostrophe's find, insert and update methods in favor of directly modifying the apos.docs.db MongoDB collection will not automatically restrict itself to the current locale.

Usually, this is perfectly fine. Many command line tasks and migrations should operate on all docs, regardless of whether they are part of a particular locale. And many direct uses of apos.docs.db in project-level code are already limiting an update operation to a specific _id, which will already be specific to a locale.

However, if you need to work directly with MongoDB while respecting a specific locale, you can check the workflowLocale property as part of your MongoDB query. The values of workflowLocale will match the locale name, except that docs in draft locales will have a -draft suffix appended to the locale name you were expecting.

Since not all doc types are subject to workflow, you may need to build your criteria like this:

{
  $and: [
    {
      workflowLocale: {
        $in: [
          'en', null
        ]
      }
    },
    {
      // YOUR OWN CRITERIA GO HERE
    }
  ]
}

Again, this is often unnecessary. Code that is already operating on specific docs as specified by _id will already touch only one locale, because docs are replicated across locales with different _id properties. The localized versions of each doc will have different _id properties, but the same workflowGuid property.

In general, you should use Apostrophe's own methods rather than direct MongoDB access unless you have a compelling reason, such as access to $set or $inc. See also setPropertiesAcrossLocales, below, for a convenient way to access $set.

workflowModified: must be set true if you make changes subject to workflow

Apostrophe automatically sets workflowModified: true on any draft document when it is modified via Apostrophe's update or insert APIs.

However, if you modify documents directly via MongoDB, you will need to set the workflowModified property to true yourself.

If you missed this, and encounter difficulties later because Apostrophe does not invite the user to commit the document, you can refresh the workflowModified property of all draft documents by running the apostrophe-workflow:recompute-modified command line task. Just bear in mind that you shouldn't have to use this task on a regular basis! It is completely automatic unless (1) you have made direct modifications via MongoDB and (2) you wish the document to become "committable" in that situation.

setPropertiesAcrossLocales: modifying a document programmatically across locales

The setPropertiesAcrossLocales method can quickly update properties of a doc across some or all locales:

var workflow = self.apos.modules['apostrophe-workflow'];
// `doc` is a doc we already fetched normally for the current locale
return workflow.setPropertiesAcrossLocales(req, doc,
  { age: 50 },
  [ 'us', 'fr' ],
  {},
callback);

This call will set the age property to 50 in both the us and fr locales, which must be configured in the workflow module.

This affects only these two live locales. To affect both live and draft locales, write:

return workflow.setPropertiesAcrossLocales(req, doc,
  { age: 50 },
  [ 'us', 'fr' ],
  { mode: 'both' },
callback);

To affect only draft locales, write:

return workflow.setPropertiesAcrossLocales(req, doc,
  { age: 50 },
  [ 'us', 'fr' ],
  { mode: 'draft' },
callback);

To affect all locales:

return workflow.setPropertiesAcrossLocales(req, doc,
  { age: 50 },
  'all',
  {},
callback);

To affect all locales, but live docs only, not drafts:

return workflow.setPropertiesAcrossLocales(req, doc,
  { age: 50 },
  'all',
  { mode: 'live' },
callback);

This method bypasses the excludeProperties option and also does not invoke docAfterSave, etc.

"What about inserting a new doc?" A newly inserted doc is pushed to all locales, however its trash flag is true in all of them except the current locale. If you want the new doc to be instantly available in all locales, then after the insert is complete, you can use setPropertiesAcrossLocales to set the trash property to false.

Writing safe afterInsert and docAfterInsert handlers, etc.

Recognizing inserts due to localization

When a document is "born" in one locale, it is immediately replicated to all others, although it will initially be in the trash in many of them.

If the replicateAcrossLocales option is set to false, this does not occur. However documents are always replicated at least between the draft and live versions of the same locale.

In some cases, the work you do in your beforeInsert handlers, etc. should not be done in this situation, for instance because you are inserting many repetitions of an event, and that will already happen when the original document is created in the first locale.

You can detect this situation by looking for the doc._workflowPropagating property. If it is true, the document being inserted is being copied from another locale.

Always finish the job before continuing

If you are writing custom code that includes afterInsert or afterUpdate methods for pieces modules, or docAfterInsert or docAfterUpdate methods in any module, and these handlers update the doc, then your code must complete its own work BEFORE invoking the original version of the method, or the callback.

If you do not follow this rule when inserting a new doc, the workflow module may encounter a race condition when adding corresponding docs for other locales. In recent versions of apostrophe-workflow, this will result in a unique index error. In older versions it may result in duplicate docs for the same workflowGuid + workflowLocale combination. Either way: a bad thing.

For best results, all implementations of Apostrophe callbacks should wait to complete their own work before invoking the callback. It produces the most predictable result. However, you can bend this rule if you are not updating the doc itself in the database.

Workflow event hooks

The module emits events for major workflow stagins, including afterSubmit, afterCommit, afterExport, afterForceExport and afterForceExportWidget. They were originally added with the intent to use them with the apostrophe-external-notifications module, but are generally available as needed. Each event includes data relevant to the related action, which can be captured with an event handler such as:

// in lib/modules/apostrophe-workflow/index.js
// ...
construct: function (self, options) {
  self.on('apostrophe-workflow:afterCommit', 'logCommitData', function (req, data) {
    self.apos.utils.info('The commit data is', data);
  });
},
// ...

See the documentation of "Custom Server-side Event Handlers with Promise Events" for more information.

Avoiding Express sessions for anonymous site visitors

By default, ApostropheCMS will require session storage for all site visitors, even anonymous, logged-out visitors. Of course this has a performance impact.

It can be avoided by configuring the apostrophe-express core module as follows:

// in app.js
modules: {
  'apostrophe-express': {
    csrf: {
      disableAnonSession: true
    }
  }
}

If you choose to do this, there is one consequence for workflow: locale switching will not work unless either (a) you have fully distinguished all of your locales with URL prefixes and domains, or (b) the user is logged in. Since it is always a best SEO practice to always fully distinguish locales in this way, you shouldn't have any problems in production with this setting. Just remember that in early development you may not want to enable it unless you have already set up URL prefixes for all locales.

Technical approach

For 2.x, the draft and live versions of a doc are completely separate docs as far as most of Apostrophe is concerned. A workflowGuid property ties them together. This greatly reduces the scope of changes required in the rest of Apostrophe and improves performance by removing the need to move content around on every page view or load content for locales you are not looking at.

As the term locale suggests, the 2.x workflow module also implements localization of content by introducing paired live and draft locales for each country or culture you wish to support.

Use of jsondiffpatch

This module relies somewhat on jsondiffpatch to calculate diffs between commits and offer a patch to be applied to the drafts of other locales. jsondiffpatch is also used to visualize differences in the commit modal.

Here is documentation of how the diff deltas work. Our code taps into this diff output format to visualize differences.

As it turns out this algorithm is best suited to exporting changes to the schema fields of a doc.

Patching and exporting of widgets

jsondiffpatch is not well suited to patching widgets and other items with globally unique ids that can be leveraged to always recognize them even if they have moved around in a document. For this reason a separate algorithm is applied first to handle exporting and patching of widgets.

Legacy tasks

Cleaning up duplicate homepages

If you experimented with the pre-npm-publication Apostrophe 2.x version of this module before 2017-07-26, you may need to clean up duplicate homepages created by the parked page mechanism before it was made locale-aware. If you suffer from this problem you will likely see that the "reorganize" view does not show any children of the home page.

No one else should ever need this task for any reason, and you should only need it once.

You can fix the issue with this command line task:

node app apostrophe-workflow:remove-numbered-parked-pages

This task will permanently remove all "parked" pages with a slug that ends in one or more digits. By default the only parked pages are / and /trash, neither of which should ever end in a digit. If your custom configuration of parked pages includes pages with slugs that should end in a digit, this task is not suitable for you as written. But again, you almost certainly do not need it, unless you were a user of this module prior to 2017-07-26.

Additional options

The following additional options can be set on the apostrophe-workflow module to adjust the default behavior.

defaultMode

By default, when a user logs in, they are in draft mode beginning in version 2.31.0. However, if this does not suit your use case, you may set defaultMode to draft, live or preview. Choosing live may make the most sense if the user cannot access a locale they are actually allowed to edit until they have logged in, but in most cases being able to edit immediately is the superior choice.

forceExportExistingRelatedDocuments

By default, when force exporting, a choice is also offered to force export related documents even if they already exist in the target locale, overwriting the document in the target locale. If this option is explicitly set to false, this choice is not offered.

changelog

Changelog

2.40.3 (2023-03-06)

  • Removes apostrophe as a peer dependency.

2.40.2 (2022-12-21)

  • Fixed security scanner report by removing deep-get-set package. There was no actual vulnerability due to the way the package was used in Apostrophe.

2.40.1 (2022-10-25)

  • Fixed a bug when workflow is combined with the blog module and a blog post is dated in the future. This caused problems when editing the blog post again.

2.40.0 (2022-05-03)

forceExportExistingRelatedDocuments: by default, when force exporting, a choice is also offered to force export related documents even if they already exist in the target locale, overwriting the document in the target locale. If this option is explicitly set to false, this choice is not offered.

2.39.7 (2022-01-21)

  • Fixes the workflow batch export progress bar, not taking related documents into account to compute the percentage.

2.39.6 (2021-11-15)

2.39.5 (2021-10-21)

  • To mitigate issues with joins not always populating, "Batch Force Export" now force-exports related documents before selected documents. However for data integrity reasons pages continue to be batch force exported in tree order, even if they are merely "related to" the documents directly selected.

2.39.4 (2021-08-30)

  • When fetching the live version of a document for comparison, do so using the appropriate manager module, so that all cursor filters for that type are consulted. Thanks to Michelin for their support of this work.

2.39.3 (2021-06-28)

  • Fix assembly crashing when creating new site or hanging on startup in the presence of headless. A regression introduced in version 2.39.2.
  • Adds CircleCI configuration file.
  • Documents Node 10 requirement. This was already enforced by the yargs dependency not supporting Node 8.

2.39.2 (2021-06-21)

  • Get autoCommitPageMoves option from workflow module.

2.39.1 (2021-06-07)

  • Fixes duplicate key errors in sync page tree task. For live pages not already updated with the same path.

2.39.0 (2021-05-20)

  • Adds autoCommitPageMoves flag to commit only pages moves automatically.

2.38.3 (2021-03-01)

  • Fixes page slug updated twice when committing a page move.

2.38.2 (2020-10-21)

  • Sets the new forbiddenFields option for the Manage Workflow view, a "virtual" piece type which has the unusual property of viewing content of all types, to an empty array.

2.38.1

  • Fixes an issue with apostrophe-workflow:replicate-locale where pages were not always replicated in page tree order, resulting in errors.

2.38.0

  • Adds the apostrophe-workflow:replicate-locale command line task, with --from=locale1 and --to=locale2 options, to drop the current contents of locale2 and replace them with a copy of the contents of locale1. If you have already done work in the to locale, we recommend running apostrophe-attachments:recompute-all-doc-references afterwards, to ensure attachments do not lose track of whether it is time to disable access.

2.37.0

  • Adds options.removeWhenLive to apostrophe-admin-bar, a list of items to be removed from the admin bar during preview/live mode. It can be added to from other apostrophe-admin-bar improvers like so:
    beforeConstruct: function(self, options) {
      options.removeWhenLive = [ 'some-module-name' ].concat(options.removeWhenLive || []);
    },

2.36.0

  • UX improvement: users report that if they have permission to commit, the "submit" button does not make sense to them. They often think they must use both buttons, and it leads to user frustration. Now, the "submit" button is no longer shown to users by default, unless there are modified documents they can submit for review but not commit themselves. According to users this should be a big improvement, but if you prefer the legacy old behavior, you may set the committersSeeSubmit option to true.
  • For performance, the workflow module only checks to see if there are modified documents that need committing in certain situations. This is good, but some modules like apostrophe-palette modify documents via their own API routes, and the workflow UI does not "see" the change until the page is refreshed. The apostrophe-workflow module now recognizes the workflowModified event, which simply triggers a refresh of the on-page workflow UI, recognizing any changes that were made in your code. You can emit this event in browser-side code by calling: apos.emit('workflowModified')

2.35.0

  • Namespaced the i18n calls, so that the use of objectNotation with the apostrophe-i18n module does not break the permissions editor. Peer dependency formally set to apostrophe 2.107.0, but we recommend npm update to the latest update of the same major version for all Apostrophe modules when updating Apostrophe.
  • Added the missingPrefixRedirectStatusCode option. By default access to the root of a domain that has multiple locales distinguished by prefixes will redirect to the one specified by defaultLocalesByHostname with status code 302. You can set this to 301 for a permanent redirect. This is not recommended until you are very comfortable it is working properly.
  • Corrected a documentation error: defaultLocalesByHostname was incorrectly documented as defaultHostnamesByLocale.

2.34.1

If a document is fetched without the type property due to a projection, operations such as "force export" could potentially crash. This issue has been fixed.

2.34.0

Security: required update for compatibility with version 2.106.0 of the apostrophe module, which fixes an issue that could lead to a leak of piece and page fields not intended for public consumption via certain APIs. See the apostrophe module changelog.

2.33.0

  • Send the Apostrophe-Locale header in all Apostrophe lean mode API calls, just like we do in jQuery-based API calls. When logged out and using URL prefixes for locales, this is necessary to affect the right locale without making every single API call that any developer makes specifically locale aware, because APIs are not URL-prefixed by locale. This change fixes apostrophe-forms submissions for such locles.

2.32.1

  • Dead code elimination. No functional changes.

2.32.0

  • This module now emits the apostrophe-workflow:afterSubmit event, for use with the module apostrophe-external-notifications (and other needs).
  • Various documentation corrections.

2.31.0

  • A regression introduced into the Batch Force Export feature in version 2.29.0 has been fixed. This regression prevented the documents directly selected from being force exported, unless related documents were also selected. Thanks to Michelin for making this work possible via Apostrophe Enterprise Support.
  • When you log in, workflow now defaults to draft mode. Defaulting to live mode caused a great deal of user frustration as users expected to be able to edit right away. However, you can set the defaultMode option for the apostrophe-workflow module to draft, live or preview to override this. Thanks to J. Garijo for contributing this feature.
  • Private locales are now locked down fully even in cases where they have hostnames, subdomains, prefixes, or defaultLocaleByHostname configuration that might otherwise imply they should be visible to the public. Thanks again to Michelin.

2.30.0

  • The slug property of a parked page can now be localized. Rather than a simple string, just provide an object with locale names as property names and slugs as values. You may use the property name _default to specify a slug to be used for all other locales. Thanks to Michelin for making this work possible via Apostrophe Enterprise Support.

2.29.0

  • "Force Export" and "Force Export Related" now ask whether you really want to consider force exporting related documents that already exist in the destination locales. This pairs well with the recommended replicateAcrossLocales: false option, as a means of helping users to avoid doing unintentional harm with force exports. Thanks to Michelin for making this work possible via Apostrophe Enterprise Support.

2.28.2

  • When exporting a commit to another locale, a change in an attachment property is now properly applied as part of the patch.

2.28.1

  • Fixed a bug that sometimes prevented editing from working properly in draft mode, generating errors similar to "item does not exist." This bug was caused by a conflict between the i18n module and apostrophe-workflow in their understanding of draft locales.

2.28.0

  • Joins in object fields now remap properly when committing. Thanks to Eric Wong for this contribution.
  • The new apostrophe-workflow:remove task removes workflow from a project. YOU MUST remove apostrophe-workflow from app.js immediately AFTER you run this task. NOTE: if you are localizing content, you will lose ALL BUT ONE of your locales if you remove workflow! You really should READ THE DOCUMENTATION for more information about the consequences and the right way to use this in production.

2.27.0

  • When using "Batch Force Export," if you check the box to also force export related documents, you are now prompted to confirm whether you want to force export each related document type. This allows you to avoid inadvertently exporting pages that are joined via link widgets, for instance, when you only had images and files in mind. A similar change is likely to come to the individual "force export" options in page settings and the pieces editor soon. Thanks to Michelin for making this work possible via Apostrophe Enterprise Support.

2.26.0

  • A "Force Export Related" option has been added to the Workflow menu of Page Settings and the edit view of pieces. This option is identical to exporting related documents after a force export, except that the current document is not force exported again — just the related documents. Thanks to Freshworks for making this work possible via Apostrophe Enterprise Support.
  • Workflow is now more compatible with the global prefix option of Apostrophel. Thanks to Sebastian Gassner for the well-analyzed bug report.
  • An overflow issue with commit previews was resolved. Thanks to Bharathkumar Chandrasekaran of Freshworks for this contribution.
  • The Apostrophe-Locale HTTP header sets the locale for the current request only and does not modify the session. This prevents subtle race conditions that can break the locale switching UI.

2.25.5

  • Versions 2.25.3 and above treat a default locale for a hostname as a firm signal of the appropriate locale. This makes sense for pages and is necessary when other middleware and modules may have set req.locale incorrectly prior to the workflow middleware's execution. However, Apostrophe's internal APIs do not carry a workflow prefix. As a result, this change broke editing API calls for locales that have a prefix when a default locale for a particular hostname is also present. To resolve this, beginning with 2.25.5 all editing API calls now carry an Apostrophe-Locale header which is authoritative.

2.25.4

  • Version 2.25.3 introduced a regression that broke routing to locales determined by both prefix and hostname in the presence of a matching defaultHostnameByLocale. Version 2.25.4 corrects this bug and introduces an additional unit test to make sure none of these cases break in the future.

2.25.3

  • Fix crashing bug when a child page is force exported but its parent is not in the target locale, which should make it a child of the home page in that locale. Unit test for that situation (test fails without the fix in place). Guarantee that "Force Export" processes pages parent first, to minimize odd page tree outcomes.
  • Unit tests of reorganize, commit, export, batch export operations with replicateAcrossLocales: false.
  • If the hostname is a configured one for workflow and no prefix match is present but defaultLocalesByHostname is set, treat that as a clear signal that should always set req.locale rather than deferring to preexisting values in req.locale.

2.25.2

  • Improved getting started documentation: the defaultLocale option should never specify a private locale, as this is detrimental to the experience of site visitors (if the locale cannot be inferred from prefix or hostname, the first page they visit is a 404). No code changes.

2.25.1

  • Moved "also force export related documents, such as images" to the top of the modal to prevent conflict when there are many locales and to make it easier to find.

2.25.0

  • When using the batch "Force Export" option via the "Manage Workflow" modal, users are now also invited to force export "related documents" such as images.

Thanks to Michelin for making this feature possible through Apostrophe Enterprise Support.

  • The "Commit" button now appears properly after moving pages via "reorganize." Thanks to Bharathkumar-Chandrasekaran of Freshworks for this fix.

2.24.0

  • replicateAcrossLocales: false is now much more usable. Formerly, upon adding a new locale with this setting enabled, the new locale was completely empty except for parked pages and the global doc. In practice this is too little content because of the missing documents that those documents are closely joined to, such as their images. Now, the documents just one hop away from the "foundational documents" are also replicated to a new locale.
  • When "Force Export" is selected for an entire page or piece, you are now invited to "Force Export" related documents as well. This complements the change above with a more flexible solution when manually exporting more content to other locales. Note that a similar feature already exists for "Commit" and for the regular "Export" feature that follows it.
  • For a good user experience for non-admin users with some editing privileges, this version ensures that the "context menu," including the locale picker and draft/live switch, are present on the page even in Apostrophe 2.96.0, which otherwise would be more aggressive about removing this markup when the user lacks editing privileges on the current page.

2.23.1

Unit tests passing.

Regression tests passing.

  • Those with editing privileges, but not committing privileges, can now see the submit button properly.
  • The "manage workflow" modal now displays edit buttons, commit buttons, etc. only to appropriate parties. (Inappropriate parties were never able to actually do these things, there was no security issue.)
  • Silenced more promise warnings.

2.23.0

Unit tests passing.

Regression tests passing.

  • A new "preview" mode is available. Pull down the usual "Live / Draft" menu and you will discover a "Preview" option. When selected, "preview" mode shows draft content, but without the editing interface, so you can better evaluate what will happen if you commit the changes. Thanks to Siddharth Joshi for this contribution.

2.22.0

Unit tests passing.

Regression tests passing.

  • You are now invited to optionally export as well after performing a batch commit, similar to a regular commit. Thanks to Javier Mabarca.
  • This module now emits apostrophe-workflow:afterCommit, apostrophe-workflow:afterExport, apostrophe-workflow:afterForceExport and apostrophe-workflow:afterForceExportWidget events, for use with the new module apostrophe-external-notifications, which can send a notice to Slack in these situations.

2.21.2

Unit tests passing.

Regression tests passing.

  • Various issues fixed when using replicateAcrossLocales: false. These centered on the overly simplified implementation of replication for parked pages and the global doc. We now use the same full-strength algorithm for these special docs that has historically been applied to all docs when replicateAcrossLocales is true (the default).

Thanks to Michelin for making these fixes possible through Apostrophe Enterprise Support.

2.21.1

Unit tests passing.

Regression tests passing.

  • localization urls should include the global prefix if any, thanks to Sebastian Gassner for flagging the issue.

2.21.0

Unit tests passing.

Regression tests passing.

  • Fixed bugs in the "force export" logic for individual widgets, introduced in 2.20.0.
  • apostrophe-workflow:dereplicate task. This task makes sense to run only once, when transitioning to the new replicateAcrossLocales: false option.
  • If apostrophe-express.csrf.disableAnonSession is set, do not store the current locale in the session. Note that this means all locales must be distinguished by hostname, prefix or a combination of the two. If this is not yet the case in your configuration, you should fix that in any case, because for SEO purposes the same URL should never yield two different pages.

2.20.0

Unit tests passing.

Regression tests passing.

Added the replicateAcrossLocales: false option. When this option is explicitly set false, the workflow module does not immediately replicate all documents across all locales. The user can still make a document available in a new locale in the following ways: Export (after commit), Force Export, Force Export of a Single Widget (if the doc is not in the locale at all yet, the entire doc exports), Switch Locale (via the locale switching UI for logged-in editors).

The only difference website editors are likely to notice is that a document that doesn't exist in the locale yet cannot be found by looking under "Trash" in the locale. Instead it must be exported from a locale where it does exist.

Although it is not the default for backwards compatibility, this new approach is preferred because:

  • It removes any database size penalty or performance penalty for scenarios where there are many locales, and a large percentage of documents are only of interest in one locale, or a few locales.
  • It removes clutter from the "trash" view of the locales where a significant percentage of docs on the site are irrelevant.

When adopting this new option for an existing site, currently there is no official migration strategy for removing existing docs that are considered "irrelevant" in certain locales. It is not always possible to distinguish a document in the trash due to never having been relevant to the locale from a document that was intentionally placed there. If you wish you may remove them from the mongodb database according to your own preference and practices.

2.19.0

Unit tests passing.

Regression tests passing.

  • The "Modified Documents" dialog box has been rebranded as the "Manage Workflow" dialog box, and now includes a "Modified" filter. Initially you see all documents subject to workflow; you can pick "Yes" from the "Modified" dropdown to see only those that need to be committed.
  • The "Manage Workflow" dialog box now has a "Last Commit" column, which is sortable (click the column heading). This is very handy to find recently committed documents.
  • The "Last Edited" column can also be sorted.
  • Requires apostrophe 2.86.0.

Thanks to Michelin for making these features possible through Apostrophe Enterprise Support.

2.18.0

Unit tests passing.

Regression tests passing.

  • Added a sortable "Last Commit" column to the "Manage Pieces" view. Click once to display the most recently committed pieces first. This is helpful when many users are committing and you don't have time for a "submit" process but still want to keep tabs on what is happening.

Many thanks to Michelin for making this feature possible via Apostrophe Enterprise Support.

2.17.0

Unit tests passing.

Regression tests passing.

  • Added the disableExportAfterCommit option, for compatibility with how boolean editable fields work in the apostrophe-override-options module.
  • Unit test coverage to verify that the new support for adding default values from the schema for new fields of the global doc does not break in the presence of workflow.

2.16.2

Unit tests passing.

Regression tests passing.

  • Setting submittedModal to false now works as expected. Thanks to Fredrik Ekelund for the fix.
  • As the documentation has always stated, if a command line task is invoked without the --workflow-locale option, the default locale is assumed. Beginning with this release, req.locale is always populated accordingly, eliminating the risk that code will not explicitly check for a missing req.locale setting when workflow is known to be present.

2.16.1

Unit tests passing.

Regression tests passing.

  • Bug fix: don't crash if a document is updated during application startup, after apostrophe-docs is initialized but before apostrophe-workflow. Note that for best results with workflow it is not recommended to write documents to the database prior to the apostrophe:modulesReady event unless the document types are exempt from workflow.

2.16.0

Unit tests passing.

Regression tests passing.

Many thanks to Michelin for making these improvements possible via Apostrophe Enterprise Support.

  • Added a "Modified Documents" dialog box. This new dialog box provides an interface similar to "Manage Pieces," allowing you to browse all of the documents that are considered modified and could potentially be committed. In particular, clicking the "select everything" box and then selecting a batch operation like "commit" or "revert" is very useful here. You can also filter by document type, filter by whether documents were submitted for review, etc.

By default this dialog box is not enabled, but we recommend that you enable it by configuring the apostrophe-workflow-modified-documents module in app.js. You do not need to pass any options, just turn it on, like this:

// in app.js

require('apostrophe')({
  'modules': {
    'apostrophe-workflow': { ... various config here },
    'apostrophe-workflow-modified-documents': {}
  }
});

You do not need to separately install it, you just need to turn it on in app.js.

If you have grouped your admin bar, you'll want to add it to your "Workflow" group as apostrophe-workflow-modified-documents.

With this module in place, the "Submitted" dialog box is a bit redundant. If you wish, you can disable it by setting the submittedModal option of apostrophe-workflow to false.

  • This release includes a database migration. If that migration is not run, you will not see the "Commit" button on any pages until it is run.

You should always run the apostrophe-migrations:migrate command line task after upgrading apostrophe modules in your dev environment. In production, running that task should always be part of your deployment process, and if you are using our Stagecoach deployment scripts then it already is.

You can run the migration task like this:

node app apostrophe-migrations:migrate

You only need to run this task right after upgrades and as part of deployments. You can also write your own migrations.

  • Major performance improvements to workflow. These optimizations, and the modified documents dialog box feature, are the reasons why the migration is needed.

  • The exportAfterCommit option, which can be set to false to disable the "Export" modal that otherwise appears after each commit, can now be set via apostrophe-override-options.

  • The mechanism that adds missing locales for documents as needed now has a final failsafe that will operate correctly if all other heuristics fail to spot a missing document.

  • The mechanism that determines whether the "Commit" button should appear produces far fewer false positives.

Additional updates

  • 404 handling for the "switch locale" mechanism has been improved. Rather than a custom log message the user is now redirected to a reasonable guess (based on the slug) in the desired locale, typically leading to a normal 404 page that is more helpful to the user. Thanks to Fredrik Ekelund.

2.15.2

Unit tests passing.

Regression tests passing.

  • Suppress loader recursion warnings for getEditable, which is not part of normal page rendering. These warnings made the console of the server very noisy and are not significant in this particular case. When encountered in ordinary page rendering they are quite significant because they typically mean joins have not been locked down with projections and excessive queries are being made due to recursion.

2.15.1

Unit tests passing.

Regression tests passing.

Certain frequently accessed, long-running API routes of this module were causing session storage race conditions, with observed consequences such as draft content being received when live content should have been, et cetera. This was fixed by marking those routes as read-only with respect to the session via the apos.utils.readOnlySession(req) API. Note that apostrophe 2.75.0 (or better) must be used with this version of apostrophe-workflow.

2.15.0

Unit tests passing.

Regression tests passing.

  • "Reorganize" (aka "Manage Pages") and Workflow finally play together well!

In the past, it worked like this: any movement of a page in the tree automatically took place across all locales where that page was not considered trash. Including draft and live locales.

This was widely considered a bug. It was actually a choice made due to challenges implementing true workflow for "reorganize," but it certainly felt like a bug.

Certain permissions restrictions added frustration and did not reduce the risk associated with the feature as much as hoped.

Now, beginning with version 2.15.0, it works like this:

  • To move a page via "reorganize," you simply need permission to edit that page and the page you are dropping it on, just like normal Apostrophe.
  • Until you commit this has no effect on the live version of the page.
  • When you commit, the same move operation is applied to the live version of the page.
  • When you export, or force export, that move operation is applied to the draft versions of the appropriate locales.

In short... it works like most users always assumed it did!

We hope you find this to be a substantial improvement in usability and safety for this feature.

2.14.0

Unit tests passing.

Regression tests passing.

  • defaultLocalesByHostname option for situations where the hostname still has several prefixes but one is a sensible default other than the global default.

2.13.3

Unit tests passing.

Regression tests passing.

  • Do not crash when legacy locales no longer in the configuration are present for some but not all documents when moving a page in the tree.
  • The applyToSubpages dropdown on the page settings permissions tab is a one-time action that happens on save, it's not a setting. So toggling it to see what that setting is currently in "live" does not make sense.

2.13.2

Unit tests passing.

Regression tests passing.

2.13.1

Unit tests passing.

Regression tests passing.

  • Removed time-consuming migration that belongs in apostrophe core that previously ran each time locale changes were detected.
  • Better feedback when adding missing locales at startup.
  • Add trailing slash to URL when redirecting visitors to locale homepage, saving a redirect.

2.13.0

Unit tests passing.

Regression tests passing.

  • When committing via the main "Commit" button, you are now also invited to commit any uncommitted trashed docs. Thanks to Fredrik Ekelund.
  • You have always been able to have more than one locale with the same hostname, as long as each one had a prefix. You may now have one (1) locale without a prefix on a hostname that also features other locales. However, there is a catch: you must help Apostrophe distinguish page URLs that should switch the locale from API URLs that should not. If you don't want to do that, just continue to prefix all of your locales that share a hostname. See the documentation of the new addApiCalls option. Thanks to Manoj Krishnan.
  • At commit time, if a property that exists in the schema has been completely deleted, it is now deleted from the live doc too.
  • The history modal is now available even in the absence of localization for pieces. It was already available in this scenario for pages.

2.12.1

Unit tests passing.

Regression tests passing.

  • The commit history modal dialog box is now available when using the workflow module without internationalization/localization features. This makes sense because the history modal permits you to roll the draft version of the document back to an earlier commit, in addition to offering export features that are intended for localization.

2.12.0

Unit tests passing.

Regression tests passing.

  • Workflow-related batch operations for pages. These work just like the batch operations for pieces. You can find them in the "Reorganize" view (also reachable via "Pages" on the admin bar).
  • Option to disable "export after commit." For some users, this is only an occasionally useful feature, and it doesn't make sense to present it 100% of the time. To shut that behavior off, set the exportAfterCommit option to false. To facilitate the discovery of alternatives, the "History" workflow menu item is now labeled "History and Export." Note that you can export any commit, not just the most recent one, which is useful if you skipped several.

2.11.2

Packaging only.

2.11.1

Documentation only.

2.11.0

Unit tests passing.

Regression tests passing.

  • Eliminated $or MongoDB queries for performance. Performance tests show $ne: true is as good or better.
  • Run quiet.
  • Documentation improvements: table of contents. Big thanks to Fredrik Ekelund.
  • Clarified that running the add-missing-locales task is currently mandatory for migrating existing sites. This may become automatic soon.
  • Always use the manager of a doc type to update it with a new commit, if possible. This means that beforeSave, beforeUpdate, afterSave and afterUpdate methods of a pieces module will be called in this situation.

2.10.3

Unit tests passing.

Regression tests passing.

  • Locale stylesheets now have long cache lifetimes in the browser. In addition, the default locale stylesheet now has a cachebusting generation identifier in its URL, ensuring this is safe. (Locale-specific stylesheets already did.)
  • UX bug fix: if permissions for specific locales are granted for a particular permission, you should be able to see these immediately if you reopen the settings for that group later. Prior to this fix you had to toggle the permission off and on again to open the tree of locales.
  • All direct calls to MongoDB's docs.db.find() are now docs.db.findWithProjection, which is patched by Apostrophe to always work the way the MongoDB 2.x driver find() method did, even if the 3.x driver is in use via the forthcoming apostrophe-db-mongo-3-driver module.

2.10.2

Unit tests passing.

Regression tests passing.

  • Use apos.locks.withLock to avoid race conditions when manipulating the configuration. Also: use, and pass, the apostrophe lint standards rather than the punkave standards, which mostly meant using apos.utils.error and friends, but also revealed a missing dependency on qs.

2.10.1

Unit tests passing.

Regression tests passing.

  • apostrophe-workflow again operates properly with no prefixes option. This was an oversight in the logic introduced in 2.10.0 to optimize the add-locale-prefixes task and make it automatic at startup. Test coverage has been added to make sure this scenario is always checked for in the future.
  • The "live/draft" toggle in schema-driven forms now works properly with field types that have more than one label element, provided they themselves use self.findFieldset in the usual way to scope everything else.

2.10.0

Unit tests passing.

Regression tests passing.

  • Developers no longer have to explicitly run the apostrophe-workflow:add-missing-locales and apostrophe-workflow:add-locale-prefixes command line tasks. Much like parked pages, these are implemented automatically when starting up your node server process or when running command line tasks such as apostrophe-migrations:migrate. It is still possible to run these tasks explicitly.
  • apostrophe-workflow:add-locale-prefixes can now remove a slug prefix that is no longer present in your configuration, provided that it was present on a previous startup with this version or newer. All historical locale slug prefixes are remembered for consideration in the cleanup process.
  • apostrophe-workflow:add-locale-prefixes has been optimized to avoid unnecessary work.

2.9.4

Unit tests passing.

Regression tests passing.

  • When the commit option is chosen but there is no actual change to commit, don't get hung up in the progress display. This was an issue only when accessed via a workflow dropdown menu.

2.9.3

Unit tests passing.

Relevant regression tests passing.

  • Exempt the locale specific stylesheet generation routes from loading apostrophe-global, for performance. Significant performance boost when apostrophe-global contains joins.

2.9.2

Unit tests passing.

Regression tests passing.

  • Updated package dependencies; passing npm audit.

2.9.1

Unit tests passing.

Relevant regression tests passing.

  • You can once again add apostrophe-workflow to an existing project that already has a database before the point at which apostrophe-workflow is enabled. You do still need to follow the documentation's instructions to run the apostrophe-workflow:add-missing-locales --live task before first use after adding workflow. Thanks to Carsten for reporting the bug.

2.9.0

Unit tests passing.

Regression tests passing.

  • New Revert Draft button available in the History modal, allowing users to roll the draft back to the content of any past commit. "Page Versions" is available too, but this is often more intuitive.

2.8.2

Unit tests passing.

Regression tests passing.

  • Unit test fix to accommodate a performance improvement in Apostrophe's queries. No code changes to this module.

2.8.1

Unit tests passing.

Regression tests passing.

  • Index on path, level, workflowLocale for performance.

  • Dropdown menu name attributes for nightwatch functional test compatibility.

2.8.0

Unit tests passing.

Regression tests passing.

This release is focused on demystifying the process of activating docs in additional locales:

  • Activating pages and pieces in new locales is much more intuitive. The apostrophe-workflow:add-missing-locales task will base the content of a new doc on its closest available ancestor in the locale tree. For drafts, the "trash" status of a doc in a new locale will match that of its closest available ancestor.
  • The locale picker modal now clearly indicates which locales are still in the trash. If you click on one of these, you are invited to force export it or just rescue it from the trash, according to whether it has ever been modified or not in the new locale (i.e. whether it was deliberately placed in the trash there).
  • Locale prefixes are not erroneously duplicated when the slug is exported.

2.7.4

Unit tests passing.

Regression tests passing.

  • Prevent various duplicate prompts about related docs to commit or export
  • Deoptimize overly complicated calculations of which related docs need exporting to eliminate bugs
  • Body classes for draft and live mode. Thanks to Raphaël DiRago
  • Eliminated race condition that led to null IDs in some exports of joins

2.7.3

Unit tests passing.

Regression tests passing.

  • Accommodations for the forthcoming apostrophe-optimizer module.

2.7.2

Unit tests passing.

Regression tests passing.

  • Fixed race condition introduced in version 2.7.1 which could lead to incorrect remapping of joined document IDs when committing or exporting.
  • Fixed scenario in which a missing doc resulted in a restart. Thanks to Sylvain Chabert.

2.7.1

Unit tests passing.

Regression tests passing.

  • Insertion of new documents sped up by 2x when many locales exist.
  • Export choices made for the first document in a series of commit and export dialogs are now the default for the next, and so on. This saves time and reduces mistakes.
  • "Commit all like this" and "skip all like this" buttons added to ease committing and exporting numerous related documents, like images for a slideshow that exists on the page. These are set up to reduce the number of steps required. You can of course ignore them and continue to commit each doc individually. The "in context" doc (the page or piece at this URL) is still prompted for separately.
  • Added apostrophe-workflow:harmonize-workflow-guids-by-parked-id task. You are unlikely to need this unless you are receiving unique index errors, due to an older database where parked pages did not always have a parkedId property and have many locales. However in that situation it is useful to clean up leftover duplication caused by situations that are no longer possible with parkedId. Like apostrophe-workflow:remove-numbered-parked-pages, this task can remove content in certain cases, so back up your database first.

2.7.0

Unit tests passing.

Regression tests passing.

  • Basic support for per-locale stylesheets. Useful when you need the same @font-face to refer to different files for different locales.

2.6.4

Unit tests passing.

Regression tests passing.

  • Fixed bug in which localization helper would crash if used in a newly saved widget. The helper does not actually display localizations until page refresh due to a deeper issue but this is only a concern when editing, not for the public.
  • Merged helper implementations into helpers.js for consistency.

2.6.3

Unit tests passing.

Regression tests passing.

  • Fixed bug in which parked properties of existing pages were not updated for draft locales.
  • Documented the strong recommendation to set a parkedId for parked pages so that you do not encounter problems later when changing or adding locale prefixes.
  • Introduced locking so that the page parking mechanism never encounters race conditions.
  • If defaultLocale is not set, the first locale configured is assumed.
  • The apostrophe-workflow:remove-numbered-parked-pages task, which continues to be of use when working around previous parking bugs, now accounts for the fact that parked may contain previously parked properties rather than being a simple boolean.
  • apostrophe-workflow:add-missing-locales task sped up approximately 3x using parallelism.

2.6.2

Unit tests passing.

Regression tests passing.

  • Run beforeInsert, afterInsert, etc. on docs inserted across locales, especially pieces. Previously apos.docs.insert was invoked; this does not invoke such module-level callbacks, although it does invoke the callAll-based callbacks.
  • Added unit tests confirming that afterInsert callbacks can successfully update a piece, and that they are invoked both for the original insert and those that cascade to insert it across locales.
  • This module now complies with an eslint code quality validation suite. A number of global variable leaks were corrected, as well as improvements in error handling, etc.

2.6.1

Unit tests passing.

Regression tests passing.

  • Prior to this release, a race condition could lead to duplicate documents with the same combination of workflowGuid and workflowLocale. A unique sparse index on workflowGuid + workflowLocale has been added to address this problem, and if it initially fails, code has been added to address any existing duplicates. A winner is chosen, and the others are moved to a workflowGuidAndLocaleDuplicates array property, just in case. Any joins from other documents to those orphaned duplicates are automatically corrected to point to the winning version.

  • Documented that when writing an afterInsert or similar method override or callAll handler, developers using workflow must not invoke the callback provided until after carrying out any update of the piece or page in question. This means you must wait for your own callback before invoking the one provided by Apostrophe. Otherwise race conditions can lead to an error in your insert operation, which will be reported as such thanks to the new index mentioned above.

  • The migration above now completes on very large doc collections thanks to the use of the allowDiskUse: true flag. Thanks to Sylvain Chabert.

*

2.6.0

Unit tests passing.

Regression tests passing.

  • Implemented lang helper to easily set the lang attribute of the html tag. It is your responsibility to call it if you choose, see the documentation.
  • UX improvement: no export modal should be displayed after committing if you can only edit one locale anyway based on permissions.

2.5.1

Unit tests passing.

Regression tests passing.

  • The new apos.docs.ensureSlug method is called in beforeInsert to ensure there is no chicken-and-egg problem when working with new documents.

2.5.0

Unit tests passing.

Regression tests passing.

  • Beginning with this release, the private-locales permission is enforced. You must give out the "View Private Locales" permission to your editors via groups in the admin bar. If you are still using hardcoded groups via the groups option of the apostrophe-users module, you must add the private-locales permission to relevant groups and restart. However we recommend commenting out the groups option if you're using workflow; odds are you have a need to manage groups and their permissions through the UI at this point.

Users with the global admin permission do not need to be given this permission separately.

2.4.1

Unit tests passing.

Regression tests passing.

  • Do not fetch areas and joins when simply checking whether an individual related doc has been changed (we already fetched areas and joins on the original page, this is about those related to it). We were not using the information and it made the check for committable related docs very expensive and slow.
  • Reject invalid slug prefixes (those not beginning with /) and those not currently implemented (those with more than one /). Without this check an accidentally left out / can result in many duplicate parked pages.
  • Leverage the new apostrophe-jobs module for long-running batch operations on pieces with progress display.

2.4.0

Unit tests passing.

Regression tests passing.

  • Fixed a bug that generated duplicate pages in certain cases when new locales were encountered in the configuration at startup.
  • Batch operations on pieces for workflow: submit, commit (with an optional export step), and force export. Since these are batch operations, they do not display a preview and a confirmation modal for each and every document, so they should be used only when you know a large volume of documents are "ready to go."
  • Factored a javascript API up from the routes for easier programmatic use.
  • Facilitate local dev testing of hostnames feature by accommodating the port number of the original request when generating cross-locale links.
  • Do not park pages when running add-locale-prefixes.

2.3.3

Unit tests passing.

Regression tests passing.

  • Safely ignore widgets with no manager.
  • When autoconfiguring the hostnames feature based on the legacy subdomains setting, keep port numbers unless they are 80 or 443.
  • Override getBaseUrl method of apostrophe-pages to push out the right absolute URLs based on the hostnames option.

2.3.2

Unit tests passing.

Regression tests passing.

  • If an area is present in live but absent in draft, remove it during a commit, unless it is excluded from workflow. Presents the commit button from appearing where it shouldn't if an area has been removed from the template.

2.3.1

Unit tests passing.

Regression tests passing.

  • Even if --live is not specified, the add-missing-locales task still helps you out by making sure docs don't start out in the trash in draft locales; very confusing otherwise.
  • Many instances of the "commit" button appearing needlessly were eliminated by normalizing the difference between the trash property being false and the trash property being nonexistent.
  • Documentation note regarding tags and localization.

2.3.0

Unit tests passing.

Regression tests passing.

  • Introduced setPropertiesAcrossLocales method, which is convenient for programmatically updating many properties of a doc across some or all locales.

2.2.2

Unit tests passing.

Regression tests passing.

  • When exporting, moves should be handled from the top down (parent widgets first). Otherwise nested widgets can be lost.
  • New unit tests covering this.

2.2.1

Unit tests passing.

Regression tests passing.

  • When exporting, reordering widgets should export only the change in ordering; it should not erase localizations performed on those widgets in the target locale.

2.2.0

Unit tests passing.

Task-relevant regression tests passing.

  • workflow-locale command line option for tasks. Also new documentation on the subject of workflow and how it interacts with tasks.

2.1.5

Unit tests passing.

Task-relevant regression tests passing.

  • Do not park pages when running the apostrophe-workflow:add-missing-locales task, this prevents duplication and an apparent loss of pages (it is actually a duplicate home page without children that you are seeing).

If you were affected by this, there is a task that can be used as a one-time cleanup:

node app apostrophe-workflow:remove-numbered-parked-pages

2.1.4

Unit tests passing.

Regression tests passing.

  • Performance and accuracy improvements to the mechanism that decides whether to display the "submit" and "commit" buttons, eliminating false positives and reducing the amount of server work required.

  • Attempts to force-export a nested widget whose parent does not exist in the receiving locale should fail; however this was not producing a clear error message in certain cases and it was also possible to be left with the modal still on the page. Fixed.

  • Added the apostrophe-workflow:resolve-join-ids task, which can be used to resolve join ids pointing to the wrong locale. This should never happen, but did happen prior to certain recent fixes re: joins in array fields. It should be a one-time fix but can safely be run more than once.

2.1.3

Unit tests passing.

Regression tests passing.

  • If an object that is modified in the exported commit does not exist at all in the draft being patched, don't crash; just disregard it.
  • Always show live content in live mode.
  • Update the submit and commit buttons only at page load and after actual changes to areas. This significantly reduces unnecessary server load due to polling.
  • Refactored to ensure the invitation to export after a commit, etc. works for contextual pieces that eventually refresh the page on save.
  • Eliminated false positive change detections caused by the advisoryLock property.

2.1.2

Unit tests passing.

Regression tests passing.

  • The commit preview now displays deleted widgets properly when they have child widgets. Prior to this fix any subwidgets were absent from the preview, which was confusing and caused crashes in cases where templates were not tolerant of this situation.

  • Beginning in version 2.1.0 a crash was possible after exporting a document if related documents were joined without type in the projection. This has been fixed.

2.1.1

Unit tests passing.

Regression tests passing.

  • When exporting a widget, all properties are now exported via the smallest "diff" possible, avoiding unnecessary overwrites of other properties that were not changed in the commit. Previously this was only true for subwidgets.
  • Joins nested in array schemas now participate properly in the add-missing-locales task and related mechanisms.
  • After a doc is exported, an invitation is offered to export any related docs (such as the images in a slideshow) that have never been exported to the locales in question and are not yet live in those locales.
  • If the "force export" button for an individual widget is used, and the widget is nested deeply in a context that doesn't exist in the destination locale (such as a nested area), an informational error is reported and no crash occurs.

2.1.0

Unit tests passing.

Regression tests passing.

  • You can now configure your own mapping of locales to URL prefixes and hostnames. You may use any combination of the two, as long as it is always possible to determine exactly which locale is intended. For instance, if a hostname is not shared by two locales, then that locale does not need a URL prefix, while another hostname might have three locales and thus require prefixes for all three. The subdomains and prefixes options are still supported for simpler cases.
  • Single sign-on is provided for sites using multiple hostnames for different locales. As long as you use the provided locale switcher button to change locales, your login session will be carried through to the other locale.
  • A bug was fixed which could cause the generation of superfluous copies of parked pages at startup in certain circumstances involving prefixes. These can be removed via the command line task node app apostrophe-workflow:remove-numbered-parked-pages. This is a one-time correction.

2.0.7

All tests passing.

  • Subwidgets were being lost in the export process in the event that the parent widget also had some property changes of its own. This has been fixed. If the parent widget has changed properties that are not subwidgets, the entire parent widget is exported, including all subwidgets. Fine-grained patching is of course still done if the only modifications are to subwidgets. At some point in the future fine-grained patching of schema fields within widgets may also be performed, however this requires further consideration.

2.0.6

All tests passing.

  • The locale picker modal now generates intermediate links that will complete the locale switch without being confused by issues such as the "best page" mechanism of apostrophe-pieces-pages considering the wrong locale. Although this could have been addressed by iterating over all of the locales and performing requests with each locale, it would have been very slow, and the getLocalizations method involved also supports the front end of sites, where excessive overhead is to be avoided. Therefore it makes more sense to do the full work to build the final URL for the document being linked to only once, for the locale the user actually clicks on.

2.0.5

All tests passing.

  • The locale picker modal now can link to valid URLs for pieces. Previously an insufficiently generous mongo projection left out the type.

2.0.4

All tests passing.

  • Previously the locale picker code assumed that localizations of a given document always had a _url property which led to crashes in the build filter. Now, if there is no _url property available, no link is available for that locale in the locale picker.

2.0.3

All tests passing.

  • Fixed a bug that caused joined documents, such as images, not to be displayed in previews of old commits. This bug has been addressed, however previews of commits that predate the fix will still not show joined content.

Note that since any joined document can unexpectedly be missing, for instance because the joined document has been moved to the trash, your templates must always test for or otherwise tolerate this situation in any case. Not doing so could result in a template error on the actual page, not just on a preview of the older commit.

2.0.2

All tests passing.

  • Use new accommodations for unit tests of related modules in Apostrophe.
  • Better feedback about the submitted state of the page.
  • Use global busy mechanism to avoid race conditions.

2.0.1

All tests passing.

  • A bug was fixed that caused joins in widgets in draft documents to point to ids in the live locale after the user clicks "commit," in effect clearing the selection for that join. This will not occur in future commits.

2.0.0

Initial release.