Automating the Deployment of an Office InfoPath Form Template 

 

Shiraz Cupala
Joshua Bell
Mike Talley
Microsoft Corporation

August 2005

Applies to:
   Microsoft Office InfoPath 2003

Summary: Microsoft Office InfoPath 2003 enables you to publish form templates to a specific location, such as a Web server or a network file share. In this article, learn how to update part of a previously-published form template and move it to a new location. (11 printed pages)

Contents

Introduction
Using XsnFixup.js
Conclusion
Additional Resources

Introduction

When moving a form template to a new location, there are a number of items within the form template component files that need to be changed, such as the publishUrl in the manifest.xsf file. While you could do this within the InfoPath design mode, it is often more convenient and safer to use a script to modify these properties. In general, the following list represents the most common items that you may need to change:

  • The publishUrl
  • The wsdlUrl and serviceUrl of the Web service adapters
  • Database connection strings and other Data Connection parameters
  • The caption for the name of the form template
  • The form template URN

It is not necessary to modify all of these attributes for every form template deployment. In most cases, the script used to deploy a particular form template contains specific information about the form template itself, and where it is deployed.

Modification of the Processing Instructions (PI) in the template.xml file, which represents the default data for the form template, may also be necessary to avoid the conflict resolution dialog box when opening forms of the same name and version. This article does not address this specific issue, but the script could be modified to open this file and update the PI. For example code that provides a similar operation, refer to the SolutionFileModify, SolutionFileModifyVersion, and SolutionVersionUpdate functions in the xdown.js script code. This code file is used for the Downlevel Tool and can be found at Using the Downlevel Tool.

Using XsnFixup.js

The XsnFixup.js script contains 10 functions that enable you to unpackage a form template (.xsn) file, modify the manifest.xsf file, repackage the form template, and save it to a new location. The full script is included at the end of the article. Copy and paste this code into a script editor or Notepad, and save it as xsnfixup.js.

The Microsoft Cabinet Software Development Kit (SDK) files and folders should be present in the same location where you save the script file, namely, in a containing folder called "CabSDK".

To use the script as-is, call it from a command line or batch file with the following syntax:

Signature

XsnFixup.js input.xsn output.xsn XPathToModify NewValue

Example

XsnFixup.js in.xsn out.xsn "/xsf:xDocumentClass/@publishUrl" "*http://www.example.com/example.xsn*"

Parameters

  • input.xsn: The source form template name and current location.
  • output.xsn: The destination form template name and location.
  • XPathToModify: The XPath of the element or attribute whose value needs to be updated.
  • NewValue: The new value of the element or attribute.

Requirements

The XsnFixup JScript Main Function

What follows is the main function of the XsnFixup.js code. As written, it enables you to modify a single attribute, such as the publishUrl. It accepts four parameters. Here is an example syntax for changing the publishUrl using a UNC path for the input and output locations:

xsnfixup \\server1\d$\test\test.xsn \\server2\e$\public\expenses.xsn
"/xsf:xDocumentClass/@publishUrl" "http://server2/expense/expenses.xsn"

To extend this script, you may want to eliminate the third and fourth arguments of the main function and hard-code the changes in the FixupXSF function.

function main( arguments )
{
   if( arguments.length != 4 )
   {
      WScript.Echo( 
         "Usage:\n\tXsnFixup.js input.xsn output.xsn XPathToModify NewValue\n" +
         "\n" + 
         "Example:\n\tXsnFixup.js in.xsn out.xsn \"/xsf:xDocumentClass/@publishUrl\" \"http://www.example.com/example.xsn\""
      );
      WScript.Quit();
   }

   FixupXSN( arguments(0), arguments(1), arguments(2), arguments(3) );
}
main( WScript.Arguments );

The FixupXSF Function

The FixupXSF function is called from the FixupXSN function, immediately following the extraction of the form template component files from the XSN.

If you modify the main function to eliminate the third and fourth arguments, it is necessary to eliminate the arguments from this function as well as from where this function is called, in the FixupXSN function:

function FixupXSF( xsfInputPath, xsfOutputPath, xpath, newvalue )
{
   var fso = new ActiveXObject ("Scripting.FileSystemObject");

   var xsfDom = new ActiveXObject("MSXML2.DOMDocument.5.0");
   xsfDom.async = false;
   xsfDom.validateOnParse = false; // Ignore xsi:nil errors
   xsfDom.load( xsfInputPath );
   xsfDom.setProperty( "SelectionNamespaces", "xmlns:xsf='" + XsfNamespace + "'" );

   ModifyAttribute( xsfDom, xpath, newvalue ); 
 
   if( fso.FileExists( xsfOutputPath ) )
   {
      fso.DeleteFile( xsfOutputPath, true );     
   }

   xsfDom.save( xsfOutputPath );
}

If you need a script to modify multiple attributes, comment out the ModifyAttribute( xsfDom, xpath, newvalue ); statement and hard-code the necessary attribute modifications using new ModifyAttribute function calls. An example of changing the form template name would be as follows:

//Change form name to final deployed name
//(appears in Fill Out a Form dialog box and titlebar)
   ModifyAttribute( xsfDom, "/xsf:xDocumentClass/xsf:fileNew/xsf:initialXmlDocument/@caption",
"Your new form template name" ); 

Here's an example of how you would modify the wsdlUrl and serviceUrl of a Web service adapter data connection called "GetHoursWorked":

//Change Web Service calls to point to final deployment server
   ModifyAttribute( xsfDom,
"/xsf:xDocumentClass/xsf:dataObjects/xsf:dataObject
[@name='GetHoursWorked']/xsf:query/xsf:webServiceAdapter/@wsdlUrl",
"http://MyServer/webservices/timecard.asmx?WSDL" ); 
   ModifyAttribute( xsfDom, "/xsf:xDocumentClass/xsf:dataObjects/xsf:dataObject
[@name='GetHoursWorked']/xsf:query/xsf:webServiceAdapter/xsf:operation/@serviceUrl",
"http://MyServer/webservices/timecard.asmx" );

This example points the form template to a new Web service location. In this case, the server "MyServer" contains a Web service called "timecard" which returns, among other data, the number of hours worked. The "GetHoursWorked" Web service adapter data connection corresponds to the name for this connection that is listed in the Data Connections dialog box in the InfoPath design mode.

Other Functions in the XsnFixup JScript Code

There are eight other functions that constitute the XsnFixup JScript code. In most cases, these functions do not need to be modified:

  • ExtractFilesFromXSN
  • CreateXSNFromFiles
  • ModifyAttribute
  • FixupXSN
  • PathCombine
  • QuoteString
  • ShellExecute
  • SaveToFile

Additional Considerations

If you are digitally signing the form template prior to deployment to a production environment, the digital signature should be the last step in the deployment process. Modifying any of the attributes of the form template after it has been signed will invalidate the digital signature.

If you need to deploy a form template to multiple locations, such as two different test environments, create two script files with specific information for each server, and then use a batch (.bat) file that invokes multiple script files in succession.

The Full XsnFixup.js Code

Included here is the full code that can be copied into a text editor and saved as XsnFixup.js.

// InfoPath form template extraction and creation script for
// modification of a single XPath value in the manifest.xsf

var g_CabSdkBin = ".\\CabSdk\\bin";
var g_ExtractExe = PathCombine( g_CabSdkBin, "extract.exe" );
var g_MakecabExe = PathCombine( g_CabSdkBin, "makecab.exe" );
var g_XsfNamespace = "https://schemas.microsoft.com/office/infopath/2003/solutionDefinition";
var g_GetSpecialFolder_TemporaryFolder = 2;

// Helper functions 
 
function PathCombine( dir, file )
{
   return dir + "\\" + file ;
}
 
function QuoteString( string )
{
   return ( "\"" + string + "\"" );
}
 
function ShellExecute( string, flagWait )
{
   if( flagWait == null )
     flagWait = true;
   var WScriptShell = WScript.CreateObject( "WScript.Shell" );
   return WScriptShell.Run( string, 0, flagWait );
}

// Called from CreateXSNFromFiles, writes final XSN to filePath

function SaveToFile( data, filePath )
{
   var fso = new ActiveXObject( "Scripting.FileSystemObject" );
   fso.CreateTextFile( filePath, true );
   var TextStream = fso.OpenTextFile( filePath, 2 );
   TextStream.Write( data );
   TextStream.Close(  );
}

// Called from FixupXSN, extracts files from XSN cab

function ExtractFilesFromXSN( xsnInputPath, outputFolder )
{
   var Parameters;
 
   // "/Y" prevents prompting before overwriting
   // "/E" extract all files
   // "/L" location to extract to
   
   if( outputFolder != null )
      Parameters = "/Y /E /L " + QuoteString( outputFolder );
 
   ShellExecute( QuoteString( g_ExtractExe ) 
      + " " + Parameters
      + " " + QuoteString( xsnInputPath ), true );
} 

// Called from FixupXSN, creates XSN cab from source files

function CreateXSNFromFiles( inputFolder, xsfName, xsnOutputPath )
{
   var fso = new ActiveXObject ("Scripting.FileSystemObject");
   var tempName   = fso.GetTempName();
   var tempFolder = fso.GetSpecialFolder(g_GetSpecialFolder_TemporaryFolder);
   var tempPath   = PathCombine( tempFolder, tempName );

   var ddfString = "";
   ddfString += ".Set DiskDirectoryTemplate='"+ tempFolder +"'\r\n";
   ddfString += ".Set CabinetNameTemplate='" + tempName + "'\r\n";
 
   var xsfDom = new ActiveXObject( "MSXML2.DOMDocument.5.0" );
   xsfDom.async=false;
   xsfDom.validateOnParse=false; // for ignoring xsi:nil errors..
   xsfDom.load( PathCombine( inputFolder, xsfName ) );
   xsfDom.setProperty( "SelectionNamespaces", "xmlns:xsf='" + g_XsfNamespace + "'" );
 
   ddfString += QuoteString( PathCombine( inputFolder, xsfName ) ) + "\r\n";
   var fileNodes = xsfDom.selectNodes( "//xsf:files/xsf:file/@name" );
   for( var i = 0; i < fileNodes.length; i++ )
   {
      ddfString += QuoteString( PathCombine( inputFolder, fileNodes( i ).text ) ) + "\r\n";
   }
 
   var ddfPath = PathCombine( tempFolder, "makecab.ddf" );
   SaveToFile( ddfString, ddfPath );
   ShellExecute( QuoteString( g_MakecabExe ) + " /v1 /f " + QuoteString( ddfPath ), true );
   fso.DeleteFile( ddfPath );

   // Move the XSN to its new home
   if( fso.FileExists( xsnOutputPath ) )
   {
      fso.DeleteFile( xsnOutputPath, true );     
   }
   fso.MoveFile( tempPath, xsnOutputPath );

   // Delete setup files output into current directory by CAB process
   var oScratchFiles = new Array( "setup.inf", "setup.rpt" );
   for( var i in oScratchFiles )
   {
      var strScratchFile = oScratchFiles[i];
      if( fso.FileExists( strScratchFile ) )
      {
         fso.DeleteFile( strScratchFile, true );     
      }
   }
}

// Called from FixupXSF, modifies value of specified XPath

function ModifyAttribute( dom, xpath, value )
{
   var attributeList = dom.selectNodes( xpath );

   for(;;)
   {
      var attribute = attributeList.nextNode();

      if( attribute == null )
         break;

      WScript.Echo( "Replace:\t" + attribute.nodeValue + "\nWith:\t" + value );

      attribute.nodeValue = value;  
   }
}

// Called from FixupXSN, creates DOM, loads XSF, and invokes
// ModifyAttribute to change specified XPath to new value

function FixupXSF( xsfInputPath, xsfOutputPath, xpath, newvalue )
{
   var fso = new ActiveXObject ("Scripting.FileSystemObject");

   var xsfDom = new ActiveXObject("MSXML2.DOMDocument.5.0");
   xsfDom.async = false;
   xsfDom.validateOnParse = false; // Ignore xsi:nil errors
   xsfDom.load( xsfInputPath );
   xsfDom.setProperty( "SelectionNamespaces", "xmlns:xsf='" + g_XsfNamespace + "'" );

   ModifyAttribute( xsfDom, xpath, newvalue ); 
 
   if( fso.FileExists( xsfOutputPath ) )
   {
      fso.DeleteFile( xsfOutputPath, true );     
   }

   xsfDom.save( xsfOutputPath );
}

// Called from Main, invokes three other functions to extract,
// modify, and repackage XSN

function FixupXSN( xsnInputPath, xsnOutputPath, xpath, newvalue )
{
   var fso = new ActiveXObject( "Scripting.FileSystemObject" );
   
   if( ! fso.FileExists( xsnInputPath ) )
   {
      WScript.Echo( "File does not exist: " + xsnInputPath );
      WScript.Quit();
   }

   // Create temporary folder and explode the XSN
   var tempFolderPath = fso.CreateFolder( PathCombine( fso.GetSpecialFolder(g_GetSpecialFolder_TemporaryFolder),
fso.GetTempName() ) ).Path;
   ExtractFilesFromXSN( xsnInputPath, tempFolderPath );

   // Modify the XSF in place   
   var xsfPath = PathCombine( tempFolderPath, "manifest.xsf" );
   FixupXSF( xsfPath, xsfPath, xpath, newvalue );

   // Generate the new XSN
   CreateXSNFromFiles( tempFolderPath, "manifest.xsf", xsnOutputPath );

   // Cleanup
   var tempFolderPath = fso.DeleteFolder( tempFolderPath );
}

// Main function to accept source and destination XSN locations,
// a single XPath to modify, and the new value

function main( arguments )
{
   if( arguments.length != 4 )
   {
      WScript.Echo( 
         "Usage:\n\tXsnFixup.js input.xsn output.xsn XPathToModify NewValue\n" +
         "\n" + 
         "Example:\n\tXsnFixup.js in.xsn out.xsn \"/xsf:xDocumentClass/@publishUrl\" \"http://www.example.com/example.xsn\""
      );
      WScript.Quit();
   }

   FixupXSN( arguments(0), arguments(1), arguments(2), arguments(3) );
}

main( WScript.Arguments );

Conclusion

When deploying a form template to a new location, such as from a test server to a production server, it is best to automate this process with a script.

Additional Resources

For more information about InfoPath, see the following resources:

© Microsoft Corporation. All rights reserved.