|WilanskySPC049_Speeding InfoPath 2010 download.zip|
One significant improvement in Microsoft Office SharePoint 2010 is the ability to easily create custom Microsoft Office InfoPath 2010 forms to perform create, read, and update operations against SharePoint lists. You create custom InfoPath 2010 forms to replace the out-of-the box forms for performing these typical list operations.
A common reason for using custom forms is to include in your form client-side field validation rules, such as verifying that a value entered by a user falls within a certain range of allowed values.
The information in this article will help you with deploying your own custom InfoPath forms to external lists across multiple SharePoint farms while managing your InfoPath forms in a source control system.
Publishing a Form to an External List
The steps you follow to create and publish a custom form to a single external list aren't difficult. One way is to open the list using SharePoint Designer 2010, and click Design Forms in InfoPath in the ribbon's Action tab.
Figure 1 shows this option for an external list named Currencies. When you click Item under Design Forms in InfoPath, an additional ASP.NET forms page will appear under the Forms section in SharePoint Designer 2010 for each supported operation.
For example, the Currencies list includes three new ASP.NET forms that host the BrowserFormWebPart: editifs.aspx (for the update item operation), newifs.aspx (for the new item operation), and displayifs.aspx (for the view item operation).
SharePoint Designer automatically sets these ASP.NET forms as the default forms for the list and generates the custom InfoPath form, template.xsn. The BrowserFormWebPart hosts the custom InfoPath form in WebPart pages. SharePoint Designer saves all the ASP.NET files and the .xsn file to the Item folder of the target list.
After these SharePoint Designer operations are complete, SharePoint Designer launches InfoPath Designer 2010 to load the template.xsn file for subsequent user customizations and for publishing to the SharePoint list. (See the Sidebar "Designing an InfoPath Form after Closing It.")
You can publish the .xsn file to the SharePoint list by clicking the Quick Publish button in InfoPath 2010, as Figure 2 shows. This procedure is called Quick Publish because InfoPath modifies the .xsn content slightly and saves the .xsn file back to the location from where it was opened.
You'll encounter one additional step the first time you click Quick Publish for an external list form. You must save the form locally.
As a result of the local save operation, InfoPath stamps the form definition file (manifest.xsf) inside the compressed .xsn file with the local save location to further process the file contents for Quick Publish. Note that Quick Publish isn't available if you have full-trust code behind the InfoPath form.
In that case, you must publish the form template to Central Administration, then deploy the form to a site collection, as you had to do in SharePoint 2007.
The custom form creation and Quick Publish process is simple for a single SharePoint farm deployment. However, there are some pitfalls you'll encounter when developing a solution that must deploy rapidly, is customized by more than one developer or forms designer, must be versioned in source control, and must be promoted from one farm to the next.
Code and artifact promotion are common when you move your solutions from development through Q&A and into production.
Managing the Development Lifecycle of InfoPath
If you add your InfoPath form (.xsn file) to a source control system, you'll gain fundamental benefits, such as check-in, check-out, work-item associations, conflict resolution, and per-computer/per-user workspaces.
The problems with source control and artifact promotion relate to dependencies injected into the manifest.xsf file, which is part of the .xsn template package. The .xsf file is an XML document that you can view and edit with a text editor.
The first hard dependency is the publishUrl attribute’s value in the xsf:xDocumentClass element. InfoPath Designer 2010 uses this attribute to determine where the template.xsn file was saved previously.
Each time a form designer gets the latest content from source control, the designer’s local workspace updates with the latest checked-in version of template.xsn. After checking out and making changes to the form using InfoPath, then clicking Quick Publish, a dialog box appears stating that you must save the form before it’s published.
Saving the file to a designer’s local workspace will change the publishUrl path to the designer’s local workspace path. The next form designer who opens the form will encounter the same save-location problem, unless both form designers have their workspaces mapped to the same location on a disk.
Sharing a single workspace or enforcing that everyone maps to a single workspace location on a disk is not common when working within a source control system. The extra Save dialog probably sounds inconsequential, but it is a time waster when working with more than one or two forms.
The second hard dependency in an InfoPath template is the absolute URL to a SharePoint list, which appears as a value in three attributes within manifest.xsf. This path is the publish location in a SharePoint list for the template.xsn file.
There are two attributes that matter for artifact promotion from the SharePoint environment hosting the list to another SharePoint environment. The attributes are originalPublishUrl, inside of the xsf3:solutionMode element, and path in the xsf2:entity element.
InfoPath Designer 2010 writes the SharePoint list URL into the InfoPath template file when the file is first created. However, there's no way within InfoPath Designer 2010 to change the URL.
It’s common to change the Quick Publish target for a form when publishing to a different SharePoint farm or when publishing a form using a different file name. You must change the file name if you want to use a different form for the create, read, or update operations.
I explore the multiple-form name scenario later in the article. Using a script to modify the XML attributes described earlier simplifies Quick Publish by avoiding the extra Save dialog and allowing the form designer to publish the form to any SharePoint farms in the artifact promotion path.
The script SetQuickPublishUrl.ps1, which is available for download, first extracts the content of the .xsn file by using the Microsoft Expand utility. (To download the code, go to www.sharepointproconnections.com, InstantDoc ID 125998, and click the Download the Code link at the top of the article.)
Once all the source files, including the .xsf file are available, the script modifies the XML elements and attributes using classes in the System.Xml namespace, which the code in Figure 3 at the end of this article shows.
The path and originalPublishUrl attributes are modified based on a URL value you pass in from the command line.
The script automatically sets the publishUrl attribute based on the location of the template.xsn file on the local disk (typically a local disk workspace location when working with a source control system such as Team Foundation Server).
After you make the modifications, the files that the script expands are repackaged into an updated template.xsn file by the MakeXsnFromFiles function and placed in a folder created by the script.
The folder is named “Quick Publish - <datetime>” where datetime is the date and time when the script ran. MakeXsnFromFiles uses the MakeCab utility to create the cabinet file that can then be Quick Published in InfoPath or directly by the script. The script then cleans up the expanded files.
When you run SetQuickPublishUrl.ps1 from the PowerShell command prompt, you must specify the full path to the template file, the SharePoint list URL, and whether you want SetQuickPublishUrl.ps1 to publish the form to an external list, as this example shows:
.\SetQuickPublishUrl.ps1 -templateFile ‘d:\code\InfoPath\template.xsn’ -url 'http://sharepoint/lists/mylist/item/template.xsn' -autoPublish
Because the example uses the autoPublish parameter, the script publishes the template.xsn to the targeted SharePoint external list by handing off the work of uploading the form to UploadFileToList.ps1 (included with this article’s download).
SetQuickPublish.ps1 then adjusts the .xsf file (as Figure 3 shows) and calls the BrowserEnableUserFormTemplate method contained in the InfoPath Form Services service, as Figure 4 at the end of this article shows.
The .xsf-specific adjustment and calling the InfoPath Form Services method is necessary to allow the form to open in a browser window.
UploadFileToList.ps1 is a general purpose script for uploading any files to SharePoint lists, not just InfoPath templates. SetQuickPublishUrl.ps1 expects the local file path containing the file name and the URL to a target SharePoint list.
The script also contains an optional list folder parameter to specify a SharePoint folder name as the target location for the file. If you don't specify the list folder parameter, the script uploads a file to the root of the target SharePoint list.
SetQuickPublishUrl.ps1 calls UploadFileToList.ps1 like this:
.\UploadFileToList.ps1 -filePath $xsnPublishFile -listUrl $listUrl -listFolder "Item"
SetQuickPublishUrl.ps1 hard codes the listFolder parameter to the Item folder because this is the default folder location for Quick Published InfoPath Designer 2010 forms. The filePath and listUrl parameters get their values based on what was originally entered for the SetQuickPublishUrl.ps1.
With the previous SetQuickPublishUrl.ps1 command-line example, UploadFileToList.ps1 will upload the template file at d:\code\InfoPath\template.xsn to the Item folder of the SharePoint list located at http://sharepoint/lists/mylist.
Because the upload script relies on the SharePoint server object model, it has to run local to the SharePoint instance. Rewriting the script to call a web service or possibly the SharePoint client object model would enable publishing from outside the server, which InfoPath Designer 2010 supports.
As mentioned earlier, a SharePoint 2010 list using InfoPath forms can use a different form for create (new type), update (edit type), and read (display type) operations. It's also possible for two operations to use the same form, such as one form for create and update and another form for read.
There are many reasons why one form might not be enough. For example, you might want to show different fields based on the operation, or you might want to turn off the ribbon at the top of the form when performing a read operation, but you want to show the ribbon when performing a create or update operation.
After you publish your forms to a list folder using SetQuickPublishUrl.ps1, you can then change which InfoPath form gets used when performing an operation from within SharePoint Designer. Figure 5 shows the forms section of a list opened via SharePoint Designer that's using custom InfoPath forms.
The figure shows accessing the ASP.NET page assigned to the read operation. Open the advanced view of the page in SharePoint Designer. From there, you can change the FormLocation web part property of the BrowserFormWebPart to point to a renamed custom InfoPath form.
Figure 6 shows that the name "template-displayifs.xsn" is assigned as the custom form for the read operation. The SetQuickPublishUrl.ps1 script is designed to handle forms with different names.
As the previous command-line parameter for SetQuickPublishUrl.ps1 shows, the -url parameter contains the full path, including the file name, to the custom form. The script updates the .xsf file with the new form name before repackaging the renamed .xsn file.
Improving the Process
The scripts described in this article should save you a lot of time if you work in a multiform designer and multifarm deployment environment. We will also be finding a place to post the script on CodePlex so that you can contribute to improving the script for the SharePoint development/form designer community.
Ultimately, we hope that Microsoft makes these scripts obsolete by providing an easy way to update the dependencies you have to handle through the script or, in some cases, removing the dependencies altogether.
Microsoft has made Business Connectivity Services (BCS) solutions and many other types of SharePoint solutions first-class citizens in Visual Studio and Team Foundation Server. Once Microsoft does the same for InfoPath, developers can begin taking InfoPath seriously as a professional SharePoint forms development platform.
Figure 3: PowerShell Snippet Demonstrating how XML Attributes are Updated in the Manifest.xsf
# modify the .xsf file (xml)
\\[xml\\]$xmlDoc = Get-Content -Path $manifestXsfPath
# the namespace manager is important to resolving searches later
$nsmgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable)
# the next four lines update the quick publish urls
# locate the xsf2:solutionPropertiesExtension/xsf2:entity element and set the path attribute
$elementToModify = GetXmlNode $xmlDoc $nsmgr "//xsf2:solutionPropertiesExtension/xsf2:entity"
# Locate the xsf3:solutionPropertiesExtension2009/xsf3:solutionMode element and update the originalPublishUrl attribute.
$elementToModify = GetXmlNode $xmlDoc $nsmgr "//xsf3:solutionPropertiesExtension2009/xsf3:solutionMode"
# InfoPath stores the physical path of where the file was last saved.
# If the file is different from the location you currently work in (which happens a lot when working under source control)
# you will be prompted to save the file while publishing.
# To avoid that, in a case where you don't autopublish, you adjust the path according to the location of the file.
# However, to be able to autopublish the file, you need to set that path to empty string, otherwise the form will fail when brower enabling.
$xsnQuickPublishFolder = get-item -Path $envFolder
$xsnFile = Get-Item -Path $templateFile
$xsnQuickPublishFile = \\[System.IO.Path\\]::Combine($xsnQuickPublishFolder.FullName, $xsnFile.Name)
$elementToModify = GetXmlNode $xmlDoc $nsmgr "//xsf:xDocumentClass"
# Save the .xsf with the changes.
Set-Content -Path $manifestXsfPath -Value $xmlDoc.OuterXml
Figure 4: PowerShell Snippet Demonstrating how an InfoPath Form can be Browser Enabled
# Reference SharePoint assemblies.
$infopathAssembly = New-Object System.Reflection.AssemblyName("Microsoft.Office.InfoPath.Server, Version=184.108.40.206, Culture=Neutral, PublicKeyToken=71e9bce111e9429c")
# Get a form services service for the sharepoint farm
$localFarm = \\[Microsoft.SharePoint.Administration.SPFarm\\]::Local
$farmServices = $localFarm.Services
# Get a service by using the generic GetValue method. It works only when not specifying any method parameters,
# so the call looks like this: services.GetValue<FormsService>().
# The solution was posted by Lee Holmes
# and modified to support parameter-less method invocations.
# The line is wrapped to fit the page.
$formService = .\Invoke-GenericMethod $farmServices GetValue `
# Get the SPFile object representing the uploaded form.
$spSite = New-Object Microsoft.SharePoint.SPSite($listUrl)
$spWeb = $spSite.OpenWeb()
$uri = New-Object System.Uri $url
$listWebRelUrl = $uri.LocalPath.Substring(0, $uri.LocalPath.LastIndexOf("/"))
$formWebRelUrl = \\[string\\]::Concat($listWebRelUrl, "/", $xsnFile.Name)
$spFile = $spWeb.GetFile($formWebRelUrl)
# Call the service method to browser enable the form.