First of all, I would like to thank Walid Hadjadj to create this part, it was fun working with him on this project.

Ok, so we now know what to look for, soap calls when the InfoPath form is being saved back to the list.

If you want to read up on the formservice.asmx and the entire design, you can download the pdf file here. It’s about the only “complete” information that I could find on the net.

The soap call that we are interested in the most is “SetSchemaChangesForList”. This is the one that is responsible for telling the list to start using the InfoPath aspx pages that we’ve found in the previous blog post.

SetSchemaChangesForList

So first things first:

  • I’m re-using my previous solution, so if you don’t have it yet you can get up to speed by just downloading it here.

The extended lookup field, article unit price gave me issues with the redeployment of the InfoPath form, so I’ve removed it.

Also make sure that you download cablib, the file is needed to “extract” and “compress” the InfoPath file on a local storage drive (at the moment it is set to the C:windowsTemp folder, with cleanup functionality)

  • Make a reference to the web service “_vti_bin/formsservices.asmx”
  • Ok, a forms services helper is included so that the forms are being updated, checked and pushed to the lists (the formshelper.cs will be explained later in the blog)

At the moment all of the information is being pushed via the properties of a feature. Necessary data contains

  1. Location of the xsn file
  2. Content Type ID
  3. List url

only question remains: how are we going to get the InfoPath files in the new site collection …..

The only proper way to do this, is to provision the forms in a solution and deploy them to a document library, from there you can pick them up in code and deploy them to the lists

  • So add your InfoPath forms that you’ve downloaded to a directory in the solution and set the type to elements file. Also don’t forget to add the file to the elements.xml, so that the forms are being provisioned to a document library.

either via my admin webpart, where  you can just click “download InfoPath forms” or drag them in SharePoint Designer to a document library and download them from there

  • Your solution should look like the picture below solution

Ok , so the basics are there, now explaining how we did it.

First of all, there is a lot of logging to the SharePoint logs that I’ve implemented. So if you want to track what all the features are doing, you can use the ULS viewer and see all the lines being added.

  • Helpers –> formservices.cs

FieldSchema

This is just a default schema that will be used, if you look at the top print screen you can see the newfields tag and the update tag, so this is just a template that is being used later.

clientwebService

We need to instantiate a new forms web service object and bind the object to the current url the forms services asmx file.

The formsServices cannot work cross web, so if the list is listed in the web, you need to reference that web and put the asmx behind the web url.

Next is something very interesting:

designchecker

Design check form template is a WSDL Operation that checks if your InfoPath form is good or not.

The line above calls a method to get the form template. I’ll come back to this later.

Now for the next part you need CabLib.DLL, it is included in the zip package. But you can download it here or here if you want.

GetUpdatedFormTemplate1

  • First we need to retrieve the xsn file from the document library, I’ve passed the url value of the xsn file in the document via the fileUrl attribute.

GetUpdatedFormTemplate2

  • Since the InfoPath xsn file is in essence a cabinet file, it can be extracted and compressed by cablib.dll. But we need a location for that, so this is something to watch out for, I’ve chosen the temporary directory in the windows directory on the C drive. In that directory I’m creating a new one that with a random guid number as name. But first I’m checking if that directory already exists or not. This entire method is being run for each InfoPath file so a new directory is created each time with a new guid number of course. And at the end of the method a cleanup is done = deleting the directory.

GetUpdatedFormTemplate3

  • Next I’m using extract to extract the xsn file to the temporary folder. Also I’m loading the manifest.xsf in the Xmldocument object to manipulate the xml file.

manifest

  • Pay very close attention to the “PreserveWhiteSpace = true” line. This is very important. Apparently InfoPath cannot handle the following situation.

<Node>

         <SubNode> Value

         </SubNode>

</Node

  • InfoPath will give an error if you want to design it afterwards, because (and I’m not lying) the closing tag of “SubNode” is below the opening tag. The opening/closing of the “Node” tag isn’t a problem, but the “SubNode” tag is. This has taken up a lot of time to find and after a small pair programming session with Milanco (TL at the client) we found the issue. The location of placing the “PreserveWhiteSpace = true” is also from great importance. But it a line lower and it doesn’t do the same thing. You can check here why not Glimlach .

GetUpdatedFormTemplate4

  • Next up I’m going to change the first value and it’s called “baseUrl”, indicating the location of the xsn file in SharePoint structure.

GetUpdatedFormTemplate5

  • After setting the value of the “baseUrl” we are going to loop through the nodes looking for a node with the attribute “relativeListUrl”

GetUpdatedFormTemplate6

  • when we found the “relativeListUrl” we are getting the SPList object because we need to replace the hardcoded List ID with the new one

GetUpdatedFormTemplate7

  • we are also changing the contentTypeID, but I’m not sure if this step is required, because the Content Type ID will remain the same when deploying the CT via the same feature as you deployed to development environment
  • After all this the only thing that is left to do is to compress the directory back to an xsn file and return a Base64String back to the first method so that it can run through the Design Checker information object. And cleaning up the 2 directories as well.

GetUpdatedFormTemplateTheEnd

 

  • And last, we are pushing the initial value of InfoPath form location, ContentTypeID and relative list url via a feature activation with properties in the feature file

featureProperties

  • you call them via the feature receiver and just pass them to the formsservices helper

featureProperties2

This may look like an easy task but all of this needed to be investigated and took us almost 2-3 weeks to build. So I hope I’m saving some of you a lot of effort.

You can download the solution .

Want to learn more?

So, the foundation is there, we have our fields/ content types and lists. Now it’s time to create and make the InfoPath forms deployable.

Let’s go:

First, we need to “design” the forms… by default the new form of the item looks like the picture below, so a normal SharePoint new form.

OldNewForm

 

If you are getting an error in rendering the InfoPath form “The form cannot be rendered. This may be due to a misconfiguration of the State Service.

Than follow the instructions on this blog.

After some fun with the InfoPath designer, the form can look like this. Much better no?

NewClientform

 

So what does SharePoint do in the back-end to make this work?

  • it first creates (or uses) a folder on the lists directory, so you have “lists / list name / content type name / InfoPath pages”

OldAspx

 

  • In this directory (content type name) you can find 3 .aspx files and an InfoPath .xsn file

Newaspx

And these are the files that make up your InfoPath in browser form.

If you are now thinking that deployment is easy and you just need to create these files in the correct location and stuff… well think again..

Even if you put these files in the correct location AND you manage to change the hardcoded list ID’s in the InfoPath form than the SharePoint list will simple ignore this. The list needs to be told to use the InfoPath forms from now on, otherwise it will not use it.

Second problem is, how are you going to get the template.xsn? You can get it by going directly to the link and downloading the file. But are you going to do this for 80 lists? I guess the answer is no (or maybe)…

So I’ve created a small “admin” webpart to make your life easier. It checks if the file exists. If so than the template.xsn is downloaded to a document library (if the library does not exist than it’s being created) and renamed to the list-CTname.xsn .

So how does it all work?

When you click on the “customize form” button to generate the InfoPath form than a lot of stuff is happening in the back-end.

  • InfoPath is opened
  • some soap calls are being done to the lists.asmx and formservices.asmx

If we look at what happens via fiddler than you can see there are calls that are being done to the web services.

NewIPformWebServices

But these calls are when the InfoPath is being opened, we need to look at the calls that are being done when we are saving the InfoPath form to the list.

So first of all we need content types and fields.

Our example will be 3 content types:

  • Invoices with the fields: tax%, articles (multi lookup), quantity, invoice total, client (lookup)
  • client with the fields: name, address, city, telephone number
  • article with the fields: article name, price per unit , description

In the example that you can download (link will be provided at the bottom) I went a little overboard with the naming of my fields. But you can clearly see what fields are for article, client and invoice. 

All the types of fields can often be challenging to remember all the attributes that are necessary, so I often use this blog to check if I forgot anything.

A good link with explanation about lookups.

Pay attention to this line in his blog:

Checking the field from code showed that the LookupList property of the site column was “Lists/Tasks”, so it was not resolved to the list GUID on feature activation.

Now the way he fixes the issue isn’t that bad, but after looking for some deeper issue as to why it behaved like described I came across another blog writing in depth why and the solution on how to solve it “cleanly”.

The summary of the blog:

So long as you specify Overwrite="true" for Field elements of type Lookup, LookupMulti, TaxonomyFieldType, etc., relative references will work. And you can completely disregard the "created in the same feature" statement in the MSDN reference.

Defining the fields, content type and lists:

Now if you want to define all the content types in a single elements file and all the fields in a single elements file.

No problem what so ever, but  when it’ comes to lookup fields you’re going to have an issue… especially when you are creating lists that use these content types.

The chicken or the egg paradox will happen here. You are defining a lookup field with a soft reference to a list that doesn’t exist yet in the flow of deployment. So your lookup field will be deployed but will not work (lists and fields will be empty)

LookupEmpty

What to do?

1. Either you define all the fields and content types without the lookup fields, define them later in the flow and attach them via a feature receiver to the correct content type

2. Define all the independent content types (content types that does not contain lookup fields) separately, create the lists accordingly and only then define the content type with the lookup fields and create that list as last.

In the end you’ll notice that the first step isn’t the best one, but the easiest one. If you have 80 content types, 70 lists and 400 fields + 30 lookup fields than good luck keeping track…

So the solution has an additional feature with an element “LookupFields” and a feature receiver.

In this feature receiver I’m going to look for the lookup fields in the site columns and add it to the content types. After that I’m calling for a content type update with the attribute true, so that all the changes are being pushed to everything that inherits the content type.

In the next chapter it’s time for some InfoPath talk.

Download:

While we started looking at all the issues that InfoPath can give us, we would have never guessed all the issues that it could give us.

Given elements that we took into account:

  • Not able to be deployed to other farms, new web applications, not even new site collections (all because the list ID would be changed)
  • Code behind (in InfoPath) was not necessary so this was a big plus (if you have ever done code behind than you know what a pain it can be)
  • nice and easy way to design forms
  • generates the entire form for us so that we can style it any way we want
  • some issue that you could have when using the formservices.asmx via sandbox see here

elements that we later discovered during development:

  • at the moment only “deployment” via the formservices.asmx is possible (so when attaching the xsn file to the list, we only found 1 way on how to do this, more on that in a later chapter)
  • if you are using lookup fields in combination with InfoPath you are in for a long way

The SharePoint list form can’t be customized with InfoPath because fields of an unsupported data type are marked as required, or because fields are corrupted. In SharePoint, try deleting the columns or editing the column properties to remove the required attribute.

– Field Name (Lookup)

When you will see this error it’s either 2 things, the lookup in the list points to nothing (maybe an inconsistency with the parent content type, see if the lookup field in the content type is also not working, if it is, just edit the field, change nothing, ok – ok and try the InfoPath form again.

The second way is the hard way.. depending on how you deploy your lookup field this can be an issue, with or without webID…. (will be explained in chapter 3)

  • did I mention the “deployment procedure” already? Glimlach
  • even the white spacing in the manifest.xsf file will give you issues when editing it (manifest.xsf is an internal file in the .xsn file)

for InfoPath it’s not the same if the structure of your node is

<node>

</node>

while it must be <Node></Node>

These are the things that I can think of at the moment, if more come to mind I will add them here.

Next chapter is about content types and fields (also how to deploy a lookup that InfoPath can work with)

Situation: :

A project must be implemented with a lot of InfoPath forms (around 50), only problem is… InfoPath isn’t designed by default to be “deployable”. But a positive side is that there is no code behind in the InfoPath form, so only the form designer is used.

Also we have around 70 content types and around 400 fields, so a lot of stuff that needs to be done and implemented.

I’ll skip forward to the InfoPath designs and deployment.

If you are asking yourself the question, can an InfoPath form be deployed without going via the InfoPath form services, or a forms library… than the answer is yes…. but…

As I’ve previously said, InfoPath is not “deployable as a solution” by design. The xsn file is actually a cabinet file that contains multiple files.

explanation about the .xsn file from FileExt

The XSN file type is primarily associated with ‘InfoPath’ by Microsoft Corporation. InfoPath is the Microsoft Office information gathering and management program. The .XSN file is a kind of cabinet (CAB) file that contains additional parts and files. 

Also you cannot deploy InfoPath via the other 2 ways (InfoPath form services or via a forms library) because there are the content types to consider. And they are all connected via a certain hierarchy. If I would deploy the forms via any of the other ways that I loose that connection in the hierarchy and the content type would exist on it’s own.

OK: so we’ve set the scene, we know all possible outcomes and which way to follow… , this one is going to be bumpy.

Step 1: Create all the fields and content types first – content analysis

Step 2: let’s say that each content type has a list (to keep things simple)

you can add multiple content types to the list and via list settings –> Form settings you can select the content type and customize (or create) the InfoPath form

ListFormSettings

and in the form settings page you can select the content type for which you want to customize the new/edit/display form.

CustomizeIP

If you use “Customize form” via the ribbon, than you’ll only customize (or create) the default content type.

CustomizeInfoPathViaList

Step 3: create a couple of InfoPath forms per content type

Ok, now the setup is complete and we have all the components that we need, all we need to do now is to be able to reproduce all this stuff via code to another farm…