Disclaimer
I am NOT a formally trained programmer. I am attending the computer science school of Google. Please try not to laugh too hard at my code.
I am not responsible for any medical issue that arises from hilarity that results from looking at my code.
Introduction
This article provides code that can be used to create a custom workflow action that will copy an item from a document library on one site to a library on another site. When adding the action in SPD, the user will be able to provide the destination web site URL and destination library.
This action WILL NOT copy the document metadata to the destination site. It will only copy the item. This action WILL NOT copy the author's name to the destination site. It will only copy the item. This action WILL NOT copy the item version number to the destination site. It will only copy the item.
Background
After scouring the Internet for an already done code example, I was forced to figure it out on my own. As I am not a programmer, it has taken a while to put this together. Hopefully it will help someone like me that needs a good starter to get them going.
This code has been tested on my development environment and on a small scale installation of SharePoint 2010 Enterprise.
Using the code
I will not go into how to create a custom workflow action project. There are quite a few good articles out there on how to do this. I have put some references below.
Once you have setup your project, replace code below where needed. For newbies like me, that means:
- Ensure that the usings listed here are in your project
- Replace the code inside the curly braces after your namespace declaration
- Be sure to register your project within the windows assembly and replace the public token listed here with your public token.
The first section of code is the clsCopyItem.cs item in your activity library. Within this code, the variables for the workflow context as well
as the destination web and destination library are defined. I have also make use of the CreateHistoryEvent class in various places to add an entry
in the workflow history for tracking and troubleshooting. Feel free to add or eliminate this as needed.
I also have not placed extensive error handling or disposal methods in this code as I am still trying to figure that out. I will update the code as I learn this or if someone is really helpful and provides some suggestions.
using System;
using System.ComponentModel;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Workflow;
using Microsoft.SharePoint.WorkflowActions;
namespace ActivityLibrary
{
public partial class clsCopyItem : SequenceActivity
{
public clsCopyItem()
{
InitializeComponent();
}
#region Dependency Properties
public static DependencyProperty SiteUrlProperty = DependencyProperty.Register("SiteUrl",
typeof(string), typeof(clsCopyItem), new PropertyMetadata(""));
[DescriptionAttribute("Url of site where item is to be moved to")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Optional)]
public string SiteUrl
{
get { return ((string)(base.GetValue(clsCopyItem.SiteUrlProperty))); }
set { base.SetValue(clsCopyItem.SiteUrlProperty, value); }
}
public static DependencyProperty LibraryNameProperty =
DependencyProperty.Register("LibraryName", typeof(string),
typeof(clsCopyItem), new PropertyMetadata(""));
[DescriptionAttribute("Name of library to move item to")]
[BrowsableAttribute(true)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[ValidationOption(ValidationOption.Optional)]
public string LibraryName
{
get { return ((string)(base.GetValue(clsCopyItem.LibraryNameProperty))); }
set { base.SetValue(clsCopyItem.LibraryNameProperty, value); }
}
public static DependencyProperty __ContextProperty =
System.Workflow.ComponentModel.DependencyProperty.Register(
"__Context", typeof(WorkflowContext), typeof(clsCopyItem));
[Description("Context")]
[Category("Context")]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get { return ((WorkflowContext)(base.GetValue(clsCopyItem.__ContextProperty))); }
set { base.SetValue(clsCopyItem.__ContextProperty, value); }
}
public static DependencyProperty __ListIdProperty =
System.Workflow.ComponentModel.DependencyProperty.Register("__ListId", typeof(string), typeof(clsCopyItem));
[Description("List Id")]
[Category("List Id")]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string __ListId
{
get { return ((string)(base.GetValue(clsCopyItem.__ListIdProperty))); }
set { base.SetValue(clsCopyItem.__ListIdProperty, value); }
}
public static DependencyProperty __ListItemProperty =
System.Workflow.ComponentModel.DependencyProperty.Register(
"__ListItem", typeof(int), typeof(clsCopyItem));
[Description("List Item")]
[Category("List Item")]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public int __ListItem
{
get { return ((int)(base.GetValue(clsCopyItem.__ListItemProperty))); }
set { base.SetValue(clsCopyItem.__ListItemProperty, value); }
}
#endregion
public Guid workflowId = default(System.Guid);
public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties();
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
ISharePointService wfService = executionContext.GetService<ISharePointService>();
SPListItem item = workflowProperties.Item;
string targetSiteUrl = SiteUrl;
string targetLib = LibraryName;
string LogMessage = "Message logged to workflow history: ";
SPWeb contextWeb = __Context.Web;
SPList contextList = contextWeb.Lists[new Guid(__ListId)];
SPListItem contextItem = contextList.GetItemById(__ListItem);
SPFile contextFile = contextItem.File;
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite targetSite = new SPSite(targetSiteUrl))
{
using (SPWeb targetWeb = targetSite.OpenWeb())
{
try
{
if (contextFile != null)
{
SPFolder targetFolder = targetWeb.RootFolder.SubFolders[targetLib];
string strfutureFilePath = targetFolder + "/" + contextFile.Name;
SPFile isItThere = targetWeb.GetFile(strfutureFilePath);
if (isItThere.Exists)
{
SPWorkflow.CreateHistoryEvent(targetWeb, __Context.WorkflowInstanceId, 0,
targetWeb.CurrentUser, TimeSpan.Zero, "Information", LogMessage +
"Target file exists, going to check it out if needed", string.Empty);
if (isItThere.CheckOutType == SPFile.SPCheckOutType.None)
{
isItThere.CheckOut();
}
else
{
isItThere.UndoCheckOut();
isItThere.CheckOut();
}
}
SPWorkflow.CreateHistoryEvent(targetWeb, __Context.WorkflowInstanceId, 0,
targetWeb.CurrentUser, TimeSpan.Zero, "Information",
LogMessage + "Checking out source file", string.Empty);
if (contextFile.CheckOutType == SPFile.SPCheckOutType.None)
{ contextFile.CheckOut(); }
else
{ contextFile.UndoCheckOut(); contextFile.CheckOut(); }
SPWorkflow.CreateHistoryEvent(targetWeb, __Context.WorkflowInstanceId, 0, targetWeb.CurrentUser,
TimeSpan.Zero, "Information", LogMessage +
"Copying file to destination", string.Empty);
SPFile movedFile = targetFolder.Files.Add(contextItem.File.Name, contextItem.File.OpenBinary(), true);
string movedFileComment = "Copied from " + contextFile.DocumentLibrary.Title + ".";
movedFile.CheckIn(movedFileComment);
string checkInComment = "File has been copied to the " +
targetFolder.Name + " library on the " + targetWeb.Title + " site";
contextFile.CheckIn(checkInComment);
contextFile.Publish(checkInComment);
contextFile.Approve(checkInComment);
SPWorkflow.CreateHistoryEvent(targetWeb, __Context.WorkflowInstanceId, 0,
targetWeb.CurrentUser, TimeSpan.Zero, "Information",
LogMessage + "File copied and checked in", string.Empty);
}
}
catch (Exception ex)
{
string Message = ex.Message;
SPWorkflow.CreateHistoryEvent(targetWeb, __Context.WorkflowInstanceId, 0,
targetWeb.CurrentUser, TimeSpan.Zero, "Error",
"Message: " + Message, string.Empty);
throw ex;
}
}
}
});
return ActivityExecutionStatus.Closed;
}
}
}
This next section of code is to be used in your CopyItemToAnotherLibrary.actions file. It defines the parameters that the workflow action needs such as destination URL and destination Library. Remember, you will need to update the PublicKeyToken with your token when you register the assembly.
="1.0"="utf-8"
<WorkflowInfo>
<Actions Sequential="then" Parallel="and">
<Action Name="Move Item to Another Library"
ClassName="ActivityLibrary.clsCopyItem"
Assembly="ActivityLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5cd1b18d3e72bd18"
Category="Custom Actions"
AppliesTo="all">
<RuleDesigner Sentence="Send Item to Library %2 at Site URL %1">
<FieldBind Field="SiteUrl" Text="Site URL" DesignerType="TextArea" Id="1"/>
<FieldBind Field="LibraryName" Text="Library Name" DesignerType="TextArea" Id="2"/>
</RuleDesigner>
<Parameters>
<Parameter Name="SiteUrl" Type="System.String, mscorlib" Direction="In" DesignerType="StringBuilder"
Description="URL of the destination site. Must be in the form of http://ServerName/(SiteName)" />
<Parameter Name="LibraryName" Type="System.String, mscorlib" Direction="In" DesignerType="StringBuilder"
Description="Name of the destination library." />
<Parameter Name="__Context"
Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions"
Direction="In" DesignerType="Hide"/>
<Parameter Name="__ListId" Type="System.String, mscorlib, mscorlib" Direction="In" DesignerType="Hide"/>
<Parameter Name="__ListItem" Type="System.Int32, mscorlib, mscorlib" Direction="In" DesignerType="Hide"/>
</Parameters>
</Action>
</Actions>
</WorkflowInfo>
Points of Interest
As this is my first attempt at writing a custom action, I have found there are a lot of good articles on writing custom actions and good snippets of code a used to put this together. I am sure someone will see their code snippet and I thank you for publishing it. I am including a few helpful links I found along the way below.
References
Develop a custom workflow activity
Create a custom workflow site activity
SharePoint 2010 Custom Workflow Activities
Sandboxed Workflow Activities
How to log to the history list
History
Initial posting - Will update when suggestions, etc. are provided or I learn more and create updates.