{"id":175,"date":"2010-12-14T22:22:14","date_gmt":"2010-12-14T22:22:14","guid":{"rendered":"http:\/\/www.stuartroberts.net\/?p=175"},"modified":"2013-05-19T20:10:46","modified_gmt":"2013-05-19T19:10:46","slug":"programatically-register-infopath-form-for-web-browsing-in-sharepoint","status":"publish","type":"post","link":"https:\/\/www.stuartroberts.net\/index.php\/2010\/12\/14\/programatically-register-infopath-form-for-web-browsing-in-sharepoint\/","title":{"rendered":"Programatically register InfoPath form for Web Browsing in SharePoint"},"content":{"rendered":"<p>A topic that is not particularly well document with SharePoint is how to deploy a web browser compatible InfoPath form via a feature.<\/p>\n<p>Deploying a form through the Central Administration site provides such a form. This isn&#8217;t much use if you want to make the package that you ship to a client installable without involving some manual steps.<\/p>\n<p>Installing and registering an InfoPath form, along with a custom content type is pretty straight forward once you know the required steps.<\/p>\n<p>Start by creating a content type that inherits from the <em>Forms<\/em> type.  The following is a sample content type&#8217;s markup.<br \/>\n<!--more--><\/p>\n<pre lang=\"xml\">\r\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Elements xmlns=\"http:\/\/schemas.microsoft.com\/sharepoint\/\">\r\n  <ContentType ID=\"0x0101010076AEDEA7CA2A44B59ECB3D491618E052\"\r\n        Name=\"Custom Form CT\"\r\n        Group=\"Custom\"\r\n        Inherits=\"TRUE\"\r\n        RequireClientRenderingOnNew=\"FALSE\"\r\n        Version=\"0\">\r\n    <FieldRefs>\r\n    <\/FieldRefs>\r\n    <DocumentTemplate TargetName=\"\/FormServerTemplates\/CustomForm.xsn\" \/>\r\n  <\/ContentType> \r\n<\/Elements>\r\n<\/pre>\n<p>The important sections from the above markup are the <em>ID<\/em> and <em>RequireClientRenderingOnNew<\/em> attributes.<\/p>\n<p>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.<\/p>\n<p>The second attribute, <em>RequireClientRenderingOnNew<\/em>, 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 <em>FormServer aspx<\/em> page that&#8217;s located in the <em>LAYOUTS<\/em> folder.<\/p>\n<p>The next step is to create your InfoPath form.  My post <a href=\"http:\/\/www.stuartroberts.net\/index.php\/2010\/11\/30\/deploying-infopath-form-sharepoint\">here<\/a> describes the necessary steps for publishing a web compatible form.  The content type used in that post is designed for a workflow&#8217;s instantiation page.  Deploying it that way automatically tries to register the form for web browsing.<\/p>\n<p>Of course, that&#8217;s not much use if you want to deploy the form as content in a Forms library, for instance.<\/p>\n<p>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.<\/p>\n<div id=\"attachment_499\" style=\"width: 300px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/XSN-Properties.jpg\"><img aria-describedby=\"caption-attachment-499\" decoding=\"async\" loading=\"lazy\" src=\"http:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/XSN-Properties.jpg\" alt=\"\" title=\"XSN Properties\" width=\"344\" height=\"325\" class=\"aligncenter size-full wp-image-1472\" srcset=\"https:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/XSN-Properties.jpg 344w, https:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/XSN-Properties-300x283.jpg 300w\" sizes=\"(max-width: 344px) 100vw, 344px\" \/><\/a><p id=\"caption-attachment-499\" class=\"wp-caption-text\">XSN Properties<\/p><\/div>\n<p>Expand the <em>Deployment Location<\/em> node and clear the <em>Path<\/em> attribute.  This will change the deployment location for your xsn file to the root of the relevant feature.<\/p>\n<p><strong>Important<\/strong>, 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).<\/p>\n<pre lang=\"xml\">\r\n<Module Name=\"XSN\" Url=\"FormServerTemplates\" >\r\n  <File Path=\"TestForm.xsn\" Url=\"TestForm.xsn\" Type=\"GhostableInLibrary\" \/>\r\n<\/Module>\r\n<\/pre>\n<p>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 <em>Microsoft Office InfoPath<\/em> group with your form associated.  The assembly and class values are shown below.<\/p>\n<p><em>Receiver Assembly<\/em> = Microsoft.Office.InfoPath.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c<br \/>\n<em>Receiver Class<\/em> = Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver<\/p>\n<p>When using this feature receiver, you&#8217;ll also have to add an activation dependency to your feature, otherwise the receiver may not fire.  The dependency is for the <em>InfoPath Forms Service support<\/em> feature with the GUID <em>C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5<\/em>.<\/p>\n<div id=\"attachment_501\" style=\"width: 310px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/Activation-Dependency.png\"><img aria-describedby=\"caption-attachment-501\" decoding=\"async\" loading=\"lazy\" src=\"http:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/Activation-Dependency-300x48.png\" alt=\"\" title=\"Activation Dependency\" width=\"300\" height=\"48\" class=\"size-medium wp-image-501\" srcset=\"https:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/Activation-Dependency-300x48.png 300w, https:\/\/www.stuartroberts.net\/wp-content\/uploads\/2010\/12\/Activation-Dependency.png 484w\" sizes=\"(max-width: 300px) 100vw, 300px\" \/><\/a><p id=\"caption-attachment-501\" class=\"wp-caption-text\">Activation Dependency<\/p><\/div>\n<p>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 <em>Form<\/em> content type.  If your content type is once removed then this step will fail.<\/p>\n<p>The last method is for content types that do not directly inherit from the <em>Form<\/em> type.<\/p>\n<p>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.<\/p>\n<p>Add the <em>Microsoft.Office.InfoPath.Server.dll<\/em> assembly to your project.<\/p>\n<p>Next add the following using declarations:<\/p>\n<pre lang=\"csharp\">\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.IO;\r\nusing System.Runtime.InteropServices;\r\nusing System.Security.Permissions;\r\n\r\nusing Microsoft.SharePoint;\r\nusing Microsoft.SharePoint.Administration;\r\nusing Microsoft.SharePoint.Utilities;\r\nusing Microsoft.SharePoint.Security;\r\nusing Microsoft.Office.InfoPath.Server.Administration;\r\n<\/pre>\n<p>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.<\/p>\n<p>The installed method is shown below.<\/p>\n<pre lang=\"csharp\">\r\npublic override void FeatureInstalled(SPFeatureReceiverProperties properties)\r\n{\r\n    base.FeatureInstalled(properties);\r\n\r\n    FormsService formsService = GetFormsService();\r\n    if (formsService == null)\r\n    {\r\n        throw new ArgumentNullException(\"formsService\",\r\n                        string.Format(\"Unable to retrieve FormsService during installation of \\\"{0}\\\". Argument formsService was null.\"),\r\n                                        properties.Feature.Definition.Name);\r\n    }\r\n\r\n    \/\/ Get list of form templates that are being deployed as part of this feature.\r\n    List<String> formTemplates = GetInfoPathFormTemplates(properties.Definition.Properties, properties.Definition.RootDirectory);\r\n    foreach (string formTemplate in formTemplates)\r\n    {\r\n        if (formsService.FormTemplates.ItemFromFile(formTemplate) == null)\r\n        {\r\n            FormTemplateCollection.RegisterFormTemplate(formTemplate, properties.Definition, false);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>This method gets the FormsService using a custom private method which we&#8217;ll come to later.  The FormsService object allows you to access the currently registered forms.<\/p>\n<p>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&#8217;ll show this later.<\/p>\n<p>The last step of this event is to call the static method <em>RegisterFormTemplate<\/em> of the <em>FormTemplateCollection<\/em> class.  The full namespace for this class is <em>Microsoft.Office.InfoPath.Server.Administration<\/em>.  This method will register the form on the farm.<\/p>\n<p>The uninstall event is very similar but this time unregisters the forms.<\/p>\n<pre lang=\"csharp\">\r\npublic override void FeatureUninstalling(SPFeatureReceiverProperties properties)\r\n{\r\n    base.FeatureUninstalling(properties);\r\n\r\n    FormsService formsService = GetFormsService();\r\n    if (formsService == null)\r\n    {\r\n        throw new ArgumentNullException(\"formsService\",\r\n                            string.Format(\"Unable to retrieve FormsService during uninstallation of \\\"{0}\\\". Argument formsService was null.\"),\r\n                            properties.Feature.Definition.Name);\r\n    }\r\n\r\n    \/\/ Get list of form templates that were deployed as part of this feature.\r\n    List<String> formTemplates = GetInfoPathFormTemplates(properties.Definition.Properties, properties.Definition.RootDirectory);\r\n    foreach (string formTemplate in formTemplates)\r\n    {\r\n        if (formsService.FormTemplates.ItemFromFile(formTemplate) != null)\r\n        {\r\n            formsService.FormTemplates.UnregisterFormTemplate(formTemplate, properties.Definition);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>The main difference here is that to unregister we don&#8217;t call a static method of <em>FormTemplateCollection<\/em> but instead call the <em>UnregisterFormTemplate<\/em> method of the <em>FormTemplates<\/em> property contained in the <em>FormsService<\/em> object that was retrieved at the start of the method.<\/p>\n<p>The two methods used by the installing and activated events are shown below:<\/p>\n<pre lang=\"csharp\">\r\nprivate List<String> GetInfoPathFormTemplates(SPFeaturePropertyCollection properties, string featureRootPath)\r\n{\r\n    FileInfo[] filesInfo = new DirectoryInfo(featureRootPath).GetFiles(\"*.xsn\");\r\n    List<String> infoPathFormFileSpecs = new List<String>();\r\n    foreach (FileInfo fileInfo in filesInfo)\r\n    {\r\n        infoPathFormFileSpecs.Add(Path.Combine(fileInfo.DirectoryName, fileInfo.Name));\r\n    }\r\n\r\n    return infoPathFormFileSpecs;\r\n}\r\n\r\nprivate FormsService GetFormsService()\r\n{\r\n    FormsService formsService = null;\r\n    if (SPFarm.Local != null)\r\n    {\r\n        formsService = SPFarm.Local.Services.GetValue<FormsService>(string.Empty);\r\n    }\r\n    else\r\n    {\r\n        formsService = SPContext.Current.Site.WebApplication.Farm.Services.GetValue<FormsService>(string.Empty);\r\n    }\r\n    return formsService;\r\n}\r\n<\/pre>\n<p><em>GetInfoPathFormTemplates<\/em> looks in the root path of the feature for any file with the xsn extension and returns the path information.<\/p>\n<p><em>GetFormsService<\/em> 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.<\/p>\n<p>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 <em>Form<\/em> type.<\/p>\n<pre lang=\"csharp\">\r\nprivate const string MINIMALACTIVEXTOOPEN = \"SharePoint.OpenXmlDocuments.2\";\r\n\r\npublic override void FeatureActivated(SPFeatureReceiverProperties properties)\r\n{\r\n    SPSite site = properties.Feature.Parent as SPSite;\r\n    SPWeb currentWeb = site.RootWeb;\r\n\r\n    \/\/ Update the custom content type to allow rendering in web browser.\r\n    SPContentType ct = currentWeb.ContentTypes[new SPContentTypeId(\"0x0101010076AEDEA7CA2A44B59ECB3D491618E05200726740CD03C4478FB0897E6993DDE8D0\")];\r\n    if (ct.NewDocumentControl != MINIMALACTIVEXTOOPEN || ct.RequireClientRenderingOnNew)\r\n    {\r\n        ct.NewDocumentControl = MINIMALACTIVEXTOOPEN;\r\n        \/\/ Ensure the RequireClientRenderingOnNew property is correctly set for web browsing.\r\n        ct.RequireClientRenderingOnNew = false;\r\n\r\n        \/\/ Don't forget to update the content type with the changes.\r\n        ct.Update(true, false);\r\n    }\r\n}\r\n<\/pre>\n<p>As this example feature is scoped to site level, we only need to cast the <em>Parent<\/em> object to SPSite.<\/p>\n<p>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.<\/p>\n<p>The two properties that are being updated are <em>NewDocumentControl<\/em> and <em>RequireClientRenderingOnNew<\/em>.<\/p>\n<p><em>NewDocumentControl<\/em> identifies the application used to create new document, which for InfoPath forms should be set to &#8220;SharePoint.OpenXmlDocuments.2&#8221;.<\/p>\n<p>The second property, <em>RequireClientRenderingOnNew<\/em> will have already been defined in the content type markup, but it&#8217;s worthwhile making sure at this stage, otherwise  you won&#8217;t be able to view the form in the web browser.<\/p>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8217;t much use if &hellip; <a href=\"https:\/\/www.stuartroberts.net\/index.php\/2010\/12\/14\/programatically-register-infopath-form-for-web-browsing-in-sharepoint\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_mi_skip_tracking":false,"jetpack_post_was_ever_published":false,"jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":[]},"categories":[9,3],"tags":[84,81],"jetpack_publicize_connections":[],"aioseo_notices":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/plx2I-2P","_links":{"self":[{"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/175"}],"collection":[{"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/comments?post=175"}],"version-history":[{"count":11,"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/175\/revisions"}],"predecessor-version":[{"id":185,"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/posts\/175\/revisions\/185"}],"wp:attachment":[{"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/media?parent=175"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/categories?post=175"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.stuartroberts.net\/index.php\/wp-json\/wp\/v2\/tags?post=175"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}