In our current SharePoint project, we have a custom site definition based on the out-of-box Publishing Site with Workflow. We found a little problem with this though, because when the out-of-box Features for Publishing and it's Parallel Approval workflow are activated, the default SharePoint Approvers group is the only group that is set to approve a publishing page that has been sent into the workflow. The problem is, we want to inherit the parent site's workflow settings - otherwise known as the association data - because our application has 'channels' of content that each has a different group who can approve the content in their appropriate channel.
The group that can approve publishing pages in each channel is defined in the root web of the channel by doing nothing more than editing the Parallel Approval workflow on the Pages list, and then setting the correct group on the People Picker for the workflow approvers property. Since channel owners can create new sites in their channel using our custom provisioning process, we don't want them to have to go into the Pages list workflow settings to set the correct group every time, because as we all know, people make mistakes. Why not automate this process and set the right approvers group for the channel owner when the new site is created?
To do this, we created a Feature that we included in our custom site definition. When a new site is created and the Feature is activated, we execute the following code:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Diagnostics;
using Microsoft.SharePoint.Workflow;
using System.Xml;
using Microsoft.SharePoint.Publishing;
namespace Demo
{
public class FeatureReceiver : SPFeatureReceiver
{
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
//get the SPWeb object for the Feature's scope
SPWeb currentWeb = properties.Feature.Parent as SPWeb;
//set the AllowUnsafeUpdates property to true so we can apply the change to the workflow
currentWeb.AllowUnsafeUpdates = true;
//get the parent web of the current web - this is where the correct workflow settings are
SPWeb parentWeb = currentWeb.ParentWeb;
//get the Pages list from the current web using the PublishingWeb object
SPList currentWebPublishingList = PublishingWeb.GetPublishingWeb(currentWeb).PagesList;
//get the current web Pages list's workflows
SPWorkflowAssociationCollection currentWebWorkflows = currentWebPublishingList.WorkflowAssociations;
//if the site has workflows, we need to get the parent web's workflow and apply the same approvers to it
if (currentWebWorkflows.Count >= 1)
{
//find the parallel approval workflow
SPWorkflowAssociation currentWorkflowAssociation = currentWebWorkflows.GetAssociationByName("Parallel Approval", currentWeb.Locale);
//empty container for the parent pages list workflow
SPWorkflowAssociation parentWorkflowAssociation = null;
//get the parent site's pages list
SPList parentPagesList = PublishingWeb.GetPublishingWeb(parentWeb).PagesList;
//if there's workflows attached, we need to find the Parallel Approval workflow
if (parentPagesList.WorkflowAssociations.Count > 0)
{
parentWorkflowAssociation = parentPagesList.WorkflowAssociations.GetAssociationByName("Parallel Approval", parentWeb.Locale);
}
//if we have the parent workflow, we need to copy the review settings to the child
if (parentWorkflowAssociation != null)
{
//xmldoc for the parent association data
XmlDocument parentAssociationData = new XmlDocument();
parentAssociationData.LoadXml(parentWorkflowAssociation.AssociationData);
//xmldoc for the child workflow association data
XmlDocument currentAssociationData = new XmlDocument();
currentAssociationData.LoadXml(currentWorkflowAssociation.AssociationData);
//we need to create a namespace manager object because the xml data we want is
//namespaced with 'xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD"'
//the following code was obtained from John Wood's blog
//url: http://www.dotnetjunkies.com/WebLog/johnwood/archive/2005/08/25/132153.aspx
//this code will add all namespaces in the xml to the namespace manager, allowing us to correctly
//create XPath queries to retrive the data we need
XmlNamespaceManager nsmgr = new XmlNamespaceManager(parentAssociationData.NameTable);
foreach (XmlAttribute attr in parentAssociationData.SelectSingleNode("/*").Attributes)
{
if (attr.Prefix == "xmlns")
nsmgr.AddNamespace(attr.LocalName, attr.Value);
}
//get the Reviewers node from the parent workflow
XmlNode parentReviewers = parentAssociationData.SelectSingleNode("//my:Reviewers", nsmgr);
//get the Reviewers node from the child workflow
XmlNode currentReviewers = currentAssociationData.SelectSingleNode("//my:Reviewers", nsmgr);
//set the Reviewer node of the child workflow to be the same as the parent
currentReviewers.InnerXml = parentReviewers.InnerXml;
//update the child workflow's association data
currentWorkflowAssociation.AssociationData = currentAssociationData.OuterXml;
//update the child workflow association in the database
currentWebPublishingList.UpdateWorkflowAssociation(currentWorkflowAssociation);
}
}
//make sure you dispose of the parentWeb object
parentWeb.Dispose();
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
}
public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
}
}
}