A short blog on how the Promoted Fields seem to behave for InfoPath forms stored in SharePoint.
They are created as fields (Date, Text, …) and have the XPath property configured to the field in InfoPath (example: /my:myFields/my:field1)
When the form is stored in the library, the value of the XPath’s InnerText is stored in the List Item. It is by no means a live evaluation of the XPath.
Through code one can update the XPath value of the SPField. This does not affect existing forms since the value is persisted during form save, only new forms are affected.
SPSite site = new SPSite(url); SPWeb web = site.OpenWeb(); SPList list = web.GetList(url); SPField field = list.Fields["Field1"]; field.XPath = "/my:myFields/my:field1"; field.Update();
When you set the XPath to an element that has child elements, the InnerText will render all the elements contents as you’d expect:
value 1
value 2
When the XPath is set to "/my:myFields”, the Promoted Field value will be “\n\t11\n\t22\n” (it preserves indents).
That’s all for this quick blog post !
I’m currently testing our InfoPath Web Forms for an upcoming migration to InfoPath 2010 and SharePoint 2010 and have come up with a reproducible issue.
Issue
InfoPath Web Forms cannot use values from other Data Sources as parameters in a Submit Data Connection. When the form is submitted a warning “There has been an error while processing the form.” shows. From that point on the only working option is Exit Form.
- It does not affect data connections to XML files, only when the query data connections connects to a Web Service
- It does not affect the offline version of the form (InfoPath Filler 2010)
- Tested in both December 2010 Cumulative Update and February 2011 Cumulative Update for SharePoint 2010
Set up
- Create a new InfoPath Web Form
- Add a Query Data Connection to retrieve data from a Web Service
-
Add a Submit Data Connection to submit data to a Web Service. Configure at least 1 parameter (if more then one) to link to a value from the above Query Data Connection
- Configure the form to submit using the above connection
- Publish the form to SharePoint 2010 and open a new form in the browser
Workaround
Store the value of the query data connection in an intermediary field in the form (“Main” Data Connection). Configure the Submit Data Connection to use the intermediary field value as parameter for the Submit. You can store the value as late as possible using Rules in Submit Options or your own submit buttons.
InfoPath Form Template URL
We have a solution that makes extensive use of online and offline InfoPath forms in a MOSS 2007 environment. An upgrade to SharePoint 2010 is due in a few months, including a redesign of Managed Paths and Site Collection ‘hierarchy’ having a direct effect on the URL of our Site Collection.
I thought this meant a lot of work for converting the thousands of InfoPath XML Forms by script, because they all contained an absolute URL to the InfoPath Form Template.
However as it seems after restoring the Site Collection all forms automagically had a corrected absolute URL to their template !!
xml version="1.0"?>
mso-infoPathSolution productVersion="12.0.0" PIVersion="1.0.0.0" href="https://public.contoso.com/sites/csapp/FormServerTemplates/LeaveRequest.xsn" name="urn:schemas-microsoft-com:office:infopath:LeaveRequest:-myXSD-2009-05-29T07-59-01" solutionVersion="1.0.0.422" ?>
mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.2"?>
<my:Data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dfs="http://schemas.microsoft.com/office/infopath/2003/dataFormSolution"
xmlns:tns="http://public.contoso.com/webservices"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2009-05-29T07:59:01"
xmlns:xd="http://schemas.microsoft.com/office/infopath/2003"
xml:lang="nl-be">
my:Data>
Default Alternate Access Mapping
What happens is that when you restore a Site Collection backup, it looks up the Web Application by the URL you specified as a parameter. From that Web Application, it takes the Alternate Mapping configured on the "Default” Zone and uses that URL to construct the absolute URL in the InfoPath XMLs.
Only at restore time
This mechanism only seems to trigger when the restore takes place. Changing the Default AAM afterwards to a different URL has no effect. You could temporarily change the AAM for the restore operation and then set it back if you want. Beats any script iterating and updating all Form XMLs any day.
Most of the times you expect the worst from SharePoint, but then it catches you by surprise and brings a huge smile to the face :)
When you have multiple Form Content Types with each their respective form templates assigned to the same Form Library by some magic it will automatically save a new form in the correct Content Type. How it does this ?
The new form’s XML contains an InfoPath processing instruction with a “href” attribute set to the XSN template URL. At save time the Content Types configured in the Form Library are iterated and a match is made based on Template URL. If no match is found it will use the default Content Type.
Even works in code if you do a SPFileCollection.Add() so no need to specify the Content Type explicitly during saves !
It doesn’t seem to take Alternate Access Mappings into account so that’s a bit of a downer, but still powerful.
Using xsd.exe you can generate a Class from your form schema (xsd) and then deserialize a form to an instance of that class. This makes it a lot easier to interact with its data.
The code for serialization and deserialization might look like this:
public static T Deserialize(Stream s)
{
T result = default(T);
XmlSerializer serializer = new XmlSerializer(typeof(T));
using (XmlTextReader reader = new XmlTextReader(s))
{
result = (T)serializer.Deserialize(reader);
}
s.Close();
return result;
}
public static T Deserialize(string s)
{
return Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(s)));
}
public static string Serialize(T o)
{
string result = null;
XmlSerializer serializer = new XmlSerializer(typeof(T));
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.Encoding = Encoding.UTF8;
using (MemoryStream stream = new MemoryStream())
{
using (XmlWriter writer = XmlTextWriter.Create(stream, settings))
{
serializer.Serialize(writer, o);
}
stream.Flush();
result = Encoding.UTF8.GetString(stream.ToArray());
}
return result;
}
However you lose the original processing instructions at the top of the XML file. If you want to keep those either do custom serialization using an XmlWriter or do some kind of merge code with the original XML and the XML coming from serialization.
Quite obvious if you think about it but I keep forgetting it :)