Wednesday 4 July 2012

Sharepoint workflow - deploy without Designer!

We all know how powerful and important workflows are in SharePoint 2010. Today, let's create a reusable workflow which can be deployed to a production farm without using SharePoint Designer.

There are a lot of firms which are skeptical about enabling SharePoint Designer on their production farm (due to security reasons supposedly!), but they have requirements that involve developing workflows, invariably SharePoint Designer is the one of THE tools used for such a requirement.

So, let's assume a requirement for our workflow.
1. There is a custom list which inherits from a custom content type on a team site, this list contains multiple columns out of which there exists a "Status" column.
2. When there is a change in the Status column, mails are sent out to certain users with custom content in the body of the mail.

Here is the catch, we have to deploy this solution to production without using Designer.

When I first thought about how this can be achieved, there were a lot of options that were to be looked at while creating the workflow in designer. On investigation, content type association was the best way to implement this, but then again not everything comes without a price. Here, we have to make sure that the content type Id to which the workflow would be associated to would remain constant across Dev, UAT, Test, Prod farms.

Below are steps to be followed when creating such a reusable workflow.

1. Create a new reusable workflow, associate with content type in Sharepoint Designer.
2. Choose the Custom Content type which would have been created.

3. Make changes accordingly to your workflow emails.
4. Save, Publish and "save as template" the workflow, this would get saved a .wsp file.  IMPORTANT!
5. Create the custom content type (that was created in dev) with the same guid in your UAT/Test/Prod farm. I have added some xml code and a powershell script below which will help you do the same. Create a list which inherits from this new custom content type.
Use the below code to create a content type, modify the fields as required and save it as a .xml file. Make sure your copy the right guid for the content type.

<?xml version="1.0" encoding="utf-8"?>

<ContentTypes>
<ContentType ID="0x01001BDC25A2E5635040AF4C0BEB48E7DAF6" Name="CustomWfContent" Group="WorkflowContent" Version="1"><Folder TargetName="_cts/CustomWfContent" /><Fields><Field ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" Name="ContentType" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="ContentType" Group="_Hidden" Type="Computed" DisplayName="Content Type" Sealed="TRUE" Sortable="FALSE" RenderXMLUsingPattern="TRUE" PITarget="MicrosoftWindowsSharePointServices" PIAttribute="ContentTypeID" Customization=""><FieldRefs><FieldRef ID="{03e45e84-1992-4d42-9116-26f756012634}" Name="ContentTypeId" /></FieldRefs><DisplayPattern><MapToContentType><Column Name="ContentTypeId" /></MapToContentType></DisplayPattern></Field><Field ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" SourceID="http://schemas.microsoft.com/sharepoint/v3" StaticName="Title" Group="_Hidden" Type="Text" DisplayName="Title" Required="TRUE" FromBaseType="TRUE" Customization="" ShowInNewForm="TRUE" ShowInEditForm="TRUE" /><Field Type="Choice" DisplayName="QuestionStatus" Required="FALSE" EnforceUniqueValues="FALSE" Indexed="FALSE" Format="Dropdown" FillInChoice="FALSE" Group="Custom Columns" ID="{f00d1f96-c728-4a09-bfc3-c585af475c1c}" SourceID="{4715a8a4-e48c-4687-9b42-0bb680c97edd}" StaticName="QuestionStatus" Name="QuestionStatus" Customization=""><Default>Unanswered</Default><CHOICES><CHOICE>Unanswered</CHOICE><CHOICE>In Progress</CHOICE><CHOICE>Commercial</CHOICE><CHOICE>Actioned</CHOICE><CHOICE>Complete</CHOICE></CHOICES></Field></Fields><XmlDocuments><XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"><FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms"><Display>ListForm</Display><Edit>ListForm</Edit><New>ListForm</New></FormTemplates></XmlDocument></XmlDocuments></ContentType> </ContentTypes>

In case you want to generate a Content type xml file like the above one you can use the code below


#Powershell to output the content type to a Xml file
$sourceWeb = Get-SPWeb http://yourwebsite

$xmlFilePath = "C:\Script-SiteContentTypes.xml"
#Create Export File
New-Item $xmlFilePath -type file -force
#Export Content Types to XML file
Add-Content $xmlFilePath "<?xml version=`"1.0`" encoding=`"utf-8`"?>"
Add-Content $xmlFilePath "`n<ContentTypes>"
$sourceWeb.ContentTypes | ForEach-Object {
    if ($_.Name -eq "AgentPortalCustomContentType") {
        Add-Content $xmlFilePath $_.SchemaXml
    }
}
Add-Content $xmlFilePath "</ContentTypes>"
$sourceWeb.Dispose()



Below is the powershell script which creates the content type as saved in the .xml file. 
$destWeb = Get-SPWeb http://yourwebsite
$xmlFilePath = "C:\Script-SiteContentTypes.xml"


#create the columns required for the content type
$fields = $xmlFile.SelectNodes("/ContentTypes/ContentType/Fields/Field")
$fields | Foreach-Object{
$fieldXml = $_.OuterXml.ToString()
$destWeb.Fields.AddFieldAsXml($fieldXml)
write-host "Column " $_.Name "created"
}
$destWeb.update()


#Create Site Content Types
$ctsXML = [xml](Get-Content($xmlFilePath))
$ctsXML.ContentTypes.ContentType | ForEach-Object {
    #Create Content Type object inheriting from parent
    $spContentType = New-Object Microsoft.SharePoint.SPContentType ($_.ID,$destWeb.ContentTypes,$_.Name)
    
    #Set Content Type description and group
    $spContentType.Description = $_.Description
    $spContentType.Group = $_.Group
    
    $_.Fields.Field  | ForEach-Object {
        if(!$spContentType.FieldLinks[$_.DisplayName])
        {             #Create a field link for the Content Type by getting an existing column
            $spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink ($destWeb.Fields[$_.DisplayName])
        
            #Check to see if column should be Optional, Required or Hidden
            if ($_.Required -eq "TRUE") {$spFieldLink.Required = $true}
            if ($_.Hidden -eq "TRUE") {$spFieldLink.Hidden = $true}
        
            #Add column to Content Type
            $spContentType.FieldLinks.Add($spFieldLink)
        }
    }
    
    #Create Content Type on the site and update Content Type object
    $ct = $destWeb.ContentTypes.Add($spContentType)
    $spContentType.Update()
    write-host "Content type" $ct.Name "has been created"
}
$destWeb.Dispose()

6. Go to your Site Settings -> Solutions -> Upload solution and choose the attached .wsp file and activate the solution in the ribbon.
7. Go to your Site Features and activate the feature which starts with “Workflow template”.
8. Now, go back to the list and add a new workflow for the list under the new content type.
9. Choose a name for the workflow and select the option which says the workflow will fire on “New” or “Edit” item and save.

There you go, now we just deployed a custom workflow without using designer!!!

No comments:

Post a Comment