Showing posts with label attachments. Show all posts
Showing posts with label attachments. Show all posts

Sunday, August 21, 2011

Moving CRM Attachments to SharePoint

CRM 2011 allows you to store documents in SharePoint and view them from within CRM.  Someone called Maria left me a comment asking if you could move attachments from CRM to SharePoint. My previous blogs about creating SharePoint document locations from CRM were actually prerequisites for doing exactly this.  I wanted to take a CRM attachment and move it to SharePoint. In my case it was an attachment on a letter activity as the result of a mailmerge. To be accurate it is an attachment on an annotation (or note) on a letter activity. I needed to move the attachment to SharePoint and then leave a link to the document on the letter activity.  To achieve this we created a plugin on the PreCreate action of an annotation. Then in the execute method of the plugin you can instantiate the annotation entity

if (null != context && null != context.InputParameters)
{
      if (context.InputParameters.Contains("Target") &&
          context.InputParameters["Target"] is Entity)
     {
           // Obtain the target entity from the input parmameters.
          Entity entity = (Entity)context.InputParameters["Target"];
     }
}
Once you have the annotation then you can get the contents of the attachment as a byte array.

// Retrieve the base64Encoding document body
// and convert it to byte base64encoding
// see below for the DecodeFrom64 function
    byte[] documentBody = DecodeFrom64(entity.Attributes["documentbody"].ToString());
    // get the filename
   System.IO.FileInfo annotationFileInfo = new System.IO.FileInfo(fileName);
To remove the attachment you need to use this code

entity.Attributes["documentbody"] = null;
entity.Attributes["filename"] = null;
entity.Attributes["filesize"] = null;

The helper method for decoding the attachment to a byte array

internal static byte[] DecodeFrom64(string encodedData)
{
     byte[] encodedDataAsBytes = System.Convert.FromBase64String(encodedData);
     return encodedDataAsBytes;
}

To upload the document to SharePoint you should use the CopyIntoItems method of the Copy web service located at http://sharepointurl/_vti_bin/Copy.asmx. But before you do, you need to replace any illegal characters in the filename - in our case we replaced an illegal character with a hyphen.

internal static string ReplaceSpecialCharacters(string input)
{
   Regex r = new Regex("(?:[^a-z0-9 ]|(?<=['\"])s)", RegexOptions.IgnoreCase |                         RegexOptions.CultureInvariant | RegexOptions.Compiled);
   return r.Replace(input, "-");
}
I may have posted this code too late to help Maria, but I hope someone else finds it useful.

Wednesday, November 28, 2007

Voyages with BlackPearl - 4. Replace InfoPath Attachments

I am using InfoPath as the user interface to a K2 BlackPearl workflow. The form allows attachments to be added and they will need to be accessible to other users as the workflow progresses. Since the attachments are quite large, I wanted to strip the attachments off and replace them with a link. This workflow allows mutliple attachments and there are several different files that can be attached. The workflow itself makes no refrence to the attachments. I would have used the K2 wizard to store the attachment to a SharePoint document library but it doesn't work for repeating nodes of file attachments and I would need extra code to find the file name and store this in the InfoPath form as a hyperlink, and then delete the original attachment.
My solution was to build a web service that does exactly what I want. Remove the attachment, store it in SharePoint, put the path to the document on another field and repeat this for all occurrences in the repeating node.
The challenge then is to forward the now amended InfoPath form onto the K2 InfoPathService.asmx.
Here is how its done. You'll need a class defined for an XmlNode Array because that is how the InfoPath form is submitted to the InfoPathService.asmx.
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.com/office/infopath/2003/myXSD/2007-05-17T16:24:11")] [System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.microsoft.com/office/infopath/2003/myXSD/2007-05-17T16:24:11", IsNullable = false)]
public class nodeArray { [System.Xml.Serialization.XmlArray()] [System.Xml.Serialization.XmlAnyElement()] public System.Xml.XmlNode[] node; }

Then the web service method looks like this:
public void InfoPathReplaceAttachment(Object infoPathFormXml) {
nodeArray nodes = new nodeArray();
nodes.node = (System.Xml.XmlNode[]) infoPathFormXml;
string s = System.Web.HttpUtility.HtmlDecode(nodes.node[0].OuterXml);
// load the string into an XML dom called myDoc and replace the attachments
//.....
//Finally submit the XML to the InfoPathService.asmx
// do this to use escaped XML
XmlNode escNode = myDoc.CreateTextNode(myDoc.OuterXml);
// replace node with the amended version
nodes.node[0] = escNode;
infoPathFormXml = nodes.node;
K2submit.InfoPathService ws = new K2submit.InfoPathService();
ws.Credentials = System.Net.CredentialCache.DefaultCredentials;
ws.SubmitInfoPathData(infoPathFormXml);
ws.Dispose();
myDoc = null;