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.

Wednesday, August 4, 2010

What about the competition - new roadmap for SAP NetWeaver Portal

As a SharePoint adept, I don't really consider the SAP Enterprise Portal as competition. Let's face it, the typical UI is horrible, and it lacks many of the capabilities the SharePoint platform brings. But moreover, the impression [within the market] is that SAP has stopped with actual new major developments in this product (Web2.0, content management, social media). SAP is putting it's intellectual resources and budget mainly on the business capabilities, and less on fancy user interaction. Fair enough, as it leaves room for integration and interoperability via foreign UIs, as SharePoint.
Contradicting the above, it's knownable that at SAPPHIRE some new additions for SAP NetWeaver Portal were announced. Most notable the concept of 'Enterprise Workspaces'. Read and see more of this in SAP NetWeaver Portal – SAPPHIRE NOW Summary.
It aims to provide SAP users with an iGoogle- or myYahoo-type experience within SAP, allowing them to build their own pages with structured and unstructured assets -- reports, RSS feeds and so on. It applies a self-service approach within defined roles and security. [SAP NetWeaver Portal roadmap includes 'Workspaces,' easier third-party integration]
Well, the UI impressions and the self-service concepts are rather impressive and promising. I do hold on to my opinion that SharePoint is a better overall portal platform. But it looks as if SAP could be making a new step forward in its own portal product.