Friday, May 8, 2009

Modify tasks blocked by a workflow

Sometimes using tasks on workflows we wander to modify their properties "manually".
But when we update the task item, we get an Exception reporting that the task is blocked by a workflow.

Here is the way to sort around this problem:
First of all we have to get the ItemID of the task inside the TaskList.
To know how maybe you would like to take a look here.

Then only we have to implement a little function that allows us to modify the task.
On this example, the task is modified to set as Completed.


private void CompleteTask(string taskID)
{
workflowProperties.Web.AllowUnsafeUpdates = true;

foreach (SPWorkflowTask task in workflowProperties.Workflow.Tasks)
{
if (task.RecurrenceID == taskID)
{
task["Status"] = "Completed";
task["PercentComplete"] = 1.0f;
task[SPBuiltInFieldId.WorkflowVersion] = 1;
task.SystemUpdate();
}
}

workflowProperties.Web.AllowUnsafeUpdates = false;
}

TaskItemId on Workflows

When we create new task on a workflow, after its being created, if we access the TaskProperties.TaskItemId property, we receive -1 but not the real new TaskItemId corresponding to the new item ID recently created on the TaskList assigned to the workflow.

Unfortunately after the workflow finishes the createTask event, the TaskItemId remains on -1 value, and it never being set to the proper value.
Why this is happening? Really seems to be a WSS bug...

But there is a way to find this ID with a few lines of code.
The first idea should be to access the TaskList and get the last item Id and increase it on one unit, then we will get the next Item id.
But this is not the good way because the Item ID property on a TaskList library is auto-incremental. And what does this mind? This mind that if we have 5 items created with each ID assigned (1,2,...,5) if we delete the last 2 items (4 and 5), next time we create new item the ID assigned to this item will be 6 and not 4.

How can we know the proper NextItemID of the TaskList?
Well this simple code executes a query how returns the next available item ID on a List getting by parameter the SPSite object where the list belongs and the GUID of the list.

Therefore we can get the nextItemId that will be assigned to the new task and then set the TaskItemId property with this value.
Since this moment every time we access to this property everywhere on the workflow process, we will get the proper ItemID corresponding to the Task we have created.



private int GetNextAvailableIdFromList(SPSite site, Guid listId)
{
int NextAvailableId = -1;

if (site.WebApplication.ContentDatabases.Count > 0)
{
foreach (SPContentDatabase contentDB in site.WebApplication.ContentDatabases)
{
string DBConnString = contentDB.DatabaseConnectionString;
SqlConnection con = new SqlConnection(DBConnString);
try
{
con.Open();
SqlCommand com = con.CreateCommand();
com.CommandText = String.Format("select tp_NextAvailableId from AllLists where tp_ID = '{0}'", listId.ToString());
NextAvailableId = (int)com.ExecuteScalar();

if (NextAvailableId <> -1){
con.Close();
return NextAvailableId;
}
}
catch { }
finally
{
con.Close();
}
}
}

return NextAvailableId;
}


This is an example of how can we use this function.


TaskProperties.TaskItemId = GetNextAvailableIdFromList(workflowProperties.Site, WorkflowProperties.TaskListId);