Publication add-ons for WCM 2.2.0

h1. Extended Publication Plugin {anchor:id=CONTref.Extensions.AuthoringExtension.ExtendedPublicationPlugin}

h2. States {anchor:id=CONTref.Extensions.AuthoringExtension.ExtendedPublicationPlugin.States}

This extended publication has new states and new profiles that are enabled in WCM 2.2.0.

* Profiles
** Author: This profile can edit a content and mark this content as redacted.
** Approver: This profile approves a pending content (marked by the Author).
** Publisher: This profile publishes contents or marks them as "Ready for publication" in multi-server mode.
** Archiver: An administrative profile which moves contents to an archive storage.
* States
** enrolled: It is a pure technical state, generally used for content creation.
** draft (Author): Content is in editing phase.
** pending (Author): The author validates the content.
** approved (Approver): A content is approved by the manager.
** inreview (Manager): This state can be used when a second approval state is needed (for i18 translation for example).
** staged (Publisher): A content is ready for publication (multi-server mode).
** published (Publisher or Automatic): A content is published and visible in the *Live* mode.
** unpublished (Publisher or Automatic): A content is not visible in the *Live* mode.
** obsolete: A content can still be published but it is not in an editing lifecycle anymore.
** archived (Automatic): A content is archived and ready to be moved in the archive workspace if enabled.

h2. Start/End publication dates {anchor:id=CONTref.Extensions.AuthoringExtension.ExtendedPublicationPlugin.StarEndPublicationDates}

In most cases, you do not want to publish a content directly, but at a defined date and you can also want to unpublish automatically the content after that.
New properties are added to the new publication plugin, that allows you to manage this:
* publication:startPublishedDate
* publication:endPublishedDate

The WCM 2.2.0 rendering engine does not know anything about publication dates, so another service needs to manage that.
When the publisher sets start/end publication dates, he can "stage" the content. The content will go automatically to "published" state when the start date arrives and to "unpublished" state after end date.
A cron job checks every hour (or less) all contents which need to be published (start date in the past + "staged" state) or unpublished (end date in the past + "published" state).

Thus, the publication dates are not mandatory and a content can go to:
* Staged: in multi-server mode, the publisher can only put the content to the "staged" state and wait for auto-publication.
* Published: in single-server mode, the publisher can directly publish a content (with or without publication dates).

h2. New Publication Mixin {anchor:id=CONTref.Extensions.AuthoringExtension.ExtendedPublicationPlugin.NewPublicationMixin}

{code:xml}
<nodeType name="publication:authoringPublication" isMixin="true" hasOrderableChildNodes="false" primaryItemName="">
  <supertypes>
    <supertype>publication:stateAndVersionBasedPublication</supertype>
  </supertypes>
  <propertyDefinitions>
    <propertyDefinition name="publication:startPublishedDate" requiredType="Date" autoCreated="false"
      mandatory="true" onParentVersion="IGNORE" protected="false" multiple="false">
      <valueConstraints />
    </propertyDefinition>
    <propertyDefinition name="publication:endPublishedDate" requiredType="Date" autoCreated="false" mandatory="true"
      onParentVersion="IGNORE" protected="false" multiple="false">
      <valueConstraints />
    </propertyDefinition>
  </propertyDefinitions>
</nodeType>
{code}

h3. Publication plugin UI {anchor:id=CONTref.Extensions.AuthoringExtension.ExtendedPublicationPlugin.PublicationPluginUI}

Note that some labels containing special or non-ASCII characters could not be well displayed in the publication UI.
You can extend the width of the current UI State button by adding:

{code:css}
.UIPublicationPanel .StatusTable .ActiveStatus {
  width: 75px !important;
}
{code}

Also, for the publication date inputs, UIPublicationPanel should not initialize the dates to any default value.
The publishing and unpublish CRON jobs will do this:
* A staged document with null publication start date is published instantly.
* A document with null publication end date is published forever.

See the export section for more information about the CRON jobs.

h1. Publication Manager {anchor:id=CONTref.Extensions.AuthoringExtension.PublicationManager}

The Publication Manager manages lifecycles and contexts in the WCM platform. It allows to manages different lifecycles based on different publication plugin in the platform.

{code:java}
public interface PublicationManager {

	public List<Lifecycle> getLifecycles();

	public List<Context> getContexts();

	public Context getContext(String name);

	public Lifecycle getLifecycle(String name);

	public List<Lifecycle> getLifecyclesFromUser(String remoteUser, String state);
}
{code}

In which:

* getLifecycles: returns a list of lifecycles (see below), with lifecycle name, publication plugin involved and possible states.
* getContexts: returns a list of context, with name, related Lifecycle and other properties (see below).
* getContext: returns a context by its name.
* getLifecycle: returns a lifecycle by its name.
* getLifecycleFromUser: returns a list of lifecycles in which the user has rights (based on membership property).

h2. Lifecycle {anchor:id=CONTref.Extensions.AuthoringExtension.PublicationManager.Lifecycle}

A lifecycle is defined by a simple vertical workflow with steps (states) and profiles (membership). Each lifecycle is related to a Publication plugin (compliant with the JBPM or Bonita business processes).

For example: _Two lifecycles with/without states_

{code:xml}
<external-component-plugins>
  <target-component>org.exoplatform.services.wcm.publication.PublicationManager</target-component>
  <component-plugin>
    <name>AddLifecycle</name>
    <set-method>addLifecycle</set-method>
    <type>org.exoplatform.services.wcm.publication.lifecycles.StatesLifecyclePlugin</type>
    <init-params>
      <object-param>
        <name>lifecyles</name>
        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig">
          <field name="lifecycles">
            <collection type="java.util.ArrayList">
              <value>
                <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$Lifecycle">
                  <field name="name">
                    <string>lifecycle1</string>
                  </field>
                  <field name="publicationPlugin">
                    <string>States and versions based publication</string>
                  </field>
                </object>
              </value>
              <value>
                <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$Lifecycle">
                  <field name="name">
                    <string>lifecycle2</string>
                  </field>
                  <field name="publicationPlugin">
                    <string>Authoring publication</string>
                  </field>
                  <field name="states">
                    <collection type="java.util.ArrayList">
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>draft</string>
                          </field>
                          <field name="memberships">
                            <collection type="java.util.ArrayList">
                              <value>
                                <string>author:/CA/communicationDG</string>
                              </value>
                              <value>
                                <string>author:/CA/alerteSanitaire</string>
                              </value>
                              <value>
                                <string>author:/CA/alerteInformatique</string>
                              </value>
                              <value>
                                <string>author:/CA/informations</string>
                              </value>
                            </collection>
                          </field>
                        </object>
                      </value>
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>pending</string>
                          </field>
                          <field name="membership">
                            <string>author:/platform/web-contributors</string>
                          </field>
                        </object>
                      </value>
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>approved</string>
                          </field>
                          <field name="membership">
                            <string>manager:/platform/web-contributors</string>
                          </field>
                        </object>
                      </value>
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>staged</string>
                          </field>
                          <field name="membership">
                            <string>publisher:/platform/web-contributors</string>
                          </field>
                        </object>
                      </value>
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>published</string>
                          </field>
                          <field name="membership">
                            <string>automatic</string>
                          </field>
                        </object>
                      </value>
                    </collection>
                  </field>
                </object>
              </value>
              <value>
                <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$Lifecycle">
                  <field name="name">
                    <string>lifecycle3</string>
                  </field>
                  <field name="publicationPlugin">
                    <string>Authoring publication</string>
                  </field>
                  <field name="states">
                    <collection type="java.util.ArrayList">
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>draft</string>
                          </field>
                          <field name="membership">
                            <string>author:/platform/web-contributors</string>
                          </field>
                        </object>
                      </value>
                      <value>
                        <object type="org.exoplatform.services.wcm.publication.lifecycles.impl.LifecyclesConfig$State">
                          <field name="state">
                            <string>published</string>
                          </field>
                          <field name="memberships">
                            <collection type="java.util.ArrayList">
                              <value>
                                <string>publisher:/CA/communicationDG</string>
                              </value>
                              <value>
                                <string>publisher:/CA/alerteSanitaire</string>
                              </value>
                              <value>
                                <string>publisher:/CA/alerteInformatique</string>
                              </value>
                              <value>
                                <string>publisher:/CA/informations</string>
                              </value>
                            </collection>
                          </field>
                        </object>
                      </value>
                    </collection>
                  </field>
                </object>
              </value>
            </collection>
          </field>
        </object>
      </object-param>
    </init-params>
  </component-plugin>
</external-component-plugins>
{code}

In the last example, there are three lifecycles:

* Lifecycle 1: based on _States and versions based publication_.
** This allows to be backward compliant with older WCM releases. If all your site contents are using an existing plugin, you can create a lifecycle for it and it will work.
** For new instances, you should use the new plugin with dynamic states capabilities.

* Lifecycle 2: based on new _Authoring publication_.
** Visibility: Define only the "visible" steps. In this example, there is no step for 'enrolled'. Even if this step exists, it will not be displayed in the UI.
** Automatic: Set a step as "automatic". In this mode, the step will be visible in the UI but it will be managed by the system (a cron job for example).
* Lifecycle 3: Simulates the _States and versions based publication_ plugin. Note that this simple lifecycle will work in a single server configuration.

h3. Listen to a lifecycle {anchor:id=CONTref.Extensions.AuthoringExtension.PublicationManager.ListenToLifecycle}

When a state is changed, you can broadcast an event to add features.
The event could look like this:

{code:java}
listenerService.broadcast(AuthoringPlugin.POST_UPDATE_STATE_EVENT, null, node);
{code}
Listener declaration could look like this:

{code:xml}
<external-component-plugins>
  <target-component>org.exoplatform.services.listener.ListenerService</target-component>
  <component-plugin>
    <name>PublicationService.event.postUpdateState</name>
    <set-method>addListener</set-method>
    <type>org.exoplatform.services.wcm.publication.listener.post.PostUpdateStateEventListener</type>
    <description>this listener will be called every time a content changes its current state</description>
  </component-plugin>
</external-component-plugins>
{code}

h2. Context {anchor:id=CONTref.Extensions.AuthoringExtension.PublicationManager.Context}

A context is defined by simple rules. In WCM 2.2.0, you can select to enroll the content in a specific lifecycle (for example, publication plugin) based on context parameters.
There are three parameters used to define contexts:
* Remote User: The current user who can create/edit the content.
* Current sitename: The Site from where the content is created (not the storage but the navigation).
* Node: The node which you want to enroll.

From these parameters, you can easily connect and define contexts based on:
* Membership: Does the current user have this membership?
* Site: On this particular site, you want to enroll contents in a specific lifecycle.
* Path: You can enroll contents in the lifecycles based on their path (from the Node).
* Type of content: You can enroll contents in the lifecycles based on their nodetype (from the Node).

Because each site has a content storage (categories + physical storage), you can select the right lifecycle for the right storage/site.
To avoid conflicts on contexts, you can set a priority (The less is the best).

For example, _Different Contexts_

{code:xml}
<external-component-plugins>
  <target-component>org.exoplatform.services.wcm.publication.PublicationManager</target-component>
  <component-plugin>
    <name>AddContext</name>
    <set-method>addContext</set-method>
    <type>org.exoplatform.services.wcm.publication.context.ContextPlugin</type>
    <init-params>
      <object-param>
        <name>contexts</name>
        <object type="org.exoplatform.services.wcm.publication.context.impl.ContextConfig">
          <field name="contexts">
            <collection type="java.util.ArrayList">
              <value>
                <object type="org.exoplatform.services.wcm.publication.context.impl.ContextConfig$Context">
                  <field name="name">
                    <string>contextdefault</string>
                  </field>
                  <field name="priority">
                    <string>200</string>
                  </field>
                  <field name="lifecycle">
                    <string>lifecycle1</string>
                  </field>
                </object>
                <object type="org.exoplatform.services.wcm.publication.context.impl.ContextConfig$Context">
                  <field name="name">
                    <string>context1</string>
                  </field>
                  <field name="priority">
                    <string>100</string>
                  </field>
                  <field name="lifecycle">
                    <string>lifecycle1</string>
                  </field>
                  <field name="membership">
                    <string>*:/platform/web-contributors</string>
                  </field>
                  <field name="site">
                    <string>acme</string>
                  </field>
                  <field name="path">
                    <string>repository:collaboration:/sites content/live/acme/categories</string>
                  </field>
                </object>
                <object type="org.exoplatform.services.wcm.publication.context.impl.ContextConfig$Context">
                  <field name="name">
                    <string>context2</string>
                  </field>
                  <field name="priority">
                    <string>100</string>
                  </field>
                  <field name="lifecycle">
                    <string>lifecycle1</string>
                  </field>
                  <field name="site">
                    <string>classic</string>
                  </field>
                </object>
                <object type="org.exoplatform.services.wcm.publication.context.impl.ContextConfig$Context">
                  <field name="name">
                    <string>context3</string>
                  </field>
                  <field name="priority">
                    <string>80</string>
                  </field>
                  <field name="lifecycle">
                    <string>lifecycle3</string>
                  </field>
                  <field name="membership">
                    <string>manager:/company/finances</string>
                  </field>
                  <field name="path">
                    <string>repository:collaboration:/documents/company/finances</string>
                  </field>
                </object>
                <object type="org.exoplatform.services.wcm.publication.context.impl.ContextConfig$Context">
                  <field name="name">
                    <string>context4</string>
                  </field>
                  <field name="priority">
                    <string>50</string>
                  </field>
                  <field name="lifecycle">
                    <string>lifecycle4</string>
                  </field>
                  <field name="memberships">
                    <collection type="java.util.ArrayList">
                      <value>
                        <string>manager:/CA/communicationDG</string>
                      </value>
                      <value>
                        <string>manager:/CA/alerteSanitaire</string>
                      </value>
                      <value>
                        <string>manager:/CA/alerteInformatique</string>
                      </value>
                      <value>
                        <string>manager:/CA/informations</string>
                      </value>
                    </collection>
                  </field>
                  <field name="path">
                    <string>repository:collaboration:/documents/company/finances</string>
                  </field>
                  <field name="nodetype">
                    <string>exo:article</string>
                  </field>
                </object>
              </value>
            </collection>
          </field>
        </object>
      </object-param>
    </init-params>
  </component-plugin>
</external-component-plugins>
{code}

The logic is very simple. When creating a content, it should be attached a lifecycle with the lifecyle priority:
* _context4_ is the most important (priority=50): you will enroll the content in the lifecycle 'lifecycle4' if:
** The content creator has the 'manager:/company/finances' membership.
** The content is stored in 'repository:collaboration:/documents/company/finances' or any subfolders.
** The content is a 'exo:article'.
* If not, you will continue with _context3_.

The logic is very simple. When you create a content, go lifecycle by lifecycle starting with the better priority:
* _context4_ is the most important (priority=50): you will enroll the content in the lifecycle 'lifecycle4' if:
** the content creator has the 'manager:/company/finances' membership.
** the content is stored in 'repository:collaboration:/documents/company/finances' or any subfolders.
** the content is a 'exo:article'.
* if not, you will continue with _context3_.

{note}
The contexts will be used only when the content is created and when you want to enroll it in a lifecycle for the first time.
Once you have the corresponding lifecycle, you will set the lifecycle inside the content (see New authoring Mixin) and the context service will not be called again for this content.
{note}

h2. New Authoring Mixin {anchor:id=CONTref.Extensions.AuthoringExtension.PublicationManager.NewAuthoringMixin}

{code:xml}
<nodeType name="publication:authoring" isMixin="true" hasOrderableChildNodes="false" primaryItemName="">
  <propertyDefinitions>
    <propertyDefinition name="publication:lastUser" requiredType="String" autoCreated="false" mandatory="false"
      onParentVersion="IGNORE" protected="false" multiple="false">
      <valueConstraints />
    </propertyDefinition>
    <propertyDefinition name="publication:lifecycle" requiredType="String" autoCreated="false" mandatory="false"
      onParentVersion="IGNORE" protected="false" multiple="false">
      <valueConstraints />
    </propertyDefinition>
  </propertyDefinitions>
</nodeType>
{code}
When adding the content in a lifecycle, set the _{noformat}publication:lifecycle{noformat}_ property with the corresponding lifecycle.

{note}
A content can be in one lifecycle only.
{note}
Each time you change from one state to another, set the user who changed the state in _{noformat}publication:lastUser{noformat}_.

h3. Querying based on publication status {anchor:id=CONTref.Extensions.AuthoringExtension.PublicationManager.QueryBaseOnPublicationStatus}

By adding this mixin to contents, you can access contents by simple queries based on the current user profile.
For example:
* All your draft contents:
** query: select * from nt:base where publication:currentState='draft' and publication:lastUser='benjamin';
* All the contents you have to approve.
** call: PublicationManager.getLifecycles('benjamin', 'approved') => returns lifecycles where you can go to the 'approved' state.
** query: select * from nt:base where publication:currentState='pending' and (publication:lifecycle='lifecycle1' or publication:lifecycle='lifecycle3');
* All the content that will be published tomorrow.
** query: select * from nt:base where publication:currentState='staged' and publication:startPublishedDate>'xxxx';