Using
Microsoft Office SharePoint Designer 2007, you can design workflows without writing custom code. Microsoft has provided an article on
Create a Workflow which helps you to get started by explaining some key design considerations and providing a basic procedure, but the problem is there are only few limited actions available in SharePoint Designer 2007 to develop your custom workflows (Figure 1).
You can develop custom actions using Microsoft Visual Studio 2005 or higher and deploy in your SharePoint server. Then you will see your custom action when you click More Actions (Figure 1).
In this article I’m going to show you how to add custom actions to SharePoint site and use it in SharePoint Designer 2007 to develop SharePoint workflows. For your ease here I have included all the necessary screen shots and coding step by step.
Building Custom Workflow
First create new project in Microsoft Visual Studio, there select Project Type as workflow and template as Workflow Activity Library (Figure 2). I have given "
MyFirstWorkflow" for the name.
Then Add reference to the "
Microsoft.SharePoint.dll" and "
microsoft.sharepoint.WorkflowActions.dll". They can be found in your SharePoint server’s ISAPI folder (Figure 3 & 4).
You should copy those files from the same directory to a directory on your local computer if you are developing your project on a machine not having SharePoint or MOSS.
Now you can give a name to your Activity, Here I gave "
SampleActivity" as Figure 5 and drag-and-drop codeActivity from Toolbox (Figure 6).
Now replace your
Activity1.cs file using following code. If you are using different name spaces and class names, you have to change this code according to those names.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SharePoint;
using System.Diagnostics;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
namespace MyFirstWorkflow
{
public partial class SampleActivity : SequenceActivity
{
SPList _list;
private EventLog _log;
public SampleActivity()
{
InitializeComponent();
}
public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(SampleActivity));
[DescriptionAttribute("__Context")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(SampleActivity.__ContextProperty)));
}
set
{
base.SetValue(SampleActivity.__ContextProperty, value);
}
}
public static DependencyProperty ListIdProperty = DependencyProperty.Register("ListId", typeof(string), typeof(SampleActivity));
[DescriptionAttribute("ListId")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public string ListId
{
get
{
return ((string)(base.GetValue(SampleActivity.ListIdProperty)));
}
set
{
base.SetValue(SampleActivity.ListIdProperty, value);
}
}
public static DependencyProperty ListItemProperty = DependencyProperty.Register("ListItem", typeof(int), typeof(SampleActivity));
[DescriptionAttribute("ListItem")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
public int ListItem
{
get
{
return ((int)(base.GetValue(SampleActivity.ListItemProperty)));
}
set
{
base.SetValue(SampleActivity.ListItemProperty, value);
}
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
_log = new EventLog("Add Description");
_log.Source = "Share Point Workflows";
try
{
//Execute method as a elevated method
SPSecurity.CodeToRunElevated elevatedExecuteMethod = new SPSecurity.CodeToRunElevated(ExecuteMethod);
SPSecurity.RunWithElevatedPrivileges(elevatedExecuteMethod);
}
catch (Exception ex)
{
_log.WriteEntry("Error" + ex.Message.ToString(), EventLogEntryType.Error);
}
return ActivityExecutionStatus.Closed;
}
private void ExecuteMethod()
{
try
{
//retrieveing the Site object
SPSite _site = new SPSite(__Context.Site.Url);
//retrieveing the Web object
SPWeb _web = (SPWeb)(__Context.Web);
//retrieveing the list object
_list = _web.Lists[new Guid(this.ListId)];
//retrieveing the list item object
SPListItem _listItem = _list.GetItemById(this.ListItem);
_site.AllowUnsafeUpdates = true;
_web.AllowUnsafeUpdates = true;
_listItem["Description"] = "This is sample description";
_listItem.Update();
_list.Update();
_site.AllowUnsafeUpdates = false;
_web.AllowUnsafeUpdates = false;
}
catch (Exception ex)
{
}
}
}
}
In the above code you can see some methods to get the current Site, Web, List and List Item. You don’t need to change those things. In the above simple example I have fill the "
Description" column using my custom action written inside the "
ExecuteMethod()" method. You can write any custom event which cannot be done using given actions in SharePoint designer.
Signing your Project
To deploy this custom workflow to GAC and the Microsoft Office SharePoint Server, we should assign a Strong Name key and sign the control.
To do this, right click the "
MyFirstWorkflow" node in Solution Explorer and select Properties. Then select the Signing tab from the choices on the left. Check the "
Sign the assembly" box and select from the "Choose a strong name key file" drop down list (Figure 7).
There give a key file name and click ok. Now you can build your project and ready to deploy.
Deploying to Server
To deploy your custom workflow action to the SharePoint server, follow these simple steps.
Drag and drop the compiled DLL (You can find it in your project folder's bin folder) into the Global Assembly Cache. The Global Assembly Cache is a special folder located at %WINDIR%\assembly where %WINDIR% is the full path to your Windows folder (e.g. C:\Windows or C:\Winnt).
Get the publicKeyToken property of our assembly. You can find it by right click on the file and select properties in "assembly" folder (Figure 8).
Update the "web.config file" by inserting fillowing line between
<authorizedTypes> and
</
authorizedTypes> tags. (You can find your site’s web.config file in SharePoint server’s "C:\Inetpub\wwwroot\wss\VirtualDirectories\
<your_site_port
>" directory).
<authorizedtype Assembly="MyFirstWorkflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8f2766b10f337a33" Namespace="MyFirstWorkflow" TypeName="*" Authorized="True" />
Update the WSS.ACTIONS file by adding following line between
<Actions Sequential="
then"
Parallel="
and"
> and
</
Actions> tags. (You can find WSS.ACTIONS file in SharePoint server’s "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\Workflow" directory).
<action Name="Add Description"
ClassName="MyFirstWorkflow.SampleActivity"
Assembly="MyFirstWorkflow, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=8f2766b10f337a33"
AppliesTo="all"
Category="Extras">
<ruledesigner Sentence="Add Description to %1">
<fieldbind Field="ListId,ListItem" Text="this list" Id="1" DesignerType="ChooseListItem" />
</RuleDesigner>
<parameters>
<parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" Direction="In" />
<parameter Name="ListId" Type="System.String, mscorlib" Direction="In" />
<parameter Name="ListItem" Type="System.Int32, mscorlib" Direction="In" />
</Parameters>
</Action>
Now deploying is completed, reset IIS and you can use your newly added custom action in the SharePoint Designer by going to "More Actions…" (Figure 1) when creating a workflow.
If you want more details on how to use custom actions when creating workflows follow my article "
Use custom actions in SharePoint Designer".
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using Microsoft.SharePoint;
namespace ProjectView{
public class Chart{
protected string _BGColor = "FFFFFF";
protected string _ChartColor = "0099ff,ff0000";
protected int _ChartHeight = 400;
protected LegendPositions _ChartLegend;
protected string _ChartTitle = "Burn Down Chart";
protected ChartTypes _ChartType;
protected int _ChartWidth = 720;
protected string _DataField = "Planned Effort,Actual Effort";
protected string _LabelField = "Title";
protected string _ListName = "";
protected string _SiteName = "http://merdev-moss:5050/";
protected string _ViewName = "";
public enum ChartTypes{
Line,
VerticalBar,
Pie,
Pie3D,
HorizontalBar
}
public enum LegendPositions{
horizontal_top,
vertical_left,
horizontal_bottom,
vertical_top,
vertical_bottom,
vertical_right
}
public string createChartString(string listName){
_ListName = listName;
string str = "";
string str2 = "";
SPSite site = new SPSite(this._SiteName);
SPWeb web = site.OpenWeb();
try{
if (!string.IsNullOrEmpty(this._ListName)){
SPList list = null;
SPListItemCollection items = null;
try{
string[] strArray3;
int num5;
list = web.Lists[this._ListName];
if (!string.IsNullOrEmpty(this._ViewName)){
SPView view = list.Views[this._ViewName];
items = list.GetItems(view);
}
else{
items = list.Items;
}
string str3 = "";
float num2 = 0f;
int num3 = 0;
bool flag = true;
char[] separator = new char[] { ',', ';' };
string[] strArray = this._DataField.Split(separator);
string[] strArray2 = new string[strArray.Length];
foreach (string str4 in strArray){
str2 = str2 + "<br>Series=" + str4;
if (!list.Fields.ContainsField(str4)){
str = str + "<p>Data List column not specified or not found:" + str4 + "</p>";
flag = false;
}
}
if (!list.Fields.ContainsField(this._LabelField)){
str = str + "<p>Label List column not specified or not found:" + this._LabelField + "</p>";
flag = false;
}
if (!flag){
goto Label_0868;
}
foreach (SPListItem item in items){
num3++;
try{
int index = 0;
foreach (string str5 in strArray){
string[] strArray7;
IntPtr ptr2;
float num = Convert.ToSingle(item[str5]);
if (num > num2){
num2 = num;
}
if (!string.IsNullOrEmpty(strArray2[index])){
string[] strArray6;
IntPtr ptr;
(strArray6 = strArray2)[(int)(ptr = (IntPtr)index)] = strArray6[(int)ptr] + ",";
}
string str6 = num.ToString().Replace(",", ".");
(strArray7 = strArray2)[(int)(ptr2 = (IntPtr)index)] = strArray7[(int)ptr2] + str6;
index++;
}
}
catch{
str = str + "<p>Data column error:" + this._DataField + "</p>";
}
try{
if (str3 != ""){
str3 = str3 + "|";
}
if ((this._LabelField != "LinkTitle") && (item.Fields[this._LabelField].FieldValueType == typeof(DateTime))){
str3 = str3 + ((DateTime)item[this._LabelField]).ToString("MMM yyyy");
}
else if (item[this._LabelField] != null){
str3 = str3 + item[this._LabelField].ToString();
}
continue;
}
catch{
str = str + "<p>Label column error:" + this._LabelField + "</p>";
continue;
}
}
string str7 = "";
string str8 = "";
string str9 = "";
string str10 = "";
string str11 = "";
string str12 = "";
string str13 = "";
if (!string.IsNullOrEmpty(str3)){
str3 = str3.Replace("&", "%26");
}
string str14 = num2.ToString().Replace(",", ".");
switch (this._ChartType){
case ChartTypes.Pie:
str7 = "p";
str9 = str3;
str12 = "0," + str14;
goto Label_051D;
case ChartTypes.Pie3D:
str7 = "p3";
str9 = str3;
str12 = "0," + str14;
goto Label_051D;
case ChartTypes.Line:
str7 = "lc";
str8 = "x,y";
str10 = "0:|" + str3;
str11 = "1,0," + str14;
str12 = "0," + str14;
goto Label_051D;
case ChartTypes.VerticalBar:
str7 = "bvg";
str8 = "x,y";
str10 = "0:|" + str3;
str11 = "1,0," + str14;
str12 = "0," + str14;
str13 = "a";
goto Label_051D;
case ChartTypes.HorizontalBar:
str7 = "bhg";
str8 = "x,y";
strArray3 = str3.Split(new char[] { '|' });
str3 = "";
num5 = strArray3.Length - 1;
goto Label_04DE;
default:
str7 = "p";
goto Label_051D;
}
Label_04AE:
if (str3 != ""){
str3 = str3 + "|";
}
str3 = str3 + strArray3[num5];
num5--;
Label_04DE:
if (num5 >= 0){
goto Label_04AE;
}
str10 = "1:|" + str3;
str11 = "0,0," + str14;
str12 = "0," + str14;
str13 = "a";
Label_051D:
if (str == ""){
object obj2 = str;
str = string.Concat(new object[] { obj2, "?chs=", this._ChartWidth, "x", this._ChartHeight });
if (!string.IsNullOrEmpty(this._ChartTitle)){
string str15 = this._ChartTitle.Replace(" ", "+");
str = str + "&chtt=" + str15.Replace("&", "%26");
}
str = (str + "&cht=" + str7) + "&chts=000000,17" + "&chd=t:";
int num6 = 0;
string[] strArray8 = strArray;
for (int i = 0; i < strArray8.Length; i++){
string text1 = strArray8[i];
if (num6 > 0){
str = str + "|";
}
str = str + strArray2[num6];
num6++;
}
str = str + "&chf=bg,s," + this._BGColor;
if (this._ChartColor != ""){
str = str + "&chco=" + this._ChartColor;
}
if (str9 != ""){
str = str + "&chl=" + str9;
}
if (str8 != ""){
str = str + "&chxt=" + str8;
}
if (str10 != ""){
str = str + "&chxl=" + str10;
}
if (str11 != ""){
str = str + "&chxr=" + str11;
}
if (str12 != ""){
str = str + "&chds=" + str12;
}
if (str13 != ""){
str = str + "&chbh=" + str13;
}
str = str + "&chg=" + ((100.0 / ((double)num3))).ToString("F") + ",20";
if (strArray.Length > 1){
str = str + "&chdl=";
int num7 = 0;
foreach (string str16 in strArray){
if (num7 > 0){
str = str + "|";
}
str = str + str16;
num7++;
}
str = str + "&chdlp=";
switch (this._ChartLegend){
case LegendPositions.horizontal_top:
str = str + "t";
break;
case LegendPositions.horizontal_bottom:
str = str + "b";
break;
case LegendPositions.vertical_top:
str = str + "tv";
break;
case LegendPositions.vertical_bottom:
str = str + "bv";
break;
case LegendPositions.vertical_left:
str = str + "l";
break;
case LegendPositions.vertical_right:
str = str + "r";
break;
}
}
str2 = str2 + "<br>" + str.Replace("&", "<br>&");
str = "http://chart.apis.google.com/chart" + str;
}
}
catch{
str = this._ListName + ": Sharepoint List not found!";
}
}
else{
str = "No Sharepoint List defined!";
}
}
catch (Exception){
str = "<br>Site not found:" + this._SiteName;
}
Label_0868:
return str;
}
}
}