Friday, July 30, 2010

Obscure SharePoint 2010 Workflow Status Link Error

Here’s an obscure error I ran into today, and a nasty workaround.  In SharePoint 2010, if you add a Library or List to a page, and the view you show has an associated workflow status column, then clicking the link to show the workflow status will result in an error.  If you dig in the ULS logs, the error is ‘Index Out of Bounds’.  Looking closely at the URL, you can see that the QueryString is not properly formed – it contains an empty List parameter: WrkStat.aspx?List=&WorkflowInstanceId=<guid>

Here’s repro steps:

  • Create a new blank site
  • Create a new Document library named ‘Test Docs’
  • Associate any workflow with the library, set to start when item created
  • Edit the blank site’s home page
  • Add ‘Test Docs’ as a web part to the page
  • Change the view to ‘All Documents’
  • Upload a document
  • Click the workflow status link
  • Observe an error occurs.
  • Observe the URL QueryString contains ‘List=&’ (ie a blank List)

The cause of the error is apparently that WrkStat.aspx attempts to use the “List” parameter passed in to the page.  (ie, currentWeb.Lists[listId]).  This is Bad Design (tm) – your page should be resilient enough to handle junk input in the querystring.  Fortunately, that parameter is not strictly required. Removing the List parameter altogether from the querystring results in the page being displayed correctly.  Unfortunately, that’s not exactly trivial to do.

When you add a List to a page in SharePoint 2010, behind the scenes you are really adding an XSLTListViewWebPart.  This web part can be customized in SharePoint Designer using XSLT.  If you’re not familiar with XSLT, you’re very fortunate.  It’s a language used to transform XML into something else – in this case HTML to render the list.   It’s powerful, but can be a beast to work with.  For example, it doesn’t have a built-in “string replace” function, which is just the thing you’ll need to fix up that bum url.  Luckily, I found a string replace template online, which gave me what I needed to fix the link.

So, here’s the workaround until this is patched:

  • Open the site in sharepoint designer, edit homepage (or, whatever page has the issue)
  • place the text cursor in the Workflow Status cell that has the bug
  • type ‘test’ – this is to break into custom xslt, and to help you mark where to place the snippets.
  • switch to split view
  • find the <td></td> that contains some xslt and the word ‘test’.  Your cursor _should_ be there already. Replace everything between the <td></td> with Snippet 2. 
  • Add Snippet 1 to the bottom of the stylesheet, immediately before  </xsl:stylesheet>
  • Remove the word ‘test’ that you typed, if you didn’t replace it above
  • Save

Snippet 1

   <xsl:template name="string-replace-all">

    <xsl:param name="text" />

    <xsl:param name="replace" />

    <xsl:param name="by" />

    <xsl:choose>

      <xsl:when test="contains($text, $replace)">

        <xsl:value-of select="substring-before($text,$replace)" disable-output-escaping="yes" />

        <xsl:value-of select="$by" disable-output-escaping="yes" />

        <xsl:call-template name="string-replace-all">

          <xsl:with-param name="text"

          select="substring-after($text,$replace)" />

          <xsl:with-param name="replace" select="$replace" />

          <xsl:with-param name="by" select="$by" />

        </xsl:call-template>

      </xsl:when>

      <xsl:otherwise>

        <xsl:value-of select="$text" disable-output-escaping="yes" />

      </xsl:otherwise>

    </xsl:choose>

  </xsl:template>   

Snippet 2

  <xsl:if test="@ClassInfo='Menu' or @ListItemMenu='TRUE'">

                <xsl:attribute name="height">100%</xsl:attribute>

                <xsl:attribute name="onmouseover">OnChildItem(this)</xsl:attribute>

      </xsl:if>

                <xsl:attribute name="class">

                <xsl:call-template name="getTDClassValue">

                                <xsl:with-param name="class" select="$class" />

                                <xsl:with-param name="Type" select="@Type"/>

                                <xsl:with-param name="ClassInfo" select="@ClassInfo"/>

                </xsl:call-template>

      </xsl:attribute>

      <xsl:variable name="workflowStatusLink">

      <xsl:apply-templates select="." mode="PrintFieldWithECB">

        <xsl:with-param name="thisNode" select="$thisNode"/>

      </xsl:apply-templates>

      </xsl:variable>

      <xsl:variable name="workflowStatusLinkFixedUp">

    <xsl:call-template name="string-replace-all">

      <xsl:with-param name="text" select="$workflowStatusLink" />

      <xsl:with-param name="replace" select="'List='" />

      <xsl:with-param name="by" select="''" />

    </xsl:call-template>

  </xsl:variable>

      <xsl:value-of select="$workflowStatusLinkFixedUp" disable-output-escaping="yes"/>

12 comments:

Unknown said...

Holy crap! This is unreal

Satya Kanithi said...
This comment has been removed by the author.
Satya Kanithi said...
This comment has been removed by the author.
Satya Kanithi said...

Perfect, have been looking for this for a while. Worked perfectly...

Unknown said...

You can solve this much more easily. Problem is, that the default.aspx is not stored in any library.

So just create a document library and move default.aspx with SharePoint Designer into this library. Then the link is correctly generated.

Another option is to activate Publishing or Wiki Home Page feature, that directly create Pages Libraries.

Daniel Root said...

Interesting. I'll definitely look into that fix Uwe. Certainly anything would be better than this monstrosity ;)

Unknown said...

As per Uwe's comment... I cant seem to find a way to move the default.aspx to another document library.

Any ideas?

Unknown said...

Just open the website with SharePoint Designer, click on All Files in the navigation area (left lower area). There you will find the default.aspx. Just move this file to a doc lib.

Unknown said...

Sorry was being very slow. Will give it a shot!

Unknown said...

Ok moved the file... but now obviously I dont have a default landing page for the site. Is there any way to set the moved default.aspx as the default landing page?

Thanks.

Unknown said...

Sure, just open the file in the browser. In the ribbon on the Page tab there should be a link "Make Homepage".

Unknown said...

Apologies again... it just requires you to right click in Desinger and set as homepage!

And just to confirm, this does indeed work!

Thanks buddy. Just need to work out my last issue now with claiming tasks from a webpart. It opens the page behind within the modal dialog box!