Chapter 15: Creating Workflow Elements in Visual Studio

This article is an excerpt from Professional Microsoft Office SharePoint Designer 2007 by Woodrow W. Windischman, Brian Phillips, and Asif Rehmani from Wiley Publishing (ISBN 978-0-470-28761-3, Wiley Publishing, Inc., 2009, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.

Chapter 9 demonstrated how to use the Workflow Designer in SharePoint Designer to create custom workflows using the default actions delivered with Windows SharePoint Services. This chapter shows you how to take your workflows to the next level by creating custom actions in Visual Studio and configuring them to work inside SharePoint Designer. Specifically, this chapter examines how to:

  • Create custom actions.

  • Deploy custom actions.

  • Configure custom actions.

  • Test your custom actions.

Contents

  • What You Need to Get Started

  • Introduction to Custom Actions

  • Deploying and Configuring Your Custom Action

  • Testing Your Custom Action

  • Summary

  • Additional Resources

What You Need to Get Started

Before you jump in to create new actions, ensure that you have the correct tools. These tools include:

I chose General Development Settings when configuring Visual Studio 2008 for the first time. If you chose different settings, your screens may differ from the screenshots in this chapter.

If you want to reset your Visual Studio 2008 settings, select Tools > Import and Export Settings, and use the wizard to reset your settings. In the wizard, you have the option of backing up your existing settings if you want to revert to them later on.

Introduction to Custom Actions

The built-in actions in Windows SharePoint Services are a good starting point for functionality to your workflows, but if you need functionality that these actions do not provide, you have to create a custom action. Once you have deployed a custom action to Windows SharePoint Services and configured it to run, it appears in the SharePoint Workflow Designer. This section provides an example in which you build a custom action to create child sites that can be called as a part of a SharePoint Designer workflow.

Creating a Custom Action

Begin the process of creating a custom action by opening Visual Studio and selecting File > New > Project. The result is the New Project dialog, as shown in Figures 15-1 (Visual Basic) and 15-2 (C#). Ensure that .NET Framework 3.0 has been chosen from the dropdown list in the upper-right corner of the dialog. Do not choose .NET Framework 3.5 unless your server farm has been configured to support .NET Framework 3.5.

Figure 15-1

Figure 15-1

Figure 15-2

Figure 15-2

In the Project Types list on the left, expand either the Visual Basic or Visual C# node, depending on the language you want to use, and then click on Workflow in that node. (This example uses Visual Basic.)

A list of Project templates will be displayed on the right. Choose the Workflow Activity Library template, and then enter a name (CustomActionsLibrary for this example) and location for the Project in the lower section of the dialog. Click OK, and the Project to contain your custom action is created, as shown in Figures 15-3 (Visual Basic) and 15-4 (C#).

Figure 15-3

Figure 15-3

Figure 15-4

Figure 15-4

By default, Visual Studio automatically creates a file named Activity1.vb or Activity1.cs which you do not need. Remove the file by right-clicking Activity1.vb or Activity1.cs in the Solution Explorer window and selecting Delete from the context menu. A confirmation dialog appears. Click OK to confirm the deletion.

Assemblies that contain custom actions must be strong-named to run inside Windows SharePoint Services. A strong name ensures that your assembly will not be mistaken for another assembly, even if both have the same name. To learn more about strong names, read the Strong-Named Assemblies article on MSDN.

To configure the Project to have a strong name, right-click CustomActionsLibrary in the Solution Explorer window and select Properties to open the Project properties screen, as shown in Figures 15-5 (Visual Basic) and 15-6 (C#).

Figure 15-5

Figure 15-5

Figure 15-6

Figure 15-6

Click the Signing tab, and click the Sign The Assembly check box. Select New from the Choose a strong name key file dropdown list to open the Create Strong Name Key dialog (see Figure 15-7). Enter Key.snk in the Key file name text box, uncheck the Protect my key file with a password check box, and click OK. Click the Save icon on the toolbar and close the Project properties screen.

Figure 15-7

Figure 15-7

Before you add your custom action, you must add references to the SharePoint assemblies (DLLs) that will allow your custom action to participate in SharePoint workflows. Right-click CustomActionsLibrary in the Solution Explorer window, and select Add Reference. In the Add Reference dialog (see Figure 15-8), select the SharePoint assemblies by holding the Ctrl key down and clicking these items in the list box:

  • Windows SharePoint Services

  • Windows SharePoint Services Security

  • Windows SharePoint Services Workflow Actions

Release the Ctrl key, and click OK.

Figure 15-8

Figure 15-8

To create your first custom action, you must add an activity to the Project. Right-click CustomActionsLibrary in the Solution Explorer window, and select Add > New Item from the context menu. The Add New Item dialog opens (see Figure 15-9). Choose Workflow from the Categories on the left, and select the Activity template from the list on the right, enter a suitable name for the action, and click OK. For this example, name your custom action CreateSiteAction.

Figure 15-9

Figure 15-9

Visual Studio adds an empty action to the Project to hold the functionality you will create. After the action is created, Visual Studio automatically opens it in Design view. Close this view by selecting File > Close. Bring up the Code view for your custom action by right-clicking CreateSiteAction.vb, and selecting View Code. This chapter provides code in both Visual Basic and C#. The following code is displayed:

Public class CreateSiteAction
    Inherits SequenceActivity
End Class
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;

namespace CustomActionsLibrary
{
   public partial class CreateSiteAction: SequenceActivity
   {
      public CreateSiteAction()
      {
         InitializeComponent();
      }
   }
}

Now, you can start modifying this code to provide a mechanism that allows SharePoint Designer to provide values for your custom action before you add your custom functionality. This mechanism is called a property and is explained in detail in the next section.

To make it easier to use the classes in SharePoint and Windows Workflow Foundation, add these statements to the top of the file:

Imports Microsoft.SharePoint
Imports Microsoft.SharePoint.WebControls
Imports Microsoft.SharePoint.WorkflowActions
Imports System.ComponentModel
Imports System.Web
Imports System.Workflow.ComponentModel
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WorkflowActions;
using System.Web;

Creating Properties for Your Custom Action

To allow SharePoint Designer to pass values to your custom action when the workflow runs, you must create public properties inside your custom action. In this example, you will create a custom action that will create a new site within SharePoint whenever executed inside a SharePoint Designer workflow. To create a new site, SharePoint will need values for the following:

  • SiteUrl: Where the site will be located.

  • SiteTitle: Title for the new site.

  • SiteDescription: Description for the new site.

  • SiteTemplate: Template to use to create the new site.

  • InheritPermissions: True, if the new site will inherit permissions from its parent site.

For each field, you create a property in your custom action to allow the users to specify these values when designing their workflow in SharePoint Designer. When creating properties for a custom action, you create the property itself and a matching DependencyProperty. The DependencyProperty class allows SharePoint Designer to bind the property to the UI in the workflow wizard, where the user specifies the values of these properties in the workflow being created. Later in the chapter, you will create an ACTIONS file to define the UI to display to the user configuring the custom action and to define how the controls in that UI will bind to the properties of the custom action. Inside the class, add the following code to create a property and DependencyProperty for SiteUrl:

Public Shared SiteUrlProperty As DependencyProperty = _
    DependencyProperty.Register("SiteUrl",
        GetType(String), _
        GetType(CreateSiteAction))

Public Property SiteUrl() As String
    Get
        Return CType(Me.GetValue(SiteUrlProperty), String)
    End Get
    Set(ByVal value As String)
        Me.SetValue(SiteUrlProperty, value)
    End Set
End Property
public static DependencyProperty SiteUrlProperty =
    DependencyProperty.Register("SiteUrl", typeof(string),
    typeof(CreateSiteAction));

public string SiteUrl {
    get { return this.GetValue(SiteUrlProperty).ToString(); }
    set { this.SetValue(SiteUrlProperty, value); }
}

To be able to access the workflow's context to get a reference to the current SharePoint site, add this property:

Public Shared __ContextProperty As DependencyProperty = _
    DependencyProperty.Register("__Context",
        GetType(WorkflowContext), _
        GetType(CreateSiteAction))

Public Property __Context() As WorkflowContext
    Get
        Return CType(Me.GetValue(__ContextProperty), WorkflowContext)
    End Get
    Set(ByVal value As WorkflowContext)
        Me.SetValue(__ContextProperty, value)
    End Set
End Property
public static DependencyProperty __ContextProperty =
    DependencyProperty.Register("__Context", typeof(WorkflowContext),
    typeof(CreateSiteAction));

public WorkflowContext __Context {
    get { return (WorkflowContext) this.GetValue(__ContextProperty); }
    set { this.SetValue(__ContextProperty, value); }
}

Copy and paste the code again for each new property, replacing SiteUrl with the name of the new property. The resulting class should look like this:

Imports Microsoft.SharePoint
Imports Microsoft.SharePoint.WebControls
Imports System.ComponentModel
Imports System.Web
Imports System.Workflow.ComponentModel

Public Class CreateSiteAction
    Inherits SequenceActivity

    Public Shared SiteUrlProperty As DependencyProperty = _
        DependencyProperty.Register("SiteUrl", GetType(String), _
        GetType(CreateSiteAction))

    Public Property SiteUrl() As String
        Get
           Return CType(Me.GetValue(SiteUrlProperty), String)
        End Get
        Set(ByVal value As String)
           Me.SetValue(SiteUrlProperty, value)
        End Set
    End Property

    Public Shared SiteTitleProperty As DependencyProperty = _
        DependencyProperty.Register("SiteTitle", GetType(String), _
        GetType(CreateSiteAction))

    Public Property SiteTitle() As String
        Get
           Return CType(Me.GetValue(SiteTitleProperty), String)
        End Get
        Set(ByVal value As String)
           Me.SetValue(SiteTitleProperty, value)
        End Set
    End Property

    Public Shared SiteDescriptionProperty As DependencyProperty

        DependencyProperty.Register("SiteDescription", GetType(String), _
        GetType(CreateSiteAction))

    Public Property SiteDescription() As String
        Get
           Return CType(Me.GetValue(SiteDescriptionProperty), String)
        End Get
        Set(ByVal value As String)
           Me.SetValue(SiteDescriptionProperty, value)
        End Set
    End Property

    Public Shared SiteTemplateProperty As DependencyProperty = _
       DependencyProperty.Register("SiteTemplate", GetType(String), _
      GetType(CreateSiteAction))

    Public Property SiteTemplate() As String
        Get
           Return CType(Me.GetValue(SiteTemplateProperty), String)
        End Get
        Set(ByVal value As String)
           Me.SetValue(SiteTemplateProperty, value)
        End Set
    End Property

    Public Shared InheritPermissionsProperty As DependencyProperty = _
        DependencyProperty.Register("InheritPermissions", GetType(Boolean), _
        GetType(CreateSiteAction))

    Public Property InheritPermissions() As Boolean
        Get
           Return CType(Me.GetValue(InheritPermissionsProperty), Boolean)
        End Get
        Set(ByVal value As Boolean)
           Me.SetValue(InheritPermissionsProperty, value)
        End Set
    End Property

    Public Shared __ContextProperty As DependencyProperty = _
        DependencyProperty.Register("__Context", GetType(WorkflowContext), _
        GetType(CreateSiteAction))

    Public Property __Context() As WorkflowContext
        Get
           Return CType(Me.GetValue(__ContextProperty), WorkflowContext)
        End Get
        Set(ByVal value As WorkflowContext)
          Me.SetValue(__ContextProperty, value)
        End Set
    End Property

End Class
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
using System;
using System.ComponentModel;
using System.Web;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
namespace CustomActionsLibrary {
   public class CreateSiteAction : SequenceActivity {
      public static DependencyProperty SiteUrlProperty =
         DependencyProperty.Register("SiteUrl", typeof(string),
         typeof(CreateSiteAction));

      public string SiteUrl {
         get { return this.GetValue(SiteUrlProperty).ToString(); }
         set { this.SetValue(SiteUrlProperty, value); }
      }

      public static DependencyProperty SiteTitleProperty =
         DependencyProperty.Register("SiteTitle", typeof(string),
         typeof(CreateSiteAction));

      public string SiteTitle {
         get { return this.GetValue(SiteTitleProperty).ToString(); }
         set { this.SetValue(SiteTitleProperty, value); }
      }

      public static DependencyProperty SiteDescriptionProperty =
         DependencyProperty.Register("SiteDescription", typeof(string),
         typeof(CreateSiteAction));

      public string SiteDescription {
         get { return this.GetValue(SiteDescriptionProperty).ToString(); }
         set { this.SetValue(SiteDescriptionProperty, value); }
      }

      public static DependencyProperty SiteTemplateProperty =
         DependencyProperty.Register("SiteTemplate", typeof(string),
         typeof(CreateSiteAction));

      public string SiteTemplate {
         get { return this.GetValue(SiteTemplateProperty).ToString(); }
         set { this.SetValue(SiteTemplateProperty, value); }
      }

      public static DependencyProperty InheritPermissionsProperty =
         DependencyProperty.Register("InheritPermissions", typeof(bool),
         typeof(CreateSiteAction));

      public bool InheritPermissions {
         get { return (bool) this.GetValue(InheritPermissionsProperty); }
         set { this.SetValue(InheritPermissionsProperty, value); }
      }

      public static DependencyProperty __ContextProperty =
         DependencyProperty.Register("__Context", typeof(WorkflowContext),
         typeof(CreateSiteAction));

      public WorkflowContext __Context {
         get { return (WorkflowContext) this.GetValue(__ContextProperty); }
         set { this.SetValue(__ContextProperty, value); }
      }
   }

Adding Functionality to Your Action

The final step in creating a custom action is to add the functionality itself. Each custom action has an Execute function that is called by Windows SharePoint Services whenever that activity is reached in the workflow. To add the Execute method, put the following code before the End Class line:

Protected Overrides Function Execute(ByVal executionContext As _
        ActivityExecutionContext) As ActivityExecutionStatus

    Try

    Catch ex As Exception

    End Try

End Function
protected override ActivityExecutionStatus Execute(ActivityExecutionContext
   executionContext) {

   try {

   } catch (Exception ex) {

   }
}

Now you can place your functionality inside the Execute function.

Before you can create a new site, you need to get a reference to the current site. Add this line to the Try block to get the current site:

Dim site As SPWeb = Me.__Context.Web
SPWeb site = this.__Context.Web;

Later in the chapter, you will create a list to test this custom action. The list will include a choice column for choosing the site template. The following code will convert the user's choice into the site template ID required by the Webs.Add function:

Dim templateId As String 

Select Case Me.SiteTemplate
    Case "Team Site"
        templateId = "STS#0" 
    Case "Blank Site"
        templateId = "STS#1" 
    Case "Document Workspace"
        templateId = "STS#2"
    Case "Wiki Site"
        templateId = "WIKI#0"
    Case Else
        templateId = "BLOG#0"
End Select 
string templateId; 

switch (this.SiteTemplate) {
    case "Team Site":
        templateId = "STS#0"; 
        break;
    case "Blank Site":
        templateId = "STS#1";
        break;
    case "Document Workspace": 
        templateId = "STS#2"; 
        break;
    case "Wiki Site":
        templateId = "WIKI#0"; 
        break;
    default: 
        templateId = "BLOG#0";
        break; 
} 

Now you can use the properties you created to call the Webs.Add function to create a new subsite. Just add this line:

site.Webs.Add(Me.SiteUrl, Me.SiteTitle, Me.SiteDescription, 1033, _
    templateId, Not Me.InheritPermissions, False)
site.Webs.Add(this.SiteUrl, this.SiteTitle, this.SiteDescription, 1033,
    templateId, !this.InheritPermissions, false);

Then, you need to tell Windows SharePoint Services that the activity is finished by adding the following line to the end of the Execute function below the Try-Catch block:

Return ActivityExecutionStatus.Closed
return ActivityExecutionStatus.Closed;

Finally, you need to add code to the Try-Catch block to write to the workflow's history list in case an exception occurs. Additionally, you need to tell SharePoint that the activity failed by returning ActivityExecutionStatus.Faulting to the calling function. To do so, add the following code just after the Catch line:

Dim service As ISharePointService = CType(executionContext.GetService( _
    GetType(ISharePointService)), ISharePointService) 

If service Is Nothing Then
    Throw
End If

service.LogToHistoryList(Me.WorkflowInstanceId, _
    SPWorkflowHistoryEventType.WorkflowError, 0, TimeSpan.Zero, _
    "Error Occurred", ex.Message, String.Empty) 

Return ActivityExecutionStatus.Faulting
ISharePointService service =
(ISharePointService)executionContext.GetService(
    typeof(ISharePointService)); 

if (service==null) {
    throw;
}

service.LogToHistoryList(this.WorkflowInstanceId,
    SPWorkflowHistoryEventType.WorkflowError, 0, TimeSpan.Zero,
    "Error Occurred", ex.Message, string.Empty); 

return ActivityExecutionStatus.Faulting;

Your custom action is now complete. Select Build > Build Solution, and fix any compilation errors that appear. The only thing that remains is to create an ACTIONS file that tells SharePoint Designer what to display in the workflow wizard when the user configures your action. The next section describes this process.

Custom Actions and ACTIONS Files

An ACTIONS file is an XML file that is used by SharePoint Designer's workflow wizard to provide a user-friendly experience for configuring actions. Every action must be configured in an ACTIONS file before it can be used in SharePoint Designer. By default, there is only one ACTIONS file, located at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\1033\Workflow\WSS.ACTIONS, and it contains an entry for each of the built-in actions in Windows SharePoint Services. The following snippet and Figure 15-10 show the relationship between the workflow wizard screen and its entry in the ACTIONS file for the Log to History List action:

<Action Name="Log to History List"
   ClassName="Microsoft.SharePoint.WorkflowActions.LogToHistoryListActivity"
   Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0,
      Culture=neutral, PublicKeyToken=71e9bce111e9429c"
   AppliesTo="all"
   Category="Core Actions">
   <RuleDesigner Sentence="Log %1 to the workflow history list">
      <FieldBind
         Field="HistoryDescription"
         Text="this message"
         Id="1"
         DesignerType="TextArea"/>
   </RuleDesigner>
   <Parameters>
      <Parameter
         Name="HistoryDescription"
         Type="System.String, mscorlib"
         Direction="In" />
   </Parameters>
</Action>

Figure 15-10

Figure 15-10

In the code snippet, the Sentence attribute of the RuleDesigner element is displayed by SharePoint Designer to provide the user a UI to specify values of parameters. The sentence contains a placeholder, indicated by the percent sign and the number 1. This number matches the Id attribute of the FieldBind element whose Text attribute is displayed in place of the placeholder in the UI. The Field attribute of the FieldBind element matches the Name attribute of the Parameter element, which represents the HistoryDescription property of the LogToHistoryActivity class. The relationships between these elements will be explained further later in the chapter. The preceding code snippet may look complex, but it can be broken down into four simple parts:

  • The Action element.

  • The RuleDesigner element.

  • Zero or more FieldBind elements.

  • Zero or more Parameter elements.

Action Element

The Action element contains the location of the custom action, the name to display in the workflow wizard, and a category name that is used for filtering actions in the Workflow Actions dialog (see Figure 15-11). The Workflow Actions dialog is displayed when the user selects Actions > More Actions on the step configuration screen of the workflow wizard.

Figure 15-11

Figure 15-11

RuleDesigner Element

The RuleDesigner element contains the sentence to display to the user and the replacement variables that will be bound to the action's properties. Replacement variables begin with a percent sign and are numbered starting with 1. In the preceding snippet, the sentence Log %1 to the workflow history list contains one replacement variable.

FieldBind Element

The FieldBind element indicates the text to display in place of the replacement variable in the workflow wizard, the type of designer used to specify the value for the replacement variable, and the name of the Parameter element that will receive or store the value from the replacement variable. There will be one FieldBind element for every replacement variable.

When the workflow wizard displays the Log to History Action, it will replace %1 with the value of the Text attribute of the FieldBind element, whose Id attribute has the value of 1. In this case, %1 will be replaced with "this message" since the FieldBind element has its Text attribute set to "this message" (refer to Figure 15-10).

Parameter Element

The Parameter element indicates which properties of the custom action will be configurable in the workflow wizard. For each FieldBind element, a Parameter element must be created so that its Name attribute matches the value of the Field attribute in the FieldBind element.

Creating a Custom ACTIONS File

It may be tempting to edit the WSS.ACTIONS file directly, but don't. If you edit the ACTIONS file incorrectly, no built-in actions will work in SharePoint Designer. Rather than modifying this file directly, you can create a new ACTIONS file to contain the information for your custom actions. This file is not required to be a part of the Visual Studio Project, but you can use Visual Studio or another XML editor to create it. This example uses Visual Studio.

To create a custom ACTIONS file, open Visual Studio and choose File New File. The New File dialog opens (see Figure 15-12). From this screen, choose General from the Categories on the left and select the XML File template from the list on the right.

Figure 15-12

Figure 15-12

Click Open, and a blank XML file will be displayed in Visual Studio. Paste the following into the text editor window, replacing the existing text:

<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
 <Actions Sequential="then" Parallel="and">
   <Action Name="" ClassName="" Assembly="" AppliesTo="all" Category="">
     <RuleDesigner Sentence="">
     </RuleDesigner>
     <Parameters>
     </Parameters>
   </Action>
 </Actions>
</WorkflowInfo>

Now that you have a template to edit, you can start filling in values for your custom action. You will have to use the Strong Name Utility to get the PublicKeyToken portion of the value for the Assembly attribute. Follow these steps:

  1. If it is not already open, open the Output window by selecting View > Output.

  2. Build the custom action assembly by selecting Build > Build Solution.

  3. Note the location of the compiled DLL. For example:

    CustomActionsLibrary->C:\Users\username\Documents\Visual Studio 2008\Projects\ CustomActionsLibrary\bin\CustomActionsLibrary.dll

  4. Open the Visual Studio Command prompt by clicking Start > All Programs > Microsoft Visual Studio 2005 (or 2008) > Visual Studio Tools Microsoft Visual Studio 2005 (or 2008) Command prompt.

  5. Type sn.exe–T assemblypath, where assemblypath is the location of the DLL from step 3. If assemblypath contains spaces, you must enclose it in quotation marks.

  6. Note the public key token indicated. In the following example output, the key token is a4d44e6df3f4f726:

        Microsoft (R) .NET Framework Strong Name Utility Version 3.5.21022.8
        Copyright © Microsoft Corporation. All rights reserved.
        Public key token is a4d44e6df3f4f726

  7. Specify these values for the attributes in the Action element:

    Attribute Element

    Name

    Create Subsite

    ClassName

    CustomActionsLibrary.CreateSiteAction

    Assembly

    CustomActionsLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXX

    where XXXXX is the value obtained from step 6

    Category

    Custom Actions

  8. Specify the following as the value for the RuleDesigner element's Sentence attribute:

    Create subsite with these settings: %1, %2, %3, %4 and %5

  9. Inside the RuleDesigner element, paste the following code five times:

    <FieldBind Field="" Text="" Id="" DesignerType="TextArea"/>

  10. Specify 1 through 5 for the Id attribute of the new FieldBind elements.

  11. For each property of the custom action, set the Field attribute to the property's name, and supply a user-friendly name for the Text attribute of the new FieldBind element. Fill out one FieldBind element for each of these properties:

    • SiteUrl

    • SiteTitle

    • SiteDescription

    • SiteTemplate

    • InheritPermissions

    The FieldBind element for SiteUrl should look like this:

    <FieldBind Field="SiteUrl" Text="url" Id="1" DesignerType="TextArea"/

  12. Inside the Parameter element, paste the following code five times:

    <Parameter Name="" Type="System.String, mscorlib" Directions="In" />

  13. For each property of the custom action, set the Name attribute of the new Parameter element to the property's name. Fill out one Parameter element for each of these properties:

    • SiteUrl

    • SiteTitle

    • SiteDescription

    • SiteTemplate

    • InheritPermissions

    The Parameter element for SiteUrl should look like this:

    <Parameter Name="SiteUrl" Type="System.String, mscorlib" Directions="In" />

  14. Change the Type attribute of the Parameter element for the InheritPermissions property from "System.String, mscorlib" to "System.Boolean, mscorlib".

  15. Finally, to pass your activity the workflow's context, add this to the Parameter element:

    <Parameter Name="__Context"

        Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,

        Microsoft.SharePoint.WorkflowActions" Direction="In"/>

The resulting code should look as follows:

<?xml version="1.0" encoding="utf-8"? >
<Action Name="Create Subsite" ClassName=
"CustomActionsLibrary.CreateSiteAction" Assembly="CustomActionsLibrary,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=a4d44e6df3f4f726"
AppliesTo="all" Category="Custom Actions">
  <RuleDesigner Sentence="Create subsite with these settings: %1, %2,
%3, %4 and %5">
    <FieldBind Field="SiteUrl" Text="url" Id="1" DesignerType="TextArea"/>
    <FieldBind Field="SiteTitle" Text="title" Id="2"
DesignerType="TextArea"/>
    <FieldBind Field="SiteDescription" Text="description" Id="3"
DesignerType="TextArea"/>
    <FieldBind Field="SiteTemplate" Text="site template" Id="4"
DesignerType="TextArea"/>
  <FieldBind Field="InheritPermissions" Text="inherit permissions"
Id="5" DesignerType="TextArea"/>
  </RuleDesigner >
  <Parameters>
    <Parameter Name="SiteUrl" Type="System.String, mscorlib"
Direction="In" />
    <Parameter Name="SiteTitle" Type="System.String, mscorlib"
Direction="In" />
    <Parameter Name="SiteDescription" Type="System.String, mscorlib"
Direction="In" / >
    <Parameter Name="SiteTemplate" Type="System.String, mscorlib"
Direction="In" />
    <Parameter Name="InheritPermissions" Type="System.Boolean, mscorlib"
Direction="In" />
    <Parameter Name="__Context"
      Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,
      Microsoft.SharePoint.WorkflowActions" Direction="In"/>
  </Parameters>
</Action>

Save the ACTIONS file by selecting File > Save, entering custom.ACTIONS as the filename, selecting All Files from the Save as Type dropdown list, and clicking Save. Double-check the file extension of the saved ACTIONS file to ensure that it is ACTIONS and not XML.

Now that the ACTIONS file has been created, you can deploy your custom action to SharePoint and test it. The next section details those steps.

Deploying and Configuring Your Custom Action

There are three steps to deploying your custom action:

  1. Add the assembly to the Global Assembly Cache.

  2. Deploy the custom.ACTIONS file.

  3. Update the web.config file.

Adding the Assembly to the Global Assembly Cache

The Global Assembly Cache stores assemblies that are shared among server processes. Most SharePoint-related assemblies must be placed in the Global Assembly Cache before Windows SharePoint Services can use them. This restriction also applies to custom actions. However, placing assemblies in the Global Assembly Cache can be very dangerous, since these assemblies are accessible to the entire server farm and run without any restrictions on what code is allowed to run inside Windows SharePoint Services. You can reduce this risk by examining the code of all assemblies to be placed in the Global Assembly Cache, including those from third parties. It is better to err on the side of caution than to risk the health and stability of your farm.

To deploy the assembly to the Global Assembly Cache, open Windows Explorer and navigate to the C:\Windows\Assembly folder. Then, drag your DLL file into the folder, and it will automatically be added to the Global Assembly Cache. If you previously added your assembly to the Global Assembly Cache, you must restart IIS before your assembly will be recognized by Windows SharePoint Services.

To restart IIS, open the Run dialog by clicking Start and choosing Run. Type iisreset into the Run box, and click OK.

Deploying the ACTIONS file

To deploy the ACTIONS file, simply copy the custom.ACTIONS file to the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\1033\Workflow folder on the server. Windows SharePoint Services monitors this folder for files with the ACTIONS file extension and uses the information inside to determine which actions to display in SharePoint Designer and how to display them.

Updating the web.config File

Finally, you need to update the web.config file to allow SharePoint Designer to use your custom action.

Be aware that code running in the Global Assembly Cache runs without code restrictions and that updating the web.config file will enable this code to run inside Windows SharePoint Services. Be careful about which assemblies you add to the Global Assembly Cache and which of those assemblies are allowed to run inside Windows SharePoint Services.

Follow these steps to add the appropriate tags to the web.config file:

  1. Back up your web.config file. If you misconfigure the web.config file, your SharePoint site will not function until you correct the file. The default location of the web.config file is C:\inetpub\wwwroot\wss\VirtualDirectories\portnumber\web.config, where portnumber is the port number of your SharePoint web application.

  2. Open the web.config file.

  3. Locate the tag named <System.Workflow.ComponentModel.WorkflowCompiler>.

  4. Inside the tag named <authorizedTypes>, add a tag named <authorizedType> with the following attributes:

    Attribute Value

    Assembly

    Use the same value as the Assembly attribute of the Action element in your ACTIONS file.

    Namespace

    CustomActionsLibrary

    TypeName

    *

    Authorized

    True

    The resulting code should look like this:

    <!-- snipped code -->
    
    <System.Workflow.ComponentModel.WorkflowCompiler>
      < authorizedTypes >
    
    <!-- snipped code -->
    
        <authorizedType Assembly="CustomActionsLibrary, Version=1.0.0.0,
    Culture=neutral, PublicKeyToken=c7353bc82ef93acf"
    Namespace="CustomActionsLibrary" TypeName="*" Authorized="True"/>
    
    <!-- snipped code -->
    
      </authorizedTypes>
    </System.Workflow.ComponentModel.WorkflowCompiler>
    
    <!-- snipped code -->
    

    Note that the PublicKeyToken value for your assembly will be different from the one in this example.

  5. Save the web.config file.

Testing Your Custom Action

To test your custom action, you must create these items:

  • A list or document library.

  • A workflow in SharePoint Designer that uses your action and is attached to the list or document library.

  • A list item or document in the list or document library.

Creating a Custom List

Because it is easier to test using a custom list, create a custom list by opening your browser and navigating to your SharePoint site. Choose Create from the Site Actions menu. If you cannot see the Site Actions menu, log in to the site as a site owner. Once you click Create, the Create page is displayed (see Figure 15-13). Depending on your edition of Windows SharePoint Services and optionally installed features, you may have more or fewer options displayed on the Create page.

Figure 15-13

Figure 15-13

Choose Custom List under the Custom Lists column. On the New page, enter WorkflowTestList as the name of the list, keep the default values for the other controls, and then click Create. The All Items page is displayed.

To test your custom action, you will need to create a column for each property on the custom action. To create a new column, select Create Column from the Settings menu, and the Create Column page will be displayed (Figure 15-14). Create the five columns in the following table, using the values provided:

Column Name Type

SiteUrl

Single line of text.

SiteTitle

Single line of text.

SiteDescription

Multiple lines of text.

Select the Plain Text option.

SiteTemplate

Choice

Enter these values for the Type of each choice on a separate line text box:

Team Site

Blank Site

Document Workspace

Wiki Site

Blog

InheritPermissions

Yes/No (check box).

Figure 15-14

Figure 15-14

Now that you have created a test list, open SharePoint Designer to design a workflow that uses it.

Creating a Workflow in SharePoint Designer

In SharePoint Designer, open the site where you created the test list. Then, choose File > New > Workflow. On the initial Workflow Designer screen (Figure 15-15), select WorkflowTestList from the dropdown list and check the check boxes for the following:

  • Allow this workflow to be manually started from an item.

  • Automatically start this workflow when a new item is created.

Figure 15-15

Figure 15-15

Finally, click Next.

On the next Workflow Designer screen (see Figure 15-16), enter Step 1 for the Step Name. Then, click the Actions button, and select More Actions from the list that appears.

Figure 15-16

Figure 15-16

When the Workflow Actions dialog appears (Figure 15-17), choose Custom Actions from the Select a Category dropdown list, and your custom action will be displayed. With your custom action selected, click Add.

Figure 15-17

Figure 15-17

Now that your custom action has been added to the step, you can configure it by clicking each underlined word, clicking the fx button, and choosing the appropriate field from the list you created (Figure 15-18).

Figure 15-18

Figure 15-18

After your action is configured (Figure 15-19), click the Check Workflow button to check your workflow for errors. Finally, click Finish to save your workflow to Windows SharePoint Services.

Figure 15-19

Figure 15-19

The workflow created in this section is a very basic one used solely to test the custom action. Normally, the custom action would be used as a part of a comprehensive workflow that could include sending a notification to the site collection administrators and requesting approval of the new site before it is created.

Adding an Item to the Custom List

You configured your workflow to start when an item is inserted in the WorkflowTestList list, so navigate to the list and click the New menu item. On the New Item screen (see Figure 15-20), fill out the fields and click the OK button to return to the list.

Figure 15-20

Figure 15-20

Windows SharePoint Services automatically starts your workflow and adds a new column to the end of the list to display the workflow's status for your list item (see Figure 15-21).

Figure 15-21

Figure 15-21

Initially, your workflow will show a status of In Progress. If the workflow completes successfully, the Workflow's status changes to Completed; otherwise, it changes to Error Occurred. To verify that the site was created, click the Sites link from the Quick Launch bar at the left, and then click the name of the site on the All Site Content Page to navigate to the newly created site.

Troubleshooting Problems

During the process of creating, deploying, and using your action in SharePoint Designer, you can encounter various errors. This section explains how to resolve the most common errors.

  • Error: Your custom action does not appear in SharePoint Designer.

  • Ensure your custom ACTIONS file has been deployed to the C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\1033\Workflow\ folder and that you have restarted IIS.

  • Error: You have updated your custom action, but the workflow does not appear to use the updated version of the action.

  • Ensure that you have added your assembly to the Global Assembly Cache and that you have restarted IIS.

  • Error: Your workflow passes validation after clicking the Check Workflow button, but not after clicking the Finish button.

  • Ensure that you have added your assembly to the AuthorizedTypes section of the web.config file and that your assembly's strong name is correct and matching the strong name you configured in the ACTIONS file.

Summary

With SharePoint Designer custom actions, you can greatly extend the power of custom SharePoint Designer workflows. Additionally, these custom actions are reusable and, once deployed, can be used across all sites in your SharePoint farm. Business process automation is very popular these days, and by creating domain-specific custom actions, you will arm your SharePoint Designer users with the tools they need to maximize the workflow capabilities of Windows SharePoint Services.

Additional Resources

For more information, see the following resources: