You are on page 1of 48

Creating Custom Workflow Activities for SharePoint Server 2007

Using Visual Studio 2008


Summary: Learn how to create a custom activity for Microsoft Office SharePoint Server 2007 to send an e-mail
message that has an attachment.

Applies to: Microsoft Office SharePoint Server 2007, Microsoft Visual Studio 2008

Mike Rand, 3Sharp

June 2008

In Microsoft Visual Studio 2008, you can create custom activities that can be consumed in workflows.
Creating custom activities lets you encapsulate business logic that applies to different scenarios and
that can be used in different workflows. This Microsoft Office Visual How To demonstrates how to create
a custom activity to send an e-mail message with an attachment.

First, you must create your custom activity. Then, you can add that to a workflow project and use it to
send an e-mail message with an attachment.
Creating a Custom Activity Project
First, create a Visual Studio 2008 Workflow Activity Library project.
To create a Visual Studio 2008 Workflow Activity Library project
1. Open Visual Studio 2008.

2. On the File menu, point to New, and then click Project.

3. Under the Workflow Project Type, select the Workflow Activity Library project template.

4. In the Name box, type SendMailWithAttachmentActivity, and then click OK.

5. Rename Activity1 to SendMailWithAttachmentActivity.


Coding Custom Activity
Now that you have a Custom Activity project, you must add code to send an e-mail message with an
attachment.
To add code to send an e-mail message with an attachment

1. The first thing you must do is change your class declaration to inherit directly from Activity
instead of SequenceActivity.
Open your activity in code view, and change SequenceActivity to Activity. Your class
definition should look like the following.
C#

Copy Code
public partial class SendMailWithAttachmentActivity : Activity
{ …
2. Now you must create some Dependency Properties. Add the following fields inside your class
definition.
C#
Copy Code
public static DependencyProperty ToProperty
= DependencyProperty.Register(
"To", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty FromProperty


= DependencyProperty.Register(
"From", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty CCProperty


= DependencyProperty.Register(
"CC", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty SubjectProperty


= DependencyProperty.Register(
"Subject", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty BodyProperty


= DependencyProperty.Register(
"Body", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty AttachmentProperty


= DependencyProperty.Register(
"Attachment", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty SmtpServerProperty


= DependencyProperty.Register(
"SmtpServer", typeof(string),
typeof(SendMailWithAttachmentsTest));

public static DependencyProperty InvokeEvent


= DependencyProperty.Register(
"Invoke", typeof(EventHandler),
typeof(SendMailWithAttachmentsTest));
Notice that the last field, InvokeEvent, is of type EventHandler. This enables you to add
custom code to your activity when it is used in a workflow.
3. Now, you must add properties for your fields. Add the following code under the fields you just
added.
C#

Copy Code
[DesignerSerializationVisibility(DesignerSerializationVisibility.
Visible)]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[Description("Enter any e-mail recipients, separated by
semicolons")]
public string To
{
get { return
((string)(base.GetValue(SendMailWithAttachmentsTest.ToProperty))); }
set { base.SetValue(SendMailWithAttachmentsTest.ToProperty,
value); }
}

[DesignerSerializationVisibility(DesignerSerializationVisibili
ty.Visible)]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[Description("Enter the e-mail sender")]
public string From
{
get { return
((string)(base.GetValue(SendMailWithAttachmentsTest.FromProperty)));
}
set {
base.SetValue(SendMailWithAttachmentsTest.FromProperty, value); }
}

[DesignerSerializationVisibility(DesignerSerializationVisibilit
y.Visible)]
[ValidationOption(ValidationOption.Optional)]
[Browsable(true)]
[Description("Enter any carbon copy recipients, separated by
semicolons")]
public string CC
{
get { return
((string)(base.GetValue(SendMailWithAttachmentsTest.CCProperty))); }
set { base.SetValue(SendMailWithAttachmentsTest.CCProperty,
value); }
}

[DesignerSerializationVisibility(DesignerSerializationVisibility
.Visible)]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[Description("Enter the e-mail subject")]
public string Subject
{
get { return
((string)(base.GetValue(SendMailWithAttachmentsTest.SubjectProperty)
)); }
set {
base.SetValue(SendMailWithAttachmentsTest.SubjectProperty, value); }
}

[DesignerSerializationVisibility(DesignerSerializationVisibilit
y.Visible)]
[ValidationOption(ValidationOption.Optional)]
[Browsable(true)]
[Description("Enter the body text of the e-mail")]
public string Body
{
get { return
((string)(base.GetValue(SendMailWithAttachmentsTest.BodyProperty)));
}
set {
base.SetValue(SendMailWithAttachmentsTest.BodyProperty, value); }
}
[DesignerSerializationVisibility(DesignerSerializationVisibilit
y.Visible)]
[ValidationOption(ValidationOption.Optional)]
[Browsable(true)]
[Description("Enter an attachment file path")]
public string Attachment
{
get { return ((string)(base.GetValue
(SendMailWithAttachmentsTest.AttachmentProperty))); }
set {
base.SetValue(SendMailWithAttachmentsTest.AttachmentProperty,
value); }
}

[DesignerSerializationVisibility(DesignerSerializationVisibilit
y.Visible)]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[Description("Enter the Smtp Server for the email")]
[DisplayName("Smtp Server")]
public string SmtpServer
{
get { return ((string)(base.GetValue
(SendMailWithAttachmentsTest.SmtpServerProperty))); }
set {
base.SetValue(SendMailWithAttachmentsTest.SmtpServerProperty,
value); }
}
4. Now, you must add a property for your InvokeEvent field. This EventHandler property lets
you add code in a workflow to interact with this activity programmatically. Add this code under
the Properties you just added.
C#

Copy Code
[DescriptionAttribute("Invoke")]
[CategoryAttribute("Invoke Category")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisib
ility.Visible)]
public event EventHandler Invoke
{
add
{
base.AddHandler(SendMailWithAttachmentsTest.InvokeEvent,
value);
}
remove
{
base.RemoveHandler(SendMailWithAttachmentsTest.InvokeEvent,
value);
}
}
5. Add a using statement at the top of the class. This enables you to access the ASP.NET 2.0 Mail
namespace for sending your e-mail message. Add the following code.
C#

Copy Code
using System.Net.Mail;
6. The last thing you must do here is override the Execute method. Add the following method
inside your class definition.
C#

Copy Code
Protected override ActivityExecutionStatus
Execute(ActivityExecutionContext executionContext)
{
// Raise Invoke Event to execute custom code in the
workflow.
this.RaiseEvent(SendMailWithAttachmentsTest.InvokeEvent,
this, EventArgs.Empty);

// Set reference to Smtp Server.


SmtpClient smtp = new SmtpClient(SmtpServer);

// Create mail message.


MailMessage msg = new MailMessage();
msg.To.Add(To);
msg.From = new MailAddress(From);
if (!String.IsNullOrEmpty(CC))
{
msg.CC.Add(CC);
}
if (!String.IsNullOrEmpty(Subject))
{
msg.Subject = Subject;
}
if (!String.IsNullOrEmpty(Body))
{
msg.Body = Body;
}
if (!String.IsNullOrEmpty(Attachment))
{
msg.Attachments.Add(new Attachment(Attachment));
}

// Send the e-mail.


smtp.Send(msg);

// Indicate that the activity has completed.


return ActivityExecutionStatus.Closed;
}
Installing a Custom Activity
Before you can use your custom activity, you must install it in the global assembly cache (GAC). For an
assembly to be installed in the GAC, you must give it a strong name.
To install the custom activity to the GAC

1. First, give your assembly a strong name. In Solution Explorer, right-click the project, and
then select Properties.

2. Select the Signing tab.

3. Select the Sign the assembly check box.

4. In the Choose a strong name key file list, select New.

5. In the Key file name text box, type Key.

6. In this demonstration, we are not concerned with security, so clear the Protect my key file
with a password check box. Click OK.
Now your assembly is strong named, and all you have to do is install it to the global assembly
cache.
7. Save and build the project.
8. Open a Visual Studio 2008 command prompt.
9. Navigate to the Activity solution directory, and run the following command:
gacutil /if SendMailWithAttachmentActivity.dll
Now, that you have successfully installed your activity, you can use it from a workflow project.
Creating a Workflow Project
Next, you create a Sequential Workflow project to use your custom activity.
To create a Visual Studio 2008 Sequential Workflow project
1. Start Visual Studio 2008.

2. On the File menu, point to New, and then click Project.

3. Under the Office Project Type, select the SharePoint 2007 Sequential Workflow project
template.

4. In the Name box, type SendEmailWithAttachmentWF. Click OK.

5. Type a valid SharePoint Web URL. The example uses http://moss.litware.com. Click
Next.

6. Select a library or list to associate with your workflow, and click Next.

7. Leave the default options for how a workflow can be started, Manually by users and When
an Item is created. Click Finish.
Adding a Custom Activity to the Toolbox
Now that you have created your Sequential Workflow project, you must add your custom activity to
your Toolbox before you can use it.
To add the custom activity to the Visual Studio 2008 Toolbox

1. On the Tools menu, select Toolbox items.

2. Select the Activities tab.

3. Click Browse, and browse to the SendMailWithAttachmentActivity.dll in your Activity solution


directory. Click Open.

4. Click OK.
5. Your activity is now available in the Toolbox.
Figure 1. Toolbox with new activity available

Note:
If you have a toolbox category selected, the activity is added to that category by default. You
can drag the activity to another category if you want.
Adding a Custom Activity to the Workflow
Now, you must add your custom activity to your simple workflow.
To add the custom activity to the workflow

1. From the Toolbox, drag the SendMailWithAttachmentActivity to be under the


onWorkflowActivated1 activity.

2. Open the Properties window, and rename it to sendMailWithAttachmentActivity.


3. You can set the activity properties from within the Properties window. Instead, you will do that
in code. Select the Event view in the Properties window.

4. You should see a single event, Invoke. Double-click this property.


Figure 2. Properties window

This takes you to the code-behind class, and you will see that a method stub has been auto-
generated for you.
5. Add the following code inside this method.
C#

Copy Code
SendMailWithAttachmentActivity.To = "nicolec@litwareinc.com";
SendMailWithAttachmentActivity.From = "willisj@litwareinc.com";
SendMailWithAttachmentActivity.CC = "willisj@litwareinc.com";
SendMailWithAttachmentActivity.Subject = "Email with Attachment
Testing.";
SendMailWithAttachmentActivity.Body = "This email should have an
attachment.";
SendMailWithAttachmentActivity.Attachment
= @"C:\Documents and Settings\Administrator\Desktop\code
snippets.txt";

SendMailWithAttachmentActivity.SmtpServer =
workflowProperties.Site.WebApplication.OutboundMailServiceInst
ance.Server.Address;
6.

Note:
The purpose of this demonstration is to show you how to create and consume a custom activity. In a
real-world scenario, you would never hard-code these values. You would retrieve them
programmatically, or possibly through an Initiation form or a Task form.
Running the Workflow Project from Visual Studio 2008
Press F5 to run your workflow. When you activate this workflow on a document in a document library,
an e-mail message is generated and sent with the attachment you specified.
Figure 3. E-mail message with attachment
1. Introducing Office SharePoint Designer 2007 Workflows
Office SharePoint Designer 2007, a replacement for FrontPage, is used as an aid in the
rapid design and deployment of workflows. Office SharePoint Designer 2007 is specifically
designed to help create and customize web sites and workflows built with SharePoint
Products and Technologies. The Development process in SharePoint Designer is based on
declarative rules-based, code-free workflow editor.

SharePoint Designer (SPD) gives list administrators, business administrators, and designers
the ability to create workflows without writing any code. SPD exposes many of the
workflow activities that ship with SharePoint as "actions" that users can string together
into a sequential process. These actions are customized via sentences that describe the
action and bind data to action parameters.

However, businesses will oftentimes need more custom functionality than the actions
provided with SharePoint, requiring them to write custom code that can run on the
server. This guide describes how to write a custom code as an activity using the Visual
Studio Extensions for Windows Workflow Foundation and expose it as an action in SPD.

"Activity" refers to the basic building block of functionality in a Windows Workflow


Foundation (WF) workflow.
In Office SharePoint Designer 2007, however, each activity appears as an action,
represented by a sentence that contains variables that the user can configure using
drop-down menus and lookup dialog boxes. Users can also select conditions, which are
configurable conditional clauses that direct the flow of the workflow.

As the user is selecting and configuring conditions and actions in the workflow interface,
Office SharePoint Designer 2007 generates the two files that actually represent the
workflow class:

The workflow markup file, which contains markup language that describes the activities
included in the workflow.

The workflow rules file contains the business logic of the workflow in declarative rules
form, rather than as code.
2. Create a Microsoft Office InfoPath 2007 Form
1. Create a new Microsoft Office InfoPath 2007 Form.
2. Add two Text boxes and rename the text boxes as "Contributors" and "Readers".
3. Save the Form.
4. Publish the form with the two columns to the Microsoft Office SharePoint Server
2007 site for which the item level security needs to be implemented.

3. Create a custom activity in Visual Studio with promoted


properties
1. Navigate to Start | All Programs | Microsoft Visual Studio 2005 | Microsoft Visual
Studio 2005.
2. Select the File | New | Project menu command.
3. Visual Studio displays the New Project dialog box.
4. Select the Visual C# | Workflow project type.
5. Select the Workflow Activity Library template.
6. Enter the path in the Location field.
7. Enter the name in the Name field.
8. Click OK.
9. Visual Studio creates the activity library project, including a single activity,
Activity1, and displays a design view of Activity1.cs.

10. Rename Activity1.cs to ItemLevelSecurityActivity.cs by right clicking on Activity1.cs


and choosing rename.
11. Select the View | Toolbox menu command to open the Toolbox window.
12. From the Toolbox window, drag a Code activity and drop it onto the "Drop
Activities Here" work area of ItemLevelSecurityActivity's design view.
13. The Activity Designer displays a code icon in the work area.

14. Rename CodeActivity1 to ItemLevelSecuritycodeActivity by right clicking on


CodeActivity1 and choosing Properties and change the Name Property.
15. In the Solution Explorer, right click on References to bring up the context menu
16. Select Add Reference.
17. From the Add Reference dialog box, add references to the Windows SharePoint
Services and the Windows SharePoint Services WorkflowActions.
18. In the Solution Explorer, right click on ItemLevelSecurityActivity.cs to bring up
the context menu.
19. Select View Code.
20. Add the following namespaces in the code:

Collapse Copy Code


using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
Note: The activity wouldn't be useful if it didn't pass information on, so to make it
available to the rest of the workflow, we will need to use property promotion. Property
promotion consists of getters and setters to allow the property to be bound to variables
outside of the activity. For this example, we need to derive the Workflow Context object
to retrieve the current SharePoint Site, the List Id for which the activity is attached and
the current List Item to provide Item Level Security.
21. Place the cursor on the closing bracket of the ItemLevelSecurityActivity
constructor
and press Enter Twice.
22. Right Click on the last line you just inserted.
23. Select Insert Snippet.
24. Select Workflow.

25. Select DependencyProperty - Property.

This code snippet generates the getter/setter code and allows you to specify the
property name, type and description for the property.
26. Fill in these fields in the boilerplate code with the corresponding values. Use Tab to
move between the fields and press enter when finished with the fields.
a. For deriving the Workflow Context object to retrieve the current SharePoint
Site:
i. Name: __Context.
ii. Type: WorkflowContext.
iii. Description: Context.
b. For retrieving the List Id for which the activity is attached:
i. Name: ListId
ii. Type: string
iii. Description: List Id
c. For retrieving the current List Item to provide Item Level Security:
i. Name: ListItem
ii. Type: int
iii. Description: ListItem
Note: Remove the Category Property and add the following code in its place for all the
three dependency properties.

[ValidationOption(ValidationOption.Required)]
The resulting output should resemble the following:
With this customized, the property is exposed to the rest of the workflow. The
workflow can now bind variables to these properties for both input and output.
27. In the Solution Explorer, right click on References to bring up the context menu
28. Select Add Reference.
29. From the Add Reference dialog box, add references to the
System.DirectoryServices.
30. In the Solution Explorer, right click on ItemLevelSecurityActivity.cs to bring up
the context menu.
31. Select View Code.
32. Add the following namespaces in the code:

Collapse Copy Code


using System.DirectoryServices;
33. In the Solution Explorer, right click on ItemLevelSecurityActivity.cs to bring up
the context menu.
34. Select View Designer
35. Double click the ItemLevelSecuritycodeActivity activity to view the
ItemLevelSecuritycodeActivity _ExecuteCode method and write the custom
code behind your activity for implementing the Item Level Security as displayed.
36. In the Solution Explorer, Right Click on Properties and choose Open from the
context menu.
37. Navigate to the Signing tab.
38. Check the Option Sign the Assembly.
39. Select the New option from the Drop down below
40. In the Create Strong Name Key dialog box, enter the Key File Name
41. Uncheck the Protect my key file with a password option.
42. Select the Build | Build Solution menu command.

4. Deploy the dll to the GAC and add it to the web.config file
The next step is to compile your activity and deploy it to the GAC
(C:\WINDOWS\assembly). The assembly must be signed. You can drag and drop this into
the GAC directory or use the .net gacutil tool. Keep a note of the assembly, class,
version, and public key token, as they'll be used later on in the .ACTIONS file.
1. Navigate to the root port directory of the SharePoint Site for which the activity is
created located in "C:\inetpub\wwwroot\wss\virtualdirectories" and search for
"web.config" file. These xml files contain lists of authorized types that can be used
for that server port.

2. Open the file and search for "WorkflowActions", which should take you to an entry
that looks like this:

Collapse Copy Code


<authorizedType
Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c"
Namespace="Microsoft.SharePoint.WorkflowActions"
TypeName="*"
Authorized="True" />
Underneath this tag, add a corresponding tag for your dll. For example:

Collapse Copy Code

<authorizedType
Assembly="ItemLevelSecurityActivity, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=7211fc0c4fbd8603"
Namespace="ItemLevelSecurityActivityLibrary"
TypeName="*"
Authorized="True" />
The activity dll is now ready to be run on the server
5. Edit the WSS.ACTIONS file to describe the activity
parameters
The final step of preparing an activity for SPD is to change the WSS.ACTIONS file. This
xml file describes the types of the promoted properties of the activity and how to map
them into a rules sentence.
1. Navigate to "C:\Program Files\Common Files\Microsoft Shared\web server
extensions\12\TEMPLATE\1033\Workflow" folder and open the WSS.ACTIONS file.

2. Navigate to the end of the file.

3. Append the following code before the </Actions> tag.

Collapse Copy Code


<Action
Name="Item Level Security activity in sharepoint designer"
ClassName="ItemLevelSecurityActivityLibrary.ItemLevelSecurityActivity"
Assembly="ItemLevelSecurityActivity, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=7211fc0c4fbd8603"
AppliesTo="all"
Category="Extras">
<RuleDesigner Sentence="Apply security to %1 Document Library">
<FieldBind
Field="ListId,ListItem"
Text="this"
Id="1"
DesignerType="ChooseDoclibItem" />
</RuleDesigner>
<Parameters>
<Parameter
Name="__Context"
Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,
Microsoft.SharePoint.WorkflowActions"
Direction="In"/>
<Parameter
Name="ListId"
Type="System.String, mscorlib"
Direction="In" />
<Parameter
Name="ListItem"
Type="System.Int32, mscorlib"
Direction="In" />
</Parameters>
</Action>
Note:
The "Action" tag defines basic information about the action with the following attributes:
� Name � name of the action (this is what SPD displays in its actions list)

� ClassName � the name of the activity, i.e. Namespace.WorkflowClassName

� Assembly � details of the GAC'ed assembly

� AppliesTo � describes what types of lists this activity can be used for, either "list",
"doclib", or "all"
� Category � category that SPD will display in the categories for available actions

RuleDesigner

Collapse Copy Code

<RuleDesigner Sentence="Apply security to %1 Document Library ">


<FieldBind
Field="ListId,ListItem"
Text="this list"
Id="1"
DesignerType="ChooseDoclibItem" />
</RuleDesigner>
The next section is the "RuleDesigner" section. This describes a rule sentence for the
action as well as how to bind them to the activity properties. These are the sentences
SPD displays in its rules wizard.

The variable parameters in the sentence, e.g. %1, etc., are exposed as customization
links. When displayed, they will be replaced with the "FieldBind" tags below, where %1 will
be replaced with the FieldBind with Id=1,etc.
The "FieldBind" tag describes each variable parameter. The "Field" attribute corresponds
to the parameter, or activity property, as it is described in the Parameter tag in the
markup. "DesignerType" describes what type of interface to show when the user clicks on
the link. For example, if you want to show a select user dialog, you would use
"SinglePerson" for the designer type. The "Text" attribute is how the field is displayed in
the sentence.
The end result for the above markup would look something like this in SPD:

Parameters

Collapse Copy Code

<Parameters>
<Parameter
Name="__Context"
Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,
Microsoft.SharePoint.WorkflowActions"
Direction="In"/>
<Parameter
Name="ListId"
Type="System.String, mscorlib"
Direction="In" />
<Parameter
Name="ListItem"
Type="System.Int32, mscorlib"
Direction="In" />
</Parameters>
Finally, the "Parameters" tag tells the RuleDesigner how to map the fields to the promoted
properties on the workflow activity.
Each "Parameter" describes the name of the property (which should match the
corresponding FieldBind above), the system type of the property, and direction ("In"
means that the activity gets that parameter, and "Out" means the activity sets that
parameter).
Once you have this file laid out, save your file.

6. Boot SPD
1. Do an iisreset.
2. Navigate to Start | All Programs | Microsoft Office | Microsoft Office
SharePoint Designer 2007.
3. Select the File | New | Workflow menu command.
4. In the Workflow Designer dialog box, enter a name for the Workflow and select
the SharePoint Document library list for which the activity should be attached from
the Dropdown.

5. Check the Automatically start this workflow when a new item is created option.
6. Check the Automatically start this workflow whenever an item is changed option.
7. Click Next.
8. Click Actions
9. Select the More Actions
10. In the Workflow Actions dialog box, click the dropdown for the Select a Category
and choose Extras.
11. In the Choose an Action box, select the Item Level Security activity in sharepoint
designer will appear and click Add.
12. Note that Apply security to this Document Library is displayed.
13. Click on this option in the Actions
14. From the Choose List Item dialog box, Select the Current Item option and click OK

15. Now the Actions is changed by displaying the current item's name.

16. Click Finish.


17. The Workflow can now be checked by creating a new InfoPath form, by
entering
the Authors and Readers fields.
18. Save the InfoPath form to the SharePoint site.
19. Select Manage Permissions from the Context Menu of the item saved and
check for
the permissions.
7. Conclusion
Though this topic covers implementing Item Level Security by creating custom activity,
the task can also be achieved by creating Item_Saved and Item_Updated Event
Handlers for Lists. Though there are multiple ways of implementing the task, creating
custom activity is more effective by means of reusability and lesser coding in VS 2005.

Add custom workflow activities to SharePoint Designer (Part 1)

Without a doubts one of the best things of MOSS 2007 is the new SharePoint Designer, and within him,
the workflow designer, that allows us speedy and simple form to design workflows.

As I have commented in other occasions, if we wish to make more complex workflows, or


state machines, we need to use Visual Studio.

SharePoint Designer, allows us to make sequential workflows with predetermined activities.


Nevertheless, the possibility to extend the basic activities exists and we can extend the functionality
of ours workflows without to write them completely in Visual Studio.

Out-the-box, SharePoint Designer includes 22 actions.


Those actions are defined in the file WSS.ACTIONS, that resides in the directory “C:\Program
Files\Common files\Microsoft Shared \web server extensions\12\1033\TEMPLAT\Workflow”.

In this file, the conditions and the actions that appear within SharePoint Designer are defined.
<Condition Name="Creado en un intervalo de fechas determinado"
FunctionName="CreatedInRange"
ClassName="Microsoft.SharePoint.WorkflowActions.Helper"
Assembly="Microsoft.SharePoint.WorkflowActions,
Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c"
AppliesTo="list"
UsesCurrentItem="true">
<RuleDesigner Sentence="creado entre %1 y %2">
<FieldBind Id="1" Field="_1_" Text="fecha" DesignerType="Date"/>
<FieldBind Id="2" Field="_2_" Text="fecha" DesignerType="Date"/>
</RuleDesigner>
<Parameters>
<Parameter Name="_1_" Type="System.DateTime, mscorlib" Direction="In"/>
<Parameter Name="_2_" Type="System.DateTime, mscorlib" Direction="In"/>
</Parameters>
</Condition>

Herein, we can see the assembled dll that handles the condition, to which elements he is applied,
and if it is possible to use the present item in the workflow.
Within the condition we have the rule for the designer, with the text that is going to appear; each rule
uses a series of fields tags FieldBind, that connects with the necessary parameters in the assembled
file.

Each tag FieldBind has an identifier that corresponds with the position of the field within sentence
ID=1, and that will be %1 in the sentence attribute as well.
Also we can see attributes like Text, that will be the text by defect, and the DesignerType, that is the
type of field.

After that, we have the section of Parameters with the different parameters that will go to the
assembled file. The Name attribute has to correspond with the Field attribute of tag FieldBind, and
the direction of the parameter needs to be In for input and Out for output.

<Action Name="Establecer estado de aprobación del contenido"


ClassName="Microsoft.SharePoint.WorkflowActions.SetModerationStatusActivity"
Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0,
Culture=neutral, PublicKeyToken=71e9bce111e9429c"
AppliesTo="list"
ListModeration="true"
Category="Acciones principales"
UsesCurrentItem="true">
<RuleDesigner Sentence="Establecer estado de aprobación del contenido en %1
con %2">
<FieldBind Field="ModerationStatus" DesignerType="Dropdown" Id="1"
Text="este estado">
<Option Name="Aprobado" Value="Approved"/>
<Option Name="Rechazado" Value="Denied"/>
<Option Name="Pendiente" Value="Pending"/>
</FieldBind>
<FieldBind Field="Comments" Text="comentarios" Id="2"
DesignerType="TextArea" />
</RuleDesigner>
<Parameters>
<Parameter Name="ModerationStatus" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="Comments" Type="System.String, mscorlib"
Direction="Optional" />
<Parameter Name="__Context"
Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext,
Microsoft.SharePoint.WorkflowActions" />
<Parameter Name="__ListId" Type="System.String, mscorlib" Direction="In"
/>
<Parameter Name="__ListItem" Type="System.Int32, mscorlib" Direction="In"
/>
</Parameters>
</Action>

Now, we are going to see how the actions look in the same file; they are very similar to the
conditions.

The first part includes the assembled one that will take care to handle the action.
The second one, are the rules for the designer. In this case the fields that has been used are a
combobox (DesignerType= " Dropdown ") and a line of text (DesignerType= " TextArea ")
The third part of the parameters, besides to use both fields defined in the rule of the designer, uses
three additional parameters that are:

__Context: that it is the context of the WorkFlow.


__ListId: you go of the list with which we are working
__ListItem: the present item

With these three fields (that they are pre-established, so that we can use them whenever we want) we
have as much control on the list as on the job stream that is being carried out.

At the moment A file XSD for this type of archives does not exist
Add custom workflow activities to SharePoint Designer (Part 2)

In order to add a new activity to our SharePoint Designer, first we need to begin a WorkFlow project
in Visual Studio 2005, concretely, a Workflow Activity Library (Workflow activity library).

From this point we would create our activity; for this example I am going to create an activity
that will send a text to the event viewer.
1: public partial class LogEventViewer : Activity

2: {

3:

4: public static DependencyProperty TextLogProperty =


5: DependencyProperty.Register("TextLog", typeof(string), typeof(LogEventViewer));

6:

7: public LogEventViewer()

8: {

9: InitializeComponent();

10: }

11:

12: /// <summary>

13: /// Valor que figurará en el visor de eventos

14: /// </summary>

15: /// <value>Texto</value>

16: [Description("Texto que saldrá en el visor de eventos")]

17: [Category("User")]

18: [Browsable(true)]

19: [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

20: public string TextLog

21: {

22: get { return (string) GetValue(TextLogProperty); }

23: set { SetValue(TextLogProperty, value); }

24: }

25:

26: /// <summary>

27: /// Ejecución de la actividad

28: /// </summary>

29: /// <param name="provider">Contexto de ejecución de la actividad</param>

30: /// <returns></returns>

31: protected override ActivityExecutionStatus Execute(ActivityExecutionContext provider)

32: {

33: EventLog eventLog = new EventLog("Workflow");

34:
35: eventLog.Source = "SharePoint Workflow";

36:

37: try

38: {

39: eventLog.WriteEntry(TextLog, EventLogEntryType.Information);

40: }

41: finally

42: {

43: eventLog.Dispose();

44: }

45:

46: return ActivityExecutionStatus.Closed;

47: }

48: }

First, we declare a dependent property of the workflow called TextLogProperty, which we will use
to pass the text that we wish to show in the event viewer.
The internal property of the activity will be TextLog; this internal property obtains and establishes the
value from the dependent property of the workflow.

Then we define the Execute method, that will be the method in charge to visualize the message in
the event viewer.

A good practice is that the activities include a Validador, that I have omitted in the example but that is
highly recommended although not obligatory, or if we want to use the activity inside of the visual
studio workflow designer.
Once we have made the compilation, we can test it first (another good practice) creating an
application to host the workflow and checking the activity.

Finally, after we are sure that our activity works correctly, we must install it in the GAC in our
SharePoint server. And we need to modify the Web.config to include our assembly in the following
section:
<System.Workflow.ComponentModel.WorkflowCompiler>
<authorizedTypes>
......
<authorizedType Assembly="IdeSeg.SharePoint.Workflow.Activities,
Version=1.0.0.0, Culture=neutral,
PublicKeyToken=3bba710be857fdc1"
Namespace="IdeSeg.SharePoint.Workflow.Activities"
TypeName="*"
Authorized="True" />
</authorizedTypes>
</System.Workflow.ComponentModel.WorkflowCompiler>

Now we need to modify the file WSS.ACTIONS that we saw in the first article in order to add our new
action
<Action Name="Log en visor de eventos"
ClassName="IdeSeg.SharePoint.Workflow.Activities.LogEventViewer"
Assembly="IdeSeg.SharePoint.Workflow.Activities, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=3bba710be857fdc1"
AppliesTo="all"
Category="Personalizadas">
<RuleDesigner Sentence="Logear el evento siguiente %1">
<FieldBind Field="TextLog" Text="este mensaje" Id="1"
DesignerType="TextArea"/>
</RuleDesigner>
<Parameters>
<Parameter Name="TextLog" Type="System.String, mscorlib" Direction="In"
/>
</Parameters>
</Action>
What we have done in the first place is authorize our assembly so that he is now an integral part of
the WorkFlow engine of SharePoint, and secondly, adding it to the WSS.ACTIONS file, we have
indicated to the SharePoint Designer that we have added a new activity.

When we use SharePoint Designer to publish a site, at the moment that we created a new workflow or
we published an existing one, he communicates with SharePoint and recovers the File WSS.ACTIONS
to configurate the assistant of the WorkFlow.

In this way the new actions will be part of the file XOML that the SharePoint Designer will create.
Finally the result within the SharePoint Designer will be as follow:
Creating your own Custom Workflow Activities as Components using Windows
Workflow Foundation (Framework 3.0) and deployment on SharePoint 2007.

Required Installation Setup


• Visual Studio .Net 2005
• Framework 3.0
• Windows Workflow Foundation Extension for Workflow Activity Template
• SharePoint Portal 2007,SharePoint Services and Microsoft Office SharePoint Designer
The steps to add a simple activity to the SharePoint Designer interface include:
1. Create a custom activity assembly.
2. Sign and deploy the activity assembly to the GAC.
3. Configure SharePoint to recognize the custom activity.
4. Create a .ACTIONS file to be used by SharePoint Designer.
Step 1: Create a custom activity assembly
• Open Visual Studio 2005 solution.
• Select New Project and Workflow Activity Library Template which is a project for
creating a library of activities which can later be reused as building blocks in
workflows.
Code Block
using System;<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office"
/>
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
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 System.Collections.Generic;
using System.Text;
namespace ActivityLibrary1
{
public partial class ADComponent : System.Workflow.ComponentModel.Activity
{
private string cn;
public ADComponent()
{
InitializeComponent();
}
public static DependencyProperty DirectoryUriProperty = DependencyProperty.
Register"DirectoryUri",typeofSystem.String),typeo(ActivityLibrary1.ADComponent));
[DescriptionAttribute("Please specify the URI of the directory. Either an AD Server
or an XML File.")]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
[BrowsableAttribute(true)]
[Category("Directory")]
public string DirectoryUri
{
get
{
return ((String)(base.GetValue(ADComponent.DirectoryUriProperty)));
}
set
{
base.SetValue(ADComponent.DirectoryUriProperty, value);
}
}
public static DependencyProperty QueryProperty = DependencyProperty.Register
("Query", typeof(System.String), typeof(ActivityLibrary1.ADComponent));
[DescriptionAttribute("Please specify the Username of the user to retrieve.")]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Optional)]
[BrowsableAttribute(true)]
[Category("Query")]
public string Query
{
get
{
return ((String)(base.GetValue(ADComponent.QueryProperty)));
}
set
{
base.SetValue(ADComponent.QueryProperty, value);
}
}
public static DependencyProperty RetrievedUserDataProperty = Dependency
Property.Register ("RetrievedUserData", typeof(System.String), typeof
(ActivityLibrary1.ADComponent));
[DescriptionAttribute("Please specify the Username of the user to retrieve.")]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Optional)]
[BrowsableAttribute(true)]
[Category("RetrievedUserData")]
public string RetrievedUserData
{
get
{
return ((String)(base.GetValue(ADComponent.RetrievedUserDataProperty)));
}
set
{
base.SetValue(ADComponent.RetrievedUserDataProperty, value);
}
}
public string CN
{
get
{
return cn;
}
set
{
cn = value;
}
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext
context)
{
string a = "ActiveDirectory";
switch (a)
{
case "ActiveDirectory":
ADHelper adh = new ADHelper(this.DirectoryUri);
CN = adh.FetchUser(this.Query);
break;
}
//Set the results property
this.RetrievedUserData = CN;
return ActivityExecutionStatus.Closed;
}
}
}

Code Bock For Supportive ADHelper Class

//-----------------------------------------------------------------------------//
//--------------Active Directory helper Class----(Rifaqat:2nd April 06)-------//
//-------Description: ...............................................--------//
//-------------------------------------------------------------------------//
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.DirectoryServices;
using System.Collections;
namespace ActivityLibrary1
{
internal class ADHelper
{
private string ldapPath;
DirectorySearcher search;
internal ADHelper(string ldapPath)
{
this.ldapPath = ldapPath;
search = new DirectorySearcher(new DirectoryEntry(ldapPath));
}
internal string GetUsersManager(string loginName)
{
SearchResult result;
search.Filter = String.Format("(SAMAccountName={0})", loginName);
search.PropertiesToLoad.Add("manager");
result = search.FindOne();
if (result == null)
{
return "";
}
else
{
string userPath = result.Properties["manager"][0].ToString();
System.DirectoryServices.DirectoryEntry de = new DirectoryEntry("LDAP://" +
userPath);
return de.Properties["sAMAccountName"].Value.ToString();
}
}

internal string FetchUser(string Designation)


{
string _User = "";
try
{
SearchResult result;
search.Filter = String.Format("(Title={0})", Designation);
search.PropertiesToLoad.Add("cn");
result = search.FindOne();
if (result != null)
{
_User = result.Properties["cn"][0].ToString();
}
}
catch (Exception ex)
{
}
return _User;
}
internal string FetchUseronDesg(string loginName)
{
string _User = "";
try
{
SearchResult result;
search.Filter = String.Format("(SAMAccountName={0})", loginName);
search.PropertiesToLoad.Add("title");
search.PropertiesToLoad.Add("cn");
result = search.FindOne();
if (result != null)
{
_User = result.Properties["title"][0].ToString();
}
}
catch (Exception ex)
{
string s = ex.Message;
}
return _User;
}
}
}

Your Solution Explorer will be like this:

In this code the DirectoryUri and Query are passing as inputproperty and are used to specify
the text that will be displayed in the Display Name of User as Output. We use a dependency
property to enable the workflow to bind data to it. As with all workflow activities, the
Execute method performs the action.
Step 2: Sign and deploy the activity assembly to the GAC

Step 3: Configure SharePoint to recognize the custom activity

After you build the custom activity assembly, sign it and copy it to the GAC. You then have
to tell SharePoint to trust the assembly. This is similar to configuring a web part as a safe
control, but instead of adding an entry to the <SafeControls> section, you add an entry to
the <System.Workflow.ComponentModel.WorkflowCompiler> section. Edit the web.config
file for your SharePoint web application and add an <authorizedType> element as in the
following example:
• Goto in your Site using this URL C:\Inetpub\wwwroot\wss\VirtualDirectories\10161
• Open your Config File.
• You need to add your assembly in specific site port config file:

<authorizedType Assembly="rifaqat.Components, Version=1.0.0.0, Culture=neutral,


PublicKeyToken=e8f8c868b9896b0a" Namespace="rifaqat.Components"
TypeName="*" Authorized="True" />
<authorizedType Assembly="ActivityLibrary1, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=8fd4b4c3a190a3c6" Namespace="ActivityLibrary1" TypeName="*"
Authorized="True" />
Step 4: Create a .ACTIONS file to be used by SharePoint Designer
• The final step is to create the .ACTIONS file that describes the activity to SharePoint
Designer. Since this is an XML file, you can create it using Visual Studio or any XML
editor.
• This file describes the public properties exposed by the activity and tells SharePoint
Designer how to map those properties into rules that can be displayed to the user.
The following code shows a custom .ACTIONS file for the custom Active Directory
activity.
• Goto this path for .Actions File

C:\Program Files\Common Files\Microsoft Shared\web server


extensions\12\TEMPLATE\1033\Workflow
<?xml version="1.0" encoding="utf-8" ?>
<WorkflowInfo>
<Actions Sequential="then" Parallel="and">
<Action Name="Fetch User Onbehalf of Designation"
ClassName="Microsoft.SharePoint.WorkflowActions.CollectDataTask"
Assembly="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c"
AppliesTo="all"
CreatesTask="true"
Category="Task Actions">
<RuleDesigner Sentence="Collect %1 from %2 (Output to %3)">
<FieldBind Field="Title,ContentTypeId" DesignerType="Survey" Text="data" Id="1"/>
<FieldBind Field="AssignedTo" DesignerType="SinglePerson" Text="this user"Id="2"/>
<FieldBind Field="TaskId" DesignerType="ParameterNames" Text="collect" Id="3"/>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.
WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In" />
<Parameter Name="ContentTypeId" Type="System.String, mscorlib"Direction="In" />
<Parameter Name="AssignedTo" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="Title" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="TaskId" Type="System.Int32, mscorlib" Direction="Out" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>
• The Actions tag tells SharePoint Designer what to display for each action in the set.
Within that, the Action tag describes the individual action. The Name attribute is
what gets displayed in the designer. The ClassName and Assembly attributes are
used in the generated XAML for the workflow. The interesting part is the way the
RuleDesigner and Parameter tags work. The RuleDesigner tag lets you set up a
sentence that gets displayed in the designer as you build up the workflow. The
Sentence attribute allows you to bind to the activity properties and then substitute
their values when the activity is executed.
• You can declare as many actions as you want in the file. A good rule of thumb is to
use a separate .ACTIONS file for each logical group of custom activities you wish to
deploy. Once you've created your .ACTIONS file and copied it to the server, you can
refresh the site in SharePoint Designer and your custom activity will appear in the
workflow designer as shown below.
Site Definition in SharePoint
By Dhananjay Kumar January 08, 2009

This article will explain about Site Definition, why we do need custom site
definition ?and how to create and deploy a custom site definition.

Site Definition
• Site Definitions are the foundations on which all sites and user templates are built.
• Site Definition is collection ox XML and .aspx file.
• Site Definitions are predefined components needs to be included when a site was
created in SharePoint server.
• Site Definition contains information of Web Part , Lists, Features and navigation bars
to be included in the site.
• Customizing Portal Sites and other SharePoint sites using Site Definition is most
appropriate for third party developer and SharePoint Administrator.
• Site Definitions requires access to a file system of the Web Server.
• Server administrator must be also involved in deployment of Site Definitions.
• Custom Site Definitions are Version and Upgrade independent.
• Subsequent upgrades to SharePoint products and technologies may overwrite
existing Site Definitions. Using Custom Site definition exclude site from potential
upgrade issues.
• To avoid Unghosting , Custom Site Definition is to be create.
• There are two options to create it
1. Create New Site Definition File : Either Start from Scratch and create files one
by one or Copy an existing Site Definition and modify it.
2. Copy a Site Definition and change it in Visual Studio: We can make copy an
existing site definition and modify it in visual studio to create new site
definition.
• The configuration of this site definitions are defined in XML file which is available in

C:\Program Files\Common Files\Microsoft Shared\web server


extensions\12\TEMPLATE\1033\XML folder.
• In the file system these definitions are available in C:\Program Files\Common
Files\Microsoft Shared\web server extensions\12\TEMPLATE\Site Templates.
• Each of these site definitions consists a XML folder and a Home page (default.aspx).

Steps to create custom Site Definition


1. Log on as an administrator
2. Browse to C:\Program Files\Common Files\Microsoft Shared\web server
extensions\12\TEMPLATE\SiteTemplates.
Copy STS folder-> Paste it at same place -> Rename it as TIGER.
3. Open the folder Tiger
4. Search for default.aspx
5. Open default.aspx in notepad.
6. Modify code. Here we need to add code for site definition.
Default.aspx
<%@ Page language="C#" MasterPageFile="~masterurl/default.master"

Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=12
.0.0.0,Culture=neutral,P

ublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="SharePoint"

Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral,

PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities"

Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint,
Version=12.0.0.0, Culture=neutral,

PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint"


%> <%@ Register

Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages"
Assembly="Microsoft.SharePoint,

Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<asp:Content ID="Content1" ContentPlaceHolderId="PlaceHolderPageTitle"


runat="server">

<SharePoint:EncodedLiteral runat="server"
text="<%$Resources:wss,multipages_homelink_text%>"

EncodeMethod="HtmlEncode"/> - <SharePoint:ProjectProperty Property="Title"


runat="server"/>

</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderId="PlaceHolderPageImage"


runat="server"><IMG

SRC="/_layouts/images/blank.gif" width=1 height=1 alt=""></asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea"


runat="server">
<label class="ms-hidden"><SharePoint:ProjectProperty Property="Title"

runat="server"/></label>

</asp:Content>

<asp:Content ID="Content4" ContentPlaceHolderId="PlaceHolderTitleBreadcrumb"


runat="server"/>

<asp:Content ID="Content5" ContentPlaceHolderId="PlaceHolderTitleAreaClass"


runat="server">

<style type="text/css">

TD.ms-titleareaframe, .ms-pagetitleareaframe {

height: 10px;

Div.ms-titleareaframe {

height: 100%;

.ms-pagetitleareaframe table {

background: none;

height: 10px;

</style>

</asp:Content>

<asp:Content ID="Content6" ContentPlaceHolderId="PlaceHolderAdditionalPageHead"


runat="server">

<META Name="CollaborationServer" Content="SharePoint Team Web Site">

<script type="text/javascript">

var navBarHelpOverrideKey = "wssmain";

</script>

</asp:Content>

<asp:Content ID="Content7" ContentPlaceHolderId="PlaceHolderSearchArea"


runat="server">

<SharePoint:DelegateControl runat="server"

ControlId="SmallSearchInputBox" />
</asp:Content>

<asp:Content ID="Content8" ContentPlaceHolderId="PlaceHolderLeftActions"


runat="server">

</asp:Content>

<asp:Content ID="Content9" ContentPlaceHolderId="PlaceHolderPageDescription"


runat="server"/>

<asp:Content ID="Content10" ContentPlaceHolderId="PlaceHolderBodyAreaClass"


runat="server">

<style type="text/css">

.ms-bodyareaframe {

padding: 0px;

</style>

</asp:Content>

<asp:Content ID="Content11" ContentPlaceHolderId="PlaceHolderMain" runat="server">

<table cellspacing="0" border="0" width="100%">

<tr>

<td class="ms-pagebreadcrumb">

<asp:SiteMapPath SiteMapProvider="SPContentMapProvider"
id="ContentMap" SkipLinkText=""

NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/>

</td>

</tr>

<tr>

<td class="ms-webpartpagedescription"><SharePoint:ProjectProperty
Property="Description"

runat="server"/></td>

</tr>

<tr>

<td>

<table width="100%" cellpadding=0 cellspacing=0 style="padding: 5px


10px 10px 10px;">

<tr>

<td valign="top" width="100%" colspan="3">

<WebPartPages:WebPartZone runat="server" FrameType="TitleBarOnly" ID="Top"


Title="loc:Top" />

</td>

</tr>

<tr>

<td valign="top" width="70%">

<WebPartPages:WebPartZone runat="server"
FrameType="TitleBarOnly" ID="Left"

Title="loc:Left" />

&nbsp;

</td>

<td>&nbsp;</td>

<td valign="top" width="30%">

<WebPartPages:WebPartZone runat="server"
FrameType="TitleBarOnly" ID="Right"

Title="loc:Right" />

&nbsp;

</td>

<td>&nbsp;</td>

</tr>

</table>

</td>

</tr>

<tr>

<td valign=”top” width=”100%” colspan=”3”>

<WebPartPages:WebPartZone runat="server" FrameType="TitleBarOnly"


ID="Bottom"

Title="loc:Bottom" />
</td>

</tr>

</table>

</asp:Content>
7. Register new site definition to SharePoint.
8. Open the folder
C:\Program Files\Common Files\Microsoft Shared\web server
extensions\12\TEMPLATE\1033\XML
9. Copy the file WEBTEMP.XML and save it as WEBTEMPTIGER.xml. here make a note that ,
we need to append name of folder which we created in step 2 ( in this case it is tiger) to
WEBTEMP. So here name would be WEBTEMPTIGER.xml
10. Open WEBTEMPTIGER.XML in notepad and replace it with following content.
<?xml version="1.0" encoding="utf-8"?>

<!-- _lcid="1033" _version="12.0.4518" _dal="1" -->

<!-- _LocalBinding -->

<Templates xmlns:ows="Microsoft SharePoint">

<Template Name="TIGER" ID="10009">

<Configuration ID="1" Title="Tiger Definition" Hidden="FALSE"


ImageUrl="/_layouts/images/Eye.GIF" Description="Tiger Site." DisplayCategory="Custom"
AllowGlobalFeatureAssociations="False"> </Configuration>

</Template>

</Templates>

Make sure here ID should be greater than 10000. It must not collide with id of any other
Site Definition.
11. Restart IIS. Go to Start->Run->IISRESET
12. Now you would be able to see this Site Definition inside Custom category.

You might also like