Black Blade Associates Logo A Black Blade Associates blog. Struggling with SharePoint? We can help.




Blog moved: This blog has moved to http://thingsthatshouldbeeasy.wordpress.com. Go there now to see the new posts.


Tuesday, October 16, 2007

SharePoint Designer 2007 Workflow on Task Lists

Background

During the course of prototyping some task management capability for a client, I needed to create some quick and dirty workflow examples on a WSS tasks list. I did not need the workflow example to be deployable, compiled, or have any custom code, so naturally I thought of SharePoint Designer.


I created the workflow and associated it with a tasks list that we were using to manage our tasks. When I initially created the workflow, I set it to start only start when a user explicitly started it, not automatically on item additions or updates. Once I was fairly confident that the workflow was behaving properly, I wanted to go back and modify it to start automatically upon task item additions or updates. When I tried that, SharePoint Designer told me that I had errors in my workflow. It pointed to a step where I was collecting information from the user. I thought that was odd seeing how the data collecting had been working just moments before.


What made matters worse is that I had no way to get at the actual error details. When I tried to hover the cursor over the error symbol, the step drop down would obscure the error. I was stumped.


The Hunt

I decided to investigate the site in the web browser to see if I could pinpoint the problem. I remember working with the workflows that ship with MOSS, specifically, that they allowed me to select a history list and tasks list where workflow-specific data was stored. I looked through the site and found the history list but could not find the workflow tasks list.


I thought the lack of a workflow tasks list was odd. After all, how could the workflow be storing the data collection information if it did not have a workflow tasks list?


The Bug

The lack of the separate workflow tasks list proved to be the issue! It turns out that when SharePoint Designer 2007 creates a new workflow, it utilizes the first tasks list in the current site as the workflow tasks list. Now remember that I created the actual workflow to run on a tasks list. The reason I could not have the workflow auto-start was because SharePoint Designer was placing its data collection tasks into the same tasks list on which the workflow was defined. Knowing that, it was clear why the workflow could not auto-start: if the workflow auto-started it would start not only for task items that users added to the list but also for data collection tasks items that the workflow instances added to the list. That would cause workflows to cascade out of control.


The Workaround

Well, knowing the issue was great and all, but I had a customer breathing down my neck. It turned out that the workaround was easy to implement by using the behavior of the actual bug. If SharePoint Designer wanted to use the first tasks list in the site to store its workflow tasks, let it. I created a new site, and two tasks lists. I made sure to title the tasks list I wanted SharePoint Designer to use for workflow tasks to come alphabetically before the tasks list on which the workflow would actually run.


Here's an example. Say I wanted my workflow to run on the "Tasks" tasks list. I would create another tasks list in the site called "A Workflow Tasks". Because "A Workflow Tasks" alphabetically comes before "Tasks", SharePoint Designer will use the "A Workflow Tasks" as the workflow tasks list for the site. Life is now good.


One Catch

I stated in the workaround that I created a new site to implement the workaround. This is because once SharePoint Designer chooses a tasks list as its workflow tasks list, it will keep that for the duration of the site's existence. That's why I created the new site. I suppose given enough time and desire I could have found where SharePoint Designer stores its affinity for a workflow tasks list, but at 3 AM, creating a new site seemed like the way to go.

Update

TBone has a nice article about changing the auto-assigned workflow task. The article can be found here: http://www.sharepointblogs.com/tbone/archive/2008/01/10/ode-to-joy-changing-the-task-list-for-auto-generated-tasks-within-sharepoint-designer.aspx

Monday, October 15, 2007

Creating a "Use List Item Workspace" Capability for WSS V3 List Items

The Code

All source code relating to this article can be downloaded from CodePlex at:

http://www.codeplex.com/LIWWWV3

 
 

Background

A client of mine had asked for a task management solution based on MOSS 2007. Now, notice that they wanted a task management solution, not a project management solution. They did not want to procure, deploy, or train users on a "real" project management system. They just wanted simple tasking.

 
 

Well, almost "simple tasking". One thing they wanted was to have the concept of parent and child tasks. That is, one logically complex task could have several simpler sub tasks created and each of those sub tasks could be assigned to different people to work on. Ok, sounds simple enough. Create a task list with "Parent Task" as a lookup column to the same list. Well, that would work. I've created similar implementations in the past. The problem that that implementation eventually runs into is that as the tasks list grows in size, the tasks become increasingly difficult to manage, especially when users want to start managing the parent / sub tasks as hierarchies rather than a flat list of tasks. For example:

  • Delete all children, grand children, great grand children, etc… of a particular task.
  • Roll up task status based on parent / child relationships.
  • Set permissions on a task and all of its descendants.

 
 

A Conceptual Solution

All is of course doable, but the implementation grows in complexity rather quickly. Intuitively I though that leveraging SharePoint's capability to create sub-webs in the same fashion as meeting workspaces or document workspaces would be an elegant solution to management of the hierarchies. So, if I had a task in a top-level task list and I needed to create several logical sub-tasks for the task, I would create a sub-site for the top-level task and place the logical sub-tasks in the physical sub-site. If I then needed to create sub-tasks for one of those tasks, I would create another sub site. And so on.

 
 

Conceptually, this seemed to work. Implementing the concept in WSS V2 was another matter. In WSS V2, we could have the users trained to do this manually, but the process would become cumbersome, especially when parent tasks were deleted and orphaned sub-sites were left around. In WSS V2, there was no easy way to automate this process because the event model was only implemented for document libraries, not lists in general. Yes, we could create web parts to handle things, but issues always come up when users alter list items directly without the specialized web part. For this reason, I strayed away from implementing automation that relied on web parts.

 
 

A New Implementation Strategy

Enter WSS V3. The event model has now been extended to all lists, so I decided to revisit the problem. Right away event receivers seemed to hold the key to the solution. I figured that I could create a check box field in a list easily enough and tie an event receiver to the list that would create a sub-web for a list item that had a check in the check box. This is what the document and meeting workspaces do after all. Deploying the event receiver should be easy enough; I would simply use a feature for that.

 
 

The one snag I ran into is that I would want to be able to enable and disable the capability to list item workspaces on a list by list basis. To make that happen, I would need to add an option to the list settings page and create an ASPX page to implement the option selection. Well, I don't think I missed anything, so on to the solution components.

 
 

The Use List Item Workspace Feature

We need a new web feature, called Use List Item Workspace, that will create a list settings option to enable creating sites associated with the items in the list. We want this to be turned on or off on a list by list basis because we will need to add fields to the list.

 
 

When the Use List Item Workspace feature is activated, a new option appears in on the List Settings menu in the list toolbar to enable or disable list item workspaces for each list. This menu item is implemented as a custom action feature that takes the user to a custom ASP page deployed to the Layouts folder in the 12 hive. The ASPX page receives the list ID and a URL as parameters. The list ID tells the ASP page on which list to act. The URL parameter tells the page where to navigate once the user click the OK or Cancel buttons. The ASPX page offers the user three options: activating List Item Workspaces for the current list and two forms of deactivating List Item Workspaces for the current list. These options are discussed below.

 
 

Turning on list item workspaces for a particular list binds the event receiver to the list and creates or shows two new fields for the list schema:

  • Use List Item Workspace - Boolean - editable
  • List Item Workspace - Url - read-only

Turning off list item workspaces for a particular list prompts the user to choose one of two options:

  • Remove the capability of creating new workspaces, but leave existing workspaces intact. This option simply hides the Use List Item Workspace field but leave the List Item Workspace field and all of the created workspaces alone. Users can not create new workspaces but can still access existing workspace. The event receiver is unbound from the list. This option does not have any data loss.
  • Remove the capability of creating new workspaces and delete existing workspaces. This option deletes the Use List Item Workspace and the List Item Workspace fields. All sub-webs specified by the values of the list items' List Item Workspace fields are deleted. The event receiver is unbound from the list. This option can result in a great deal of data loss.

 
 

Deactivating the feature is equivalent to turning off the for each list and selecting the first option, removing the capability of creating new workspaces, but leaving existing workspaces intact.

 
 

The Use List Item Workspace Event Receiver

We need a list item event receiver that is attached to lists on which users have enabled list item workspaces. The event receiver handles the ItemAdding, ItemAdded, ItemUpdating , and ItemDeleting events. For ItemAdding and ItemUpdating events, the receiver checks for the current value of the Use List Item Workspace field.

  • If its value is true, the event receiver will provision a sub-web and set the value of the List Item Workspace field to the Url of the sub-web. If the sub-web already exists, nothing will occur.
  • If its value is false, the event receiver will de-provision a sub-web whose Url is specified by the value of the List Item Workspace field. The value of the List Item Workspace field will be reset to empty. If the sub-web does not exist, no error is reported to the user as the goal is the removal of the sub-web.

For the ItemDeleting event, the receiver checks for the current value of the Use List Item Workspace field. If its value is true, the event receiver will de-provision a sub-web whose Url is specified by the value of the List Item Workspace field. The value of the List Item Workspace field will be reset to empty. If the sub-web does not exist, no error is reported to the user as the goal is the removal of the sub-web.

The ItemAdded event is used to add information to the workspace site about the parent list item that were not available in the ItemAdding event. This information includes the new item ID and the item Url. Adding the a link to the workspace links list that points back to the newly created parent list item allows us to easily navigate directly back to the parent list item from its item workspace.

 
 

Adding an Event Receiver to a List

We can add an event receiver to a list using either a feature or code. The code looks like:

SPList list = web.Lists["ListName"];

List.EventReceivers.Add(SPEventReceiver.ItemAdding,

"[Fully-qualified assembly name]",

"[Namespace-qualified receiver type name]");

 
 

Why would we want to use code rather than a feature? After all, we can activate or deactivate a feature in the Site Settings user interface. A feature would add the event receiver to all lists in the site. This is not what I wanted. I went with the code option because it allows me to activate the event receiver on a per-list basis, right from the Item Workspace Settings page discussed earlier. This is very similar to the way that activating the Audience Targeting capability for list items works in MOSS.

 
 

So, the only thing the feature does is add an item to the List Settings menu to navigate to the Item Workspace Settings page. The Item Workspace Settings page in turn allows the users to enable or disable item workspaces on a list by list basis. Enabling item workspaces add the fields needed to track the item workspace information and also add the event receiver to the list to process the information in the fields. Disabling item workspaces removes the event receiver from the list and may also remove the added fields.

 
 

Working with Url Fields

During the course of this implementation, I created code that adds a text field to lists on which users activated List Item Workspaces. This text field stores the Url of the list items' List Item Workspaces. The issues I found with this approach was that while SharePoint normally renders the text as an anchor tag <A>, SharePoint does not include the full Url in the tag if there are white spaces in the Url. These white spaces normally come from the web's Url. So, I changed the field to be a Url field. Working with Url fields in not exactly intuitive when you start. You need to know about how the Url field stores its data in order to be able to work with the field in code.

 
 

The Url field stores both the Url and text associated with an anchor tag. The data is stored in the following format:

<URL><comma><space><Description>

"http://www.microsoft.com,, 1234, Microsoft, Inc"

 
 

This data storage is fairly straight forward. To get the Url portion, you simply look for the first <coma><space> in the string and take everything before. To get the description, take everything after the first <coma><space>. If the actual URL has a <comma><space> character combination set in it, that <comma> is escaped via another <comma>. So the <URL> will have a <comma><comma><space> character set in it. Only the first non-escaped <comma><space> character combination is treated as the field delimiter. Therefore, a <comma><space> combination in the <Description> portion is not escaped.

 
 

Possible Enhancements

As with all projects, there is never enough time to do everything you would like to do. Here are a couple of enhancements to List Item Workspaces capability I feel would be "nice to haves":

  • Make the breadcrumb trail in the item workspace to look like:

    Path to parent site -> Item List -> Item -> Item Workspace

  • Add a dataview web part to the item workspace that displays all parent item properties where the ShowInDisplayForm attribute is not false

     
     

    Surprises

    And finally, here are the things that did not behave quite as I had expected:

    • Bug in the SPField.AddFieldAsXml Method

      There is a bug in the AddFieldAsXml method on the SPField class that does not set the InternalName property of the new field to the value of the Name attribute in the Field element. Instead the method sets the InternalName to a variation of the DisplayName attribute. The workaround is to set the DisplayName attribute in the XML to be the valid InternalName you originally wanted the field to have. Then, to get a reference to the new field and change its Title property to what you wanted the DisplayName to be. This is documented on Bil Simser's blog at:

      http://weblogs.asp.net/bsimser/archive/2005/07/21/420147.aspx

       
       

    • SharePoint does not allow the creation of subwebs in the Lists folder. SharePoint generates an error saying the address is already in use.

       
       

    • SharePoint does not allow the creation of subwebs more than two folders under the web's root folder. SharePoint generates an error saying the address is already in use.

       
       

    • The BeforeProperties and AfterProperties attributes of the properties parameter are not populated for ItemDeleting event override. Use the field values on the properties.ListItem instead.

       
       

    • The ListItem attribute of the properties parameter is null on the ItemAdding event.

       
       

    • We can't edit the properties.ListItem properties directly when inside the ItemUpdating event. This causes an error message about conflicting updates. The exact error is: "Your changes conflict with those made concurrently by another user." Instead, set the key / value pairs in the AfterProperties collection with the new desired values. This is documented on Ishai Sagi's blog at:

      http://www.sharepoint-tips.com/2006/11/synchronous-list-event-handlers-ruin.html

     
     

Friday, October 05, 2007

MSDN Subscription Downloads - Error: 11008

I've received the following error for a while when trying to access the MSDN subscription downloads:

System Error

--------------------------------------------------------------------------------

An Error occurred while you were attempting to access the Subscriber Download web site. We apologize for any inconvenience this may have caused you.

Thank you for your patience.

Please try again later.

--------------------------------------------------------------------------------

Error: 11008

I've looked around and lots of people seem to be having the same issue with no resolution.

There is a very simple workaround for the time being: download Firefox and use that to access your MSDN subscriptions. I have IE and Firefox installed on my laptop. I get the error when accessing the site from IE, but Firefox works fine. Not an ideal fix, but it will get people up and running while the issue is diagnosed.