There are several ways to move a form library to another web or server. In my case I setup a new SharePoint 2007 server, defined a new web application and attached the original content database to it.
Problem
After shutting down the old server the InfoPath forms of a form library were not accessible anymore, because the template was also shut down together with the server and there was no other copy in the network known by the InfoPath framework. Therefore InfoPath couldn’t show the form. So what to do?
An InfoPath form is a xml file having a couple of processing-instuctions in its header. Example:
<?xml version="1.0" encoding="UTF-8"?> <?mso-infoPathSolution solutionVersion="1.0.0.121" productVersion="12.0.0" PIVersion="1.0.0.0" href="http://www.domain.de/FormServerTemplates/MyForm.xsn" name="urn:schemas-microsoft-com:office:infopath:MyForm:http---www-domain-de-MyForm" language="de" ?> <?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?> ... form data go here ...
The processing instruction “mso-infoPathSolution” contains a href attribute which points to the form template. In my case the href points to the Forms Server. The form template must be accessible by InfoPath to open the form.
Task
Update the template references of all forms!
Solution
Since I do a lot of my SharePoint administration stuff with batch files, I wrote the console application “UpdateIPFormTemplateLocation”.
Usage: UpdateIPFormTemplateLocation.exe -web [web url] -lib [library name] -loc [form template url]
Example: UpdateIPFormTemplateLocation.exe -web http://www.yourdomain.com/yourweb -lib yourlibname -loc http://www.anotherdomain.com/FormServerTemplates/MyForm.xsn
The complete C# source code is listed below. I guess it is self explaining.
Conclusion
The tool “UpdateIPFormTemplateLocation” (see source code below) updates the references of InfoPath forms to their template. It is useful after relocation of form libraries or template libraries.
Before running the tool you should check associated workflows. Workflows may be attached to the form library or the form’s content type and may be triggered by updating the forms. If you don’t want further actions by workflows while updating you need to disable the workflows before you start the tool. After all done renable the workflows again.
You may also slightly modify the source code to update any data fields of InfoPath forms.
Hope it helps and feel free to improve my solution.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Xml;
using Microsoft.SharePoint;
namespace UpdateIPFormTemplateLocation
{
/// <summary>
/// This tool updates all InfoPath forms of a form library after
/// the library has been relocated to another server or web.
/// The update adjusts the "href" attribute of the form's
/// processing-instruction to new location of the form template.
/// </summary>
class Program
{
/// <summary>
/// Thread sleep time [ms] after one form has been updated
/// </summary>
private static int _SleepTime = 5000;
/// <summary>
/// Main entry
/// </summary>
/// <param name="args">command line parameters</param>
/// <returns>error code. 0=no error. 1=error detected.</returns>
static int Main(string[] args)
{
// Parse command line
Dictionary parameters = ParseCommandLine(args);
try
{
// Update all forms in library
UpdateForms(parameters);
Console.WriteLine("Done.");
return 0;
}
catch (KeyNotFoundException)
{
// Command line error
Console.WriteLine("Usage: UpdateIPFormTemplateLocation.exe -web [web url] -lib [library name] -loc [form template url]");
Console.WriteLine("Example: UpdateIPFormTemplateLocation.exe -web http://www.yourdomain.com/yourweb -lib yourforms -loc http://www.anotherdomain.com/MyForm.xsn");
Console.WriteLine("Execution aborted.");
return 1;
}
catch (Exception e)
{
// Execution error
Console.WriteLine(e.Message);
Console.WriteLine("Execution aborted.");
return 1;
}
}
/// <summary>
/// Update all forms of a form library
/// </summary>
private static void UpdateForms(Dictionary parameters)
{
// Get site
string absWebUrl = parameters["web"];
string libName = parameters["lib"];
string templateUrl = parameters["loc"];
// Open site
using (SPSite site = new SPSite(absWebUrl))
{
// Get site relative web url
string webUrl = absWebUrl.Replace(site.Url, "");
// Open web
using (SPWeb web = site.OpenWeb(webUrl))
{
// Walk through the library
SPDocumentLibrary lib = web.Lists[libName] as SPDocumentLibrary;
foreach (SPListItem item in lib.Items)
{
// Update the InfoPath form
UpdateForm(item, templateUrl);
// Sleep a little bit to give others a chance too
Thread.Sleep(_SleepTime);
}
}
}
}
/// <summary>
/// Update an InfoPath form
/// </summary>
/// <param name="item">the InfoPath form</param>
/// <param name="formTemplateUrl">the InfoPath form template location</param>
private static void UpdateForm(SPListItem item, string formTemplateUrl)
{
// Output progress message
Console.WriteLine("Updating \"" + item.File.Name + "\" ...");
// Create new xml document
XmlDocument form = new XmlDocument();
// Load InfoPath form
using (MemoryStream inStream = new MemoryStream(item.File.OpenBinary()))
{
form.Load(inStream);
}
// Replace form template url
ReplaceTemplateReference(form, formTemplateUrl);
// Save document
using (MemoryStream outStream = new MemoryStream())
{
form.Save(outStream);
outStream.Seek(0, SeekOrigin.Begin);
item.File.SaveBinary(outStream);
}
}
/// <summary>
/// Replace the form template reference of an InfoPath form
/// </summary>
/// <param name="form">the InfoPath form</param>
/// <param name="formTemplateUrl">url of the form template</param>
private static void ReplaceTemplateReference(XmlDocument form, string formTemplateUrl)
{
// Select the InfoPath processing-instruction (pi)
XmlNode pi = form.SelectSingleNode("processing-instruction('mso-infoPathSolution')");
// Parse the pi value and replace the href attribute value
string[] attrs = pi.Value.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
string newPiValue = string.Empty;
foreach (string attr in attrs)
{
// Insert separator
newPiValue += " ";
// If href attribute replace value otherwise keep all
newPiValue += (attr.StartsWith("href")) ? "href=\"" + formTemplateUrl + "\"" : attr;
}
// Set new pi value
pi.Value = newPiValue.Trim();
}
/// <summary>
/// Parse command line parameters
/// </summary>
/// <param name="args">command line parameters</param>
/// <returns>parsed command line</returns>
private static Dictionary ParseCommandLine(string[] args)
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < args.Length; i++)
{
if (args[i].StartsWith("-") && i + 1 < args.Length)
{
// Add parameter name and its value to dictionary
dictionary.Add(args[i].Substring(1).ToLower(), args[++i]);
}
}
//Done
return dictionary;
}
}
}