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.
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-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
.jpg)
Figure 15-4
.jpg)
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 (http://msdn.microsoft.com/en-us/library/wd40t7ad.aspx).
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
.jpg)
Figure 15-6
.jpg)
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
.jpg)
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
.jpg)
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
.jpg)
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.
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
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
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.
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
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:
-
If it is not already open, open the Output window by selecting View > Output.
-
Build the custom action assembly by selecting Build > Build Solution.
-
Note the location of the compiled DLL. For example:
CustomActionsLibrary->C:\Users\username\Documents\Visual Studio 2008\Projects\ CustomActionsLibrary\bin\CustomActionsLibrary.dll
-
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.
-
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.
-
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
-
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 |
-
Specify the following as the value for the RuleDesigner element's Sentence attribute:
Create subsite with these settings: %1, %2, %3, %4 and %5
-
Inside the RuleDesigner element, paste the following code five times:
<FieldBind Field="" Text="" Id="" DesignerType="TextArea"/>
-
Specify 1 through 5 for the Id attribute of the new FieldBind elements.
-
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"/
-
Inside the Parameter element, paste the following code five times:
<Parameter Name="" Type="System.String, mscorlib" Directions="In" />
-
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" />
-
Change the Type attribute of the Parameter element for the InheritPermissions property from "System.String, mscorlib" to "System.Boolean, mscorlib".
-
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.