{toc}
h1. Publication addons for WCM 1.2.2


h2. Extended Publication Plugin

h3. Start/End publication dates

In most cases, the customers don't want to publish directly a content but at a defined date and they also want to unpublish automatically the content after that.
In the new publication plugin, we add new properties to manage this :
* publication:startPublishedDate
* publication:endPublishedDate

h3. States

In this extended publication, we will allow new states and new profiles. These states already exist in WCM but not used and we will enable them.

# 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's a pure technical state, generally used for content creation.
#* draft (Author) : Content is in editing phase
#* pending (Author) : Author validates the content
#* approved (Approver) : 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) : Content is ready for publication (multi-server mode)
#* published (Publisher or Automatic) : Content is published and visible in Live mode
#* unpublished (Publisher or Automatic) : Content is not visible in Live mode
#* obsolete : Content can still be published but we consider it's not in an editing lifecycle anymore
#* archived (Automatic) : Content is archived and ready to be moved in archive workspace if enabled.

The WCM 1.2.2 rendering engine doesn't 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 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).

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

h3. Publication Mixin for stage specify 
The system is added new publication mixin for specifying the time for publication and unpublication

{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}
h4. Publication plugin UI

Another quick note about 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 export section for more information about cron jobs.

h2. Publication Manager

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}
* getLifecycles : returns a list of lifecycles (see below), with lifecycle name, publication plugin involved and possible states
* getContexts : return a list of Context, with name, related Lifecycle and other properties (see below)
* getContext : return a context by its name
* getLifecycle : return a lifecycle by its name
* getLifecycleFromUser : returns a list of Lifecycles in which the user has rights (based on membership property)


h3. Lifecycle

A lifecycle is defined by a simple vertical workflow with steps (states) and profiles (membership). Each lifecycle is related to a Publication plugin (to be compliant with JBPM or Bonita business processes).
+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-param>
  </component-plugin>
</external-component-plugins>
{code}
In the last example, we have 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, we recommend to use the new plugin with dynamic states capabilities
# lifecycle 2 : based on new _Authoring publication_\#\* Visibility : we define only the "visible" steps. In this example, there's no step for 'enrolled'. Even if this step exists, it will not be displayed in the UI.
#* Automatic : we can 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


h4. Listening to a lifecycle

When we change state, we broadcast an event in order for partners to add features if they want to.
Event could look like this :
{code}
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}

h3. State Authorization

On each state, one can define conditions that will be used to enable/disable the UI for reaching this state.

h4. membership
Supported fields are _membership_ and _memberships_ to specify several memberships. 
OR condition will be used to check.
The fields values support the usual permission syntax : MEMBERSHIP_TYPE:GROUP
Wildcard ('\*') is supported as usual for MEMBERSHIP_TYPE, for example : _*:/platform/web-contributors_
It means any member of the group will be authorized to reach the state.


h4. role

{warning}
WARNING : Considering current limitations in the Publication manager, this part about Roles is not implemented in WCM 2.x.
{warning}


the field _role_ can also be used on state.

It means the state will be reachable by role on _any_ group that has write access to the node. The system will check the node's permissions and user's memberships. It will make sure the user has write access on the node and also that the user has the appropriate role in a group.

This syntax is useful when publication is driven by roles in groups  (validator of XXX) rather than by technical groups of publishers  (member:/platform-web-publishers).

_Exemple:_

Consider the following node's permissions :

|| identity || read || add_node || set_property || remove ||
|  *:/org/finance | (/) | (x) | (x) |(x) |
|  *:/org/human-resources | (/) | (/) | (/) |(/) |

Consider the following users 
* *bill* : redactor:/org/human-resources and validator:/org/finance
* *tom* : validator:/org/human-resource

Configure the state like this : 
{code}
<field name="state"><string>staged</string></field>
<field name="role"><string>validator</string></field>
{code}

While displaying the publication panel for the node :
* _staged_  state will be enabled for *tom* because he is validator in group /org/human-resources and this group has write access*
* _staged_ will NOT be enabled for *bill* because readonly access is defined for  /org/finance group. 

{tip}_write access_ consists in having the _set_property_ permission{tip}


h3. Context

A context is defined by simple rules. In WCM 1.2+, we can choose to enroll the content in a specific lifecycle (i.e. publication plugin) based on context parameters.
We have 3 parameters we can use to define contexts :
* Remote User : The current user who create/edit the content.
* Current sitename : The Site from where the content is created (not the storage but the navigation)
* Node : The node which we want to enroll.

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

Because each site has a content storage (categories + physical storage), we can choose the right lifecycle for the right storage/site.
We can have conflicts on contexts and we will avoid that by setting a priority (less is best)


+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-param>
  </component-plugin>
</external-component-plugins>
{code}
The logic is very simple. When we create a content, we will go lifecycle by lifecycle starting with the better priority :
* _context4_ is the most important (priority=50) : we 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 we will continue with _context3_


We can see that _context1_ and _context2_ have the same priority. We can do that if the contexts are different (ex : different sites)

{note:title=on creation only}It's important to notice that contexts will be used only when the content is created and when we want to enroll it in a lifecycle for the first time.
Once we have the corresponding lifecycle, we will set the lifecycle inside the content (see New authoring Mixin) and the context service will not be called again for this content.
{note}
h3. New Authoring Mixin

{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, we set the _publication:lifecycle_ property with the corresponding lifecycle.

{note:title=one lifecycle only}A content can be in one lifecycle only.
{note}Each time, we change from one state to another, we set the user who changed the state in _publication:lastUser_.

h4. Querying based on publication status
By adding this mixin to contents, we can access contents by simple queries based on the current user profile.
For example :
* All my draft contents
** query : select * from nt:base where publication:currentState='draft' and publication:lastUser='benjamin';
* All the contents I have to approve
** call : PublicationManager.getLifecycles('benjamin', 'approved') => returns lifecycles where I 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';


