A client of mine has a SharePoint task list that has a SharePoint Designer workflow attached to it. The workflow was a basic notification workflow. When an item is added to the list, the workflow kicks off and sends reminder emails to the assigned person 30, 15, 10,7,3 and 1 days before the task is due if it’s not completed.
It’s a straight forward workflow modeled off of the Notification workflow that comes with the Employee Training site template provided by Microsoft.
The Problem:
When items are deleted from the list, the workflows are not cancelled. People were still getting emails for items that were deleted!
An easy fix for this is to attach a simple event handler to the list and on the “OnDeleting” event, remove all workflows from the item. This will works for all items moving forward, but it doesn’t stop the workflows that have already been started. In this case there were many items that had been deleted with running workflows.
What I wanted to do was to go through the recycle bin and delete all of the workflows from all items that came from that list. I figured this would be easy enough, but it turns out I was mistaken. There is no way to get an instance of a workflow to kill it without having the ID of the item it was attached to, and you can’t get the ID of an item that resides in the recycling bin.
Solution:
Let me start by saying I think that this solution is “hackish” at best.
Here are the steps I took
1. Using an SPQuery object, Take a snap shot of the current list (Before)
2. Restore all of the items out of the recycle bin that came from the list
3. Using an SPQuery object, take another snap shot of the list after restoring all items (After)
4. Check each item in the “after” list and see if it resides in the “before” list
5. If it does not exist
a. The item was a restored item
b. Get all workflows for this restored item and delete all associated workflows
c. Send the item back to the recycle bin
That’s it.
The code is posted below.
Hope this helps someone else!
static void Main(string[] args)
{
WriteToLogFile(" ****** Starting on:" + DateTime.Now.ToString() + " ****** ");
try
{
using (SPSite site = new SPSite(ConfigurationManager.AppSettings["SITE_URL"]))
{
using (SPWeb web = site.OpenWeb())
{
WriteToLogFile("Opened Web:" + web.Url);
SPList list = web.Lists[ConfigurationManager.AppSettings["LIST_NAME"]];
string url = list.DefaultViewUrl.Substring(0, list.DefaultViewUrl.LastIndexOf("/"));
url = url.Substring(1);
WriteToLogFile("Got List:" + url);
SPListItemCollection before = GetListItems(list);
WriteToLogFile("Count before:" + before.Count.ToString());
before.GetDataTable().WriteXml("Before.xml");
SPRecycleBinItemCollection bin = web.RecycleBin;
WriteToLogFile("Url:" + url);
for (int i = bin.Count - 1; i >= 0; i--)
{
WriteToLogFile("DirName:" + bin[i].DirName);
if (url == bin[i].DirName)
{
WriteToLogFile("Restoring:" + bin[i].Title);
bin[i].Restore();
}
else
{
WriteToLogFile("Not restoring:" + bin[i].Title);
}
}
SPListItemCollection after = GetListItems(list);
WriteToLogFile("Count after:" + after.Count.ToString());
after.GetDataTable().WriteXml("After.xml");
if (before.Count == after.Count)
{
WriteToLogFile("Before and After counts match. No work to be done!");
return;
}
WriteToLogFile("Before and After do not match. Digging through after list to see which items have been restored");
for (int i = after.Count-1; i >= 0; i--)
{
SPListItem itemBefore = null;
try
{
itemBefore = before.GetItemById(after[i].ID);
WriteToLogFile("Item found. Not Deleting item ID:" + itemBefore.ID.ToString() + " - " + itemBefore.Title);
}
catch
{}
//Item Not Found before, remove workflows and delete it.
if (itemBefore == null)
{
WriteToLogFile("Restored item found. ID:" + after[i].ID.ToString() + " - " + after[i].Title);
RemoveWorkflowsFromItem(after[i]);
WriteToLogFile("Sending Item ID:"+after[i].ID.ToString()+" to Recycle bin");
after[i].Recycle();
}
}
}
}
}
catch (Exception ex)
{
WriteToLogFile(ex.ToString());
}
WriteToLogFile(" ****** Complete on:" + DateTime.Now.ToString() + " ****** ");
}
private static void RemoveWorkflowsFromItem(SPListItem item)
{
SPWorkflowManager mgr = item.ParentList.ParentWeb.Site.WorkflowManager;
SPWorkflowCollection coll = mgr.GetItemWorkflows(item);
if (coll.Count > 0)
{
WriteToLogFile("Removing " + coll.Count.ToString() + " workflows for:" + item.Title);
for (int w = coll.Count - 1; w >= 0; w--)
{
mgr.RemoveWorkflowFromListItem(coll[w]);
}
}
else
{
WriteToLogFile("No workflows to remove from " + item.Title);
}
}
private static SPListItemCollection GetListItems(SPList tasks)
{
SPQuery query = new SPQuery();
query.Query = "
return tasks.GetItems(query);
}
private static void WriteToLogFile(string message)
{
System.IO.StreamWriter sw = new System.IO.StreamWriter("Workflowcleaner.txt",true);
sw.WriteLine(message);
sw.Close();
}
}