Programatically register InfoPath form for Web Browsing in SharePoint

A topic that is not particularly well document with SharePoint is how to deploy a web browser compatible InfoPath form via a feature.

Deploying a form through the Central Administration site provides such a form. This isn’t much use if you want to make the package that you ship to a client installable without involving some manual steps.

Installing and registering an InfoPath form, along with a custom content type is pretty straight forward once you know the required steps.

Start by creating a content type that inherits from the Forms type. The following is a sample content type’s markup.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ContentType ID="0x0101010076AEDEA7CA2A44B59ECB3D491618E052"
        Name="Custom Form CT"
        Group="Custom"
        Inherits="TRUE"
        RequireClientRenderingOnNew="FALSE"
        Version="0">
    <FieldRefs>
    </FieldRefs>
    <DocumentTemplate TargetName="/FormServerTemplates/CustomForm.xsn" />
  </ContentType> 
</Elements>

The important sections from the above markup are the ID and RequireClientRenderingOnNew attributes.

For your new content type to be usable in a Forms library (or for that matter the XmlFormView class) and use a web compatible InfoPath form it must inherit from the Form content type, which has an ID of 0x010101. To inherit from this all you have to do is add two zeros and then your own identifier.

The second attribute, RequireClientRenderingOnNew, specifies if the browser should use the application specified by the ProgId attribute (not shown here) when creating or viewing a form. By default this is set to true, setting to false will configure the content type to render the form in the browser, using the FormServer aspx page that’s located in the LAYOUTS folder.

The next step is to create your InfoPath form. My post here describes the necessary steps for publishing a web compatible form. The content type used in that post is designed for a workflow’s instantiation page. Deploying it that way automatically tries to register the form for web browsing.

Of course, that’s not much use if you want to deploy the form as content in a Forms library, for instance.

Now that you have your form and have added it to your project, the next thing you should do is change the deploy location for the xsn file. This is an important step, as the InfoPath feature receiver (mentioned in the next paragraph) will only look in the root folder of the feature for candidate files and by default when you add a file to a module it is deployed to its folder. To do this, select you xsn file in the Solution Explorer, then bring up the Properties pane.

XSN Properties

Expand the Deployment Location node and clear the Path attribute. This will change the deployment location for your xsn file to the root of the relevant feature.

Important, now edit the elements xml file and ensure the type attribute is set to GhostableInLibrary for the form. Failing to do this will mean the file will not be editable and the feature activation will not process the form and enable it for web browsing. I also set the Url for the module to point to the FormServerTemplates library, which will be created automatically as the feature code attempts to web browser enable the form(s).

<Module Name="XSN" Url="FormServerTemplates" >
  <File Path="TestForm.xsn" Url="TestForm.xsn" Type="GhostableInLibrary" />
</Module>

Here you have three options. The first is good if you are not bothered about the content type that is used. If this is the case, you can ignore the creation of the content type from the start of this post. This is because if you deploy the form as part of a feature and specify the XsnFeatureReceiver class from the Microsoft.Office.InfoPath.Server assembly as the receiver, your form is automatically registered for web browsing and a suitable content type is created under the Microsoft Office InfoPath group with your form associated. The assembly and class values are shown below.

Receiver Assembly = Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
Receiver Class = Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver

When using this feature receiver, you’ll also have to add an activation dependency to your feature, otherwise the receiver may not fire. The dependency is for the InfoPath Forms Service support feature with the GUID C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5.

Activation Dependency

With the second method if you were to deploy the above content type and then the form with the XsnFeatureReceiver feature receiver, not only will your form be registered, but your content type will also be enabled for displaying the form in the browser. This is only the case if your content type directly inherits from the Form content type. If your content type is once removed then this step will fail.

The last method is for content types that do not directly inherit from the Form type.

Add an event handler to the feature that deploys your form, this will remove the reference to any existing receiver that you may be referencing.

Add the Microsoft.Office.InfoPath.Server.dll assembly to your project.

Next add the following using declarations:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Permissions;
 
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Security;
using Microsoft.Office.InfoPath.Server.Administration;

This feature will implement the FeatureInstalled, FeatureActivated and FeatureUninstalling events. On installation we will register all forms that are being deployed as part of your feature. The activated event will update the content type to allow web rendering and uninstalling will unregister the forms from the site collection.

The installed method is shown below.

public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
    base.FeatureInstalled(properties);
 
    FormsService formsService = GetFormsService();
    if (formsService == null)
    {
        throw new ArgumentNullException("formsService",
                        string.Format("Unable to retrieve FormsService during installation of \"{0}\". Argument formsService was null."),
                                        properties.Feature.Definition.Name);
    }
 
    // Get list of form templates that are being deployed as part of this feature.
    List<String> formTemplates = GetInfoPathFormTemplates(properties.Definition.Properties, properties.Definition.RootDirectory);
    foreach (string formTemplate in formTemplates)
    {
        if (formsService.FormTemplates.ItemFromFile(formTemplate) == null)
        {
            FormTemplateCollection.RegisterFormTemplate(formTemplate, properties.Definition, false);
        }
    }
}

This method gets the FormsService using a custom private method which we’ll come to later. The FormsService object allows you to access the currently registered forms.

Next we call the GetInfoPathFormTemplates method, which again is a custom private method. This method returns a generic list of paths to the forms that are referenced in your feature. As with the previous method, I’ll show this later.

The last step of this event is to call the static method RegisterFormTemplate of the FormTemplateCollection class. The full namespace for this class is Microsoft.Office.InfoPath.Server.Administration. This method will register the form on the farm.

The uninstall event is very similar but this time unregisters the forms.

public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
    base.FeatureUninstalling(properties);
 
    FormsService formsService = GetFormsService();
    if (formsService == null)
    {
        throw new ArgumentNullException("formsService",
                            string.Format("Unable to retrieve FormsService during uninstallation of \"{0}\". Argument formsService was null."),
                            properties.Feature.Definition.Name);
    }
 
    // Get list of form templates that were deployed as part of this feature.
    List<String> formTemplates = GetInfoPathFormTemplates(properties.Definition.Properties, properties.Definition.RootDirectory);
    foreach (string formTemplate in formTemplates)
    {
        if (formsService.FormTemplates.ItemFromFile(formTemplate) != null)
        {
            formsService.FormTemplates.UnregisterFormTemplate(formTemplate, properties.Definition);
        }
    }
}

The main difference here is that to unregister we don’t call a static method of FormTemplateCollection but instead call the UnregisterFormTemplate method of the FormTemplates property contained in the FormsService object that was retrieved at the start of the method.

The two methods used by the installing and activated events are shown below:

private List<String> GetInfoPathFormTemplates(SPFeaturePropertyCollection properties, string featureRootPath)
{
    FileInfo[] filesInfo = new DirectoryInfo(featureRootPath).GetFiles("*.xsn");
    List<String> infoPathFormFileSpecs = new List<String>();
    foreach (FileInfo fileInfo in filesInfo)
    {
        infoPathFormFileSpecs.Add(Path.Combine(fileInfo.DirectoryName, fileInfo.Name));
    }
 
    return infoPathFormFileSpecs;
}
 
private FormsService GetFormsService()
{
    FormsService formsService = null;
    if (SPFarm.Local != null)
    {
        formsService = SPFarm.Local.Services.GetValue<FormsService>(string.Empty);
    }
    else
    {
        formsService = SPContext.Current.Site.WebApplication.Farm.Services.GetValue<FormsService>(string.Empty);
    }
    return formsService;
}

GetInfoPathFormTemplates looks in the root path of the feature for any file with the xsn extension and returns the path information.

GetFormsService attempts to get the FormsService object from the local farm, first by referencing the SPFarm object directly. Where this is null, it tries to retrieve it from SPContext.

The last piece of code to complete the registration is for the activated event. Here we will be updating a custom content type, twice removed from the Form type.

private const string MINIMALACTIVEXTOOPEN = "SharePoint.OpenXmlDocuments.2";
 
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = properties.Feature.Parent as SPSite;
    SPWeb currentWeb = site.RootWeb;
 
    // Update the custom content type to allow rendering in web browser.
    SPContentType ct = currentWeb.ContentTypes[new SPContentTypeId("0x0101010076AEDEA7CA2A44B59ECB3D491618E05200726740CD03C4478FB0897E6993DDE8D0")];
    if (ct.NewDocumentControl != MINIMALACTIVEXTOOPEN || ct.RequireClientRenderingOnNew)
    {
        ct.NewDocumentControl = MINIMALACTIVEXTOOPEN;
        // Ensure the RequireClientRenderingOnNew property is correctly set for web browsing.
        ct.RequireClientRenderingOnNew = false;
 
        // Don't forget to update the content type with the changes.
        ct.Update(true, false);
    }
}

As this example feature is scoped to site level, we only need to cast the Parent object to SPSite.

Using the root SPWeb object the custom content type is retrieved by its identifier. This could just as easily be a collection of content types, but for this example we are only updating one.

The two properties that are being updated are NewDocumentControl and RequireClientRenderingOnNew.

NewDocumentControl identifies the application used to create new document, which for InfoPath forms should be set to “SharePoint.OpenXmlDocuments.2”.

The second property, RequireClientRenderingOnNew will have already been defined in the content type markup, but it’s worthwhile making sure at this stage, otherwise you won’t be able to view the form in the web browser.

Now when you activate the feature you will have an InfoPath form that is able to be viewable within the browser. Remember that for this to work you will require the Enterprise version of SharePoint.

20 comments

  1. Vandeput says:

    Hi,

    Tnx for the great post!

    But how can I put values of the form in site columns when e publish the form this way? So I can you this information in an eventreceiver? I use the 2nd method.

    Regards

    Vandeput

    • Stuart says:

      Hi Vandeput,

      You’ll need to promote your columns in InfoPath. You do this from the Advanced Form Options settings within InfoPath. From this dialog there’s a section called Property Promotion that allows you to list the fields in the form that you want to promote.

      Stuart

  2. Lordan says:

    Hi Stuart

    I follow the post, but still open the form in client not in the browser, I dont know why that happen.

    also I have set RequireClientRenderingOnNew=”FALSE”

    • Stu says:

      One thing that I didn’t originally mention in my post was that the InfoPath feature receiver expects the XSN files to be in the root of the feature folder and not within a child folder. So, if you have your XSN in a module, check in Visual Studio the properties for it and expand the Deployment Location node. If there is a path declared, delete it – this will cause the file to be deployed to the root of the feature folder.

      Failing that it’s always a good idea to completely remove the solution and manually remove your content type (if it wasn’t automatically removed), perform an IIS reset and re-deploy.

    • Stu says:

      Also worth mentioning that while you’re activating the feature, have a program like the MSDN ULS Viewer running so you can examine what is happening as it’s activating.

  3. BKK says:

    Stuart, Thanks for the blog. I am trying to use the method with content type. The problem for me is I am still forced to open the form in client. Here are the steps I took
    1. Create the content type derived from Form as parent
    2. Added a new module and added my published xsn (published to network location)
    3. The same module also has the managed code dll
    4. Changed the Receiver assembly and class to refer to the XSN Feature Receiver.
    It deploys well and always opens the form in client. I have even made it to open in Browser on the Advanced settings in the library where content type is deployed.
    Is there any settings on module element file that I need to do. How does your module element file look like?

    Any help would be greatly appreciated.
    Thanks

    • Stuart says:

      When you created your InfoPath form was it marked as browser compatible?

      You can check this by going to the Tools menu in InfoPath Designer, then Form Options > Compatibility. Browser Compatibility should be checked.

      Failing that, where does your form get deployed to, the FormServerTemplates library in the root it the site collection?

  4. BKK says:

    Thanks for reply. The form options on the InfoPath is setup as the Browser compatibility. The form is set to Domain Trust as well.
    Form is getting deployed to FormServerTemplates in the root.

    Thanks again

  5. BKK says:

    I forgot to add. I am thinking it may be due to the fact that I have managed code. I am not sure how to incorporate the DLL in the elements file. Do I need to deploy this to GAC? or need to add to FormServerTemplates root?

  6. Atul Chhoda says:

    BKK, You would need to add the dll to features folder where the xsn is deployed, I tried GAC in 2007 and it didn’t work for me.

    Check out for deploying managed code:
    http://atulchhoda.wordpress.com/2010/01/13/deploying-an-infopath-form-with-code-behind-i-e-dll-as-content-type/

    Stuart, excellent article .

  7. Pooja says:

    Hi
    @BKK: did you find a way to acheive what you were trying. I have a similar situation and I am using Infopath 2010 and Sharepoint Server 2010. Please help.

  8. Allan says:

    I know this is slightly off topic but…. I cannot seem to get the content from my IPForm saved in the library. Can you give me a direction on what type of submit action I should be using? Submit to Hosting Environment or Submit to Document Library? And if it is the latter, how can I make it dynamic so that when I deploy to another environment, I don’t have to update and republish the IPForm.

  9. Caroline says:

    Thanks a lot for this post!
    I also wrote a blogpost with my findings:
    http://carolinepoint.wordpress.com/2012/12/06/how-to-deploying-an-infopath-form-as-a-feature/
    The event receivers are mostly yours 🙂

  10. Do you mind if I quote a few of your posts as long as I
    provide credit and sources back to your site? My blog is in
    the very same niche as yours and my visitors would certainly
    benefit from a lot of the information you provide here.
    Please let me know if this okay with you. Regards!

  11. Josh says:

    Hey Stu. I’m having a little trouble following your article after the line that says,”Here you have three options.” If I choose Option 1 (or two?) do I have to do the rest of the article with the code? That sounds dumb, but I’m trying not to go overboard if I don’t have to. I added the feature dependency and packaged and deployed, but don’t see any new content types. Your help would be greatly appreciated!
    Also, is this going to be my best approach to getting a hard-coded ContentType ID for my form? We’re using some SPMetal generated code that is dependent on the Content Type ID (built on a Dev machine).
    Thanks!

    • Stu says:

      Hi Josh,

      If you go for option one or two then you can ignore the code within the article.

      I’ve updated the post slightly as there’s a couple of important steps I didn’t mention. You need to ensure the XSN file is ghostable in the library by setting the type to GhostableInLibrary. Also, you need to set the Url to FormServerTemplates, which is the name of the forms library the form will be provisioned to:

      <Module Name="XSN" Url="FormServerTemplates" >
        <File Path="TestForm.xsn" Url="TestForm.xsn" Type="GhostableInLibrary" />
      </Module>
  12. Helen says:

    Stuart, thank You for the blog. I am trying to use this method with content type. A created content type, list definitions from content type and form into InfoPath (but I choice Security Level = “Full trust”). I deployed solution, go to site and see a SharePoint form by click “Add new item” or edit item into list.
    Where can I find a mistake?

Leave a Reply

Your email address will not be published. Required fields are marked *

Solve the maths problem shown below before posting: *

Follow

Get every new post delivered to your Inbox

Join other followers: