Thursday, January 27, 2011

Workflow Action : Attach Document to ListItem

I have been really disapointed when realizing that I couldn't Attach a document to an item when I designed a workflow. So, to me this action was definitly missing. I needed it in a document library list with incoming email feature activated in order to be able to send emails to this library and those emails to be managed to be linked automatically to other items in other lists... For this custom activity, I have been assisted a member of my team: Yassine Hachime.


I won't explain here The principle on How to create Custom Activity on Sharepoint 2010, this topic has already been explained several times on the web :

http://www.chaholl.com/archive/2010/03/13/make-a-custom-activity-available-to-sharepoint-designer-2010.aspx or
http://social.msdn.microsoft.com/Forums/en/sharepointworkflow/thread/4e7a77f4-c29b-47b2-a1de-79470f0bb1c9


My plan is just to give you one more action to add to your SPD.
I will just provide you the code for the action file and the bin. I'll leave you the rest of the job.


Here is the action file :



<?xml version="1.0" encoding="utf-8"?>

<WorkflowInfo>
<Actions Sequential="then" Parallel="and">
<Action Name="Attach Doc to Item"
ClassName="AttachMailWFActivity.AttachDocToItem"
Assembly="AttachMailWFActivity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=46f60639ee1bfb57"
AppliesTo="all" Category="List Actions">
<RuleDesigner Sentence="Attach %1 to %2">
<FieldBind Text="this document" Field="LibId,DocItem" DesignerType="ChooseDoclibItem" Id="1" />
<FieldBind Text="this item" Field="ListId,ListItem" DesignerType="ChooseListItem" Id="2"/>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In" />
<Parameter Name="LibId" Type="System.String, mscorlib" Direction="In" InitialBinding="__list"/>
<Parameter Name="DocItem" Type="System.Int32, mscorlib" Direction="In" InitialBinding="__item" DesignerType="ListItem" Description="ID of the list item used by this action." />
<Parameter Name="ListId" Type="System.String, mscorlib" Direction="In"/>
<Parameter Name="ListItem" Type="System.Int32, mscorlib" Direction="In" DesignerType="ListItem" Description="ID of the list item used by this action." />

</Parameters>
</Action>

</Actions>

</WorkflowInfo>



And the code of the
AttachMailWFActivity.AttachDocToItem assembly is :



using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Linq;
using System.IO;
using Microsoft.SharePoint;
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 Microsoft.SharePoint.WorkflowActions;
using System.Workflow.Activities.Rules;

namespace TSP.AttachMailWFActivity
{
public partial class AttachDocToItem : Activity

{

public static DependencyProperty LibIdProperty = DependencyProperty.Register("LibId", typeof(string), typeof(AttachDocToItem));
public static DependencyProperty ListIdProperty = DependencyProperty.Register("ListId", typeof(string), typeof(AttachDocToItem));
public static DependencyProperty DocItemProperty = DependencyProperty.Register("DocItem", typeof(Int32), typeof(AttachDocToItem));
public static DependencyProperty ListItemProperty = DependencyProperty.Register("ListItem", typeof(Int32), typeof(AttachDocToItem));
public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(AttachDocToItem));

[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[Description("ID of the document library storing the doc that will be attached to the item")]
public string LibId
{
get { return ((string)(base.GetValue(AttachDocToItem.LibIdProperty))); }
set { base.SetValue(AttachDocToItem.LibIdProperty, value); }
}

[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[Description("ID of the list keeping the item to which the document will be attached to")]
public string ListId
{
get { return ((string)(base.GetValue(AttachDocToItem.ListIdProperty))); }
set { base.SetValue(AttachDocToItem.ListIdProperty, value); }
}

[Description("ID of the list item to which the document will be attached to")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Int32 ListItem
{
get { return ((Int32)(base.GetValue(AttachDocToItem.ListItemProperty))); }
set { base.SetValue(AttachDocToItem.ListItemProperty, value); }
}

[Description("ID of the document item to which the document will be attached to")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Int32 DocItem
{
get { return ((Int32)(base.GetValue(AttachDocToItem.DocItemProperty))); }
set { base.SetValue(AttachDocToItem.DocItemProperty, value); }
}

[Description("Context")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get { return ((WorkflowContext)(base.GetValue(__ContextProperty))); }
set { base.SetValue(__ContextProperty, value); }
}


protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
try
{

__Context.Web.AllowUnsafeUpdates = true;

Guid id = new Guid(ListId);
SPList list = __Context.Web.Lists[id];
Guid did = new Guid(LibId);
SPList docLib = __Context.Web.Lists[did];
SPListItem sourceItem = docLib.GetItemById(DocItem);
SPFile sourceDoc = sourceItem.File;
byte[] fichierbyte = sourceDoc.OpenBinary();

SPListItem destinationItem = list.GetItemById(ListItem);
destinationItem.Attachments.Add(sourceItem.Name, fichierbyte);
destinationItem.Update();
return ActivityExecutionStatus.Closed;

}
catch (Exception Ex)
{

return ActivityExecutionStatus.Faulting;
throw new Exception(Ex.Message + "&&&&" + Ex.StackTrace);

}
finally
{

}
return ActivityExecutionStatus.Closed;
}

public AttachDocToItem()
{
InitializeComponent();
}
}
}




Now you've got that.
Just :
- Create a sharepoint 2010 empty project
- Create a class and copy the code
- Create an action file and copy the code
- Sign your assembly
- Package your wsp
- Install the wsp on you WFE. Deploy it.
- Add the correct line in the AuthorizedType tag of your web.config.


The result :



(for the capture... it's because my SPD Is in french)


I hope it helps.
Don't hesitate to contact me whether you are in trouble using it !!!


3 comments:

  1. hi,

    at time of build,i am getting below error :

    The name 'InitializeComponent' does not exist in the current context.

    If i comment InitializeComponent() line, project builds properly, i can see action in sharepoint designer, but action cannot insert the action.

    Any suggestions on this....thanks in advance

    ReplyDelete
  2. Hi,
    Sorry for the late answer!! The InitializeComponent method is inherited from the "Activity" interface.

    Have you added the AuthorizedType tag in the Web.config?

    ReplyDelete
  3. Can you help my for implement the InitializeComponent pleas

    ReplyDelete