Monday, March 18, 2013

Packaging and Deploying InfoPath forms as SharePoint Sandbox Solutions via Visual Studio 2010

 

Introduction


SharePoint 2010 allows two ways of deploying InfoPath form templates containing business logic driven by managed code:
  1. Sandboxed solutions enable users to upload form templates with code or data connections in environments without full trust.
  2. Administrator-approved form templates are individually verified, uploaded, and activated by an administrator with full trust to the domain. More information on this is available here. http://technet.microsoft.com/en-us/library/cc262921.aspx

Any custom solution developed for SharePoint can only be deployed as a WSP package.  So if a custom solution contains an InfoPath form template, then following the deployment practice it’s required to package the form template in the form of a WSP.
This post focuses on how we can deploy an InfoPath 2010 form template with managed code as a sandbox solution using Visual Studio 2010 SharePoint project template.
The approach discussed in this post revolves around the Forms Services web service which provides methods to interface and work with InfoPath forms on SharePoint.
The Form Services web service exposes various web methods which allow interacting with forms published on the SharePoint server. Out of these methods, following are the two methods of our interest:
  1. BrowserEnableUserFormTemplate
  2. DesignCheckFormTemplate

 

BrowserEnableUserFormTemplate

 
This web method converts a form template at the specified SharePoint URL in a format that can be rendered in a Web browser. This method interns coverts the InfoPath form template with managed code as sandbox solution and uploads the same to the Solutions gallery of the site collection.
Parameter Name
Description
Example Values
formTemplateLocationA string representing the url of the form template.http://siteurl/library/forms/template.xsn

DesignCheckFormTemplate

 
Allows verifying whether an InfoPath 2010 form template can open and work correctly in a Web browser.
Parameter Name
Description
Example Values
lcidA string containing the locale id of the SharePoint site.1033
base64FormTemplatebase64 encoded current form template string.
applicationIdThe version of the InfoPath. This parameter must have value as InfoPath 14.InfoPath 14


Note: While publishing a form to SharePoint, InfoPath 2010 designer internally makes several calls to different SharePoint web services including the theses two methods of the Forms Services web service.

 

Article Prerequisites


In order to develop and execute the sample application used to demonstrate the approach, you must have the following:
  • Microsoft Visual Studio 2010
  • InfoPath Designer 2010
  • Microsoft Visual Studio Tools for Applications
  • A server that is running Microsoft SharePoint Server 2010

 

Solution Approach


The approach is divided in to the following part:
  1. Getting the SharePoint Environment Ready.
  2. Creating an InfoPath 2010 form template with managed code
  3. Creating the VS2010 solution
  4. Viewing the sample application.


Getting the SharePoint Environment Ready

Let’s first start by setting the SharePoint site to work with this sample.
  1. On your target SharePoint 2010 site collection, login as a site collection administrator and create a sub site using the Team Site template.
  2. On the newly created team site, add a new form library and name it EmployeeRecords. This will be the library where we will deploy our sandbox InfoPath form.
  3. Now create a new web part page named EmployeeForm on the team site under the Site Pages library.
  4. On the EmployeeForm web part page, add a new InfoPath Form Web Part available in the Forms category.
 



 Figure 1: Adding InfoPath Form Web Part


  1. Save the page and exit the page design mode.

In the above steps, we have added a web part page which will surface the InfoPath form from the EmployeeRecords form library via the EmployeeForm web part.


Creating an InfoPath 2010 form template with managed code

In this section we’ll be creating the InfoPath form with code behind to be hosted as browser-enabled form. The form template will contain a repeating table and two buttons with managed code allowing the user to add/remove rows in the repeating table.
  1. Create a blank InfoPath form.
  2. Add a new repeating table with 3 columns to the design surface. This action will add a group group1 along with its child group and fields under myFields in the form main data source.
  3. Now rename the repeating table groups and fields as shown in the image below:

Figure 2: InfoPath Form field schema

  1. Add two new button controls to the form design surface.
  2. From the button properties, set the first button’s label to Add Record and ID to AddRecord.
  3. Similarly, set the second button label as Delete Record and ID as DeleteRecord.
  4. In the form design surface, right click on the Employee repeating table and select Repeating Table Properties.
  5. On the Data tab in Repeating Table Properties window, uncheck the Allow users to insert and delete rows check box. Click Ok to close the properties.
This will hide the OOB insert menu on the repeating table thus restricting the end users to add/remove rows directly from the context menu.

Figure 3: Repeating Table Properties

  1. You can further update the form’s look and feel as required. Click Save to save the InfoPath form on the file system. Specify the form name as Employees.xsn.

Figure 4: Employee Information Form in design mode

  1. On the Developer tab inside the InfoPath form, click Language.
  2. Make sure the Form template code language is set to C#. Optionally you can also change the Project location.
  3. Click OK to close form options.
  4. On the developer tab click Code Editor to launch VSTA project for this form.
  5. Back in the InfoPath form, select the button Add Record and from the Properties ribbon tab, click Custom Code button to generate the click event handler for this button.
  6. In the VSTA project, replace the following code segment
public void AddRecord_Clicked(object sender, ClickedEventArgs e)
       {
           // Write your code here.
 }

With:
 
public void AddRecord_Clicked(object sender, ClickedEventArgs e)
       {
            const string employeegroup = "/my:myFields/my:Employees";
               XmlDocument xmldoc = new XmlDocument();
//Create a navigator at the employeegroup xpath.
               XPathNavigator employeeNavigator = MainDataSource.CreateNavigator().SelectSingleNode(employeegroup, NamespaceManager);
               if (employeeNavigator != null)
               {
//Create a new repeating group.
                   XmlNode group = xmldoc.CreateElement("EmployeeGroup", NamespaceManager.LookupNamespace("my"));
                   if (group != null)
                   {
//Create new repeating fields.
                       XmlNode name = xmldoc.CreateElement("Name", NamespaceManager.LookupNamespace("my"));
                       group.AppendChild(name);


                       XmlNode id = xmldoc.CreateElement("ID", NamespaceManager.LookupNamespace("my"));
                       group.AppendChild(id);


                       XmlNode age = xmldoc.CreateElement("Age", NamespaceManager.LookupNamespace("my"));
                       group.AppendChild(age);


                       xmldoc.AppendChild(group)
// Add the new repeating group back to the main group.       employeeNavigator.AppendChild(xmldoc.DocumentElement.CreateNavigator());
                   }
               }
           }
           catch
           {
               // Display on UI/Log.
           }
 }
In above button event code, we are adding a new row to the Employee repeating table. Build the solution to save changes.
Note: The XPath of the Employee group can be obtained by right clicking the Employee group in the form fields task pane and select Copy XPath from the context menu.

  1. Back in the InfoPath form, select the button Delete Record and from the Properties ribbon tab, click Custom Code button to generate the click event handler for this button.
  2. In the VSTA project, replace the following code segment
public void DeleteRecord_Clicked(object sender, ClickedEventArgs e)
       {
           // Write your code here.
 }
 
With:
 
public void DeleteRecord_Clicked(object sender, ClickedEventArgs e)
       {
try
           {
               const string employeegroup = "/my:myFields/my:Employees";
               //Create a navigator at the employeegroup xpath.
               XPathNavigator employeeNavigator = MainDataSource.CreateNavigator().SelectSingleNode(employeegroup, NamespaceManager);
               if (employeeNavigator != null)
               {
                   // Select all the childrens.
                   XPathNodeIterator iterator = employeeNavigator.SelectChildren(XPathNodeType.Element);


                   // Move to the first children.
                   iterator.MoveNext();
                   // Check if this is the last row or not.
                   if (iterator.Count > 1)
                   {
                       XPathNavigator current = iterator.Current;
                       // Delete the element.
                       current.DeleteSelf();
                   }
                   else
                   {
                       // Call the AddRecord method to add a blank row as last row.
                       AddRecord_Clicked(null, null);
                       // Delete the last row.
                       XPathNavigator current = iterator.Current;
                       current.DeleteSelf();
                   }
               }
           }
           catch
           {
               // Display on UI.
      }
 }

In above button event code, we are removing the first row in the Employee repeating table. Also when we reach the last row in the table, then we call the AddRecord_Clicked method to insert a blank row.

  1. Build the solution and close VSTA editor. Now the next step is to make the form template ready to be published as a sandbox solution.
  2. Back in the InfoPath client, click on File menu to open the office backstage area and select click Publish.
 Figure 5: InfoPath Backstage Area
  1. Click SharePoint Server to publish the form to SharePoint.

Figure 6: InfoPath Backstage Area - Publish Menu

  1. Enter your SharePoint team site URL and click Next.
Figure 7: Publishing Wizard

  1. Select Administrator-approved form template (advanced) option and click Next.
Figure 8: Publishing Wizard

  1. In the next window, click Browse to open the local folder where you saved the form.
  2. Create a new folder PublishedForm and save the form as Published_Employees.xsn.
  3. Click Next button twice.
  4. Click Publish to publish the form to the selected folder and then click Close.
  5. Close InfoPath designer.

In the above steps we have published the form as an Administrator-approved form template. This updates the form manifest file with some required changes and enables the form to be published as a sandbox solution.



Creating the VS2010 solution

In this section, we will create a VS2010 SharePoint solution which will deploy the form we created in the above section as a sandbox solution.
  1. Create a new Empty SharePoint Project and name it InfoPathSandboxDeployment. Ensure that InfoPathSandboxDeployment is deployed as a Farm solution on the team site we created in the above section.
  2. Add a new web scoped feature to the InfoPathSandboxDeployment project and name it EmployeeForm. Update the feature display name and description as per the image below.

Figure 9: Employee Record Form feature

  1. Right click on the EmployeeForm feature in the solution explorer and select Add Event Receiver from the context menu to add the feature receiver class for this feature.
  2. Figure 10: Adding Event Receiver

    Later we will hook the necessary code in the feature receiver to deploy our form template when the EmployeeForm feature is activated.
  3. Now add a new Module SharePoint item to the project and name it InfoPathForms. The InfoPathForms module will contain the InfoPath form template we want to deploy.
  4. Note: The InfoPathForms module automatically gets added to the EmployeeForm feature as shown below.
    Figure 11: Employee Record Form Feature with InfoPathForms module

  5. Remove the default Sample.txt file from the module.
  6. Copy the Published_Employees.xsn form from the PublishedForm folder and paste it under the InfoPathForms module. 
  7. Note: It’s mandatory that you copy the published Published_Employees.xsn file and not the original Employee.xsn. 
  8. From the Elements.xml file under the InfoPathForms module, remove the following file tag.
  9. <File Path="InfoPathForms\Published_Employees.xsn" Url="InfoPathForms/Published_Employees.xsn" />
  10. Add a Web Reference in the project to the Forms Services web service at the following URL and provide the web reference name as FormsService: http://siteurl/_vti_bin/formsservices.asmx
  11. Figure 12: Forms Service Web Service
  12. Add new class file to the project and name it SharePointHelper. The SharePointHelper class will contain helper methods to support the deployment.
  13. Add new class file to the project and name it FormsServicesHelper. The FormsServicesHelper class will contain methods wrapping the Forms Services web methods.
  14. At this stage, your project should look similar to the image below. Now we are all set to write the required code to deploy our form as a sandbox solution to SharePoint. 
  15. Let’s start by adding code to the SharePointHelper class. In the SharePointHelper class, replace the following code segment
    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;



    namespace InfoPathSandboxDeployment

    {

       class SharePointHelper
       {
       }
    }


    With:
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Utilities;


    namespace InfoPathSandboxDeployment
    {
       // Helper functions for SharePoint operations.
       public class SharePointHelper
       {
           ///








           /// Provisions the given file stream as file in SharePoint library.
           ///


       /// SharePoint library.
       /// Stream to be provisioned.
       /// SharePoint file object.

       public static SPFile ProvisionFileToFormsFolder(SPList list, Stream stream)
       {
           if (list == null)
           {
               throw new ArgumentNullException("list", "list cannot be a null reference.");
           }


           if (stream == null)
           {
               throw new ArgumentNullException("stream", "stream cannot be a null reference.");
           }


           try
           {
               // Gets the FORMS folder in the form library.
               SPFolder forms = list.RootFolder.SubFolders.OfType<SPFolder>().FirstOrDefault(i => i.Name.ToUpperInvariant() == "FORMS");
               if (forms != null)
               {
                   // Add the file with the name combining the site name, list name and file name.
                   string formName = string.Format(list.ParentWeb.UICulture, "{0}_{1}_template.xsn", (string.IsNullOrEmpty(list.ParentWeb.Name) ? list.ParentWeb.Title : list.ParentWeb.Name), list.Title);
                   SPFile formtemplate = forms.Files.Add(formName, stream, true);


                   return formtemplate;
               }
           }
           catch
           {
               throw;
           }


           return null;
       }


       ///








       /// Updates the content type document template.
       ///

       /// SharePoint list.
       /// New template path.
       public static void ChangeContentTypeDocumentTemplate(SPList list, string templatepath)
       {
           if (list == null)
           {
               throw new ArgumentNullException("list", "list cannot be a null reference.");
           }


           if (string.IsNullOrEmpty(templatepath))
           {
               throw new ArgumentNullException("templatepath", "templatepath cannot be a null reference.");
           }


           try
           {
               // Get the default content type.
               SPContentType ctype = list.ContentTypes[0];
               // Update the document template path.
               ctype.DocumentTemplate = SPUtility.ConcatUrls(list.RootFolder.ServerRelativeUrl, templatepath);
               // Update the content type.
               ctype.Update();
           }
           catch
           {
               throw;
           }
       }


       ///








       /// Copies and renames the form template file to the system default file name - template.xsn
       ///

       /// SPFile object for the file to be renamed.
       public static void CopyAndRenameFormTemplate(SPFile formTemplate)
       {
           if (formTemplate == null)
           {
               throw new ArgumentNullException("formTemplate", "formTemplate cannot be null.");
           }


           try
           {
               string folderPath = SPUtility.GetFullUrl(formTemplate.Web.Site, formTemplate.ParentFolder.ServerRelativeUrl);
               // Create a new path with new file name.
               string newFormTemplatePath = SPUtility.ConcatUrls(folderPath, "template.xsn");
               // Copy the old form template at the new path.
               formTemplate.CopyTo(newFormTemplatePath, true);
               // Delete the old form template.
               formTemplate.Delete();
           }
           catch
           {
               throw;
           }
       }


       ///








       /// Cleans the solution gallery by removing the previously deployed sandbox solution with the same name.
       ///

       /// Absolute URL of the site.
       /// Name of the solution to be deleted.
       public static void DoSolutionsGalleryCleanup(string url, string solutionName)
       {
           try
           {
               using (SPSite site = new SPSite(url))
               {
                   // Get the collection of all the sandbox solutions.
                   SPUserSolutionCollection userSolColl = site.Solutions;
                   // Find the solution name matching the priovided name.
                   IEnumerable<SPUserSolution> solutions = userSolColl.OfType<SPUserSolution>().ToList().FindAll(i => i.Name.Contains(solutionName));
                   foreach (SPUserSolution solution in solutions)
                   {
                       // Remove the solution.
                       userSolColl.Remove(solution);
                   }


                   // Gets the solution file reference from the Solutions gallery.
                   SPList solutionsGallery = site.GetCatalog(SPListTemplateType.SolutionCatalog);
                   List<SPFile> files = solutionsGallery.RootFolder.Files.OfType<SPFile>().ToList().FindAll(i => i.Name.Contains(solutionName));
                   foreach (SPFile file in files)
                   {
                       // Delete the file.
                       file.Delete();
                   }
               }
           }
           catch
           {
               throw;
           }
       }
   }
}

The above class contains the following 4 methods

  1. ProvisionFileToFormsFolderAdds the given stream of the form template as a new file on the given SharePoint library’s FORMS folder. The name of the new file is derived based on the current site name and current library name. Naming the file in this manner helps to keep the file name unique throughout the site collection once the form is deployed to the Solutions gallery.
  2. ChangeContentTypeDocumentTemplateUpdates the document template URL to the new URL of the default content type in the given SharePoint library.
  3. CopyAndRenameFormTemplate Copies the given SharePoint file to the same folder location with the name as template.xsn and deletes the original file. This helps to follow the SharePoint OOB way of naming the template files for a library.
  4. DoSolutionsGalleryCleanupCleans the site collection’s Solution gallery by deactivating and deleting the sandbox solution which matches the supplied solutionname. This method helps to clean the solution gallery every time the solution or EmployeeForm feature is activated.
  • Build the solution.
  • In the  FormsServicesHelper  class, replace the following code segment
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;


    namespace InfoPathSandboxDeployment
    {
       class FormsServicesHelper
       {
       }
    }


    With:
    using System.Linq;
    using InfoPathSandboxDeployment.FormsServiceProxy;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Utilities;
    using System;
    using System.IO;


    namespace InfoPathSandboxDeployment
    {
       ///








       /// Forms services class containing
       /// helper methods for sandbox deployment.
       ///

  •    public class FormsServicesHelper
       {
           ///








           /// Static object of the formsservice class.
           ///

           private static FormsServicesWebService formsServiceProxyObj = null;


           ///








           /// Converts a stream object equivalent to base64 string.
           ///

           /// Stream object.
           /// Base64 tring equivalent of the stream.

           public static string ConvertStreamToString(Stream stream)
           {
               if (stream == null)
               {
                   throw new ArgumentNullException("stream", "stream cannot be a null reference.");
               }


               try
               {
                   string base64text = string.Empty;
                   var br = new BinaryReader(stream);
                   byte[] byteArr = br.ReadBytes((int)stream.Length);
                   if (byteArr != null && byteArr.Length > 0)
                   {
                       // Convert the stream to base64 strig format.
                       base64text = Convert.ToBase64String(byteArr, Base64FormattingOptions.None);
                   }


                   return base64text;
               }
               catch
               {
                   throw;
               }
           }


           ///








           /// Checks if the formtemplate can be browser enabled.
           ///

           /// Base64 presentation of the Form template.
           /// Absolute url of the site.
           /// Ture if the formtemplate can be browser enabled else False.

           public static bool IsBrowserFormTemplate(string templateMarkup, string url)
           {
               if (string.IsNullOrEmpty(templateMarkup))
               {
                   throw new ArgumentNullException("templateMarkup", "templateMarkup cannot be empty.");
               }


               if (string.IsNullOrEmpty(url))
               {
                   throw new ArgumentNullException("url", "url cannot be empty.");
               }


               try
               {
                   // Checking if the formsServiceProxyObj is null.
                   if (formsServiceProxyObj == null)
                   {
                       // Initializing the formsServiceProxyObj object.
                       CreateFormsServiceProxy(url);
                   }


                   // Calling the DesignCheckFormTemplate method to verify the form template.
                   // Storing the result in DesignCheckerInformation object.
                   // InfoPath 14 reflects the current InfoPath version.
                   DesignCheckerInformation designInfo = formsServiceProxyObj.DesignCheckFormTemplate(1033, templateMarkup, "InfoPath 14");
                   // Checking if the DesignCheckerInformation contains any error messages.
                   Message errmsg = designInfo.Messages.FirstOrDefault(i => i.Id == 1 && i.Type == MessageType.Error);
                   if (errmsg != null)
                   {
                       // The form template cannot be browser enabled.
                       return false;
                   }
                   else
                   {
                       // The form template can be browser enabled.
                       return true;
                   }
               }
               catch
               {
                   throw;
               }
           }


           ///








           /// Converts the given form template to Browser enabled form.
           ///

           /// SharePoint File object of the form template.
           /// Stores error message if an exception occures while conversion.
           /// True if the operation is successfull else False.

           public static bool ConvertFormTemplateToBrowserForm(SPFile formTemplate, ref string error)
           {
               if (formTemplate == null)
               {
                   throw new ArgumentNullException("formTemplate", "formTemplate cannot be null.");
               }


               try
               {
                   string webURL = formTemplate.Web.Url;
                   // Create the absolute URL to the file.
                   string fileUrl = SPUtility.ConcatUrls(webURL, formTemplate.Url);
                   // Do the solutions gallery cleanup before adding a new sandbox solution.
                   SharePointHelper.DoSolutionsGalleryCleanup(formTemplate.Web.Site.Url, string.Concat("_" + formTemplate.Name + "_"));
                   // Call the BrowserEnableUserFormTemplate method.
                   if (BrowserEnableUserFormTemplate(fileUrl, ref error))
                   {
                       // Rename the browser enabled form template file to template.xsn.
                       // This will ensure that all OOB links to the form template will be intact.
                       SharePointHelper.CopyAndRenameFormTemplate(formTemplate);
                       // Return true of the convrsion was successfull.
                       return true;
                   }
               }
               catch
               {
                   throw;
               }


               return false;
           }


           ///








           /// Creates an instance of the formssservice object.
           ///

           /// Absolute URL of the SharePoint site.
           private static void CreateFormsServiceProxy(string siteurl)
           {
               formsServiceProxyObj = new FormsServicesWebService();
               formsServiceProxyObj.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
               // Setting the URL to point the current site's forms service.
               formsServiceProxyObj.Url = SPUtility.ConcatUrls(siteurl, "/_vti_bin/formsservices.asmx");
           }


           ///








           /// Calls the forms services BrowserEnableUserFormTemplate to convert the given form to browser enable.
           ///

           /// Absolute URL of the form template file.
           /// Stores error message if an exception occures.
           /// True if the operation is successfull.

           private static bool BrowserEnableUserFormTemplate(string formTemplateURL, ref string error)
           {
               try
               {
                   // Execute the web method and collect result in MessagesResponse.
                   MessagesResponse msgResponse = formsServiceProxyObj.BrowserEnableUserFormTemplate(formTemplateURL);
                   // Check if the MessagesResponse contsins any error message.
                   if (msgResponse != null)
                   {
                       if (msgResponse.Messages.Count() > 0)
                       {
                           // Initialize the error string with error response.
                           error = "Error converting the form to browser enabled.
    "
    + msgResponse.Messages[0].DetailedMessage;
                       }
                       else
                       {
                           // Successfull conversion.
                           return true;
                       }
                   }
               }
               catch
               {
                   throw;
               }


               return false;
           }
       }
    }
    The FormsServicesHelper class is the heart of the application and contains all the methods required for deploying the InfoPath form as sandbox solutions. The class acts as a wrapper over the Forms Services web service and contain the following methods:
    1. CreateFormsServiceProxyInitializes the proxy object of the Forms Services web service in the context of the current SharePoint site.
    2. ConvertStreamToStringHelper method which converts the given stream to the equivalent base64 encoded string.
    3. IsBrowserFormTemplate – Ensure whether the given form template can be browser enabled. This method accepts the base64 encoded string of the form template to be checked and calls the DesignCheckFormTemplate web method of the Forms Services web service. The resultant of this call is captured in DesignCheckerInformation object which is then evaluated to see if the form is convertible to browser form.
    4. BrowserEnableUserFormTemplateCalls the BrowserEnableUserFormTemplate web method of the Forms Services web service and converts the form template at the given URL to a browser enabled form. The response to this call is captured in the MessagesResponse object which is evaluated to check for a successful conversion. Error message during the conversion is assigned to a ref error object.A successful conversion in-turn deploys the InfoPath form as a sandbox solution to the current site collection.
    5. ConvertFormTemplateToBrowserFormDeploys the given SharePoint file object corresponding to the InfoPath form template as a sandbox solution. The method first of all internally calls the DoSolutionsGalleryCleanup method of the SharePointHelper class to check and remove any previously deployed sandbox solution for the same InfoPath form template. Further the BrowserEnableUserFormTemplate method is called to convert the form to browser enabled form. If the conversion is successful, then the CopyAndRenameFormTemplate method of the SharePointHelper class is called to rename the converted form template file as template.xsn.  Any error during the form conversion is initialized in a ref error object.


  • Build the solution.
  • At this stage we are done writing all the helper methods for supporting the form deployment as a sandbox solution. In the next steps we’ll wire the feature receiver with these helper methods and complete the deployment.
  • Open the EmployeeFormEventReceiver class and add the following using statements:
    using System.IO;
    using Microsoft.SharePoint.Utilities;
  • Further replace the commented FeatureActivated method with the one below:
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
           {
               // Current web object.
               SPWeb currentWeb = null;

               try
               {
                   string error = string.Empty;
                   // Initializing the web object.
                   currentWeb = properties.Feature.Parent as SPWeb;
                   if (currentWeb != null)
                   {
                       // Turning on the unsafeupdates property to enable content update.
                       currentWeb.AllowUnsafeUpdates = true;
                       // Reading the InfoPath form template from the feature folder.
                       Stream formStream = properties.Definition.GetFile("InfoPathForms\\Published_Employees.xsn");
                       // Converting stream to base66 string format.
                       string formAsString = FormsServicesHelper.ConvertStreamToString(formStream);
                       // Checking if the desired form library exist and creating the SP object.
                       SPList formLibrary = currentWeb.Lists.TryGetList("EmployeeRecords");
                       // Checking if the form template can be browser enabled.
                       if (formLibrary != null && FormsServicesHelper.IsBrowserFormTemplate(formAsString, currentWeb.Url))
                       {
                           // Provisioning the form template to form library.
                           SPFile formTemplate = SharePointHelper.ProvisionFileToFormsFolder(formLibrary, formStream);
                           // Converting the uploaded form template to browser form.
                           if (FormsServicesHelper.ConvertFormTemplateToBrowserForm(formTemplate, ref error))
                           {
                               // updating the library's default content type's document template to the new form template.
                               SharePointHelper.ChangeContentTypeDocumentTemplate(formLibrary, "/forms/template.xsn");
                           }
                           else if (!string.IsNullOrEmpty(error))
                           {
                               // Transfer to error page with description.
                               SPUtility.TransferToErrorPage(error);
                           }
                       }
                   }
               }
               catch
               {
                   throw;
               }
               finally
               {
                   if (currentWeb != null)
                   {
                       // Turning off the unsafeupdates.
                       currentWeb.AllowUnsafeUpdates = false;
                   }
               }
           }

    The following section summarizes the activities in the Feature Activated event:
    1. The event starts by creating a reference to the current SharePoint web object and setting the AllowUnsafeUpdates web property to true. This will ensure that any update to the SharePoint database is made persistent.
    2. Next step reads the Published_Employees.xsn file from the Feature definition and creates a stream object formStream. The stream is further processed to create a base64 equivalent string.  
    3. In the next statement a reference to the target EmployeeRecord form library is created.
    4. If the EmployeeRecord library exists, a call is made to the IsBrowserFormTemplate method to ensure that this form is ready for browser conversion.
    5. If the above condition evaluates to true, then the formStream is provisioned as a SPFile to the EmployeeRecord form library.
    6. The SPFile reference to the uploaded form template is passed as a parameter to the ConvertFormTemplateToBrowserForm method for  deployment.
    7. If the deployment is successful, then the default content type of the EmployeeRecord form library is updated to point to the converted browser form template.
    8. If the deployment is a failure then the error during conversion is thrown as a message to the SharePoint error page.
    9. The finally block turns off the AllowUnsafeUpdates web property.
     
  • Build and deploy the solution.

  •  



    Viewing the sample application


    1. After the deployment is successful, navigate to the Solutions gallery of the target SharePoint site collection. The Published_Employees.xsn form should be visible as a Sandbox solution in the activated state.


    Figure 14: Solutions Gallery

     

    1. Also, navigate to the Manage Site Feature page under site settings of the team site created above. The feature Employee Record Form is visible and active.


     Figure 15: Employee Record Form feature activated
     
    1. Navigate to the EmployeeForm.aspx page created in the above section.
    2. Edit the InfoPath Form Web Part by clicking the link Click here to open the tool pane on the web part body.
    3. In the InfoPath Form Web Part tool pane, select EmployeeRecord under the List or Library dropdown.
    4. Click OK on the tool pane to accept the changes.
    5. This displays the Employee form in the browser.

     Figure 16: Employee Information Form in browser

     
    1. Click Add Record button on the form to add a new row to the table. This ensures that the managed code is working as expected.
    2. Click Delete Record button on the form to remove the first row from the table.
     

     

    Summary

    In this post, we have learned how we can leverage the SharePoint 2010 Forms Services Web Service to deploy an InfoPath 2010 Form template with managed code as a sandbox solution using VS2010.

     

    3 comments:

    1. hi, i get the following error when calling DesignCheckFormTemplate.

      The current user does not have appropriate permissions for verifying browser compatibility on this site.

      any ideas? i am a farm admin.

      ReplyDelete
    2. ASIA PACIFIC OFFSET provides a first class mock-up service and a high quality press proofing service that combines the best craftsmanship and the latest technology.
      Central America offshore printing services

      ReplyDelete
    3. Excellent! Thanks for this - I've been looking at this feature for ages. Followed your instructions and it works a treat!

      ReplyDelete