Friday 1 August 2014

Difficulties overriding XLV web parts with external stylesheets in SharePoint libraries

Overriding an XSLT List Web Part runs into some limitations when using an external stylesheet with the XslLink property. The problem is that the ECB menu (which comes up when you click the arrow next to a list item name or title) is broken. Also, many of the item related ribbon buttons under the Documents tab are greyed out. The combination of those two problems makes for a very crippled web part which is good for not much more than displaying the list items.
Reproducing the problem is more specific than I've suggested. Only external stylesheets located in the SharePoint content database have these problems. In other words, stylesheets located in one of your document libraries (such as the root site's Style Library) break the ECB and the ribbon. Stylesheets located on the file system in the _layouts folder do not. This is all explained in some detail by Glyn Clough in his SharePoint blog - XLV Bug: XSL Link Property and the ECB Menu.
I've done a bit of my own debugging and found that the XslLink problem affects the ECB menu and the ribbon whether you use a fully qualified absolute URL, a server relative URL, or a page relative URL. The ECB menus and ribbon controls are rendered as a result of respective AJAX calls to obtain HTML to add to the DOM node tree. This includes script source​, which is executed by the browser. It's a rather clunky and insecure way to do it, if you ask me, and it may even be vulnerable to cross-site scripting exploits. Nevermind that. Long story short, when the XSLT stylesheet is in the web part definition in the page itself, no problem. When the stylesheet is external an in the content database, the SharePoint application page /_layouts/inplview.aspx is unable to create the output required.
For the ECB menu, the URL would look something like this:
http://[my host]/[path to my site]/_layouts/inplview.aspx?Cmd=Ctx&List={GUID of list}&View={GUID of view}&ViewCount=1&IsXslView=TRUE&Field=LinkFilename&ID=5&ListViewPageUrl=[URL of my page]
Plugging that URL into a browser results in the following response:
<div class="ms-vb">Unable to display this Web Part. To troubleshoot the problem, open this Web page in a Microsoft SharePoint Foundation-compatible HTML editor such as Microsoft SharePoint Designer. If the problem persists, contact your Web server administrator.</div>
<br/><br/>Correlation ID:[correlation ID GUID]<br/>
The javascript doesn't find what it's looking for in the HTML source of that response, and the result is a canned alert box displaying the unhelpful message "This item is no longer available.  It may have been deleted by another user.  Click 'OK' to refresh the page."
It turns out there are a couple decent workarounds to this problem with the ECB menu apart from keeping the XSLT in the page. The simplest workaround is to edit the web part in the browser and tick "Enable Asynchronous Update" under AJAX Options in the web part properites. This causes the ECB menu to be loaded in a different way that does not rely on the bad call to inplview.aspx. What other side effects it may have, desirable or otherwise, I'm not sure.
Another good workaround that avoids editing the web part properties is this one, discovered by Henk Haarsma: XSLT in sandbox solution. This technique uses javascript in the XSLT code to change the URL in the AJAX call so that the isXslView parameter is removed. That parameter does not seem to be needed in this call. When the call is working because the conditions for the bug are not present, the removal of the query string parameter makes no difference to the output.
Neither of these workarounds solve the ribbon problem. Encouraged by the success of the "Enable Asynchronous Update" solution, I tried all sorts of other settings in the web part properties. Nothing made a difference. The javascript solution prompted my to try the same thing in another part of the stylesheet that controls the URL to the AJAX call for the ribbon settings. That URL looks something like this:
http://[my host]/[path to my site]/_layouts/inplview.aspx?List={GUID of list}&View={GUID of view}&ViewCount=1688&ListViewPageUrl=[URL of my page]&IsXslView=TRUE&Cmd=EcbView
It shows the same output as the broken ECB call and removing the isXslView query string parameter does seem to fix it; however, when the call is working properly the output with the isXslView parameter is very different. So that's not a solution for the ribbon.
There is, however, hope. The two calls seem to fail for different reasons, or in different ways. There is another way to pull in an external stylesheet. You can use a very simple inline stylesheet in the web part's Xsl property that does nothing more than an xsl:include of the external file. It looks something like this:
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl
ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20"
xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-
microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:o="urn:schemas-microsoft-com:office:office"
ddwrt:ghost="show_all">
<xsl:include href="[path to external XSL file]"/>
</xsl:stylesheet>
An xsl:import would give identical results in this case. Using this method had no effect on the rendering of the ECB menu; however, with an absolute URL supplied the ribbon does work! So you can use one of the two methods I described earlier to enable the ECB menu combined with this method for solving the ribbon problem for a fully functioning web part. It is unfortunate to have to use an absolute URL, but at least that shows it's possible. Debugging with .Net Reflector shows that strange things are happening when SharePoint is trying to resolve the URL to the stylesheet in the ribbon AJAX call, and it fails in different ways depending on how the external file is referenced. I'm hoping that with a bit more digging I'll be able to find a workaround for page relative and server relative URLs as well.

Flayman on LiveJournal (old)