CQWP: 30 Tips #10 Passing Current Position

It can often be handy within your item style template to know what row you’re processing.  For example, if you are displaying items in a table format and you want to spit out a row of column headers before the item data if you’re processing the first row.

Making this information available to your item style template is just a couple quick updates.  First, inside ContentQueryMain.xsl, update OuterTemplate.CallItemTemplate to send along the current position as a parameter.

<xsl:template name=”OuterTemplate.CallItemTemplate”>
        <xsl:param name=”CurPosition” />
        <xsl:value-of disable-output-escaping=”yes” select=”$BeginListItem” />
        <xsl:choose>
            <xsl:when test=”@Style=’NewsRollUpItem’”>
                <xsl:apply-templates select=”.” mode=”itemstyle”>
                    <xsl:with-param name=”EditMode” select=”$cbq_iseditmode” />
                </xsl:apply-templates>
            </xsl:when>
            <xsl:when test=”@Style=’NewsBigItem’”>
                <xsl:apply-templates select=”.” mode=”itemstyle”>
                    <xsl:with-param name=”CurPos” select=”$CurPosition” />
                </xsl:apply-templates>
            </xsl:when>
            <xsl:when test=”@Style=’NewsCategoryItem’”>
                <xsl:apply-templates select=”.” mode=”itemstyle”>
                    <xsl:with-param name=”CurPos” select=”$CurPosition” />
                </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select=”.” mode=”itemstyle”>
                    <xsl:with-param name=”CurPos” select=”$CurPosition” />
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:value-of disable-output-escaping=”yes” select=”$EndListItem” />
    </xsl:template>

Note that by adding that update, you’ll always send across the current position even if the receiving item style template doesn’t use or declare a CurPos parameter.

Now, whenever you want to use the current position in your item style template, just declare the CurPos parameter and use it.

</xsl:template><xsl:template name=”TableLayout” match=”Row[@Style='TableLayout']” mode=”itemstyle”>
    <xsl:param name=”CurPos”/>
      <xsl:variable name=”SafeImageUrl”>
          <xsl:call-template name=”OuterTemplate.GetSafeStaticUrl”>
              <xsl:with-param name=”UrlColumnName” select=”‘ImageUrl’”/>
          </xsl:call-template>
      </xsl:variable>
      <xsl:if test=”$CurPos = ’1′”>
          <tr>
              <td>Column Header 1</td>
              <td>Column Header 2</td>
              <td>Column Header 3</td>
              <td>Column Header 4</td>
          </tr>
      </xsl:if>
      <tr>
          <td><xsl:value-of select=”@Column1″/></td>
          <td><xsl:value-of select=”@Column2″/></td>
          <td><xsl:value-of select=”@Column3″/></td>
          <td><xsl:value-of select=”@Column4″/></td>
      </tr>
  </xsl:template>

CQWP: 30 Tips #9 Date Formatting with the Power of SP Designer

If you’re using SharePoint Designer in a DataView web part, you’ve got great guid formatting options for dates.  But those formatting options won’t cross over to CQWP without a little help.

I first posted about this in October of 2008: http://www.intranoggin.com/2008/10/765/

Josh Gaffey also wrote a nice post about it in 2009: http://blogs.msdn.com/b/joshuag/archive/2009/03/25/custom-date-formats-in-sharepoint-xsl.aspx

The quick tip is that first you add this SharePoint Designer namespace to your CQWP’s .xsl xmlns:ddwrt=http://schemas.microsoft.com/WebParts/v2/DataView/runtime

Then you can use the ddwrt:FormatDate function to format dates almost any way you’d like: <xsl:value-of select=”ddwrt:FormatDate(@date, local, formatoption)”/>

<xsl:value-of select=”ddwrt:FormatDate(@Modified, 1033, 1)”/>

Josh included a great table of local and format options in his post.

CQWP: 30 Tips #8 Updates and Comments

I don’t shy away from injecting my custom logic into the OOTB templates.  See Tip #7 where I updated the template that generates the group header display text (http://www.intranoggin.com/2012/02/1052/).

However, there almost always comes a time later on where you’ll want to see just what the heck the original code did, or you’ll find yourself looking back at an OOTB template and wondering if it came that way or if it was customized at some point.  To make my life easier, I’ve adopted this workflow.

First off, never make changes to the OOTB .xsl sheets.  Always make a copy and point your CQWP’s .dwp at your copies.  I covered this in Tip #1 (http://www.intranoggin.com/2012/02/1042/). 

Second, in those .xsl copies, if you want to update the logic of an OOTB template, comment out the original template version, then make a copy that contains the original with your changes.  I usually leave the commented version right in its original location and put my modified version at the end of the file.

If you didn’t know, comments in .xsl are anything that begins with <!– and ends with –>

As an example, the modified section of .xsl from Tip #7 resides in a file called Custom_ContentQueryMain.xsl and looks like this:

 

<!--xsl:template name="OuterTemplate.GetGroupName">
        <xsl:param name="GroupName"/>
        <xsl:param name="GroupType"/>
        <xsl:choose>
            <xsl:when test="string-length(normalize-space($GroupName)) = 0">
                <xsl:value-of select="$BlankGroup"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:choose>
                    <xsl:when test="$GroupType='URL'">
                        <xsl:variable name="Url">
                            <xsl:call-template name="OuterTemplate.FormatValueIntoUrl">
                                <xsl:with-param name="Value" select="$GroupName"/>
                            </xsl:call-template>
                        </xsl:variable>
                        <xsl:call-template name="OuterTemplate.GetPageNameFromUrlRecursive">
                            <xsl:with-param name="Url" select="$Url"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$GroupName" />
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template-->
    <xsl:template name="OuterTemplate.CallPresenceStatusIconTemplate">
        <xsl:if test="string-length(@SipAddress) != 0">
            <span class="presence-status-icon">
                <img src="/_layouts/images/imnhdr.gif" onload="IMNRC('{@SipAddress}')" ShowOfflinePawn="1" alt="" id="{concat('MWP_pawn_',$ClientId,'_',@ID,'type=sip')}"/>
            </span>
        </xsl:if>
    </xsl:template>
    <xsl:template name="OuterTemplate.GetFileNameWithoutExtension">
        <xsl:param name="input"/>
        <xsl:variable name="extension">
          <xsl:value-of select="substring-after($input, '.')"/>
        </xsl:variable>
        <xsl:choose>
            <xsl:when test="contains($extension, '.')">
                <xsl:variable name="afterextension">
              <xsl:call-template name="OuterTemplate.GetFileNameWithoutExtension">
                <xsl:with-param name="input" select="$extension"/>
              </xsl:call-template>
            </xsl:variable>
                <xsl:value-of select="concat(substring-before($input, '.'), $afterextension)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:choose>
                    <xsl:when test="contains($input, '.')">
                        <xsl:value-of select="substring-before($input, '.')"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$input"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <!--Begin Overrides and Custom Templates-->
    <xsl:template name="OuterTemplate.GetGroupName">
        <xsl:param name="GroupName"/>
        <xsl:param name="GroupType"/>
        <xsl:choose>
            <xsl:when test="string-length(normalize-space($GroupName)) = 0">
                <xsl:value-of select="$BlankGroup"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:choose>
                    <xsl:when test="$GroupType='URL'">
                        <xsl:variable name="Url">
                            <xsl:call-template name="OuterTemplate.FormatValueIntoUrl">
                                <xsl:with-param name="Value" select="$GroupName"/>
                            </xsl:call-template>
                        </xsl:variable>
                        <xsl:call-template name="OuterTemplate.GetPageNameFromUrlRecursive">
                            <xsl:with-param name="Url" select="$Url"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:call-template name="OuterTemplate.RemovePrependedOrderDigits">
                            <xsl:with-param name="GroupName" select="$GroupName"/>
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="OuterTemplate.RemovePrependedOrderDigits">
        <xsl:param name="GroupName"/>
        <xsl:choose>
            <xsl:when test="contains($GroupName,'_')">
                <xsl:variable name="OrderBits">
                    <xsl:value-of select="substring-before($GroupName,'_')"/>
                </xsl:variable>
                <xsl:choose>
                    <xsl:when test="translate($OrderBits,'0123456789','') = ''">
                        <xsl:value-of select="substring-after($GroupName,'_')"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select="$GroupName"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$GroupName"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

CQWP: 30 Tips #7, Group Ordering

 

On many occasions you’ll want to group items under a header, but control the order of the group display.  The problem here is that when you select your group by field, you can only sort that group alphabetically ascending or descending.  A quick fix is to prepend a numeric value to the field your grouping. 

For example, if I have a list of approved catering restaurants and grouped into cuisine types and I want the most popular ‘pizza’ group on top.  Rather than cuisine names Asian, Burgers, Cold cuts, Pasta, Pizza; I would use 01_Pizza, 02_Asian, 03_Cold cuts, 04_Pasta, 05_Burgers.

If you’ve done that, the next question your going to get from the customer is “Can we get rid of the leading number?” Yes, yes you can.

In your ContentQueryMain.xsl, add in this new template: OuterTemplate.RemovePrependedOrderDigits

    <xsl:template name=”OuterTemplate.RemovePrependedOrderDigits”>
        <xsl:param name=”GroupName”/>
        <xsl:choose>
            <xsl:when test=”contains($GroupName,’_')”>
                <xsl:variable name=”OrderBits”>
                    <xsl:value-of select=”substring-before($GroupName,’_')”/>
                </xsl:variable>
                <xsl:choose>
                    <xsl:when test=”translate($OrderBits,’0123456789′,”) = ””>
                        <xsl:value-of select=”substring-after($GroupName,’_')”/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:value-of select=”$GroupName”/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select=”$GroupName”/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

 

Then update the out of the box template, OuterTemplate.GetGroupName to call your new template.

<xsl:template name=”OuterTemplate.GetGroupName”>
        <xsl:param name=”GroupName”/>
        <xsl:param name=”GroupType”/>
        <xsl:choose>
            <xsl:when test=”string-length(normalize-space($GroupName)) = 0″>
                <xsl:value-of select=”$BlankGroup”/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:choose>
                    <xsl:when test=”$GroupType=’URL’”>
                        <xsl:variable name=”Url”>
                            <xsl:call-template name=”OuterTemplate.FormatValueIntoUrl”>
                                <xsl:with-param name=”Value” select=”$GroupName”/>
                            </xsl:call-template>
                        </xsl:variable>
                        <xsl:call-template name=”OuterTemplate.GetPageNameFromUrlRecursive”>
                            <xsl:with-param name=”Url” select=”$Url”/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                       <xsl:call-template name=”OuterTemplate.RemovePrependedOrderDigits”>
                            <xsl:with-param name=”GroupName” select=”$GroupName”/>
                        </xsl:call-template>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

Caveat: You’ll need to communicate that this numeric removal only takes place when groupings are prepended with a number and an underscore.

 

CQWP: 30Tips #6

If you’re customizing the .xsl styles for CQWP at all, you’ll pretty quickly typo a < or > or leave out a / somewhere.  When this happens, you won’t be getting any help from the inevitable error message.  Any and every CQWP that references your updated .xsl will suddenly be broken with out the slightest hint as to where the problem lies.  Thankfully the problem is almost always caused by invalid – not well formed – xml.

SharePoint Designer to the rescue.  In SPD 2010, you’ll find the Verify well-formed xml button on the edit tab when you are viewing .xml or .xsl files.  Clicking this button will do wonders for locating the missing character.

image

If you’re still working on a SharePoint 2007 farm or just working in SPD 2007 for some reason (there are reasons), you can do a ctrl+a select all and then right click to find the Verify well-formed xml option.