Wednesday, March 18, 2009

Getting Workflow Association from Content Type of Sharepoint Lists

Recently i have been working on Sharepoint Custom Workflows and Workflow Association with Sharepoint Lists and Content Type within the list. Thought of sharing some of the learnings from my experience.

Once the workflow is associated with Content Type/SPLists, its very common to update Workflow Instances.
As the workflow assembly keep on changing, Whenever you deploy new version of Workflow Assembly/Workflows Solution Package, SharePoint updates the workflow association to restrict new instance of the workflow. Even if the workflow class is not modified.
To over come this we need to get the workflow association object from List/Content type, depending on how we have associated the workflow.
If you want to update workflow association to allow new instances, you need to get a reference of Workflow Association Object and Enable it.
1. Getting Workflow Association Object from Content T(when workflow is associated with content type of Sharepoint List)

private SPWorkflowAssociation GetAnnualApprovalWFAssociation(SPContentType contentType)
{
SPWorkflowAssociation resultWFAssociation= null;
//Loop through all the workflow associations and return the correct one
foreach (SPWorkflowAssociation wfAssociation in contentType.WorkflowAssociations)
{
if(wfAssociation.BaseId == Constants.workflowGuid)
resultWFAssociation= wfAssociation;
}
return resultWFAssociation;
}
}
Note: SPWorkflowAssociations collection class defines two methods that can be used to get the workflow associations
  • GetAssociationByBaseID(wfGUID)
  • GetAssociationByName(name,cultureInfo)

This methods will return value only when the WorkflowAssociations are not restricted to allow new instances. Hence you can not completely trust this methods. This is why i have used a work around which loops through all the workflow associations (including deactive one) and compare the workflow association with Workflow GUID.

2. Getting workflow association with Sharepiont List

> SPList object contains a collection called WorkflowAssociations which is same as content type's workflow association object, so you can replace SPContentType object with SPList object and the above method will work.

Enabling WorkflowAssociation to AllowNewInstance

Once you have reference of SPWorfklowAssociation object, you can just set it's enabled property to true and invoke UpdateWorkflowAssociation(SPWorkflowAssociation) method on Content Type or SPLIst object.

SPWorkflowAssociation wfAssociation = GetWorkflowAssociation(myList);

wfAssociation.Enabled = true

myList.UpdateWorkflowAssociation(wfAssociation);

This action will enable the workflow association.


This is perticularly very useful when you have lots of sites already created in your environment that are using the Workflow, and you re-deploy the workflow assembly. So instead of doing it through sharepoint workflow settings page, you can write a tool that leverages the Sharepoint Object Model and updates the workflow associations for all your existing sites.

Tuesday, March 3, 2009

Iterating through Sharepoint Discussion List Replies

Hi,

Recently i had been working extensively in the integration of sharepoint discussion list and the document library.


I had one requirement where i have to iterate through the replies of a sharepoint discussion thread, i could hardly find any posts related to that.


First of all Sharepoint discussion lists are very different then other sharepoint lists. It is very different becase it stores the data differently. If you look at the settings of discussion list you will find that it contains two content types

1. Discussion Content Type

2. Messages Content Type

If you digg further you will find that discussion content type is derived from sharepoint folder content type and messages content type is derived from item content type.

Each message(reply) in the discussion list contains a field called ParentFolderId. This field is hidden from the new/edit/view forms of discussion list.

This field always contains id of the root folder for this message (e.g. id of the root discussion thread)

so if you want to get a reference of root discussion thread(aka folder), you can use following piece of code(considering that you have reference to the message)

int rootDiscussionThreadId = ListItem["ParentFolderId"].ToString();



So when you start a new discussion, internally sharepiont creates a folder withthe subject of the discussion, moving forward all the replies to the root discussion thread are nothign but items created inside the root discussion thread.

Discussion List

> Discussion - 1

-->Message 1

-->Message 2

> Discussion - 2

-->Message 1
-->Message 2


> Disccusion - 3

-->Message 1
-->Message 2





So if you iterate through all the items in the sharepoint list using he normal SPList.Items property, you will get reference of all the root folders (not the replies.)


Even when you navigate to flatView.aspx for any discussion thread, or any other page related to discussion thread you will always find a query string parameter named rootFolder, this query string parameter holds the unique id of the discussion folder.


Getting Replies of a Discussion


If you are in a situation where you have to loop through replies of a perticular discussion thread, you can use folowing code (assuming that you have obtained parent folder / reference of discussion item)



SPQuery queryText = new SPQuery();
queryText.Query = <>;
queryText.Folder = discussion.Folder;
//IMP: setting the folder will get items from that perticular folder
SPListItemCollection relatedDiscussions = discussion.ParentList.GetItems(queryText);


Please note that since we want to query items from a perticular folder of the discussion list, we are setting the folder property of SPQuery object to the folder that we want to query. Since my discussion is nothign but a folder in the discussion list, i am setting the folder using that discussion object.


When this query is executed, relatedDiscussion collection will contain list of replies for the given discussion folder,