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 !!!


Item Created Workflow won't start with incoming email document uploaded

Hello,

It's been a while since my last post.
I was not in the mood ;) (or overloaded with work)... I have a few post to write... I hope it will be done in the few next days. I'm planning to explain the last SPD WF Action I have written (Attach Document to Item)....

But right now my post just describes a problem with the library with incoming email feature activated. When you send an email to the library, the automatic workflow launched at item created are simply not raised. It works for the items you add normally though.

Workaround?
Yeess. I found this post http://social.msdn.microsoft.com/Forums/en/sharepointworkflow/thread/810d0273-06f3-4815-b690-86d0fc880919

So, you just have to run this command line :

Stsadm -o setproperty -pn declarativeworkflowautostartonemailenabled -pv true


It worked for me !!!