Saturday, July 30, 2011

Part 2 – Custom Action for Workflow Initiation

· Part 1 - Pattern for Building Stateful Workflows with SharePoint Designer 2010
· Part 2 - Custom Action for Workflow Initiation
· Part 3 - Delays for Workflow Initiation Action
· Part 4 - Using Business Connectivity Services (BCS) with SharePoint 2010 Workflow
· Part 5 - Custom Visio Services Reports with SharePoint 2010 Workflow

Part 2

Building the Custom Action

In the previous part of this blog I explained my approach and in this part I am going to show you how I created two custom actions that is needed to support my approach. I am first going to show you the code solution I created and then dive a bit into why I made the solution as is.

I can tell you honestly that I really wanted this solution to be a SharePoint 2010 Sandbox solution however I ran into a few challenges that prevented me from doing that. This forced me to build a full trust solution with Visual Studio 2010. There are basically two actions I needed to create. The first one is an action that can initiate another workflow. The second action I needed checks to see if 1 to N workflows are running on a list item, and if so, stop the running of that workflow.

It is pretty well documented on how to create a custom action, deploy it and then use that action in SharePoint Designer. The following are some good references:

· http://blog.voltje.be/?p=144

· http://msmvps.com/blogs/sundar_narasiman/archive/2010/12/26/develop-custom-workflow-activity-for-sharepoint-2010-workflow.aspx

Workflow Helper Class

First thing I did was create a simple helper class where I stored logic to initiate the workflow and to check to see if there are any existing workflows running. The ItemHasActiveWorkflows method is really straight forward as it is searching workflows that are running for the specific item. If it finds a workflow is running, it will return false. Why is this important? Well because a document or list item can only have one of a particular type running at a time. But since we broke the larger business process up into five smaller ones, we need to check to see if any of the five are running on the item. If any one of them is running, we need to block any new workflows from being started. This method will take a comma delimited list of workflow names that must be checked.

The second method is StartWorkflow which will initiate a workflow on a specified item. This code is very straight forward.

public class WorkflowHelper
{

private string siteCollectionUrl;
private string webUrl;
private string listName;
private int itemID;
private string workflowName;

public WorkflowHelper(string siteCollectionUrl, string webUrl, string listName, int itemID)
{
this.siteCollectionUrl = siteCollectionUrl;
this.webUrl = webUrl;
this.listName = listName;
this.itemID = itemID;
}

public WorkflowHelper(string siteCollectionUrl, string webUrl, string listName, int itemID, string workflowName) : this(siteCollectionUrl, webUrl, listName, itemID)
{
this.workflowName = workflowName;
}

public bool ItemHasActiveWorkflows(string workflowNames)
{
string[] seperator = new string[] {","};
string[] workflowNamesSplit = workflowNames.Split(seperator, StringSplitOptions.RemoveEmptyEntries);

//Get the site collection
using (SPSite site = new SPSite(siteCollectionUrl))
{
//Get the web
using (SPWeb web = site.OpenWeb(webUrl))
{
SPList list = web.Lists[listName];
SPListItem workflowItem = list.GetItemById(itemID);

//Loop over the workflows to look for
foreach (string workflowName in workflowNamesSplit)
{
//Loop over the workflows on the item
foreach (SPWorkflow workflow in workflowItem.Workflows)
{
//Check the state of the workflows to see if any are running
if (workflow.InternalState == SPWorkflowState.Running ||
workflow.InternalState == SPWorkflowState.Expiring ||
workflow.InternalState == SPWorkflowState.Faulting)
{
if (list.WorkflowAssociations[workflow.AssociationId].Name.ToLower() == workflowName.Trim().ToLower())
{
return true;
}
}
}
}
}
}

return false;
}

public void StartWorkflow()
{
//Open the site collection. Cannot go across site collections in
//sandbox so easier to just pull of workflow context
using (SPSite site = new SPSite(siteCollectionUrl))
{
//Just open web because we cannot go across site collections
//with a sandbox solution.
using (SPWeb web = site.OpenWeb(webUrl))
{
//Get the manager that will initiate the workflow
SPWorkflowManager workflowMgr = site.WorkflowManager;

//Get the available workflows on the list
SPWorkflowAssociationCollection workflowAssocs = web.Lists[listName].WorkflowAssociations;

//Get the list item that the workflow will be initiated on
SPListItem workflowItem = web.Lists[listName].GetItemById(itemID);

//Get the workflow that has been associated
SPWorkflowAssociation workflowAssoc = workflowAssocs.GetAssociationByName(workflowName, System.Threading.Thread.CurrentThread.CurrentCulture);

//Start the workflow
workflowMgr.StartWorkflow(workflowItem, workflowAssoc, workflowAssoc.AssociationData, true);
}
}
}
}


Start Workflow Action



Next I created an action that would start a second workflow. I created the following code.




public class InitWorkflowAction : Activity
{

#region Properties

public static DependencyProperty SiteCollectionUrlProperty = DependencyProperty.Register("SiteCollectionUrl", typeof(string), typeof(InitWorkflowAction));

[Description("Site Collection URL")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SiteCollectionUrl
{
get
{
return ((string)(base.GetValue(InitWorkflowAction.SiteCollectionUrlProperty)));
}
set
{
base.SetValue(InitWorkflowAction.SiteCollectionUrlProperty, value);
}
}

public static DependencyProperty WebUrlProperty = DependencyProperty.Register("WebUrl", typeof(string), typeof(InitWorkflowAction));

[Description("Web Url")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string WebUrl
{
get
{
return ((string)(base.GetValue(InitWorkflowAction.WebUrlProperty)));
}
set
{
base.SetValue(InitWorkflowAction.WebUrlProperty, value);
}
}

public static DependencyProperty ListNameProperty = DependencyProperty.Register("ListName", typeof(string), typeof(InitWorkflowAction));

[Description("List Name")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ListName
{
get
{
return ((string)(base.GetValue(InitWorkflowAction.ListNameProperty)));
}
set
{
base.SetValue(InitWorkflowAction.ListNameProperty, value);
}
}

public static DependencyProperty WorkflowNameProperty = DependencyProperty.Register("WorkflowName", typeof(string), typeof(InitWorkflowAction));

[Description("Workflow Name")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string WorkflowName
{
get
{
return ((string)(base.GetValue(InitWorkflowAction.WorkflowNameProperty)));
}
set
{
base.SetValue(InitWorkflowAction.WorkflowNameProperty, value);
}
}

public static DependencyProperty ItemIDProperty = DependencyProperty.Register("ItemID", typeof(int), typeof(InitWorkflowAction));

[Description("Item ID")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int ItemID
{
get
{
return ((int)(base.GetValue(InitWorkflowAction.ItemIDProperty)));
}
set
{
base.SetValue(InitWorkflowAction.ItemIDProperty, value);
}
}


#endregion

#region Methods

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
//WorkflowHelper workflowHelper = new WorkflowHelper(this.SiteCollectionUrl, this.WebUrl, this.ListName, this.ItemID, this.WorkflowName);
//workflowHelper.StartWorkflow();

WorkflowHelper workflowHelper = new WorkflowHelper(this.SiteCollectionUrl, this.WebUrl, this.ListName, this.ItemID, this.WorkflowName);
Thread thread = new Thread(new ThreadStart(workflowHelper.StartWorkflow));
thread.Start();

return ActivityExecutionStatus.Closed;
}

#endregion

}


With the StartWorkflow method you see the code is straight forward but there some threading. You may be wondering why I have implemented threading? Well I was getting the below error message in the SharePoint logs when the workflow was initiated on the same thread. I did not find any information on why this was occurring but on a hunch I thought the workflow service was competing for the same resources. My solution was to move the call to StartWorkflow into a different thread and the problem was resolved.



w3wp.exe (0x2BC4) 0x0A2C SharePoint Foundation Workflow Infrastructure xmfh Medium Workflow Compile Failed: Invalid token for impersonation - it cannot be duplicated.



w3wp.exe (0x2BC4) 0x0A2C SharePoint Foundation Workflow Infrastructure 72fs Unexpected RunWorkflow: Microsoft.SharePoint.SPException: Invalid token for impersonation - it cannot be duplicated. at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.SubCompiler.DoCompile(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, CompilationPacket& packet, DirectoryInfo& tempDir) at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.CompileBytes(Byte[] xomlBytes, Byte[] rulesBytes, Boolean doTestCompilation, String assemblyName, SPWeb web, Boolean forceNewAppDomain) at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.LoadXomlAssembly(SPWorkflowAssociation association, SPWeb web) at Microsoft.SharePoint.Workflow.SPWinOeHostServices.LoadDeclarativeAssembly(SPWorkflowAssociation association) at Microsoft.SharePoint.Workflow.SPWinOeHostServi...



w3wp.exe (0x2BC4) 0x0A2C SharePoint Foundation Workflow Infrastructure 72fs Unexpected ...ces.CreateInstance(SPWorkflow workflow) at Microsoft.SharePoint.Workflow.SPWinOeEngine.RunWorkflow(SPWorkflowHostService host, SPWorkflow workflow, Collection`1 events, TimeSpan timeOut) at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(SPWorkflow workflow, Collection`1 events, SPWorkflowRunOptionsInternal runOptions)



0x0A2C SharePoint Foundation Workflow Infrastructure 98d8 Unexpected Microsoft.SharePoint.SPException: Invalid token for impersonation - it cannot be duplicated. at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.SubCompiler.DoCompile(WorkflowCompilerParameters parameters, String xomlSource, String assemblyName, CompilationPacket& packet, DirectoryInfo& tempDir) at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.CompileBytes(Byte[] xomlBytes, Byte[] rulesBytes, Boolean doTestCompilation, String assemblyName, SPWeb web, Boolean forceNewAppDomain) at Microsoft.SharePoint.Workflow.SPNoCodeXomlCompiler.LoadXomlAssembly(SPWorkflowAssociation association, SPWeb web) at Microsoft.SharePoint.Workflow.SPWinOeHostServices.LoadDeclarativeAssembly(SPWorkflowAssociation association) at Microsoft.SharePoint.Workflow.SPWinOeHostServices.CreateIns...



w3wp.exe (0x2BC4) 0x0A2C SharePoint Foundation Workflow Infrastructure 98d8 Unexpected ...tance(SPWorkflow workflow) at Microsoft.SharePoint.Workflow.SPWinOeEngine.RunWorkflow(SPWorkflowHostService host, SPWorkflow workflow, Collection`1 events, TimeSpan timeOut) at Microsoft.SharePoint.Workflow.SPWorkflowManager.RunWorkflowElev(SPWorkflow workflow, Collection`1 events, SPWorkflowRunOptionsInternal runOptions)



Check Active Workflows Action



Below is the code that will check to see if there is an action running on a workflow item. The code is straight forward and calls my helper class that I created earlier.




public class CheckActiveWorkflowsAction : Activity
{

#region Properties

public static DependencyProperty SiteCollectionUrlProperty = DependencyProperty.Register("SiteCollectionUrl", typeof(string), typeof(CheckActiveWorkflowsAction));

[Description("Site Collection URL")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string SiteCollectionUrl
{
get
{
return ((string)(base.GetValue(CheckActiveWorkflowsAction.SiteCollectionUrlProperty)));
}
set
{
base.SetValue(CheckActiveWorkflowsAction.SiteCollectionUrlProperty, value);
}
}

public static DependencyProperty WebUrlProperty = DependencyProperty.Register("WebUrl", typeof(string), typeof(CheckActiveWorkflowsAction));

[Description("Web Url")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string WebUrl
{
get
{
return ((string)(base.GetValue(CheckActiveWorkflowsAction.WebUrlProperty)));
}
set
{
base.SetValue(CheckActiveWorkflowsAction.WebUrlProperty, value);
}
}

public static DependencyProperty ListNameProperty = DependencyProperty.Register("ListName", typeof(string), typeof(CheckActiveWorkflowsAction));

[Description("List Name")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string ListName
{
get
{
return ((string)(base.GetValue(CheckActiveWorkflowsAction.ListNameProperty)));
}
set
{
base.SetValue(CheckActiveWorkflowsAction.ListNameProperty, value);
}
}

public static DependencyProperty ItemIDProperty = DependencyProperty.Register("ItemID", typeof(int), typeof(CheckActiveWorkflowsAction));

[Description("Item ID")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int ItemID
{
get
{
return ((int)(base.GetValue(CheckActiveWorkflowsAction.ItemIDProperty)));
}
set
{
base.SetValue(CheckActiveWorkflowsAction.ItemIDProperty, value);
}
}

public static DependencyProperty HasActiveProperty = DependencyProperty.Register("HasActive", typeof(bool), typeof(CheckActiveWorkflowsAction));

[Description("Has Active")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public bool HasActive
{
get
{
return ((bool)(base.GetValue(CheckActiveWorkflowsAction.HasActiveProperty)));
}
set
{
base.SetValue(CheckActiveWorkflowsAction.HasActiveProperty, value);
}
}

public static DependencyProperty WorkflowNamesProperty = DependencyProperty.Register("WorkflowNames", typeof(string), typeof(CheckActiveWorkflowsAction));

[Description("Workfow Names")]
[Category("Custom Workflow")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string WorkflowNames
{
get
{
return ((string)(base.GetValue(CheckActiveWorkflowsAction.WorkflowNamesProperty)));
}
set
{
base.SetValue(CheckActiveWorkflowsAction.WorkflowNamesProperty, value);
}
}

#endregion

#region Methods

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
WorkflowHelper workflowHelper = new WorkflowHelper(this.SiteCollectionUrl, this.WebUrl, this.ListName, this.ItemID);
this.HasActive = workflowHelper.ItemHasActiveWorkflows(this.WorkflowNames);

return ActivityExecutionStatus.Closed;
}

#endregion
}


Deployment



The deployment of this solution was pretty easy with Visual Studio 2010.



· I created an empty SharePoint 2010 project.



· Added the above code.



· Added a Site Collection level Feature.



· Added a SharePoint mapped folder called “Workflow”



· Create a new file called InitWorkflowAction.actions in the Workflow folder.



The InitWorkflowAction.actions file describes my custom actions and makes them available to design tools such as SharePoint Designer 2010 to create a declarative workflow. Information below is very straight forward. It defines the sentence users will fill in and what type of data will be consumed or returned from the methods I created earlier.




<WorkflowInfo>
<Actions Sequential="then" Parallel="and">
<Action Name="Start Workflow"
Assembly="My.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7cb64b163bd9c1db"
ClassName="My.Activities.InitWorkflowAction"
AppliesTo="all"
Category="Custom Workflow">
<RuleDesigner Sentence="Initiate workflow %4 on item %5 in list %3 in Web %2 in Site Collection %1">
<FieldBind Field="SiteCollectionUrl"
Text="Site Collection Url"
Id="1"
DesignerType="TextBox" />
<FieldBind Field="WebUrl"
Text="Web Url"
Id="2"
DesignerType="TextBox" />
<FieldBind Field="ListName"
Text="List Name"
Id="3"
DesignerType="TextBox" />
<FieldBind Field="WorkflowName"
Text="Workflow Name"
Id="4"
DesignerType="TextBox" />
<FieldBind Field="ItemID"
Text="Item ID"
Id="5"
DesignerType="Integer" />
</RuleDesigner>
<Parameters>
<Parameter Name="SiteCollectionUrl"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="Site Collection Url"/>
<Parameter Name="WebUrl"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="Web Url"/>
<Parameter Name="ListName"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="List Name"/>
<Parameter Name="WorkflowName"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="Workflow Name"/>
<Parameter Name="ItemID"
Type="System.Int32, mscorlib"
Direction="In"
DesignerType="Integer"
Description="Item ID"/>
</Parameters>
</Action>
<Action Name="Check Active Workflow"
Assembly="My.Activities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=7cb64b163bd9c1db"
ClassName="My.Activities.CheckActiveWorkflowsAction"
AppliesTo="all"
Category="Custom Workflow">
<RuleDesigner Sentence="Check if workflows %6 on item item %4 in list %3 in Web %2 in Site Collection %1 has a running workflow (Output to %5)">
<FieldBind Field="SiteCollectionUrl"
Text="Site Collection Url"
Id="1"
DesignerType="TextBox" />
<FieldBind Field="WebUrl"
Text="Web Url"
Id="2"
DesignerType="TextBox" />
<FieldBind Field="ListName"
Text="List Name"
Id="3"
DesignerType="TextBox" />
<FieldBind Field="ItemID"
Text="Item ID"
Id="4"
DesignerType="Integer" />
<FieldBind Field="HasActive"
Text="Has Active"
Id="5"
DesignerType="ParameterNames" />
<FieldBind Field="WorkflowNames"
Text="Comma delimited list of workflow names"
Id="6"
DesignerType="TextBox" />
</RuleDesigner>
<Parameters>
<Parameter Name="SiteCollectionUrl"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="Site Collection Url"/>
<Parameter Name="WebUrl"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="Web Url"/>
<Parameter Name="ListName"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="List Name"/>
<Parameter Name="ItemID"
Type="System.Int32, mscorlib"
Direction="In"
DesignerType="Integer"
Description="Item ID"/>
<Parameter Name="HasActive"
Type="System.Boolean, mscorlib"
Direction="In"
DesignerType="ParameterNames"
Description="Has Active"/>
<Parameter Name="WorkflowNames"
Type="System.String, mscorlib"
Direction="In"
DesignerType="TextBox"
Description="Comma delimited list of workflow names"/>
</Parameters>
</Action>
</Actions>
</WorkflowInfo>


Using Custom Actions in SharePoint Designer 2010



Now I am going to show you how I used the custom actions in my workflow. Earlier in this series you saw that I had created five different SharePoint Visio 2010 workflow diagrams. I am going to skip documenting the steps on how to bring the Visio diagrams into SharePoint Designer as that is a pretty straight forward process.



Below is a screenshot of the Initiate Process workflow. The first Action is my custom action that checks to see if there is already a workflow running for the custom list item. You will notice that the sentence displayed here corresponds with the sentence in the InitWorkflowAction.actions file. Also note the first parameter is a comma delimited list of all the other workflows (Review,Manager Review,Resubmit Request,Complete Request).



Next I take the value returned from my custom action and check to see if it Yes or No. If it is No, that means there are no running workflows and that I should initiate the Review Workflow. The third sentence in the diagram below captures this. I will initiate the Review workflow on the current item and send a notification email. Otherwise the workflow will not be started and the user will be notified.



image



Note that for the configuration of this workflow, it is the only one that supports allowing the workflow to be started manually. All of the other workflows I have created have no checkboxes checked for the Start Options section. This reduces the potential for user errors when starting the workflow for the item. The reason why is that we always want users to start with the Initiate Process workflow and let the other workflows be started based on business rules.



clip_image002



Next I have another workflow called Review that corresponds to the Visio diagram in the previous part of this series. Notice again I simply wire into the custom action StartWorkflow to start Manager Review, Complete Review or Resubmit Request.



clip_image004



Below is the Manager Review workflow. As you can see if the Manager approves the Complete Request workflow will be called otherwise the Resubmit Request workflow will be called.



clip_image006



Next is the Resubmit Request workflow. The workflow checks to see if the user cancels the workflow. If they do, the item will be updated and an email will be sent. Otherwise, the item will go back to the Review workflow (for re-review).



clip_image008



Finally the Complete Request workflow is below. I have not done much with it at this time but more can be added.



clip_image010



So now once I validate and deploy my workflow I can initiate it on an item. Below is a screenshot of a user initiate the process for an item. Notice only the Initiate Process workflow is available.



clip_image012



Once I have started the workflow I come back to the workflow status screen. Here you see the Review workflow is In Progress. Notice that the Initiate Process workflow is still available. Since the workflow is in progress I want to make sure that I cannot initiate the entire workflow again while it is current running.



clip_image014



Below is a screenshot of the completed workflow.



clip_image016



Now you can see I was able to chain together several workflows that as a whole make up a larger business process. Pretty neat.



Sandbox Solutions



I alluded earlier that I really wanted to make this a sandbox solution. Why? Because I believe that you should always develop for the Sandbox first because it quick to deploy solutions, it is secure, ensures good performance and works well with Office365.



Here are some good resources on how to deploy actions into the SharePoint 2010 Sandbox:



· http://msdn.microsoft.com/en-us/library/ff798499.aspx



· http://msdn.microsoft.com/en-us/library/ff630175.aspx



· http://msdn.microsoft.com/en-us/library/ff798389.aspx



· http://www.wictorwilen.se/Post/Sandboxed-workflow-activities-in-SharePoint-2010.aspx



The process is a little different and the code above would change a tiny bit so that it is scoped to a Site Collection only. However fundamentally there is not much difference.



When I deployed the custom actions and created the workflows I would get errors initiating the workflows. I went into the logs and found the following.



SPUCWorkerProcessProxy.exe (0x1E70) 0x1EC0 SharePoint Foundation Workflow Infrastructure 72er Medium System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.SharePoint.Library.SPRequest.ClearAllVars(String bstrUrl) at Microsoft.SharePoint.SPListItem.PrepareItemForUpdate(SPWeb web, Boolean bMigration, Boolean& bAdd, Boolean& bPublish, Object& objAttachmentNames, Object& objAttachmentContents, Int32& parentFolderId) at Microsoft.SharePoint.SPListItem.UpdateInternal(Boolean bSystem, Boolean bPreserveItemVersion, Guid newGuidOnAdd, Boolean bMigration, Boolean bPublish, Boolean bNoVersion, Boolean bCheckOut, Boolean bCheckin, Boolean suppressAfterEvents, String filename) at Microsoft.SharePoint.SPListItem.Update() at Microsoft.SharePoint.Workflow.SPWinOEWSSService.CommitUpdateListItem(Transaction txn, Object[] transData)



SPUCWorkerProcessProxy.exe (0x1E70) 0x1EC0 SharePoint Foundation Workflow Infrastructure 72fe High Error in commiting pending workflow batch items: System.NullReferenceException: Object reference not set to an instance of an object. at Microsoft.SharePoint.Library.SPRequest.ClearAllVars(String bstrUrl) at Microsoft.SharePoint.SPListItem.PrepareItemForUpdate(SPWeb web, Boolean bMigration, Boolean& bAdd, Boolean& bPublish, Object& objAttachmentNames, Object& objAttachmentContents, Int32& parentFolderId) at Microsoft.SharePoint.SPListItem.UpdateInternal(Boolean bSystem, Boolean bPreserveItemVersion, Guid newGuidOnAdd, Boolean bMigration, Boolean bPublish, Boolean bNoVersion, Boolean bCheckOut, Boolean bCheckin, Boolean suppressAfterEvents, String filename) at Microsoft.SharePoint.SPListItem.Update() at Microsoft.SharePoint.Workflow.SPWinOEWSSService.CommitUpdateListItem...



SPUCWorkerProcessProxy.exe (0x1E70) 0x1EC0 SharePoint Foundation Workflow Infrastructure 72fe High ...(Transaction txn, Object[] transData) at Microsoft.SharePoint.Workflow.SPPendingWorkBatch.Commit(Transaction transaction, ICollection items)



SPUCWorkerProcessProxy.exe (0x1E70) 0x1EC0 SharePoint Foundation Workflow Infrastructure 88xr Unexpected WinWF Internal Error, terminating workflow Id# 8d547a01-bdb8-44ac-b066-586c12367dc0



The information that was provided was not very good. Here are some observations I made.



· Note that the error is in SPUCWorkerProcessProxy.exe and not w3wp.exe. This basically means the entire workflow is running inside the Sandbox service because it has a reference to an action that is deployed there.



· I would only get this error the very first time the workflow run. From then on, it worked every time flawlessly. However if I stopped and restarted the Sandbox service manually, I would get the error again.



· I would also not get the error if Workflow A started a very simple Workflow B. For instance if Workflow B was sending an email, no issues. However if Workflow B had an approval process there would be an error.



After doing some research I came to the conclusion that this error occurs based on a combination of a few things.



1. When you build a workflow in SharePoint Designer 2010 it is a declarative workflow, not a complied workflow. The workflow will be compiled the first time the workflow is initiated. This is why it always takes a long time to start a workflow the very first time.



2. Sandbox solutions have a threshold of 20 seconds before it is determined the process too intensive and will stop the execution.



This combination would push me over the 20 seconds rule the very first time running the workflow. The solution would be to write some sort of script that could manually go through each workflow and initiating them so they would be compiled and ready to go when the user comes to manually start the workflow. I did not find that to be an acceptable solution because the Sandbox cache will be remove items from memory if they are not used often. This is why I did a full trust solution.



Next Part



Hopefully this gave you a pretty good idea of how simple it is to achieve this vision I have. Now in the next part of this series I am going to extend the solution a little bit to support my reporting requirements using Visio Services.

Part 1 – Pattern for Building Stateful Workflows with SharePoint Designer 2010

· Part 1 - Pattern for Building Stateful Workflows with SharePoint Designer 2010
· Part 2 - Custom Action for Workflow Initiation
· Part 3 - Delays for Workflow Initiation Action
· Part 4 - Using Business Connectivity Services (BCS) with SharePoint 2010 Workflow
· Part 5 - Custom Visio Services Reports with SharePoint 2010 Workflow


Introduction
In the SharePoint 2007 days moderately complex workflow could not be achieved with SharePoint Designer 2007. Individuals would then try to build those workflows using Visual Studio 2007 and then run into a whole new set of complexities. In many cases individuals wanted to use visual tools to build automation inside of SharePoint 2007 only to find out that there were limitations. The end result was developers reverting back to adding event handlers to lists or writing web parts with a custom database.


With SharePoint 2010 workflow tools have matured allowing us to do more complex business process automation. Most notably:


1. Visio 2010 is now a tool that can be used to visually design workflows definitions that can be pulled into SharePoint. It also provides the ability to do reporting and data visualization where graphics can be mapped to data. Then based on the value of that data, conditional logic can be applied to change visual element in Visio (like change something from green to red, or change the shape completely).


2. SharePoint Designer 2010 workflow design tool has significantly improved tremendously. There is Visio integration, InfoPath form integration (initiation, association and task forms), better workflow designer, more workflow fields to interact with, etc. It is almost night and day versus the previous version of SharePoint Designer.


3. Visual Studio 2010 has changed tremendously providing workflow project templates which drastically reduce the level of effort to create workflow process.


4. Workflow definitions can be moved between tools fairly easily only a few limitations.


5. Workflow can be easily redeployed between dev, test and production.


6. Workflow can now run at the site level instead of only being associated to a list item.


One Big Hurdle Left


At the end of the day this is all well and good but there still some holes. Specifically you cannot do state workflows in Visio and SharePoint Designer 2010. SharePoint Designer only supports sequential works. This basically means the flow goes top to down and cannot loop back within the workflow. Not having this capability can be limiting even for a simple workflow process.


The Resolution


Have some assumptions you must take think about with this solution:


1. Let me state first that my solution is pretty clean and simple. But I really only recommend this as a solution for those simple to moderately complex workflows where you just want to be able to achieve stateful workflows without having to do a bunch of custom code.


2. Much of this solution is based on leveraging the OOB capabilities of SharePoint 2010.


3. There is a lot room for improvement of this solution so please use this as a framework to start trying to build some stateful workflows. I will NOT be supporting this solution moving forward. This is just a demonstration of showing you how you can achieve a great solution by using the SharePoint 2010 toolset.


After do some brainstorming the following two things dawned on me:


1. State is relative to the business process instance data; not the business process definition itself. What does that mean? It means you do not have to create a single process definition to manage an entire business process state. Still does not make sense? What I am saying is you do not have to create a single workflow diagram to automate a business process. Instead you can create multiple workflows and chain them together. All of these workflows will manage a single stateful item (record); like a SharePoint list item or document. I like this idea because in actuality state workflows are chained together sequential processes when it comes down to it. My goal is to come up with a pattern that will allow me to daisy chain together many sequential workflows that manage the state of a single item to go through a workflow. Doing this this will allow me to achieve my vision of building workflow in SharePoint Designer 2010 and Visio 2010.


2. The workflow diagram never looks the same as the automation workflow diagram. Visual workflow designer tools are positioned to allow business users to take their process diagrams (box, diamond and arrow diagrams) and quickly transform it into an executable that can run in some system. That is not really easy. The reality is that business person’s workflow diagram never, ever looks like the automation workflow diagram created in a workflow tool. This is because there will be tons of steps added into the diagram to support the actual automation. Using Visio Services in SharePoint 2010, I will be able to come up with a visual report.


I believe I have actually come up with a pretty simple solution change solves these challenges. The following are the elements of this solution.


· Use Visio 2010 to build up the initial workflow definition and then use the SharePoint Designer 2010 to connect the workflow to SharePoint 2010.
· Create custom workflow activity with Visual Studio 2010 that can initiate another workflow. Basically we need to create a simple .NET function that can take some parameters and then initiate a workflow. This will allow for us to chain workflows together.
· Use Visio 2010 for reporting by using its new capability to map to data. We can create a picture that the business user can understand and have it “light up” and report to a user about the current state of a business process execution.


Ok – Show Me What You Are Talking About


Is this still too complicated? Have I gotten a little nerdy on the workflow? Let’s put this into context. Below is a pretty simple review workflow by most standards.


· There is a review step.
· If the item being reviewed is greater than $20K, a manager needs to review it.
· If a request is rejected by the reviewer or manager, the request should go back to the originator for resubmission.
· Only the requestor has the ability to cancel the request.


image


So you may say what is the big deal? Why did you write all that esoteric stuff about workflow for something as simple as this? Really? Look at the diagram below, the stuff I highlighted in red could not be achieved in a tool that only supports sequential workflow. Because there is a loop back to the initiator to allow them to resubmit this immediately becomes a state workflow.


image


As well, notice anything else about either of these diagrams? They are simple and what the business users understands. There is nothing in these diagrams that show emails being sent, task items being created, permissions being set, item metadata being updated, data being sent to a database for reporting, etc. If I were to take this diagram and try to model all those steps I can assure you it will become a clutter mess!!!


Solution Design


I am excited to say my solution solves both of these problems!!! First I will be able to break this up into a bunch of connected workflows. Second I will be able to take the original diagram we draw and light it up based on where the workflow is.


So how does the solution work? I will get to the real “how does this work in SharePoint 2010” shortly. First we need to break this workflow into multiple workflows. In the screenshot below you will see that I have broken the business process into five somewhat smaller workflows.


· Initiate – This process will have the responsibility of starting the workflow and performing any activities need to get it going.
· Review – This is the part where the main reviewer reviews the request.
· Manager Review – Workflow that manages the workflow for the manager to review.
· Resubmit – Consolidates the logic for either resubmitting the request or canceling it.
· Complete – Will handle any operations needed to complete the workflow.


image


When I was designing this solution I was able to test approach with many other more sophisticated workflow patterns (i.e. complex parallel processing or a spider diagram). I was always able to break up the workflow into a set of sequential workflows that could be coordinated with each other.


Building the SharePoint Workflows with Visio 2010


Many of the readers of this blog are aware that they can build SharePoint workflow in Visio 2010. For this solution I will use it to initially build the workflows above however I will not use it for the long term. One limitation is that I will have to build two custom actions in Visual Studio 2010. Custom actions are not supported in Visio 2010 but they are supported in SharePoint Designer 2010.


Initiate Process


Below is the initiate process. Not much going on here other than an email is being sent.


image


However there are several other things we will need to add when we move this into SharePoint Designer. For instance check to see if any of the other processes are currently running. We have to use a custom action to achieve this which cannot be modeled in Visio.


Review Process


Below is the review process. As you can see I have added some steps to update the state of the Review list item and send an email. Like before, I could not add the actions to initiate the other workflows but I have put in placeholders.


image


Manager Review


Next I have the manager review. Again I have added some steps to update the review item state, send some emails and put in a placeholder to initiate other workflows.


image


Resubmit Process


Below is the resubmit process. Again I have added actions that send emails and update the state of the record.


image


Complete Workflow


Finally we have the complete workflow; pretty self-explanatory.
image


What’s Next?


Well what we now need to do is bring these workflows into SharePoint Designer 2010 and use some custom actions that can initiate each workflow. In the next part of the series I am going to focus on how these custom actions were built.

Friday, July 29, 2011

Office365 SLAs and Service Descriptions

Do you need to get your head wrapped around Office365 and what it provides? Honestly the best resources to read are the Service Level Agreements. For me, I focus a lot on SharePoint. When I read these documents I get a clear picture of what is exactly provided in SharePoint Online Services in the Office365 cloud.


Saturday, June 25, 2011

SharePoint 2010 Content Deployments

I recently had to spin up very quickly on how to do content deployments. Luckily it was very easy skimming through these articles.


I am glad I read them too. In the “Content deployment overview (SharePoint Server 2010)” there are six considerations and the first gives you specific instruction on how to set up your destination site collection.

Saturday, May 21, 2011

Deploy Branding Solution

Visual Studio Package

If you are an experienced SharePoint 2007 professional you will know that to do custom development in Visual Studio 2007 required a lot of manual work to build up a deployable package. This usually resulted in developers created half-packaged solutions where they had tons of manual steps to deploy a solution.

With SharePoint 2010 the game has changed. Visual Studio 2010 is everything I ever wanted and I wish I was out doing consulting with it now because it is so easy to build a WSP and deploy it. On top of that, Sandbox Solutions make it every more easier to deploy solutions.

In this section I am going to take a much of the code I created in this series using SharePoint Designer 2010 and pull it in to Visual Studio so that I can make a re-deployable solution.

Getting Started

To test this out I did the following:

  • Created a new web application.
  • Created a team site, site collection.
  • Added a FAST Search Center sub site.
  • Turned on all publishing features at both the site collection and site levels.
  • Modified the navigation to show the FAST sub site.

The result was the following.

clip_image002

Next I need to create a deployment package that will move all the stuff I have worked on into this new web application. There are a bunch of things I need to move into this deployment package.

  • Master Page and all the files referenced by it. This includes image and css files.
  • Content types
  • Page Layouts
  • Content Query Web Part customizations

To get this started I simply opened up Visual Studio 2010 and created an empty SharePoint project. I also made this a Sandbox Solution.

Master Pages

Setting up the Master Pages is the easier one; plus it would be the first to logically do. Back in SharePoint and Visual Studio 2007 we spend a whole bunch of time trying to recreate the SharePoint folder structure and get it all worked out in the various XML files. Visual Studio 2010 with SharePoint 2010 is going to save us a ridiculous amount of time.

I took the following steps to set up the visual studio project.

  • I added a new module called “_catalogs”. I removed the sample.txt file.
  • Under the _catalogs module I created a folder called “masterpage”. In that folder I placed both of the master pages I had created earlier in SharePoint Designer.
  • I then created another module called “Style Library”. I removed the sample.txt file.
  • I created a folder called BrandingBlog and images to be the same as the folder structure that I had created in the master page.
  • I then moved in all the css and images into the appropriate folders.
  • Then under the Features folder, I renamed Feature1 to BrandingBlogMaster. I did this because I want my first feature of this deployment package to be deploying my master pages only.

clip_image003

  • Next I modified the Feature scope to be Site so that this is set at the site collection level.

clip_image005

  • Next I had to modify the Elements.xml file in the _catalogs module.
  • I changed the module to have both the URL and Path set to “_catalogs/masterpage”.

Then I modified each of the File elements as you see below. Note that I set the Type to GhostableInLibrary .

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="_catalogs" Url="_catalogs/masterpage" Path="_catalogs/masterpage">
<File Url="BrandingBlog.master" Type="GhostableInLibrary"/>
<File Url="BrandingBlog_searchcenter.master" Type="GhostableInLibrary"/>
</Module>
</Elements>



  • Next I went to the Style Library module and modified the Elements.xml file in a similar fashion.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Style Library" Url="Style Library/BrandingBlog" Path="Style Library/BrandingBlog">
<File Url="style.css" Type="GhostableInLibrary"/>
<File Url="images/splitter.gif" Type="GhostableInLibrary"/>
<File Url="images/sidebar_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/picture.jpg" Type="GhostableInLibrary"/>
<File Url="images/li.gif" Type="GhostableInLibrary"/>
<File Url="images/bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/content.gif" Type="GhostableInLibrary"/>
<File Url="images/footer_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/head_bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/menu_bckg.gif" Type="GhostableInLibrary"/>
</Module>
</Elements>


Now this is done, it is really simple to deploy.




  • I right clicked the solution and selected to build it. Even though there is no compiled code it is just a habit of mine.


  • Next I right clicked the solution and selected Package. This will generate a WSP package which in this case would be BrandingBlog.wsp. Since I am doing a debug build, the BrandingBlog.wsp file will be generated in the Debug folder.


  • Next I went to the Site Collection >> Site Settings >> Solutions to deploy my WSP into the Sandbox. I uploaded and activated it. This subsequently but all my files into the Master Pages gallery and into the Style Library folder.


  • Next I web to the Style Library >> BrandingBlog folder and checked in all the files in the directory.


  • Next I went to Site Settings >> Master Pages and changed the top site to use the BrandingBlog.master file. The following is the result.



clip_image002[6]




  • Next I went to the FAST Search Center site and set the master page to be BrandingBlog_searchcenter.master. Below is the branded FAST Search Center.



clip_image004



Man that was a little too easy.



But I will be honest, there were a few challenges I ran into when deploying the rest of the solution.



Deploying the Rest of the Solution



Now I do have some lessons learned in regards to the deployment of the rest of the solution – which I knew was going to run into. Let’s go ahead and discuss them out before we get started.



Content Type GUIDs Lessons Learned



If you are going to use Content Types and plan on doing a re-deployment, it is best to get those content types in Visual Studio and create a WSP package as soon as possible. This is because all the content types and columns have their own unique GUIDs which are generated when you build them in the UI. All of my Page Layouts are tied to those GUIDs.



So why does that cause me heart burn in this situation? Well it is common practice for UI designer to actually do a bunch of the work in SharePoint Designer 2010, and then pull them out and give to a developer. It would be good if both the designer and the developer are using the same GUIDs. Plus the GUIDs will be the same between development and production environments making deployments smoother.



Content Query Web Parts and Managed Metadata Columns Lessons Learned



I somewhat anticipated that there would run into some bumps with re-deploying content query web parts with managed metadata columns. If you have been reading along with this blog series, you will see I was able to get a pretty decent solution put together. Remember my goal is to create a solution that can be deployed to the Sandbox. Here is what I learned.




  • Content Query Web Parts that are pre-configured into Page Layouts will require some extra manual steps when redeployed across environments. This is mostly because there are several GUIDs that get tied into the configuration of the web part. So in this example I pre-configured content query web parts on my custom page layouts to show related articles and confidential visualization. When the page layouts are redeployed someone will have to go into SharePoint Designer and manually reconfigure the web parts.


  • Content Query Web Parts that use Management Metadata columns with multiple values require some extra work. For instance, the Technology and Enterprise Keyword columns I have are only available when I select the pages library as the Source. If I try to select the first two options neither of those two columns are available. The net affect it the GUID from the pages library that will be in the content query web part configuration will be different across environments. The resolution is again, I have to reconfigure that web part once it has been re-deployed.


  • The Microsoft.SharePoint.Taxonomy namespace is not available in the Sandbox. Why is that important to this particular solution? Well my vision was a 100% re-deployable solution in the sandbox. To achieve that vision I would want to add some code in the Feature activation event to generate to managed metadata that would be mapped back to my content types.


  • Another lesson learned was the managed metadata column defined declaratively in a Feature does not have the ability to map existing term sets. The resolution is to add some code to the Feature activation to map the column to pre-existing term sets in the destination environment. That code is pretty well defined but it again requires access to the Microsoft.SharePoint.Taxonomy namespace which is not supported in the Sandbox.



What are the ramifications of all this? Why am I trying to figure out these boundaries? Well if you are looking ahead to using Office365 we are pretty much contained to working with the Sandbox. There will be opportunities to get custom code deployed in dedicate Office365 but again there is a process to go through and best to build to the Sandbox.



Knowing what I have learned, I will be taking the following actions moving forward:




  • Managed Metadata – I am not going to give up on. There are major redeeming benefits of using them. So there are some simple manual steps that will be done which I will outline.


  • Content Query Web Parts – I will go through reconfiguration of them when they are embedded into page layouts. However I am going to consider writing simple web parts in the future using LINQ to SharePoint. This way I have much more flexibility to scale later and that will work in the Sandbox.



Adding Other Components



In the first part of this Visual Studio deployment I set up the project to deploy out the master page and associated files really quickly. In this part, I am going to capture how I pulled in all of the other components of this solution and changes I had to make based on the lessons learned. Specifically I will be adding:




  • Columns and Content Type Definitions


  • Page Layout


  • Content Query Web Part


  • Custom Content Query Web Part Styles



Below is a screenshot of the updated solution with all the added files. I will discuss each of these additions.



clip_image005



Columns and Content Type Definitions



First I created a new Feature for deploying my content types called BrandingBlogContentTypes. I decided to put this into a separate Feature because I like to keep data definitions separate from functionality; just like with apps and SQL. Basic steps to create are:




  • Right clicked Features and added a new Feature called BrandingBlogContentTypes.


  • Changed the scope of BrandingBlogContentTypes to Site.


  • Right clicked the project and added content type. As part of the configuration you will be asked what your content type will inherit from. In this blog, my custom Technology Article content type inherits from Article Page, so I selected that.


  • Then I went to the elements.xml and added columns and content type definitions.


  • Below is the code that created for my Technology Article content type and column definitions.


  • First I added both the Confidentiality and Technology columns which will use managed metadata. Note that the type is TaxonomyFieldType and TaxonomyFieldTypeMulti. Reminder to generate new GUIDs. Otherwise this is the same as we did in SharePoint 2007.


  • A content type will be generated for you and you will need to add your columns to it. As you can see it took the GUID off SharePoint and then appended 00 plus new GUID. That simple little feature makes my day J


  • Next I added the Confidentiality and Technology columns. Same as in the SharePoint 2007 days.


  • Next you see I added some strange columns called TaxCatchAll, TaxCatchAllLabel, TaxKeywordTaxHTField. If you do not add these four hidden columns, your managed metadata columns will not work in a list or library. I figured this out by exporting the library definition in my development environment plus confirmed it on some readings out on web. These all get added behind the scenes for you when you create content types through the SharePoint Site Collection Admin.


  • Next I added TaxKeyword to bring in the existing Enterprise Keywords column.



Finally I added AverageRating and RatingCount so the ratings would be part of my content type definition. You have to add these two columns even though it is presented to the user as single field. Both are used in the calculation of the presentation.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

<Field ID="{C578CFD4-4A0A-4E38-B057-0A602704B317}"
Name="Confidentiality"
StaticName="Confidentiality"
DisplayName="Confidentiality"
Group="BrandingBlog"
Type="TaxonomyFieldType"
ShowField="Term1033"
EnforceUniqueValues="FALSE">
</Field>

<Field ID="{F3F1CD61-ACE3-40D1-8F4D-F6618FC09E23}"
Name="Technology"
StaticName="Technology"
DisplayName="Technology"
Group="BrandingBlog"
Type="TaxonomyFieldTypeMulti"
ShowField="Term1033"
EnforceUniqueValues="FALSE">
</Field>

<!-- Parent ContentType: Article Page (0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D) -->
<ContentType ID="0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00a88b4089676141a2baada81b07a8c125"
Name="Technology Article"
Group="BrandingBlog"
Description="Technology Article for Wiki."
Inherits="TRUE"
Version="0">
<FieldRefs>
<FieldRef ID="{C578CFD4-4A0A-4E38-B057-0A602704B317}" Name="Confidentiality"/>
<FieldRef ID="{F3F1CD61-ACE3-40D1-8F4D-F6618FC09E23}" Name="Technology"/>
<FieldRef ID="{f3b0adf9-c1a2-4b02-920d-943fba4b3611}" Name="TaxCatchAll" Hidden="TRUE" />
<FieldRef ID="{8f6b6dd8-9357-4019-8172-966fcd502ed2}" Name="TaxCatchAllLabel" Hidden="TRUE" />
<FieldRef ID="{1390a86a-23da-45f0-8efe-ef36edadfb39}" Name="TaxKeywordTaxHTField" Hidden="TRUE" />
<FieldRef ID="{23f27201-bee3-471e-b2e7-b64fd8b7ca38}" Name="TaxKeyword" />
<FieldRef ID="{5a14d1ab-1513-48c7-97b3-657a5ba6c742}" Name="AverageRating" />
<FieldRef ID="{b1996002-9167-45e5-a4df-b2c41c6723c7}" Name="RatingCount" />
</FieldRefs>
</ContentType>
</Elements>


With that, I am done with create the content types.



Page Layout Definitions



Next I needed to bring over the Page Layout I had created for the Technology Article.




  • I went to my development environment and exported the Technology Article.


  • I added TechnologyArticle.aspx to the _catalogs >> masterpage folder.


  • Then in the page layout I removed both of the content query web part configurations based on one of the limitations captured earlier. I will reconfigure it later.


  • I modified the Confidentiality and Technology GUIDs Taxonomy:TaxonomyFieldControl to match the GUIDs I had defined in my content type Feature.


  • Next I modified the elements.xml.



I made the following changes to the existing elements.xml which already had the master pages. This is because master pages and page layouts reside in the same directory.




  • I moved the TechnologyArticle.aspx File tag to be with the master pages.


  • I made it GhostableInLibrary.


  • I added a property to make sure it is a Page Layout content type entry.



I added a property that maps the Page Layout to the content type that I had just created. Notice the GUID matches.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="_catalogs" Url="_catalogs/masterpage" Path="_catalogs/masterpage">
<File Url="BrandingBlog.master" Type="GhostableInLibrary"/>
<File Url="BrandingBlog_searchcenter.master" Type="GhostableInLibrary"/>
<File Url="TechnologyArticle.aspx" Type="GhostableInLibrary">
<Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
<Property Name="PublishingAssociatedContentType" Value=";#Technology Article;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00a88b4089676141a2baada81b07a8c125;#" />
</File>
</Module>
</Elements>


Content Query Web Part Definition



Next I needed to bring in my custom content query web part definition.




  • I created a new folder called “wp” under _catalogs. Then I went to the web part library of my development environment, downloaded the Content_Query_Branding_Blog.webpart file and added to the wp folder of my project.


  • Next I modified the Module entry in the elements.xml file to be following.


  • Note do not forget to add the Property for group. Otherwise the web part will not appear.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="_catalogs" Url="_catalogs/masterpage" Path="_catalogs/masterpage">
<File Url="BrandingBlog.master" Type="GhostableInLibrary"/>
<File Url="BrandingBlog_searchcenter.master" Type="GhostableInLibrary"/>
<File Url="TechnologyArticle.aspx" Type="GhostableInLibrary">
<Property Name="ContentType" Value="$Resources:cmscore,contenttype_pagelayout_name;" />
<Property Name="PublishingAssociatedContentType" Value=";#Technology Article;#0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948130EC3DB064584E219954237AF3900242457EFB8B24247815D688C526CD44D00a88b4089676141a2baada81b07a8c125;#" />
</File>
</Module>
<Module Name="_catalogs" Url="_catalogs/wp" Path="_catalogs/wp">
<File Url="Content_Query_Branding_Blog.webpart" Type="GhostableInLibrary">
<Property Name="Group" Value="Branding Blog" />
</File>
</Module>
</Elements>


Content Query Web Part Styles Definition



The last thing I need to move over is the ItemStyle_BrandingBlog.xsl I created for the content query web part.




  • First I added a folder called “XSL Style Sheets” under Style Library.


  • I then exported ItemStyle_BrandingBlog.xsl from my development environment and added to XSL Style Sheets folder.


  • Then I modified the existing elements.xml file.


  • I just added a new Module entry for the XSL Style Sheets folder.


  • Added ItemStyle_BrandingBlog.xsl file.




<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Style Library" Url="Style Library/BrandingBlog" Path="Style Library/BrandingBlog">
<File Url="style.css" Type="GhostableInLibrary"/>
<File Url="images/splitter.gif" Type="GhostableInLibrary"/>
<File Url="images/sidebar_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/picture.jpg" Type="GhostableInLibrary"/>
<File Url="images/li.gif" Type="GhostableInLibrary"/>
<File Url="images/bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/content.gif" Type="GhostableInLibrary"/>
<File Url="images/footer_bckg.gif" Type="GhostableInLibrary"/>
<File Url="images/head_bckg.jpg" Type="GhostableInLibrary"/>
<File Url="images/menu_bckg.gif" Type="GhostableInLibrary"/>
</Module>
<Module Name="Style Library" Url="Style Library/XSL Style Sheets" Path="Style Library/XSL Style Sheets">
<File Url="ItemStyle_BrandingBlog.xsl" Type="GhostableInLibrary" />
</Module>
</Elements>


Manual Deployments Steps



Now you can see how easy it is to create a package with all these files and then deployed. Given some of the lessons I learned, I will have to perform a few simple manual steps to get everything running after activation. I know perfectly well that I could add some custom code to the Feature activation, change to custom web parts with LINQ and even use different column types. However I wanted to push through the solution as is because I wanted to keep this as a Sandbox solution.



Earlier I showed how to deploy the master page. Now we have added several other things to this deployment package. Here are a couple extra steps that you will need to do.




  • Again build and package the solution in Visual Studio.


  • The upload the WSP into the Sandbox solutions.


  • Go to the Style Library >> XSL Style Sheets and check in ItemStyle_BrandingBlog.xsl.


  • Go to Site Settings >> Site Columns and then modify the both the Confidentiality and Technology columns to the appropriate Term Set. As well for Technology you will have to check the Allow Multiple Values checkbox.


  • Open up SharePoint Designer >> Page Layouts >> TechnologyArticle.aspx and remove the existing content query web parts. Then add back the two content query web parts using the same steps defined earlier in this blog.


  • Go the pages library and add the Technology Article content type so users can create those types of pages.



In the grand scheme of things, I really did not find that these few manual steps that big of deal given the advantages of using the Sandbox and giving me the possibility of deploying to the Office365 cloud.



Conclusions



This has been a fun experience but glad I am done typing this up. I really hope this is helpful to get you started on doing branding and web content management in SharePoint 2010.



References



The following are all the references I used to help me along the way.