Monday, October 13, 2008

Deploy InfoPath as a Feature

12/11/2009 - Update for Managed Code.

1.0 Introduction
For some time now I have been pretty unhappy with InfoPath deployment. I really did not know of a way to deploy InfoPath forms other than publishing them using the InfoPath Designer. I was even more frustrated with having to go to SharePoint Central Administration to deploy a form when I had to give the form Full Trust. Sometimes I had to give Full Trust to InfoPath sometimes for making data connections. As well, I would purposely avoid adding managed code at all cost to my InfoPath forms (still if I have to put in managed code into my InfoPath form, I should use ASP.net - InfoPath and ASP.net considerations).

Well someone made a comment to my blog which showed me the light – YES you can deploy an InfoPath form as a Feature. Awesome! Totally Awesome! Some may say this is old news, but I really just wanted to share the knowledge on this one.

I am excited about deploying InfoPath as a Feature for the following reasons:

  • The InfoPath form will be deployed as a content type. I have many scenarios where I have to publish an InfoPath form to many form libraries. For instance I have business process where the InfoPath form will be initiated in one Form Library and archived in a different one. Now I do not have to go to each form library and redeploy. Since it is a content type that will be added to a Form Library, when I redeploy the form with updates, all of the form libraries are updated.
  • The form gets installed into Central Admin requiring me less clicking and less steps.
  • I can now automate the deployment of my InfoPath forms and package them up in .wsp!!! That is really where I wanted to get to.

2.0 Feature
Getting to the fun stuff first, the following is a feature that will deploy your InfoPath form for you.

2.1 Feature file

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/"
Id="DE7C8197-4FF3-41f5-B13A-6ACB94FA0C77"
Title="New User Request Form"
Description="This feature deploys the browser enabled New User Request InfoPath Form."
Version="12.0.0.0"
Scope="Site"
DefaultResourceFile="ipfscore"
ReceiverClass="Microsoft.Office.InfoPath.Server.Administration.XsnFeatureReceiver"
ReceiverAssembly="Microsoft.Office.InfoPath.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" >
<ActivationDependencies>
<ActivationDependency FeatureId="C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5"/>
</ActivationDependencies>
<ElementManifests>
<ElementManifest Location="element.xml"/>
<ElementFile Location="NewUserRequestForm.xsn"/>
</ElementManifests>
</Feature>
2.2 Element File


<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="XSN" Url="FormServerTemplates" RootWebOnly="TRUE">
<File Url="NewUserRequestForm.xsn"
Name="NewUserRequestForm.xsn"
Type="GhostableInLibrary"/>
</Module>
</Elements>


Notes:

  • The key to this whole thing is the receiver class XsnFeatureReceiver.
  • In the Feature File the Enterprise Feature is turned on by creating an activation dependency on C88C4FF1-DBF5-4649-AD9F-C6C426EBCBF5.
  • The name of the InfoPath form is an ElementFile in the Feature.xml and is a Module in the Element.xml.

3.0 Create Project
The following is what I had to do create a project for the InfoPath form.


3.1 Visual Studio

  • I created a Visual Studio C# project. Yes, I still believe in being able to do some of this stuff from scratch.
  • In the project I added a TEMPLATE, FEATURE and a feature Folder called K2Distillery.IP.NewUserForm. In the Feature folder I put the element.xml and feature.xml. The InfoPath form will be published to Feature Form later. I explain why I do this a little farther down.
  • In the project, I deleted the default class that was created and I added the InfoPath form into the root of the project.
  • I created a file called WSP.ddf in the root of the project. This will be used to create a solution package to deploy and InfoPath Feature.
  • I then finally added a manifest.xml file to the project.
  • In the properties of the project, in the Post Build Events, I added the following which will create my deployment package when the project is built:
    cd $(ProjectDir)
    MakeCAB /D outputDir=$(OutDir) /f "WSP.ddf"

The following is what the project solution looked like when I was completed.




3.2 manifest.xml File
The manifest.xml file is used by the WSP solution package to deploy the Feature.




<Solution xmlns="http://schemas.microsoft.com/sharepoint/"
SolutionId="F9D4D78A-06AA-4625-9676-0A27E6299E05">
<FeatureManifests>
<FeatureManifest Location="K2Distillery.IP.NewUserForm\feature.xml"/>
</FeatureManifests>
</Solution>

3.3 WSP.ddf File
This file is used create the WSP file that will be deployed into SharePoint. The great thing about doing this is if there are other Features I need to deploy along with this InfoPath Form Feature, I can package them up in this single solution and all of the files will be deployed across the entire farm. For instance, if I really want to get slick, I could create a different feature to create a Form Library and then write a Feature receiver to get the content type for the InfoPath form and associate it to the Form Library on the fly.




I highly recommend that you get into this habit. It is not that hard once you learn it.

.OPTION Explicit
.Set CabinetNameTemplate="K2Distillery.IP.NewUserForm.wsp"
.Set DiskDirectory1="C:\K2Distillery\IP\K2Distillery.IP\K2Distillery.IP.NewUserForm"

manifest.xml

.Set DestinationDir="K2Distillery.IP"

.Set DestinationDir="K2Distillery.IP.NewUserForm"
TEMPLATE\FEATURES\K2Distillery.IP.NewUserForm\element.xml
TEMPLATE\FEATURES\K2Distillery.IP.NewUserForm\feature.xml
TEMPLATE\FEATURES\K2Distillery.IP.NewUserForm\NewUserRequestForm.xsn

.Delete outputDir

4.0 Deployment Steps
The next step is to deploy the solution. I have two steps.

4.1 InfoPath Deployment
You may have noticed in my project I have the InfoPath form in the root of the project and in the Feature folder. You will see what I did this in a moment.

  • Open the InfoPath form in the root of the project in InfoPath Designer.
  • Select File >> Publish:
    • To Network Location
    • In the next step, navigate to the \\TEMPLATE\FEATURES\K2Distillery.IP.NewUserForm folder.
    • In this step of the publishing process, clear this text box and press the Next button. This is the reason why I have two different copies of the InfoPath form in the project. The first one is the Form I make modifications to and the second one is the form that will actually be deployed to MOSS.

  • Then Finish the wizard and close InfoPath.

4.2 Solution Deployment



  • Since the manifest.xml and WSP.ddf files have been added to the project and I have added Post Build commands to generate the WSP file, the only thing I need to do is build Project. Easy.
  • Once the project has been built a file called K2Distillery.IP.NewUserForm.wsp should have be created. The location of it is in the WSP.ddf file.
  • Then all I need to do is deploy this using stsadm.exe:

stsadm.exe -o addsolution -filename C:\K2Distillery\IP\K2Distillery.IP\K2Distillery.IP.NewUserForm\K2Distillery.IP.NewUserForm.wsp

stsadm.exe -o deploysolution -name K2Distillery.IP.NewUserForm.wsp -immediate -force



stsadm.exe -o execadmsvcjobs



  • Then finally I need to activate the Feature on the site:

stsadm.exe -o activatefeature -filename K2Distillery.IP.NewUserForm\feature.xml -url %SITEURL% -force

All of these steps could have been done through Central Administration and in Site Settings however, going down this path you will be able to create one touch deployment command files that can deploy your entire solution into a production environment.

4.3 Verifying Deployment
There are several places for you to verify that everything went well:

  • Go to Central Administration and you will see that you form has been added there.
  • Go to the site/FormServerTemplates/ and you will see the form there.
  • Go to Site Settings and you should find a new Content Type called NewUserRequestForm has been created. The InfoPath form will be set up as the document template for the Content Type.

4.4 Adding the Form to a Form Library
It is pretty common knowledge on how to add Content Type for a List, Document Library or Form Library. Well the steps have not changed. Create a Form Library, go to the form settings, go to advanced settings, allow management of content types and then add the content type for the form to the Form Library.

4.5 Promoted Fields
If you are wondering where are the Promoted Fields? Well they are there, however they are not added as a column to the Form Library when you associate the Content Type. At first I did not know where they were because I was accustomed to having promoted fields added as columns of the Form Library automatically because when I publish a form using InfoPath Designer. To add them, all you need to do is:

  • Go to the Form Library settings.
  • Scroll down to the Views section at the bottom.
  • Click on a view.
  • Check the promoted InfoPath form field which will be listed as a column. That is it.

Yes, I could probably create another Feature to do this for me too, but at this point, I need to move on J

4.6 Managed Code
Read the following posting I wrote after writing this blog.

5.0 Conclusions
Some may say you had to do just as much effort, if not more, to deploy the solution in this manner versus deploy the form using InfoPath Designer or through Central Administration. It really comes down to following a true publishing model with SharePoint. Many say if you cannot deploy something as a Feature it should not be deployed at all. I agree with this sentiment in many respects (and why I avoid using SharePoint Designer). I do agree there is a point where some manual configuration is required to deploy between SharePoint environments.

References
Deploy InfoPath Form as a Feature – Yes, I had a little help here J

28 comments:

Rem said...

Nice post...

Herschel said...

Like you said, "this may be old news", but you have created a very informative easy to read post. Thank you, this is very helpful
Jonathan K Herschel

dchillman said...

Great post. In one of the steps you state "For instance, if I really want to get slick, I could create a different feature to create a Form Library and then write a Feature receiver to get the content type for the InfoPath form and associate it to the Form Library on the fly." I was wondering if you have such an example? I am a newbie so everything is greek to me. I can create the feature for the form library, but am unsure of the steps necessary to tie the form library feature and content-type feature together. Can you point me in the right direction? thanks

Jason Apergis said...

dchillman,

I will have to work up something. I have few things in my queue...

In the meantime look in to feature activation event receivers and basically you will have to open a connection ot the web, find the content type, then create a form library instance on a site, and finally associated the content type to it...

allen said...

i for some reason cannot get this to work.

the only difference between the post and my example is that i have the infopath forms in a seperate folder, but the feature deployment works and i've verified that the feature is active on the site whose template references the feature.


i've at a loss here, because everything works at runtime until the infopath form is attempted to be referenced from my webpart and the XmlFormView instance attempts to use the XmlForm member and it is null causing the following error:

Object reference not set to an instance of an object.

Web Parts Maintenance Page: If you have permission, you can use this page to temporarily close Web Parts or remove personal settings. For more information, contact your site administrator.



if i manually add the forms via the admin portal, this works as expected?????

Jason Apergis said...

Allen - when you say "my webpart" are you referring to a custom webpart that you have written?

Jason

dchillman said...

i followed your example and added an infopath form feature to a site definition, and included the feature in the SiteFeatures node of the configuration. When I deploy the solution, the infopath form feature gets added to the site collection features and is activated. I can see the form in the site/formservertemplates, but the form is not listed as a contenttype.

dchillman said...

I think i was having problems because my development system was "dirty" and I had a misunderstanding of what site features in the onet.xml of a site definition would get activated. I had thought that when I created a site with a site defintion, site features would get activated. However, this is not true if you are creating a sub-site, hence, my infopath forms (site features) were not being activated when I thought they were being activated. Once I understood this, I simply added activated the infopath form features using stsadm commands after deploying the solution and presto, the content-types were created as you described.

Jason Apergis said...

Glad to see you figured it out.

Roman said...

Interesting, indeed.

However my biggest concern about infopath forms deployment is the moss content deployment.

It simply doesn't work. It doesn't even matter if the form contains managed code (full trust) or not... Once I have it deployed to a library (limited trust) or manually via forms management (full trust scenario) to the "content source" moss the incremental content deployment job to our live environment breaks.

Typically we have to deploy forms to both environments to not break content deployment. A requirement for content deployment is feature consistency after all:-/

There might be improvments in SP2 - but we aren't using it yet :-/

Creating a feature out of a form doesn't really help here I guess. It's a pity.

Jason Apergis said...

Roman,

I personally have not messed around too much with MOSS Content Deployments; I have heard they are some challenges with it.

I know person or two who may be able to provide you some assistance. I will forward this along...

Jason

Mario Kleine-Nathland said...

Hi,

would you please comment on how the versioning is made?
If you update the form and redeploy it, how do you assure that the old xmls can still be loaded.

Thanks

Adolfo said...

Hi Jason,

I am not a developer so when you mention at the very end to build the solution in Visual Studio I get this error

Program\\WindowsApplication1.exe' does not contain a static 'Main' method suitable for an entry point K2Distillery.IP.NewUserForm

Can someone help me please?

Thank you

Adolfo said...

Hi Jason,

I am not a developer so I dont quite know how to deploy the solution (Even though you said it was easy). When I try to build the solution i get the following error.

Program '\\WindowsApplication1\obj\Debug\WindowsApplication1.exe' does not contain a static 'Main' method suitable for an entry point K2Distillery.IP.NewUserForm

Can someone please help?
Thanks

Jason Apergis said...

Adolfo,

Easy is a relative term :-)

If you are not familiar with Visual Studio or .NET development I would not recommend this. This is a gradular way to deploy an InfoPath form for developers.

I would recommend that you just use the Publish function directly in InfoPath.

Thanks,
Jason

Todd said...

This is a very nice post, and as a newbie to SharePoint development, I found it very useful. However, I am having one problem. My form is being added to Central Admin, and my content type is being created. I can then create a Form Library, and add the content type to the library. However, when I go to the library and click "New" (to fill out the form), it is trying to download the .xsn file instead of opening it in the browser. The form itself is configured to be opened in the browser, and I have configured the form library to open the form in the browser. In fact, if I manually upload the exact form using Central Admin, the form opens in the browser perfectly. The only obvious difference I see is that the manually uploaded form is not "Workflow enabled" while the form deployed using your method is.

Any help?

Jason Apergis said...

Todd,

I was just about to ask if you had tried to publish the form using the publish feature in InfoPath or if you had done it through Central Admin manually. I have not experienced the issue you are running into...

Jason

Todd said...

Jason,

When you access "Site Settings -> Site collection features" do you see your form template listed there? When I upload through Central Admin (and everything works), there is a feature listed here. When I deploy my form through a feature, it is not listed here. If I go to Central Admin's "Manage form templates" and try to activate to my site collection, I get an error:

"This form template cannot be activated or deactivated. It is part of an installed workflow and cannot be accessed directly outside of the workflow."

It appears that I am doing something to the form to tie it to a workflow.

Does any of this ring any bells?

Jason Apergis said...

The only bells I hear right now are Jingle Bells :-)

I wrote this bit ago so I do not recall everything. What I can say for sure is that a Feature should be visible in the site collection because the scope of the Feature is set to site. I cannot say if this is related to something you have done with workflow but I would not doubt it. This actually seems vaguely familiar, think you will get that error if you try to blow away a form that is associated to a OOB workflow.

One thing you can do, if things are just getting a little harry, would be to just step back and create it from scratch, just a Hello World form that uses this blog. Then try to see where the differences are.

Todd said...

I figured out my issue, so I am going to post the answer here for the sake of posterity.

If you deploy a form using the method described in this article, you have the choice of making the form "Workflow Enabled" or not. Adding the "GloballyAvailable" property (displayed below) will make the form workflow enabled. If you leave it out, the form will not be workflow enabled. You cannot add a browser-enabled form to a form library if it is workflow enabled. THAT was my problem.

Jason Apergis said...

Todd - thanks for contributing!

renewtx said...

Hi,

I'm trying to recreate your solution and have a question -- how/where do I get the SolutionId used in manifest.xml? Is that just another std GUID I need to generate, or is it the SAME GUID as GUID I generate for the feature.xml??

Thanks

renewtx said...

I was finally able to get this to work and generate what appeared to be a valid WSP file. This WSP file I was able to deploy to a clean system with no errors during deployment. However, one thing I overlooked in the batch file to deploy the feature was to actually activate it. So I attempted to manually activate the feature (which should be fine, right?). When I did so I got the following error -- any idea what's missing or going on here?

Feature '25f5615f-b338-48ff-8eb7-2ba05dd9972c' could not be installed because the loading of event receiver assembly "Microsoft.Office.InfoPath.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=fe403af49d69c89f" failed: System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Office.InfoPath.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=fe403af49d69c89f' or one of its dependencies. The system cannot find the file specified.
File name: 'Microsoft.Office.InfoPath.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=fe403af49d69c89f'
at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
at Microsoft.SharePoint.Administration.SPFeatureDefinition.get_ReceiverObject()

Jason Apergis said...

renewtx,

Yeah it is commonplace to make little errors like that which throw everything off.

Sorry - I have to turn on moderation. I get spam postings and I do not want it to pollute my content.

Jason

Jason said...

Excellent post. 2 questions:

1. If you are deploying as a feature using this method how can you promote properties to existing site columns?

2. How can you deploy an InfoPath template and have it inherit from an exisiting content type that inherits from form. I'm starting to think this is not posssible.

Jason Apergis said...

Jason,

Question 1 - section 4.5 of this post should address your question however there is no real straight forward solution.

Question 2 - I can see how that can be a little tricky. I wish I could say I have an exact answer and I would think it is possible. However after reviewing what I wrote (which is about 2 years ago) it looks as if this may not be possible. This solution will generate a content type under the hood and you have no control over how the content type is generated. They may be a way but off hand I do not know it. Wish I could help on that.

Thanks,
Jason

Thanks,
Jason

Muhammad Farjad said...

I followed the same steps for SharePoint 2010, my form is visible in FormServerTemplates but is not visible as a content type in a form library settings. Does anyone know what might be the issue? Thanks

Jason Apergis said...

Muhammad - I developed this on SharePoint 2007 and have not rebuilt or tested on SharePoint 2010.

Jason