Skip to main content

Cloud

Adding table headers and/or footers to a Content Query Web Part Layout

Intro

Ah the Content Query Web Part (CQWP)… This little guy is a pretty powerful mechanism for displaying different views of the data within your MOSS 2007 installation. The problem I had to solve today was how do I get properly formatting table where there is a table header row with column headings and then each element returned by the query shows up as a row within the table.

Background

There are some great articles here and here about how to customize the web part look and feel. These blogs tell you that you edit the itemstyle.xsl style sheet to make it happen. The new xsl template that you create gets called once for each element the query returns. This is great but it doesn’t give you access to put any header/footer information into the layout. There is a file called Header.xsl in the same place as the itemstyle.xsl style sheet and I thought that I could use this to make my table happen. The problem is that the way header.xsl is structured; you need one xsl template for the header information and one xsl template for the footer information. So in my case the header would contain the opening table tag and the footer would contain the closing footer tag. This doesn’t work so well in xsl stylesheets since the style sheet itself has to be a well formed XML document. Splitting up those opening and closing table tags make the document no well formed. After some searching, I came across this blog posting about how to style the first item in a list. This blog pretty much says that you modify the ContentQueryMain.xsl style sheet to pass along where in the return set you are. You may be saying, "But Mike, can’t you just use the position() function in the itemstyle.xsl style sheet to get this?". The answer to that question is "no". If you do that, position will always return you 1. This is covered in the blog posting about first item styling.

Solution

After reading the blog post about styling the first item, I started to poke around the ContentQueryMain.xsl style sheet some more and found a variable that tells me what number the last row in the is. This number, combined with the fact that I now know where in the return set I am, allowed me to implement a solution to my problem. It might not be the correct one, but it works. Here it is in short form…hopefully you can follow it:

  1. Modify ContentQueryMain.xsl and add the text highlighted below. This will pass the LastRow variable down into the OuterTemplate.CallItemTemplate xsl template:

<xsl:call-template name="OuterTemplate.CallItemTemplate">

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

<!– Added this parameter so we can pass it to the item level style in itemstyle.xsl–>

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

</xsl:call-template>

  1. Modify ContentQueryMain.xsl and add the text highlighted below to pass the LastRow variable into the itemstyle template located in itemstyle.xsl:

<xsl:when test="@Style=’NewsCategoryItem’">

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

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

</xsl:apply-templates>

</xsl:when>

<xsl:when test="@Style=’AnnualInformationReport’">

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

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

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

</xsl:apply-templates>

</xsl:when>

<xsl:otherwise>

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

</xsl:apply-templates>

</xsl:otherwise>

  1. Modify Itemstyle.xsl to add your custom item style template. The name attribute and style must match the style from #2:

<xsl:template name="AnnualInformationReport" match="Row[@Style=’AnnualInformationReport’]" mode="itemstyle">

<!– These variables will be fed in from the ContentQueryMain.xsl stylesheet and tells me where in the list I am.

You can not use the position() function here because all nodes return 1.–>

<xsl:param name="CurPos" />

<xsl:param name="Last" />

<xsl:variable name="SafeLinkUrl">

<xsl:call-template name="OuterTemplate.GetSafeLink">

<xsl:with-param name="UrlColumnName" select="‘LinkUrl’"/>

</xsl:call-template>

</xsl:variable>

<xsl:variable name="LinkTarget">

_blank

</xsl:variable>

<xsl:variable name="DisplayTitle">

<xsl:call-template name="OuterTemplate.GetTitle">

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

<xsl:with-param name="UrlColumnName" select="‘LinkUrl’"/>

</xsl:call-template>

</xsl:variable>

<!– We have to do it this way in order for the tables to work out.–>

<xsl:variable name="tableStart">

<xsl:if test="$CurPos = 1">

<![CDATA[

<table>

<tr>

<td align="left">

<b>Date</b>

</td>

<td align="left">

<b>Title</b>

</td>

</tr>]]>

</xsl:if>

</xsl:variable>

<xsl:variable name="tableEnd">

<xsl:if test="$CurPos = $Last">

<![CDATA[</table>]]>

</xsl:if>

</xsl:variable>

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

<tr>

<td align="left">

<xsl:value-of select="ddwrt:FormatDateTime(string(@reportDate) ,1033 ,’MM-dd-yyyy’)" />

</td>

<td align="left">

<a href="{$SafeLinkUrl}" target="{$LinkTarget}" title="{@LinkToolTip}">

<xsl:value-of select="$DisplayTitle"/>

</a>

</td>

</tr>

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

</xsl:template>

In the above code example, I enclose the table start and end in a variables that only get populated with something if you are looking at the first row (table start) or the last row (table end). I enclose them in CDATA sections so that the XML parser ignores them when it goes to parse the style sheet. I use the "disable-output-escaping=’yes’" to allow the HTML to get written out as HTML and not with "&gt;" and "&lt;" all over the place.

I tried other ways of doing this by changing the Header.xsl style sheet but found that this was the most compact way of doing what I needed and allowed me to keep all of my rendering code in one spot so if changes were made I could see the whole thing.

Hope this helps…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Dave Scheele

More from this Author

Follow Us