Tuesday, October 22, 2013

Making InfoPath Data Connections Deployable

Lately I've been putting together a solution that involves some InfoPath forms. I love InfoPath for what it is, but let's face it, it can be a bit painful. One of the biggest pain points is that it's not easily deployable. This is mostly due to the udcx having a reference to the guid of the list they were created on.

I've read a few articles saying that you can simply change the guid to the list name. This didn't work for me so I've come up with a different way to do it which I thought I'd share.

Essentially I'm using 3 features, the first one to deploy the Data connection list instance and the list that the UDCX will connect to, the second to deploy all of the udcx's I'll need, and then the third loops through them and updates the guid and the web URL.

Let's look at it in details. I'm using WSPBuilder (downloadable from codeplex.com) and suggest others do as well.

The first step is to create a new project. Open up Visual Studio create a WSPBuilder Project



Next we need to create our first feature for the List Instances.

Right click your solution and select Add, New Item


Then Select WSPBuilder, Blank Feature, make sure you give it a name then click OK


Scope your feature to Web, and un-tick Event Handler Click finish


The following goes in the elements.xml

<?xml version="1.0" encoding="utf-8" ?>

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

<ListInstance

FeatureId = "00BFEA71-DBD7-4F72-B8CB-DA7AC0440130"

Hidden = "False"

OnQuickLaunch = "False"

RootWebOnly = "TRUE"

TemplateType = "130"

Title = "Data Connections"

Url = "DataConnections"

Description="Library to store Data Connections"

VersioningEnabled = "FALSE"/>

</Elements>


Note: The above will add a Data Connections Library, you can also add other list instances to this feature for example the list or library the UDCX will connect to.

So that's step One, we have a data connections library to hold the UDCX's.

Step Two, we need some UDCX's

Create another feature –using the same steps as above, scope it at a web level, again un-tick Event Handler.

Once it's created, right click your new feature folder and select Add, New Item

Create a new XML file. This File MUST have the same name as the library that it will be connected to and the extension needs to be .udcx (see image below for example)

The following goes in the .udcx
<?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>UDCX Display Name</udc:Name>
<udc:Description/>
<udc:Type MajorVersion="2" MinorVersion="0" Type="SharePointList">
<udc:SubType MajorVersion="0" MinorVersion="0" Type=""/>
</udc:Type>
<udc:ConnectionInfo Purpose="ReadOnly" 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=""/>
</udc:UpdateCommand>
</udc:ConnectionInfo>
</udc:DataSource>

Basically, it's an emply UDCX. You can change the display name and the description. Go ahead and create as many empty UDCX files that you need. Remember they need to be named ListDisplayName.udcx
Next we need to tell the feature to deploy them to the data connection library.
This is done with a module. The following goes into the Elements file of the UDCX feature
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="DataConnections" Url="DataConnections" Path="">
<File Url="LibraryDisplayName.udcx" Type="GhostableInLibrary" IgnoreIfAlreadyExists="FALSE">
<Property Name="Title" Value="Library Display Name.udcx"></Property>
</File>
</Module>
</Elements>

Add a File element for each of the UDCXs that you need to deploy.
OK, we now have a populated Data Connection Library. We now need to update those empty UDCX's with the right information.
Create another feature – using the same method as above, this time scope it to Web and leave the Event Handler ticked. WSPBuilder will automatically create a feature Receiver for this feature, and tell feature to use it. In the feature receiver add the following code (be sure to update your namespace and class to match your feature!)
using System;
using System.Collections.Generic;
using System.Xml;
using System.Text;
using System.Globalization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace InfopathDataConnections.EventHandlers.Features
{
public class UpdateUDCXReceiver : SPFeatureReceiver
{

const string DataConnectionsLibrary = "Data Connections";
const string ListIdXPath = "/udc:DataSource/udc:ConnectionInfo/udc:SelectCommand/udc:ListId";
const string weburlXPath = "/udc:DataSource/udc:ConnectionInfo/udc:SelectCommand/udc:WebUrl";

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{


SPWeb web = (SPWeb)properties.Feature.Parent;

foreach (SPListItem item in web.Lists[DataConnectionsLibrary].Items)
{
if (item.DisplayName != "Main Submit")
{
UpdateDataConnectionLibrary(item);
}
}

}

private void UpdateDataConnectionLibrary(SPListItem item)
{


XmlDocument doc = new XmlDocument();
using (System.IO.Stream stream = item.File.OpenBinaryStream())
{
doc.Load(stream);
}

XmlNamespaceManager nameSpaceManager = new XmlNamespaceManager(doc.NameTable);
nameSpaceManager.AddNamespace("udc", "http://schemas.microsoft.com/office/infopath/2006/udc");

XmlElement root = doc.DocumentElement;

XmlNode folderName = root.SelectSingleNode(ListIdXPath, nameSpaceManager);
SPList locationsList = item.Web.Lists[item.DisplayName]; // GetList(item.Web.Url + "/Lists/" + item.DisplayName);
folderName.InnerText = locationsList.ID.ToString("b");

XmlNode weburlfolder = root.SelectSingleNode(weburlXPath, nameSpaceManager);
weburlfolder.InnerText = item.Web.Url.ToString();

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
item.File.SaveBinary(encoding.GetBytes(doc.InnerXml));
item.File.Approve("Guid and WebUrl Added");

}

}
}


The above code firstly checks to make sure the ucx is not a Main Submit as we don't want to try to change those. It them looks for a List or Library on the current site with the same name as the udcx file and grabs the guid and puts it and the current web into the relevant areas of the udcx. It then approves the udcx so it is usable.
NOTE: you may need to change the target framework to .NET 3.5 - to do this, go to project Properties (right click your project) select the applications tab, change the Target Framework to .NET Framework 3.5 (rebuild if required)
That's it, it should all work. If you'd like to see an example, there is one over on my Downloads page or click here.
J



1 comment:

  1. I was not able to figure out how to get the title of the udcx to populate on creation or through the UI. I ended up updating it directly in the database with this.

    UPDATE [WSS_Content].[dbo].[AllUserData]
    SET nvarchar7 = tp_LeafName
    FROM [WSS_Content].[dbo].[AllUserData]
    WHERE nvarchar7 IS NULL and tp_ContentType = 'Universal Data Connection File'

    GO

    Is there some way to populate the title when the udcx is created or updated.

    ReplyDelete