Bernhard's SharePoint

February 5, 2010

How to update all library’s InfoPath forms after relocation

Filed under: Admin tools, C#, InfoPath 2007, SharePoint 2007 — Tags: , , , , — Bernhard @ 19:34

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;
        }
    }
}

4 Comments »

  1. Great Post..I see this alot, i usually brute force it.. nice idea and implementation

    Comment by fabiangwilliams — February 5, 2010 @ 23:29

    • In my case there were no chance for brute force. I had several hundred forms to manage.

      Comment by Bernhard — February 6, 2010 @ 23:23

  2. WOW! Thanks for this. It would have taken me hours!

    Comment by Pam Johnson — January 3, 2011 @ 05:35


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Theme: Silver is the New Black. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.