Showing posts with label InfoPath. Show all posts
Showing posts with label InfoPath. Show all posts

Wednesday, November 16, 2016

Recipe to duplicate InfoPath ListForm template for another content type

Context:
  • Crafted a rather rich and extensive InfoPath ListForm for content type <X>
  • Business requests similar but slightly different form for content type <Y>
Difficulties
  • InfoPath Designer does not provide a native capability to duplicate an InfoPath form to another content type
  • Drag-and-Drop from source InfoPath form template opened in InfoPath Designer to destination form template is limited, incomplete layout, no controls binding, ...
Pragmatic recipe for InfoPath Form copy
  1. Navigate to SharePoint List in a browser
  2. Go to 'List settings' \ 'Form Settings'
  3. Select content type <X>, the associated form opens in InfoPath Designer
  4. Save the template.xsn on a local location (or 'Save as' of the "source files")
  5. Return to 'List settings' \ 'Form Settings'
  6. Select content type <Y>, and select to create new form in InfoPath
  7. Immediate save as template.xsn on a local location, but make sure not to overwrite the one of <X>
  8. Navigate in Windows Explorer to the location(s) where you saved the .xsn files
  9. Rename the .xsn file-extension into .cab
  10. Extract both template.cab archives to local folders
  11. Open the folder of template for <Y>, and open template.xsf file in an editor. Lookup 'ContentTypeID' in the file, and copy the value
  12. Open the folder of template for <X>, and open template.xsf file in an editor. Lookup 'ContentTypeID' in the file, and paste the value copied from <Y>. Save the file.
  13. Make sure to reassign 'Open with' file association for template.xsf file to InfoPath Designer
  14. Double click the modified template.xsf file, and click 'Design' to open in InfoPath Designer
  15. Publish the InfoPath form to SharePoint List
Pragmatic recipe for partial InfoPath Form copy/reuse
  1. Step 1-10 as above
  2. Open the folder of template for <Y>, open in an editor the view.xsl for which you want to reuse layout from the form template of content type <X;>, and delete all content
  3. Open the folder of template for <X>, open in an editor the view.xsl that you want to reuse, select all file content for copy
  4. Paste the copied file content in the opened view.xsl of content type <Y>, and save the file
  5. Repeat this for every view in the form template that you want to duplicate/reuse
  6. Double click the modified template.xsf file, and click 'Design' to open in InfoPath Designer
  7. Publish the InfoPath form to SharePoint List

Tuesday, October 4, 2016

Automatic revert-to-self for InfoPath forms

(Functional) users working with more complex InfoPath forms may be familiar with below notorious error dialog:
A typical cause is that after saving the listitem, in the SharePoint background one or more workflows are triggered that also update the listitem. And that makes the state as loaded in the form (stateless setup) outdated, and InfoPath refuses next save due optimistic locking. Pragmatic way out is to force data reload in the form, and most simple approach to that is close the form as last action in the ‘Save’ rule. Drawback is that the user must self relocate the listitem again, reopen and set into edit modus.
Via javascript injection the standard InfoPath Forms Services handling can be overruled, and instead automate the relocate/reload/edit - revertToSelf:
var InfoPathCheck; var maxNrChecks = 10; $(document).ready(function(){ InfoPathCheck = setInterval(function() { //Wait for InfoPath to finish loading HasInfoPathLoaded(); }, 200); }); function HasInfoPathLoaded() { var selectAppl = $("#<form-id>"); if (selectAppl.length > 0 || --maxNrChecks === 0) { clearInterval(InfoPathCheck); if (selectAppl.length > 0) { var saveBtns = $("input[id^='<form-id>_FormControl'][value^='Save']"); if (saveBtns.length > 0) { $(saveBtns).click(function() { // Overload IP-function to return-to-self upon 'Save'. CurrentFormData_UrlToNavigateToOnClose = function(a) { return document.location.toString(); } }); } } } }

Wednesday, June 8, 2016

Preserve code blocks ('xd:preserve') not supported in InfoPath Forms Services

See Readonly display of MultiLookup value in InfoPath Forms Services: my initial approach was to handle this in the InfoPath template itself, via Custom XSLT:
That approach works when testing in InfoPath Designer - preview.
However, upon publishing the template to SharePoint my custom XSLT is lost. Microsoft in MSDN article Using Custom XSLT in InfoPath Form Templates states that you can protect your XSLT customization for overwriting via 'xd:preserve'. Indeed with that inserted, upon saving the template the customizations are preserved:
However, I ran into another issue that Microsoft does not make clear in the referred MSDN article: forms with preserve code blocks in it cannot be published to SharePoint.
Hidden in other MSDN article Creating InfoPath Form Templates That Work With InfoPath Forms Services it states that 'XSLT extensibility (xd:preserve blocks)' are amongst the 'Features with No Direct Parallel on the InfoPath Forms Services'.
Note that you can [still] apply Custom XSLT in InfoPath Forms Services context, the limitation is that code preservation is not supported. So on every save in InfoPath Designer, the custom XSLT code blocks get overwritten, and you would have to insert them again. That effectively makes this InfoPath customization approach [at least for me] unmanageable and unworkable for sustainable utilization in InfoPath Forms Services context. Therefore my design decision to implement the required behavior 'outside' InfoPath Forms Services, via a clientscript approach.

Readonly display of MultiLookup value in InfoPath Forms Services

Business question:

Have an InfoPath form that on one tab allows submitter to select zero to more impacted IT applications, and on another tab (for other business role) display this selection readonly.

InfoPath / IT answer:

  • Have multiple Views in the InfoPath form / template;
  • On the 'input' View, include a Multiple Selection List (MultiSelectList) control, and bind to the MultiLookup List Column (or ContentType Field);
  • On the 'display' View, visualize the current value of MultiLookup via a Repeating Table control. Via InfoPath configuration (control properties) remove the option for user to include new entries in the table: This part takes care of displaying [only] the selected value(s);
  • What then remains is to make the control readonly, to prevent that the MultiLookup value can be changed on this tab. InfoPath does not itself support to disable the Repeating Table control. However, you can achieve this for InfoPath Forms Services context via (the power of) javascript: include on 'editifs.aspx' page script with a method that sets the html control to disabled. A complexity here is that IFS loads the form asynchronously after the surrounding page is loaded in browser, and that no event is triggered to notify the form is loaded. To handle that, I've programmed a polling approach that recurring checks whether the resulting HTML table is now present in DOM, and then disable it:
    var InfoPathCheck; var maxNrChecks = 10; $(document).ready(function(){ InfoPathCheck = setInterval(function() { //Wait for InfoPath to finish loading HasInfoPathLoaded(); }, 200); }); function HasInfoPathLoaded() { var selectAppl = $("#ctl00_m_g_d1ea83c5_3306_41e4_8419_01cfcf73921e_FormControl0_V1_I1_R5"); if (selectAppl.length > 0 || --maxNrChecks === 0) { clearInterval(InfoPathCheck); if (selectAppl.length > 0) { $(selectAppl).prop('disabled', 'disabled'); } } }

Thursday, August 26, 2010

More [functional] robustness issues with OOTB InfoPath Forms Services

Earlier I blogged about some robustness issues I encountered with the application of InfoPath Forms Services in an external facing SharePoint site. The basis of the framework developed in that project is re-used in context of the company's intranet. Hereby I ran into some new robustness issues. I'll describe them here, and also the outline of the applied solutions.

  1. Placing XmlFormView webpart on a SharePoint page conflicts with inline server codeblocks in MasterPage
    Immediately upon adding the out-of-the-box Microsoft.Office.InfoPath.Server.Controls.XmlFormView webpart on a publishing page in the company's intranet, SharePoint throwed an exception:
    Source: System.Web
    Message: The Controls collection cannot be modified because the control contains code blocks (i.e. ).
    Stacktrace:
    at System.Web.UI.ControlCollection.Add(Control child)
    at Microsoft.Office.InfoPath.Server.SolutionLifetime.ScriptIncludes.AddCssLinkToHeader(HtmlHead head, String href)
    at Microsoft.Office.InfoPath.Server.Controls.XmlFormView.TryToAddCssLinksToHeader()
    at Microsoft.Office.InfoPath.Server.Controls.XmlFormView.OnDataBindHelper()
    at Microsoft.Office.InfoPath.Server.Controls.XmlFormView.OnPreRender(EventArgs e)
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.WebControls.WebParts.WebPart.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint

    First analysis learned that the problem was related to the applied SharePoint MasterPage: it manifested itself with 2 of our custom MasterPages, but not on other custom nor the standard (Publishing) Masterpages. Inspection of the particular custom MasterPages disclosed the following server-side code:
    <% Response.Write("<script>var Collaboration_SiteRelativeUrl='" + SPContext.Current.Site.Url + "';</script>"); %> 

    The inclusion of this inline server codeblocks resulted in the conflict with the server-side operation of the XmlFormView webpart. The solution was to replace the inline server codeblocks-tags '<%' and '%>' approach by a HtmlHead (which is a webcontrol) databinding approach:

    <script>var Collaboration_SiteRelativeUrl="<%# SPContext.Current.Site.Url %>";</script> 

    And in the MasterPage codebehind:

    protected override void OnLoad (EventArgs e)
    {
        base.OnLoad(e);
        Page.Header.DataBind();




  2. IE 6 gives a runtime Javascript error in case of RichTextBox control on the form
    While demonstrating the potential of the self-service WebForms functionality to internal stakeholders, I was asked to modify an earlier published InfoPath form with the placement of a RichTextBox control. After re-publishing the InfoPath form to the SharePoint target site, the form still correctly displayed in the content page; including the added RichTextBox control. However, when positioning the mouse in the RichTextBox control, the IE6 browser (Red: still the company's standard) reported a runtime Javascript error. Also the RichTextBox toolbox was not displayed, leaving the control pretty useless wrt a plain TextBox control.

    With these problem symptons, I kinda got a 'Deja-Vu' feeling. It looked very much like the problem earlier encountered with displaying InfoPath forms in IE 6. And indeed: via Javascript debugging I detected that the client-side runtime error manifested itself in the same OOTB InfoPath Inc/Core.js method as before: ErrorVisualization.ComputeAbsoluteTop(). Was then the problem caused by the usage of relative CSS-positioning applied in the custom branding, this time it proved completely InfoPath related. I could easily reproduce the same problem within a standard SharePoint WebPart page, with a standard MasterPage. The explanation of the IE 6 Javascript problem is in the HTML that InfoPath Forms Services renders for a RichTextBox control: an embedded iframe element within the DOM document. Due to the fault behaviour of the IE 6 offsetParent method implementation in combination with the non-robust/error-prone implementation of the OOTB InfoPath method, this results in the runtime Javascript error.The solution was to runtime at browser/client-side expand that OOTB InfoPath method to make it robust for the IE 6 faulty CSS offsetParent implementation.





  3. InfoPath re-publishing adds duplicate site columns to the ContentType

    Upon re-publishing an InfoPath formtemplate during the demonstration, I noticed another issue. It looked as if all promoted fields in the ContentType were now duplicate present. A quick internet search learned that this is a known InfoPath feature/behaviour, just something I myself was not aware of upto now. And neither something I was particular happy with. I think it is pretty ridiculous to add a complete other set of fields to the InfoPath ContentType each time you re-publish the associated formtemplate. You have some control-options in the InfoPath publishing wizard to prevent the creation of another set of promoted fields, but this is manual-dependent and thus error-prone; let alone it is also very cumbersone and clumsy for the functional managers (the first-class users of the self-service WebForms functionality in the company portal). Therefore I sought for a manner to afterwards correct the modified ContentType, by removing all duplicate FieldLinks. Ideally did would be done fully automatic in the scope of the InfoPath publishing, and thus transparent and invisible to the WebForms functional managers. Sadly, SharePoint does not have an EventReceiver signalling the creation or update of a ContentType. The solution is therefore to deliver a custom SharePoint application page via which the WebForms functional manager can afterwards fix the InfoPath modified ContentType. He/she selects in the dropdown the ContentType that [potentially] needs a cleanup. The custom application page then inspects
    the FieldLinks in the ContentType for duplicate occurences, and removes each one found. The changes (deletions) in the SiteCollection ContentType are propagated to all childs. Also each duplicate detected FieldLink is removed from the Fields collection of each SPDocumentLibrary that is associated with the selected InfoPath ContentType (code snippet).

Thursday, August 12, 2010

Handling save conflict within SPList EventReceiver

Background

Publishing an InfoPath form to a SharePoint site creates a new contenttype in the site. Via the InfoPath publishing wizard you have some control over the structure of the created contenttype. But in essence the control is very limited when compared with explicitly provision a contenttype yourself [see also previous post Administrate data from InfoPath form in a self-provisioned ContentType for further explanation why I prefer this above the automatic created information architecture entities]. However, it is not always possible or viable to explicit provision the InfoPath utilized IA entities. The big selling point of InfoPath is that it enables self-service (web)form-definition by functional management. A key cornerstone in this self-services proces is the InfoPath publishing functionality to allow functional managers to themselves distribute the InfoPath forms to a SharePoint site.
One of the aspects missing with the InfoPath publishing managed contenttypes is including the InfoPath promoted fields in the display form. Displaying all InfoPath form fields is in particular handy when quickly browsing the data submitted to a SharePoint forms library.

Solution approach

To fix the functional shortcoming requires to afterwards make the promoted fields displayed. This is not something you want to burden the functional managers with. The aim is therefore to execute it automatically in the context of the target SharePoint site. The ideal moment would be when the InfoPath managed contenttype is created or updated. SharePoint however does not provide an eventreceiver for these events. The next best moment is when the contenttype is associated with the forms library to which the InfoPath form data is submitted. Here you neither have a direct event notifying the contentype association. But you can receive indirect notification via SPListEventReceiver::FieldAdded. The idea is then to check each added field whether it is an InfoPath promoted field, and if so to alter its definition so that the field will appear in the standard SharePoint display form.

Issues encountered

So, easily said and done. Associate the custom ListTemplate with a SPListEventReceiver, and fill in the FieldAdded method. In my local development image it worked like a charm: after you associate via the SharePoint GUI a document library with an InfoPath contenttype, all promoted fields of that contenttype appear in the document library display form. But after deploying to the central SharePoint development farm, I ran into the first issue. Upon associating a library with a contenttype, SharePoint faulted with a weird message: The specified program requires a newer version of Windows.
Despite this misleading error message, it was immediate clear that the issue was caused by the custom FieldAdded eventhandler:
Apparently, SharePoint does not allow to update a list field in the runtime context of that field being added to a list.

Solution outline

Solution is to isolate the execution of the field-update from the field-addition runtime context. Instead of direct updating the field, schedule this work via a worker thread. One extra aspect to take into account is that the scheduled worker threads can still run into a parallel update conflict:

Save Conflict

Your changes conflict with those made concurrently by another user. If you want your changes to be applied, click Back in your Web browser, refresh the page, and resubmit your changes.

  at Microsoft.SharePoint.Library.SPRequest.UpdateField(
        String bstrUrl,String bstrListName, String bstrXML)
  at Microsoft.SharePoint.SPField.UpdateCore(Boolean bToggleSealed)
  at Microsoft.SharePoint.SPField.Update()
  at WebformulierFieldsHandler.Worker.b__1()
 

The likelihood of these save conflicts increases with the number of promoted fields in the contenttype, and can already manifest itself when that number is 4 to 5. It can be made functional robust by implementing a retry mechanism in the worker thread.

Tuesday, February 23, 2010

FormsService SSP in farm holds on to previous version of Feature re-deployed InfoPath Form

Earlier I blogged about the ALM manner to develop and deploy InfoPath forms. One major benefit of this approach is that the InfoPath forms are considered as first-class source artifacts in the development environment and project, just as ‘regular’ source code (C#, XML, .js, css). An even bigger benefit is upon deployment: no need for manual actions to publish the form templates to your deployed and provisioned SharePoint application, it’s all done automatic in the context of a feature activation.
SharePoint out-of-the-box provides the XsnFeatureReceiver class to provide the InfoPath Form Templates publishing support. However, the functionality of this standard class is not enough for real-life deployment scenarios. I ran into a few missing parts before, which I handled by extending and augmenting the XsnFeatureReceiver class. This week I was confronted with another issue. After a re-deployment with updated InfoPath forms, the earlier deployed forms were still active. That is, when opening them in the browser. When opening in InfoPath client, the new version was applied. Thus the deployment to SharePoint server was performed, but somehow for InfoPath Forms Services context the earlier deployed version per InfoPath form remained active. I did not experience this effect in my local single-server local development image. But when doing a re-deployment to the distributed test farm-environment, the issue manifests itself.
I inspected the managed forms templates administration of the FormsService SSP, and discovered that still the initial deployed versions were administered. And after uninstalling the InfoPathInfrastructure feature, the InfoPath form templates in FormsService were not cleared. This obstructs the activation of the new versions of the forms upon re-deployment. As solution I extended my InfoPathInfrastructure feature to also make sure the InfoPath forms that it installed, are removed from the FormsService SSP upon uninstalling the feature.
With this fix, I’m able to deploy and activate new versions of the InfoPath forms to the distributed target SharePoint farm environment (test, staging, production) for usage via InfoPath Forms Services. And still conform ALM principles.

Friday, January 15, 2010

Aggregate the values from an InfoPath repeating section for data administration

A common requirement for setting up (web)forms is to enter multiple rows of similar information. For example, entering the name(s) of your child(ren) for an holiday booking. The InfoPath Repeating Section enables an intuitive usage model for this. Initial the form displays only 1 section for data entrance. Via a link or button the user can on-the-fly expand the form with more occurrences of that same section.
Question is how best to administer the entered data from the repeating section in a SharePoint library. Is this a Master/Slave relationship? Or should it be considered simple as embedded in the form data self? The former requires a relation data model employed within the SharePoint information architecture. Although possible, this is not the typical SharePoint model which is namely to administrate the entity data in a single List or Library. However, for the second alternative it is still required to potentially administrate multiple values per field in the repeating section.
SharePoint supports this via the concept of AggregateFunction at Field level. To collect the data from a repeating section, specify aggregate="merge" for the field within the InfoPath form template
When explicitly self-provision the SiteColumns and ContentType (Administrate data from InfoPath form in a self-provisioned ContentType), you ran into a problem here. The FieldSpecification CAML currently does not allow the Aggregate attribute as in:

<FieldRef
  ID="{5e046132-9284-4322-b1c7-9a742d60270e}"
  Name="FirstNameChild"
  ReadOnly="TRUE"
  Node="/my:myFields/my:group/my:Children/my:FirstNameChild" />

SharePoint will fail the activation of your Feature with the error message: The 'Aggregation' attribute is not allowed.
The remedie is to skip the 'Aggregate' attribute from the CAML specification, and afterwards add it via an EventReceiver to the provisioned SPField in the RootWeb:

  SPField aggregateField = site.RootWeb.Fields[new Guid("{5e046132-9284-4322-b1c7-9a742d60270e}")];
  aggregateField.AggregationFunction = "merge";
  aggregateField.Update();

Wednesday, January 13, 2010

Administrate data from InfoPath form in a self-provisioned ContentType

When registrating via XSNFeatureReceiver an InfoPath Form Template in a SharePoint sitecollection, automatically a ContentType and the associated SiteColumns are provisioned. This ContentType enables promotion of the data entered via the form to a SharePoint library. However, I prefer to apply explicit self-provisioned ContentType and SiteColumns for the data administration. The rationale is to have full control over the created information architecture entities. In the automatic created ContentType, the ID is arbitrary and thus differerent accross (re)deployments and environments. This hampers the content deployment process between environments. And at rolling out an upgrade of the InfoPath form template in the same site collection, a new version of the ContentType is provisioned with potential another ID. The automatic provisioned SiteColumns do not expose this particular problem. Their IDs are namely contained in the InfoPath form template, and therefore remain identical accross (re)deployments. But the SiteColumns have their own issues. Most noticable is a SiteColumn with type (Multi)Lookup. This cannot be correctly provisioned via a CAML FieldSpecification only, but requires a fix afterwards (Creating Lookup columns as a feature). Another problem that both the automatic provisioned SiteColumns and ContentType suffer of, is when there are changes in the definiton while the entity is already in use. To propagate the changes at the sitecollection-defined entities to their usage at List/Library level requires some afterwards fix via custom code.
Because of these issues I prefer to explicit self-provision both the SiteColumns and the ContentType for administrating the forms data. You then control the ID of the ContentType, you can fix the Lookup-relationships for SiteColumns, and you can propagate changes within the ContentType to all List/Libraries which already use the type. It costs some extra development time, but it surely pays off upon repeated full-automic deployments, and accross environments.

Monday, January 11, 2010

Server-side generation of InfoPath email

In a SharePoint based application, we utilize InfoPath Forms Server to serve InfoPath forms. The reason to apply InfoPath are the well-known: to enable business itself to design and maintain the forms, within a rich designer tool [the InfoPath designer]. To make that viable, a design principle is to keep the forms as slim and simple as possible. Meaning, no (complex) business logic embedded in the forms, no code behind, no web services invocation; just restrict to form input and validation. Another advantage of this restricted InfoPath usage, is that business can itself directly deploy the forms. These type of forms do not require FullTrust, nor is it needed to approve and activate them by IT operations in central admin.
A requirement is to email the forms; to a functional mailbox, and conditionally to the end-user him/herself. The latter is flagged by an option on the form. A management requirement is that the mailbox per form type must be configurable, without need to modify the forms. The only allowed management actions on the form level are for altering data collection and form display/layout.
InfoPath provides standard functionality to automatically email the form upon submit. However, because the destination functional mailbox may not be hardcoded in the form (the management requirement); and because of the conditional email copy to end-user would result in a more complex submit option; I rejected that solution approach. Instead I opt for a setup in which the form email handling is done via server side code. The high level design:
  • associate an SPItemEventReceiver for the ItemAdded event of new instances of the Form ContentType. The SPItemEventReceiver can either be associated at the ContentType definition, or per FormLibrary
  • The SPItemEventReceiver handles the emailing of each new added form. One to the configurable functional mailbox. This setting can thus be server side retrieved and applied, and it can be managed without need to modify the form for this. And if the end-user selected so in the form, then also send a copy to the end-user mailbox. This conditional logic can easily be developed within the SPItemEventReceiver handling
To set up the email-version of a submitted form, it is needed to retrieve the form data, and transform that into an email body-message. Design decision here is to apply the same view translation as embedded within the source form template. Rationale for that decision is that the email layout always will correspond with the displayed form layout. In that context, it is desired to retrieve the view stylesheet directly from the source form template self. Then the email layout will remain to automatically follow the form layout, irrespective of changes made in the form layout by business. With this, the steps to setup the email body are:
  1. read in the xmfile associated with the added form item
  2. retrieve the mso-infoPathSolution processing instruction, and parse the name of the source form template
  3. download the source form template, and extract the view stylesheet from the cabinet container
  4. cache the relation <name source form template, view> for later added forms the view can directly be found without need to get it from the source form template
  5. apply the view stylesheet to transform the data of the xmlfile into html
The result is an email message in the same layout as the form rendering within InfoPath Forms Server. Warning: typically the resulting message is too large to be correctly handled by SPUtily.SendMail, so best to apply SmptClient instead for sending the email.

Thursday, January 7, 2010

Tip: submit form via InfoPath client when InfoPath Forms Server submit reports a problem

Context:

Upon submit of a form via InfoPath Forms Server, encounter the InfoPath error "An error occurred while the form was being submitted". That's it, no more error details displayed, nor to be found within the logging.

Problem analysis

So, how to detect what is the concrete problem cause? Tip: try to submit the same form directly via the InfoPath designer client. If the problem also occurs within this host context, you can here view more error details. In my situation, the error details displayed "A value in the form may be used to specify the file name. If you know the value in the form that specifies the file name, revise it and try again. Otherwise, contact the author of the form template". Translation, I forgot to specify a filename pattern within the submit options to make it unique.

Tuesday, January 5, 2010

Automatic publish InfoPath forms via Feature-based ALM deployment

Fixed the Feature-based deployment of InfoPath forms to SharePoint site while treating them as first-class source artifacts in VS + TFS environment

I’m a supporter of applying Application Lifecycle Management (ALM) principles within application development projects. This also goes for SharePoint-based custom applications. Basically this means:
  1. designing and developing the SharePoint artifacts within a team-based source repository environment (aka, Team Foundation Server);
  2. applying version control to the individual application/building blocks;
  3. daily build;
  4. and full automatic and repeatable deployments
With InfoPath development, this gives some challenges. First of all, the InfoPath forms are setup and maintained via its own designer-tool, namely the InfoPath Designer client; and not directly from within the scope of Visual Studio. However, the most significant issue is the manner of deployment: the InfoPath client contains functionality to publish the InfoPath form to the destination environment, with SharePoint site as one of the available options. Given my ALM aspirations, I want to copy this InfoPath client deployment-experience to within the regular SharePoint deployments, via the Solution framework and SharePoint Features. In essence, I want to achieve the following:
  1. Automatically provision the required Information Architecture artifacts:
    • SiteColumns
    • Masterdata for LookupFields
    • ContentTypes
    • Forms Libraries, associated with the ContentTypes, for administration of the filled in InfoPath forms
  2. Automatically provision and activate the required InfoPath infrastructure
    • InfoPath forms
    • DataConnections
The first category, the Information Architecture artifacts, is well-known SharePoint provisioning. It can be done via a Site Definition, Features, or a combination. I favor the Feature-based approach, because it is modular, and allows me to turn it off and on.
The second category gave me more challenges. On internet search, I came across some posts that mentioned the SharePoint OOTB XSNFeatureReceiver that aids in here. However, it quickly appeared to me that it only takes care of one part, namely registrating the InfoPath form templates within the target SharePoint site. To successfully achieve this, XSNFeatureReceiver has constraints on the deployable InfoPath forms. And when even a single one of them does not comply to these constraints, XSNFeatureReceiver rather silently fails to correctly install the forms. Another missing part is for the DataConnections. The upload is not the issue here, being standard SharePoint Module functionality. But rather adjusting them to the specific target environment. In my ALM principles, I want to hold on to 1 single instance per DataConnection file, and not be forced to maintain versions for each of them per target environment (development, test, staging, production).
Luckily, Microsoft has not made XSNReceiverFeature sealed. So I decided to overload it to augment the standard deployment functionality with the above described aspects. Adjusting the DataConnections appeared rather simple, with thanks to this blog (Building InfoPath Form Services Solutions using Visual Studio 2008), including a link to demo provision code. I took this as basis to realize a generic approach, feeded by an XML configuration file delivered within the feature scope
The correct deployment of the InfoPath forms appeared to be more of a challenge. Eventually, I found out that the direct cause of the failed deployment was that I earlier on published the forms during my development and testing of the forms. As soon as you publish an InfoPath form, the InfoPath client administrates this within the form itself. And this appeared to result in the malfunctioning XSNFeatureReceiver based InfoPath forms registration. This can be mitiligated by correcting the InfoPath forms. An alternative is to modify the original InfoPath form artifacts. However, this would then be a corrective action to be repeated every time you’d published an InfoPath form just for an InfoPath designer (programmer) test. I therefore went for the alternative to do the correction as part of the Feature installation procedure. This way during the development phase the team can just go one and (re)publish the forms, without worrying about the effect on deployment later on.


Another thing to take into account when doing Feature-based deployment of InfoPath forms, is that the Forms must be contained at the Feature root-directory. I like to structure Feature contents within subfolders for different deployable types. ContentTypes, SiteColumns, DataConnections, and also InfoPath forms, each within their own subfolders within the feature. However, in case of InfoPath forms deployed via XSNFeatureReceiver, this is not working. Without a concrete error message, the registrating will then just not be performed. So keep your Forms at the Feature root-level.

Deployment steps, and thus functionality of InfoPathInfrastructure feature

Upon Feature installation event:

  1. Fix the forms files, for successful automatic publishing via XSNFeatureReceiver
    • Extract the forms; this on itself presented a challenge, since .NET does not provide standard cabinet (.cab) handling
    • Inspect the manifest.xsf file on the presence of publishUrl and trustLevel; remove any of them present
    • Repack the .xsn container / file

Within the Feature Activation:

  1. upload forms files to FormsServerTemplate library
  2. Registrate the forms
  3. upload data connections to DataConnections library
  4. provision site columns
  5. provision master data lists
  6. fix the lookup references
  7. provision content types; including reference to the uploaded forms
  8. provision contents/data libraries
  9. assign content types to the contents libraries
  10. fix retrieve data connections --> associate with the master data list in the deploy environment (iso your local / development source)
  11. fix submit data connection --> associate with the content data list in the deploy environment (iso your local / development source)

Saturday, January 2, 2010

InfoPath embedded via XMLFormView conflicts in IE with CSS relative positioning

You can apply the out-of-the-box XMLFormView webpart to embed an InfoPath form within a SharePoint page. This way, the form displays as a visual integrated part within the SharePoint application, maintaining for the user the full application context (branding, navigation menu's). However, upon doing this (after applying the required infrastructure initialization steps, see this whitepaper), I continuously encounter a runtime Javascript eror when browsing the page in Internet Explorer. The error occurs in INC\Core.js in the ErrorVisualization_ComputeAbsoluteLeft function, used to position an error asterix within the form. Within this method, the DOM-tree is traversed up to the HTML-root. At least that is the intention, but somehow this goes wrong with IE; and the OOTB script is not made robust. This article gives more background details.
So, the problem cause is known. What about the resolution? I don't intend to sacrifice the correct CSS handling, just because IE doesn't handle it well. I see multiple options:
  1. Making the HTML robust by wrapping the relative positioned DIV within an absolute positioned Body-container
  2. Runtime overloading the OOTB ErrorVisualization_ComputeAbsoluteLeft function to fix it for IE behaviour
  3. Use the PageViewer webpart iso XMLFormView to embed the InfoPath form
  4. Inspect the Core.js code, and find a proper, and more localized solution
Your guess is right, I applied the latter. It appears that although the runtime errors occurs within the ErrorVisualization_ComputeAbsoluteLeft function, the solution can be found within the invoked helper ErrorVisualization_IsPositionCausingElement function. That function returns true if the inspected style object has its overflow property set, and in that case the loop within ErrorVisualization_ComputeAbsoluteLeft stops. The solution is therefore to assign an overflow setting to a proper container element of the XmlFormView. Since CSS styles are applied afterwards, it's not possible to assign this property via CSS. Options are to either place the style inline, or to add it via code - before the ErrorVisualization_ComputeAbsoluteLeft will be invoked. I applied the last. Rationale for this is that I only want to apply the IE-hack / resolution when it is needed. That is, when an XmlFormView is rendered within either IE 6 or 7. Inline style does not allow me to that, javascript does:
<script type="text/javascript">
function fixInfoPathFormView()
{
  /*@cc_on
  @if (@_jscript_version <= 5.7)
  var objFormView = document.all["__XmlFormView"];
  if (objFormView != null)
  {
    var objFormViewContainer = document.all["wrap-center"];
    if (objFormViewContainer.style != null && objFormViewContainer.style.overflow == "") objFormViewContainer.style.overflow = "auto";
  }
  /*@end
  @*/
}
</script>
<UPDATE>The above workaround is dependent on the installation of SharePoint SP2 in your environment. Prior SP2, the ErrorVisualization_ComputeAbsoluteLeft function does not check via the ErrorVisualization_IsPositionCausingElement function; but just tries to traverse the offsetParent hierarchy until reaching the document.body object. Remedie then to avoid the runtime error is option 1: set the position of the document.body container to static.</UPDATE>