Monday, October 17, 2011

XsltListViewWebPart Custom View and XSL

SharePoint 2010 introduces a new XsltListViewWebPart that is inherited from DataFormWebPart. One great feature in XsltListViewWebPart is that you can define query and fields easily and clearly. Following XML declaration demonstrates how to set a XsltListViewWebPart to show open tasks assign to current user, and link to external custom XSL to display the items.
<WebPartPages:XsltListViewWebPart runat="server" IsIncluded="True" GhostedXslLink="main.xsl" 
    ListDisplayName="Tasks" NoDefaultStyle="TRUE" ViewFlag="8" Title="Tasks" PageType="PAGE_NORMALVIEW"  
    Default="FALSE" DisplayName="Open Tasks" DetailLink="Lists/Tasks" __markuptype="vsattributemarkup" 
    partorder="3"  viewcontenttypeid="0x" >
    <XmlDefinition>
        <View MobileView="TRUE" Type="HTML" DisplayName="All Tasks" Url="Lists/Tasks/AllItems.aspx" 
            Level="1" BaseViewID="1" ContentTypeID="0x" ImageUrl="/_layouts/images/issues.png">
            <Query>
                <OrderBy>
                    <FieldRef Name="Status" Ascending="TRUE"/>
                </OrderBy>
                <GroupBy></GroupBy>
                <Where>
                    <And>
                        <Eq>
                            <FieldRef Name="AssignedTo"/><Value Type="Integer"><UserID/></Value>
                        </Eq>
                        <Neq>
                            <FieldRef Name="Status"/><Value Type="Text">completed</Value>
                        </Neq>
                    </And>
                </Where>
            </Query>
            <ViewFields>
                <FieldRef Name="LinkTitleNoMenu"/>
                <FieldRef Name="Status"/>
                <FieldRef Name="Priority"/>
                <FieldRef Name="DueDate"/>
                <FieldRef Name="PercentComplete"/>
            </ViewFields>
            <RowLimit Paged="TRUE">30</RowLimit>
            <Toolbar Type="Freeform" />
        </View>
    </XmlDefinition>
    <XslLink>Site Assets/XSL/OpenTask.xsl</XslLink>
    <parameterbindings>
        <ParameterBinding Name="dvt_sortdir" Location="Postback;Connection"/>
        <ParameterBinding Name="dvt_sortfield" Location="Postback;Connection"/>
        <ParameterBinding Name="dvt_startposition" Location="Postback" DefaultValue=""/>
        <ParameterBinding Name="dvt_firstrow" Location="Postback;Connection"/>
        <ParameterBinding Name="OpenMenuKeyAccessible" Location="Resource(wss,OpenMenuKeyAccessible)"/>
        <ParameterBinding Name="open_menu" Location="Resource(wss,open_menu)"/>
        <ParameterBinding Name="select_deselect_all" Location="Resource(wss,select_deselect_all)"/>
        <ParameterBinding Name="idPresEnabled" Location="Resource(wss,idPresEnabled)"/>
        <ParameterBinding Name="NoAnnouncements" Location="Resource(wss,noXinviewofY_LIST)"/>
        <ParameterBinding Name="NoAnnouncementsHowTo" Location="Resource(core,noXinviewofY_DEFAULT)"/>
        <ParameterBinding Name="AddNewAnnouncement" Location="Resource(wss,addnewitem)"/>
        <ParameterBinding Name="MoreAnnouncements" Location="Resource(wss,moreItemsParen)"/>
        <ParameterBinding Name="UserID" Location="CAMLVariable" DefaultValue="CurrentUserName"/>
    </parameterbindings>
</WebPartPages:XsltListViewWebPart>
The custom XSL does three things:

1. Replace priority string from "(2) Normal" by "Normal"
2. Replace default empty message "There are no items to show in this view of the {ListName} list. To add a new item, click 'New'." by "No item"
3. Replace the SP2010 Add New Item icon to old SP2007 icon

The XSL source:
<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">
    <xsl:include href="/_layouts/xsl/main.xsl"/>
    <xsl:include href="/_layouts/xsl/internal.xsl"/> 

    <xsl:template name="FieldRef_body.Priority" match="FieldRef[@Name='Priority']" mode="body">
                <xsl:param name="thisNode" select="."/>
                    <xsl:choose>
                      <xsl:when test="starts-with($thisNode/@*[name()=current()/@Name], '(1) ')">
              <xsl:value-of select="substring-after($thisNode/@*[name()=current()/@Name], '(1) ')"/>
                      </xsl:when>
            <xsl:when test="starts-with($thisNode/@*[name()=current()/@Name], '(2) ')">
              <xsl:value-of select="substring-after($thisNode/@*[name()=current()/@Name], '(2) ')"/>
            </xsl:when>
            <xsl:when test="starts-with($thisNode/@*[name()=current()/@Name], '(3) ')">
              <xsl:value-of select="substring-after($thisNode/@*[name()=current()/@Name], '(3) ')"/>
            </xsl:when>
                    </xsl:choose>
    </xsl:template>
  
  <xsl:template name="EmptyTemplate">
    <tr>
      <td class="ms-vb" colspan="99">No item</td>
    </tr>
  </xsl:template>

  <xsl:template name="Freeform" >
    <xsl:param name="AddNewText" />
    <xsl:param name="ID" />
    <xsl:variable name="Url">
      <xsl:choose>
        <xsl:when test="List/@TemplateType='119'">
          <xsl:value-of select="$HttpVDir" />/_layouts/CreateWebPage.aspx?List=
          <xsl:value-of select="$List" />&amp;RootFolder=
          <xsl:value-of select="$XmlDefinition/List/@RootFolder" />
        </xsl:when>
        <xsl:when test="$IsDocLib">
          <xsl:value-of select="$HttpVDir" />/_layouts/Upload.aspx?List=
          <xsl:value-of select="$List" />&amp;RootFolder=
          <xsl:value-of select="$XmlDefinition/List/@RootFolder" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$ENCODED_FORM_NEW" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:variable name="HeroStyle">
      <xsl:choose>
        <xsl:when test="Toolbar[@Type='Standard']">display:none</xsl:when>
        <xsl:otherwise></xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <xsl:if test="$ListRight_AddListItems = '1' and (not($InlineEdit) or $IsDocLib)">
      <table id="Hero-{$WPQ}" width="100%" cellpadding="0" cellspacing="0" border="0" style="{$HeroStyle}">
        <tr>
          <td colspan="2" class="ms-partline">
            <img src="/_layouts/images/blank.gif" width="1" height="1" alt="" />
          </td>
        </tr>
        <tr>
          <td class="ms-addnew" style="padding-bottom: 3px">
            <img src="/_layouts/images/rect.gif" alt="Add a new item" />
            <xsl:text disable-output-escaping="yes" ddwrt:nbsp-preserve="yes">&amp;nbsp;</xsl:text>
            <xsl:choose>
              <xsl:when test="List/@TemplateType = '115'">
                <a class="ms-addnew" id="{$ID}-{$WPQ}" href="{$Url}" 
                   onclick="javascript:NewItem2(event, &quot;{$Url}&quot;);javascript:return false;" target="_self">
                  <xsl:value-of select="$AddNewText" />
                </a>
              </xsl:when>
              <xsl:otherwise>
                <a class="ms-addnew" id="{$ID}" href="{$Url}" 
                   onclick="javascript:NewItem2(event, &quot;{$Url}&quot;);javascript:return false;" target="_self">
                  <xsl:value-of select="$AddNewText" />
                </a>
              </xsl:otherwise>
            </xsl:choose>
          </td>
        </tr>
        <tr>
          <td>
            <img src="/_layouts/images/blank.gif" width="1" height="5" alt="" />
          </td>
        </tr>
      </table>
      <xsl:choose>
        <xsl:when test="Toolbar[@Type='Standard']">
          <script type='text/javascript'>
            if (typeof(heroButtonWebPart <xsl:value-of select="$WPQ" />) != "undefined") {
            <xsl:value-of select="concat(' var eleHero = document.getElementById(&quot;Hero-', $WPQ, '&quot;);')" />
            if (eleHero != null) eleHero.style.display = ""; }
          </script>
        </xsl:when>
        <xsl:otherwise></xsl:otherwise>
      </xsl:choose>
      <xsl:if test="List/@TemplateType = '115'">
        <script type='text/javascript'>
          if (typeof(DefaultNewButtonWebPart <xsl:value-of select="$WPQ" />) != "undefined") {
          <xsl:value-of select="concat(' var eleLink = document.getElementById(&quot;', $ID, '-', $WPQ, '&quot;);')" />
          if (eleLink != null) { DefaultNewButtonWebPart <xsl:value-of select="$WPQ" /> (eleLink); } }
        </script>
      </xsl:if>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>
Note that linking external XSL in XsltListViewWebPart would have performance impact since a compilation process would occur each time the WebPart is loaded, and the WebPart content can't be cached anymore. So it would be better to use inline XSL if there're many WebParts in a page.