Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / productivity / SharePoint

Workflow to copy a file from one site to another server or site

5.00/5 (1 vote)
22 Jun 2012CPOL3 min read 38.1K  
This workflow will copy a file from a document library of one site to another server or web application

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:

  1. Ensure that the usings listed here are in your project
  2. Replace the code inside the curly braces after your namespace declaration
  3. 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.

C#
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();
        //private void codeCopyItem_ExecuteCode(object sender, EventArgs e)
        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: ";

            //string targetSiteUrl = "http://testpublic.mwestling-lt/DocCenter";
            //string targetLib = "Documents";

            //Added to see if I can get the context items like file, etc.
            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)
                            {
                                //Setup variables
                                SPFolder targetFolder = targetWeb.RootFolder.SubFolders[targetLib];
                                string strfutureFilePath = targetFolder + "/" + contextFile.Name;

                                //First test to see if the file exists on the target
                                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);
                                    //Check to see if it is already checked out, if so, undo the checkout
                                    if (isItThere.CheckOutType == SPFile.SPCheckOutType.None)
                                    {
                                        //If it exists, check it out
                                        isItThere.CheckOut();
                                    }
                                    else
                                    {
                                        isItThere.UndoCheckOut();
                                        isItThere.CheckOut();
                                    }
                                }
                                //Check out the source file                            
                                    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(); }
                                //Try to overwrite, if not, then change code and add in the delete
                                    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);
                                //Check in destination file
                                string movedFileComment = "Copied from " + contextFile.DocumentLibrary.Title + ".";
                                movedFile.CheckIn(movedFileComment);
                                //Check in source file and publish
                                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.

XML
<?xml version="1.0" encoding="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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)