Monday 10 June 2013

Rewire Data Connection on activate

Previously we built an InfoPath form that uses data connections to communicate with our web application, and then we converted the data connections into files and deployed them to a data connection library using a module. the only down side is when we open our data connection files to inspect the xml we very quickly notice that we have server specific paths to library's we are posting to, lists we are pulling from, soap services we are using and so on.

<?xml version="1.0" encoding="UTF-8"?>
<?MicrosoftWindowsSharePointServices ContentTypeID="0x010100B4CBD48E029A4ad8B62CB0E41868F2B0"?>
<udc:DataSource MajorVersion="2" MinorVersion="0" xmlns:udc="http://schemas.microsoft.com/office/infopath/2006/udc">
 <udc:Name>submitNewEmployee</udc:Name>
 <udc:Description>Format: UDC V2; Connection Type: SharePointLibrary; Purpose: WriteOnly; Generated by Microsoft InfoPath 2010 on 2013-05-30 at 14:27:52 by SSRAP7\Administrator.</udc:Description>
 <udc:Type MajorVersion="2" MinorVersion="0" Type="SharePointLibrary">
  <udc:SubType MajorVersion="0" MinorVersion="0" Type=""/>
 </udc:Type>
 <udc:ConnectionInfo Purpose="WriteOnly" AltDataSource="">
  <udc:WsdlUrl/>
  <udc:SelectCommand>
   <udc:ListId/>
   <udc:WebUrl/>
   <udc:ConnectionString/>
   <udc:ServiceUrl UseFormsServiceProxy="false"/>
   <udc:SoapAction/>
   <udc:Query/>
  </udc:SelectCommand>
  <udc:UpdateCommand>
   <udc:ServiceUrl UseFormsServiceProxy="false"/>
   <udc:SoapAction/>
   <udc:Submit/>
   <udc:FileName>Specify a filename or formula</udc:FileName>
   <udc:FolderName AllowOverwrite="0">http://server/New Employee/</udc:FolderName>
  </udc:UpdateCommand>
  <!--udc:Authentication><udc:SSO AppId='' CredentialType='' /></udc:Authentication-->
 </udc:ConnectionInfo>
</udc:DataSource>

so what to do about this? well there really is only one thing that I could think of, and that's to create a feature receiver and on the feature activated event:

  • traverse your data connection files
  • open them using the xmldocument class
  • update that server paths
  • save the files
  • check them back into the library

and that's it, you're done. simple right?

first lets traverse the list
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPWeb web = properties.Feature.Parent as SPWeb)
        {  
            SPList dcs = web.Lists["Data Connections"];
            foreach (SPListItem dc in dcs.Items)
            {
                if (dc.Properties["ConnectionType"].Equals("SharePointLibrary"))
                {
                    handleSharePointLibrary(web, dc);
                }
            }
        }
    });
}

this will ensure that we are only looking at data connections that submit to a library, in this next snippet we filter by the data connection name/title to ensure that we are in fact rewiring the one we are concerned with. After we rewire the file we check it back into the library.

private void handleSharePointLibrary(SPWeb web, SPListItem dc)
{
    string listURL = null;
 
    switch (dc.Properties["vti_title"].ToString())
    {
        case "submitNewEmployee":
            SPList lst = web.Lists["New Employee"];
            listURL = string.Format("{0}/{1}/", lst.ParentWeb.Url, lst.RootFolder.Url);
            break;
    }
 
    if (!string.IsNullOrEmpty(listURL))
    {
        SPFile dataConnectionFile = web.GetFile(dc.Url);
        RewireUpdateCommand(dataConnectionFile, listURL);
        dataConnectionFile.Approve("Approved via event reciever");
    }
}

Now for the magical part, in the above snippet you see a call to the RewireUpdateCommand function, lets take a look inside that sucker.

private void RewireUpdateCommand(SPFile dataConnectionFile, string listURL)
{
    string LibraryURL_xPath = "/udc:DataSource/udc:ConnectionInfo/udc:UpdateCommand/udc:FolderName";
 
    XmlDocument doc = new XmlDocument();
    doc.Load(dataConnectionFile.OpenBinaryStream());
 
    XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(doc.NameTable);
    nameSpaceManager.AddNamespace("udc""http://schemas.microsoft.com/office/infopath/2006/udc");
 
    XmlElement root = doc.DocumentElement;
 
    XmlNode ListId = root.SelectSingleNode(LibraryURL_xPath, nameSpaceManager);
    ListId.InnerText = listURL;
 
    var encoding = new System.Text.ASCIIEncoding();
    dataConnectionFile.SaveBinary(encoding.GetBytes(doc.InnerXml));
}

if you need an explanation xml document msdn