Creating Custom Workflow Activity for Attachments

n

nFirst of all let me declare , this is not one of the latest thing you can do with SharePoint but I thought this might be helpful (at least to me)
nIf you have developed any list workflow using SharePoint designer , you probably have noticed that there is no way to work with list item attachments. to overcome such scenarios you have to create your own workflow activity and deploy it on server. in Office365 case , you have Sandbox solution capabilities using which you can achieve the same.
n
nIn this case , I have tried to create a custom workflow activity which reads all attachments of the list item on which workflow is running and saves them to mentioned document library. while saving , it creates a folder whose name is a combination of list item’s Id and Title and then saves attachments into it. I have create this as a Sandbox solution so that anyone can easily deploy this wsp package on their Office365 Site and start using it.
n

n

n

n

n
n

n

n
nand custom action can be used like
n
n

n

n
n
nTo create your custom workflow actions basically three things are needed.
n

    n

  • Activity Action Class – This is the class where you will write your custom logic which will be executed when activity occurs in workflow.
  • n

  • Elements.xml – You will need to use <WorkflowActions> Element in order to deploy your custom action to SharePoint.
  • n

  • Feature – finally , a Site Collection scoped feature which will contain elements.xml you created to deploy your action to SharePoint.
  • n

n

n

n

n
n
nSo lets start creating a custom workflow activity for above mentioned case.
n
nI have created a simple SharePoint 2010 empty solution using Visual Studio 2010 SharePoint project template.
n
nNow lets add the Activity action class. Note that SaveItemAttachments method will be executed whenever our custom activity will be called from SPD workflow. this method takes a string method argument which is passed from SPD , as a document library name.
n
n
n
n

npublic class AttachmentActions

n

n{

n

n   public List<SPFile> filesCollection = null;

n

n   public string tdocLib = string.Empty;

n

n   SPUserCodeWorkflowContext tempcontext = null;

n

n

n

n   public Hashtable SaveItemAttachments(SPUserCodeWorkflowContext context, string targetDocumentLibrary)

n

n   {

n

n     Hashtable results = new Hashtable();

n

n     results[“Exception”] = string.Empty;

n

n     tdocLib = targetDocumentLibrary;

n

n     tempcontext = context;

n

n     try

n

n     {

n

n       //Get SiteCollection

n

n       using (SPSite site = new SPSite(context.CurrentWebUrl))

n

n       {

n

n         //Get Web

n

n         using (SPWeb web = site.OpenWeb())

n

n         {

n

n           //Get List

n

n           SPList list = web.Lists.GetList(context.ListId, false);

n

n           if (list != null)

n

n           {

n

n            //Access List Item

n

n            SPListItem listItem = list.GetItemById(context.ItemId);

n

n            if (listItem != null)

n

n            {

n

n             //Check for attachments

n

n             if (listItem.Attachments != null && listItem.Attachments.Count > 0)

n

n             {

n

n              //Get All Attachments

n

n              filesCollection = InitializeFilesCollection(web, listItem);

n

n              if (filesCollection != null)

n

n              {

n

n                //Get Target Document Library

n

n                SPList targetLibrary = web.GetList(targetDocumentLibrary);

n

n                if (targetLibrary != null)

n

n                {

n

n                  //Upload attachment to document library

n

n                  foreach (SPFile attachmentFile in filesCollection)

n

n                  {

n

n                    byte[] fileContents = GetFileContents(attachmentFile);

n

n                    if (fileContents != null)

n

n                    {

n

n                      string folderName = string.Format(“{0}.{1}”, listItem.ID, listItem.Title);

n

n                      string attachmentName = attachmentFile.Name;

n

n                      //Create Folder in document library

n

n                      SPFolder foundFolder = CreateFolder(folderName, targetLibrary, targetLibrary.RootFolder.SubFolders);

n

n                      if (foundFolder != null)

n

n                      {

n

n                        //Add file to created folder

n

n                        SPFile addedFile = foundFolder.Files.Add(string.Format(“{0}/{1}”, foundFolder.Url, attachmentName), fileContents, true);

n

n                        foundFolder.Update();

n

n                        }

n

n                      }

n

n                    }

n

n                    targetLibrary.Update();

n

n                    results[“Status”] = “Success”;

n

n                 }

n

n                }

n

n               }

n

n              }

n

n            }

n

n           }

n

n         }

n

n        }

n

n         catch (Exception ex)

n

n        {

n

n          results[“Exception”] = ex.ToString();

n

n        }

n

n

n

n        return (results);

n

n       }

n

n

n

n        public List<SPFile> InitializeFilesCollection(SPWeb web, SPListItem listItem)

n

n        {

n

n          SPAttachmentCollection allAttachments = listItem.Attachments;

n

n          string attachmentUrlPrefix = allAttachments.UrlPrefix;

n

n

n

n          if (allAttachments != null && allAttachments.Count > 0)

n

n          {

n

n            filesCollection = new List<SPFile>();

n

n            foreach (string attachmentName in allAttachments)

n

n            {

n

n              string attachmentPath = string.Format(“{0}{1}”, attachmentUrlPrefix, attachmentName);

n

n              SPFile attachmentFile = web.GetFile(attachmentPath);

n

n              if (attachmentFile != null)

n

n              {

n

n                 filesCollection.Add(attachmentFile);

n

n              }

n

n            }

n

n          }

n

n

n

n            return filesCollection;

n

n        }

n

n

n

n        public SPFolder CreateFolder(string folderName, SPList docLib, SPFolderCollection subFolders)

n

n        {

n

n          SPFolder foundFolder = null;

n

n          if (subFolders.Count > 0)

n

n          {

n

n            foreach (SPFolder folder in subFolders)

n

n            {

n

n              if (folder != null && !string.IsNullOrEmpty(folder.Name))

n

n              {

n

n                if (folder.Name.Equals(folderName))

n

n                {

n

n                  foundFolder = folder;

n

n                    break;

n

n                  }

n

n                }

n

n              }

n

n           //If no matching folder found – create new folder in document library root folder.

n

n              if (foundFolder == null)

n

n              {

n

n                if (!docLib.EnableFolderCreation)

n

n                {

n

n                  docLib.EnableFolderCreation = true;

n

n                  docLib.Update();

n

n                }

n

n                foundFolder = subFolders.Add(“/” + tdocLib + “/” + folderName);

n

n                docLib.Update();

n

n              }

n

n            }

n

n            return foundFolder;

n

n        }

n

n

n

n        public byte[] GetFileContents(SPFile file)

n

n        {

n

n            return file.OpenBinary();

n

n        }

n

n

n

n    }

n

n

n
n
nNow after this , let SharePoint know that what to execute whenever action will be called. This can be done by writing a elements file. SharePoint gives you a way to deploy such actions using <WorkflowActions> element where you can specify number of parameters.
n
nAdd empty element to the solution and add following xml in it.
n
n
n
n

n<Elements xmlns=http://schemas.microsoft.com/sharepoint/>

n

n  <WorkflowActions>

n

n

n

n    <!–Save Attachments Action–>

n

n    <Action Name=Move Attachments to library

n

n        SandboxedFunction=true

n

n        Assembly=$SharePoint.Project.AssemblyFullName$

n

n        ClassName=SharePoint.Sandbox.WorkflowActions.ActivityClasses.AttachmentActions

n

n        FunctionName=SaveItemAttachments

n

n        AppliesTo=all

n

n        UsesCurrentItem=true

n

n        Category=Custom Actions>

n

n      <RuleDesigner Sentence=Copy Attachments to library %1>

n

n        <FieldBind Id=1 Field=targetDocumentLibrary DesignerType=Text Text=library name/>

n

n      </RuleDesigner>

n

n      <Parameters>

n

n        <Parameter Name=__Context Type=Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions Direction=In DesignerType=Hide />

n

n        <Parameter Name=targetDocumentLibrary Type=System.String, mscorlib Direction=In DesignerType=TextBox />      

n

n      </Parameters>

n

n    </Action>

n

n   

n

n  </WorkflowActions>

n

n</Elements>

n

n

n

n
nIf you observe the <RuleDesigner> tag , there you can specify the actual sentence which will be seen in the SharePoint designer after adding this action. %1 is the input parameter for taking the document library name from the end user. this input is provided to the custom activity executing method.

n

n

n

nAs you added empty element in the solution , visual studio by default adds a feature to hold this elements.xml. Make sure that the scope of the feature is Site. 

n

n

n

nAfter this you are ready to deploy this solution to your environment.

n

n
nAfter deployment , you can create workflow using this activity and check the result.
n
n

n

nAbove example shows , after workflow completion attachments were saved to document library.
n
n

n

n
n

n

nYou can download the source code and wsp here.

n

n

n

n

n

Leave a Comment