Tuesday 23 July 2013

Remove workflow

Previously I showed you how to add a workflow and task list to all of your subsites through a powershell script, now let’s say what was done must also be able to be undone. So naturally here’s a powershell script to remove your Workflow and task list from all sub webs.

Section One (Set up)

#get the site url to deploy workflow to
param($siteUrl=$(read-host "Please provide web app url of the list to be delete (EG:'http://Wingtip'):"))

#ensure that user provided site url parameter
if($siteUrl -eq $null -or $siteUrl -eq '')
{
    throw "You must specify your server name. Value provided was null/empty."
}

#function to load SharePoint Powershell snap in
function LoadSharePointPowerShellEnviroment
{
 #clear the screen of pevious output
Clear-Host
 write-host "Setting up Powershell enviroment for Sharepoint" -foregroundcolor Blue
 Add-PSSnapin "Microsoft.Sharepoint.PowerShell" -ErrorAction SilentlyContinue
 Write-host "Sharepoint PowerShell Snapin loaded." -foregroundcolor Green
}

#call sharepoint plugin function
LoadSharePointPowerShellEnviroment

Section Two(Declare constants)

#get site using parameter
$site = Get-SPSite($siteUrl)

#Name of task list to delete
$listName = "Approval Tasks"

#approval workflow ID, specified in elements file of workflow
$APPROVALWORKFLOWID = [GUID]("64f076cb-a5f8-4ee7-9ef4-54820d291886");
Section Three(remove workflow function)
#remove workflow function
function RemoveAssociation($web, $listName)
{
      #library the workflow is runnion on
      $listInstance = $web.Lists.TryGetList($listName)
     
      #make sure the library exists
      if($listInstance -ne $null)
      {    
            #get the number of workflows running on library
            $i = $listInstance.WorkflowAssociations.Count - 1
            While($i -gt -1)
            {
                  #if workflow is ours, and it has 0 running then remove it.
                  if($listInstance.WorkflowAssociations[$i].BaseTemplate.Id.Equals($APPROVALWORKFLOWID) -and
                     $listInstance.WorkflowAssociations[$i].RunningInstances -eq 0)
                  {
                        Write-Host "Removing Workflow " $listInstance.WorkflowAssociations[$i].Name "From" $listName -ForegroundColor DarkGreen
                        $listInstance.WorkflowAssociations.Remove($listInstance.WorkflowAssociations[$i])                    
                  }
                  $i--
            }
      }    
      $listInstance = $null
      $web.Dispose()
}

Section Three(traverse all webs and remove list)

#itterate through all subsites
foreach ($web in $site.AllWebs)
{
      #get the list you whish to delete
      $listToDelete = $web.Lists.TryGetList($listName)
     
      #if the list exists delete it
      if($listToDelete -ne $null)
    {
            Write-Host $listToDelete.RootFolder $listName " has been deleted" -ForegroundColor Green
        $web.Lists.Delete($listToDelete.ID)
            $web.Update()
    }
     
      #remove the workflow from libraries
      RemoveAssociation $web "Pages"
      RemoveAssociation $web "Images"
      RemoveAssociation $web "Documents"
           
    $web.Dispose()
}

$site.Dispose()

Write-Host "Script Complete" -ForegroundColor Blue

Attach workflow to all webs

So I've made my own custom approval state machine workflow and now I need to attach it to everyone of my subsites. how do I accomplish this? Well any time you're face with a cumbersome amount of pointing and clicking you have two alternatives:

  • Feature event receiver 
  • PowerShell

Now as I learned the hard way you're event receiver has a 2 minute time out, so in my case where I'm attaching a workflow to three libraries on every subsite and creating a custom task list it really didn't pan out. Instead I ended up moving my logic out of my visual studio project and into a powershell script. I'm going to break this script down into Five sections.

Section One (Start up)

#get the site url to deploy workflow to
param($siteUrl=$(read-host "Please provide web app url of the list to be delete (EG:'http://Wingtip'):"))

#ensure that user provided site url parameter
if($siteUrl -eq $null -or $siteUrl -eq '')
{
    throw "You must specify your server name. Value provided was null/empty."
}

#function to load SharePoint Powershell snap in
function LoadSharePointPowerShellEnviroment
{
 #clear the screen of pevious output
 Clear-Host
 write-host "Setting up Powershell enviroment for Sharepoint" -foregroundcolor Blue
 Add-PSSnapin "Microsoft.Sharepoint.PowerShell" -ErrorAction SilentlyContinue
 Write-host "Sharepoint PowerShell Snapin loaded." -foregroundcolor Green
}

#call sharepoint plugin function

LoadSharePointPowerShellEnviroment

Section Two(Declare Constants)

#get site using parameter
$site = Get-SPSite($siteUrl)

#get the rootweb of the site
$rootWeb = $site.RootWeb

#ensure that approval workflow has unique name
$workFlowName = "Approval_" +  [System.DateTime]::Now.ToString()

#specify the approval task list name
$TASKLISTNAME = "Approval Tasks"

#specify the task list description
$listDescription = "Task list for ccw custom approval tasks"

#reference approval task list definition, deployed through VS project
$TaskListTemplate= $rootWeb.ListTemplates["Page Approval Task List Definition"]

#specify OOTB workflow history list, every site has one
$HISTORYLISTNAME = "Workflow History"

#approval workflow ID, specified in elements file of workflow
$APPROVALWORKFLOWID = [GUID]"64f076cb-a5f8-4ee7-9ef4-54820d291886"

#reference workflow using workflow ID

$wfTemplate = $rootWeb.WorkflowTemplates[$APPROVALWORKFLOWID]

Section Three(Create list function)

#Create List Instance function
function createListInstance($web, $listName, $listDescription)
{
      #make sure list doesn't already exist
      if($web.Lists.TryGetList($listName) -eq $null)
      {
            #create list with no spaces
            $listGuid = $web.Lists.Add($listname.Replace(" ",""), $listDescription, $TaskListTemplate)
           
            #grab the list, change the name to the original parameter passed to the function
            $list = $web.Lists[$ListGuid]
            $list.title = $listName
            $list.Update()
      }
      else
      {
            Write-Host $listName + "was not created on web" + $web.Url -ForegroundColor Red;
      }
      $web.Dispose()

}

Section Four(Attach workflow function)

#attach workflow to web
function addApprovalWorkflow($web, $listname)
{
      #get the library you want to attach the workflow to
    $library = $web.Lists.TryGetList($listname)
   
      #ensure library exists
    if($library -ne $null)
    {
            #reference tasklist, and histrory list on web
        $taskList = $web.Lists[$TASKLISTNAME]
        $historyList = $web.Lists[$HISTORYLISTNAME]
       
            #create a workflow association refer to
        $workflowAssociation = [Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation($wfTemplate, $workflowName, $taskList, $historyList);
       
            #set the properties of the workflow assocation
        $workflowAssociation.Name = $workFlowName
        $workflowAssociation.AllowManual = $true
        $workflowAssociation.AutoStartChange = $true
        $workflowAssociation.AutoStartCreate = $true
       
            #attach the workflow association to the Library
        $temp = $library.WorkflowAssociations.Add($workflowAssociation);
        $workflowAssociation.Enabled = $true;
            $web.Dispose()
    }

}

Section Five(Main)

#itterate through all subsites
$webs = $site.AllWebs
foreach ($web in $webs)
{
      #ignore the search site
      if($web.Title -eq "Search")
      {
            continue
      }
     
      Write-Host $web.Url
      #create the task list
      createListInstance $web $TASKLISTNAME $listDescription
     
      #Attach the approval wf to libraries
      addApprovalWorkflow  $web "Pages"
      addApprovalWorkflow  $web "Images"
    addApprovalWorkflow  $web "Documents"
    $web.Dispose()
}
$rootWeb.Dispose()
$site.Dispose()


write-host "Complete" -ForegroundColor Blue

Monday 22 July 2013

Asynchronous calls

public class Program
{
    private static void heavyMethod(int pauseTime)
    {
        Thread.Sleep(pauseTime);
        int threadID = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine(string.Format("My runtime was {0} miliseconds, my id was {1}"
                          pauseTime.ToString(), threadID));
    }
 
    private delegate void myDelegate(int iCallTime);
    private static myDelegate dlgt = new myDelegate(heavyMethod);
 
    private static void Main(string[] args)
    {
        Console.WriteLine("Start demo");
 
        //simple async call, go do your stuff and i don't care when you finish
        dlgt.BeginInvoke(10000, nullnull);
 
        //Async call with call back function, when compelte call fourSecond_callback method
        dlgt.BeginInvoke(4000, new AsyncCallback(fourSecond_CallBack), null);
 
        //async call with result, waits for end invoke
        Console.WriteLine("Waiting for 7s");
        IAsyncResult ar1 = dlgt.BeginInvoke(7000, nullnull);
        dlgt.EndInvoke(ar1);
 
        //asyn call with wait
        IAsyncResult ar = dlgt.BeginInvoke(3000, nullnull);
        Console.WriteLine("Doing some stuff here");
        ar.AsyncWaitHandle.WaitOne();
 
 
        //async with polling
        Console.WriteLine("5s counter start");
        IAsyncResult ar2 = dlgt.BeginInvoke(5000, nullnull);
 
        int count = 0; 
        while (!ar2.IsCompleted)
        {
            Console.WriteLine(count++);
            Thread.Sleep(1000);
        }
 
        Console.WriteLine("Finished, press any key");
        Console.ReadKey();
    }
 
    public static void fourSecond_CallBack(IAsyncResult ar)
    {
        Console.WriteLine("4s is up");
    }
}