Friday, April 13, 2007

Multi Value Columns Solution #2 - Custom Activities in SPD

In the previous post in this series (Multi-Value Columns in SharePoint Designer - Solution #1), I described a problem where SharePoint Designer can’t send e-mail to multiple recipients if those recipients exist inside of a multi-value column in a SharePoint list. The simple hacky solution I described was to temporarily turn the column into a single value column just for SharePoint Designer. But this approach has problems, and there is a better way: developing custom activities in Visual Studio for use in SharePoint Designer.

In this post I will describe how to develop a custom activity in Visual Studio that will also solve this problem and I will also describe how to install it on a SharePoint server so that SharePoint Designer clients can automatically download and use it. Before I do let me back up and tell you why, from my perspective, this approach is probably not the best way to go.

SharePoint Designer is a Microsoft Office product that replaces FrontPage, integrates tightly with SharePoint, and allows non-developers (aka “knowledge workers” in the Microsoft lingo) to create simple workflows without writing any code.

The problem is that simple workflows and multi-value columns are like oil and water: not so compatible. If you’re using multi-value columns then your knowledge workers should admit defeat and let developers create the workflows in Visual Studio using the Windows Workflow Foundation (which, incidentally, will be the topic for third article in this series).

Still, you may have a good reason for continuing to develop workflows in SharePoint Designer, and I’ve already gone through the pain of writing and installing custom activities, so hopefully this post will make life easier for someone somewhere.

Creating the Custom Activity in Visual Studio

The code to create the custom activity in Visual Studio is the most interesting part of this solution. Make sure to check out the GetEmailAddressesFromUsernames() method if you have time to review the code. Here is the procedure assuming this is your first time working with Windows Workflow Foundation in Visual Studio.

2. If you aren’t running on Windows Server 2003, then you probably need to install the Windows Workflow Foundation DLL’s. You’ll know there’s a problem if, after creating the project in Visual Studio, your SharePoint references are invalid. I picked these DLL’s up from my Windows Server 2003 machine (actually a VMWare virtual machine) from the following location:

You should install these to the Global Assembly Cache (GAC) to make Visual Studio happier, although you may still need to reference them for your project. The easy way to install to the GAC is just to copy them to somewhere on your local (non-W2K3) machine and then drag them all over to C:\Windows\assembly.

3. After completing step #1 when you open Visual Studio you should get a new tree node under “Visual C#” (or Visual Basic) called “Workflow.” Select that, then “Workflow Activity Library” then call the project something like “MultiRecipientMail”.

4. Visual Studio should automatically create a blank activity called Activity1.cs. If you want a more reasonable name then delete it and “Add” a “New Item” of type “Activity” called something like “MultiRecipientMailActivity.” Don’t make the same painful mistake as me: name your activity something other than the name of the project or anything in the namespace.

5. At this point you could drag and drop a “Replicator” activity on the design surface and put a “Send Mail” activity inside, but I’ll cover that in my next post. For now I think code is the clearer way to go. Hit F7 or right click and “View Code” and paste the following which I’ll try to comment in-line rather than in this article.

// Note: this code was adopted from Todd Baginski at http://www.sharepointblogs.com/tbaginski/archive/2007/03/08/HOW-TO_3A00_-Create-a-custom-Windows-Workflow-Activity-and-make-it-available-in-SharePoint-Designer.aspx

using System;

using System.ComponentModel;

using System.ComponentModel.Design;

using System.Collections;

using System.Drawing;

using System.Workflow.ComponentModel;

using System.Workflow.ComponentModel.Design;

using System.Workflow.ComponentModel.Compiler;

using System.Workflow.ComponentModel.Serialization;

using System.Workflow.Runtime;

using System.Workflow.Activities;

using System.Workflow.Activities.Rules;

using System.Net.Mail;

using Microsoft.SharePoint;

using System.IO;

namespace MultiRecipientMail {

publicpartialclassMultiRecipientMailActivity : SequenceActivity {

// Windows Workflow Foundation (WWF) will store the workflow state using these "instance property"

/// This logging method is NOT production ready.Obviously you need to create a C:\MultiRecipientMailLog.log

/// with appropriate for debugging.In production it should log to the event log or be removed.

///</summary>

///<param name="str"></param>

privatevoid LogThis(string str) {

FileInfo fi = newFileInfo("C:\\MultiRecipientMailLog.log");

if (fi.Exists) {

StreamWriter sw = fi.AppendText();

sw.WriteLine(String.Format("{0:g} {1}", DateTime.Now, str));

sw.Flush();

sw.Close();

}

}

}

}

If you’re new to .Net 3.0 you’ll find the whole dependency property thing pretty confusing. I’ll write about it in another post. For now just think of it as a big weakly typed hash table whose values WWF can use without prior knowledge of their existence.

6. To get this to compile you’ll need to add a reference to “Microsoft.SharePoint” and “Microsoft.Office.Server.” If you’re on a non-W2K3 machine you may need to reference the DLL’s manually that you copied from step 2. Regardless, double check you can compile (Control+Shift+B or Build->Build MultiRecipientMail).

Deploying the Custom Activity to the SharePoint Server

To give credit up front, a large part of this procedure came from Todd Baginski’s Blog. However, I still had a lot of difficulties, and based on the comments on the Microsoft forums regarding the multi-value column problem, a refinement of Todd Baginski’s work from a different perspective will be valuable to the MOSS community. In any event everything regarding this multi-value column solution is now in one place.

7. You’ll need to strongly sign the project because it needs to go into the Global Assembly Cache (GAC). To do this:

3. Type “sn -T MultiRecipientMail.dll” and copy and paste the public key token by right clicking and selecting mark then hitting Control-C. Save this token for later perhaps in notepad or commented out in your AssemblyInfo.cs file.

10. Now install your dll to the W2K3 GAC. To do this copy the dll to somewhere on the W2K3 machine (if it’s a separate machine), and either drag it to “c:\windows\assembly” or at a command prompt type something similar to:

The latter is the correct way and works in a deployment script, but I’d opt for the former for convenience.

11. The next step is get SharePoint Designer to recognize the new action. You could modify the WSS.ACTIONS file as Todd Baginski suggests, but you’re better off leaving this core file alone and creating a new one in the same directory that SharePoint will automatically pick up upon reboot (ref). So inside of:

You’ll need to substitute your own public key token from step 9 in the “Assembly=” statement.

12. Next you need to tell SharePoint designer clients to trust this new dll. To do so open the Web.Config in the virtual directory for your site (e.g. C:\Inetpub\wwwroot\wss\VirtualDirectories\34089\ web.config). Find the section called “” and add the following new line (substitute your own public token):

(again substitute your own public key token)

13. Restart IIS by opening a command prompt and typing “iisreset.” SharePoint will pick up the new MultiRecipientMail.ACTIONS file, find the dll in the GAC, and provide the new information to any SharePoint Designer clients that access the site.

14. Open SharePoint Designer and either navigate to the “Workflows\Multi Recipient Email.xoml” project from my previous article or create a new workflow project based on a list with a multi-value column. You should get a message like the following while SharePoint Designer downloads the new dll:

15. If everything worked correctly you should now be able to 1. click Actions; 2. More Actions; 3. Select the “Custom Actions” category from the dropdown (this category came from the .ACTIONS file in Step 11); and 4. Select the “Multi Recipient E-mail” action. If it doesn’t show up in the Workflow Designer page after clicking "add", then you probably referenced it incorrectly in the Web.Config.

16. Now you should be able to 1. click the Fx button next to “To;” 2. Select your multi value column (e.g. Peers To Review from my previous article); 3. Hit Ok; and 4. Fill in the remaining fields with values.

17. Now if you head back to Sharepoint, select the dropdown of a list item in the list associated with your workflow, click “Workflows”, select the workflow you created in SharePoint Designer, and click “Start” you should receive an e-mail at each address in your multi-value column.

Well … at least it worked for me. :) I did kitchen test this article, but please leave comments if it doesn't work for you.

Redeploying

If you want to make any changes to the code the steps to redeploy are:

1. Compile (you don’t need to sign again, that’s one time only)

2. Copy the dll to your W2K3 server

3. Re-install the dll to the GAC. You can just re-copy it over to c:\windows\assembly if you like.

4. Restart iis with an “iisreset” from the command line (no need to change the .ACTIONS or Web.Config files)

5. Finally, if you need to make changes to your workflow, you may need to restart SharePoint Designer to download the new dll (I usually have to).

You’re Done! Easy huh?

So you should now have a reusable component that non-developers can use in SharePoint Designer to send e-mail to multiple recipients as determined by a multi-value column in a SharePoint list. Of course if you decide that developing the workflow in Visual Studio is better, then the third article in this series may be for you.

I'm so happy to hear I kept you from blowing your head off. I wish I had experience assigning a task to a group, but unfortunately I don't. Hopefully you don't feel as strongly about the latter issue as the former.

Im able to do al lthe steps, but in Sharepoint Designer and I add my custom activity, nothing happen, I dont see the RuleDesigner Sentence written. I have no FieldBind or parameter, its a really simple workflow that always do the same thing so I dont need any parameter passed, is that why it dont work?

Hi Lee,Thanks for the post - very helpful! Between your post, Todd's, and Google I was able to get my custom activity working. I also found a slightly easier way of getting the email address from the user name by passing a WorkflowContext object as an input parameter and looking up the site or web's SiteUsers collection. I posted the info on my blog, as well as a complete example. Hope this helps! :-)Cheers,David

(I deleted my original comment and replaced with this one to fix the links)