One of my favorite web parts in SharePoint Server is the Content By Query Web Part (CQWP), that let us display a dynamic view of aggregate content in the site collection and present this in a web part. We can set a query that determines what content that should be displayed, and how the content presents. A common example where the CQWP is useful is when you need to display the latest 5 news at the start page.

However, sometimes you may need to modify how the data renders and create a design that ties with graphic profile. To achieve this you can use XSLT, CSS, CAML and jQuery or even write your own markup in HTML.

This article will not cover how to configure the CQWP or to modify ItemStyle.xsl or the other XSL files that describes the CQWP, we´re going straight to the core to the templates in the ItemStyle.xsl and to the CSS classes.
More about the CQWP at MSDN:
Common SharePoint Web Part and Field Control Customization Tasks

In the next article (Part II) II’l write about how to extend this format with jQuery.

Things we need to do with the XSLT in ItemStyle and with CSS to create format as the image above:

  • Display the created date and the month abbreviated
  • Count words, the title should be displayed by a max number of words and not be cut off in the middle. If the title is longer than 7 words, three dots should be displayed afterwards.
  • Header and footer, Latest news and More news
  • Design in general including an alternate background color for odd and even rows

Created date

Add the following namespace attribute to the root xsl:stylesheet element in ItemStyle:

xmlns:ddwrt=http://schemas.microsoft.com/WebParts/v2/DataView/runtime

Create an xsl:variable element:

<xsl:variable name="Created"><xsl:value-of select="ddwrt:FormatDateTime(string(@ArticleStartDate) ,1033 ,'d MMMM')" />
</xsl:variable>

Use the variable:

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

In this example I used a custom column called ArticleStartDate, you can just use @Created if you like. For more info about custom date and time format string, read more at MSDN

Count words

The title should be displayed by max 7 words and not be cut off in the middle and if the title is longer than 7 words, three dots should be displayed afterwards. To do this we need a ‘FirstNwords’ template, and such a template among more useful templates can be found at Codeplex.

Read more about this at Marc D Andersons blog:
Displaying the first n words of a long text column with xsl’ and check out
SPXSLT, a collection of useful XSL templates at Codeplex.

Header & Footer; First and Last row

Create a variable that gives the rows position

<xsl:variable name="CurPosition" select="count(./preceding-sibling::*)" />

Use preceding to get the first row

<xsl:if test="count(preceding-sibling::*)=0"> 
Latest news
</xsl:if>

Use following to get the last row

<xsl:if test="count(following-sibling::*)=0">
More news »
</xsl:if>

Alternate background

First you have to create a variable that gives the rows position, just like for the header and rows. No need to do this twice, secondly create a variable in order to get odd or even rows so you can set CSS classes depending if it´s odd or even rows.

<xsl:variable name="OddOrEven" >
<xsl:choose>     
<xsl:when test="($CurPosition mod 2 = 0)">        
<xsl:text>CQWP-Odd</xsl:text>     
</xsl:when>
<xsl:otherwise>
<xsl:text>CQWP-Even</xsl:text>
</xsl:otherwise>     
</xsl:choose>
</xsl:variable>

That´s all, time to try this out:

Two complete templates

The word counter template:

<!-- Word counter Template -->
<xsl:template name="FirstNWords">
  <xsl:param name="TextData"/>
  <xsl:param name="WordCount"/>
  <xsl:param name="MoreText"/>
  <xsl:choose>
    <xsl:when test="$WordCount &gt; 1 and (string-length(substring-before($TextData, ' ')) &gt; 0 or string-length(substring-before($TextData, '  ')) &gt; 0)">
      <xsl:value-of select="concat(substring-before($TextData, ' '), ' ')" disable-output-escaping="yes"/>
      <xsl:call-template name="FirstNWords">
        <xsl:with-param name="TextData" select="substring-after($TextData, ' ')"/>
        <xsl:with-param name="WordCount" select="$WordCount - 1"/>
        <xsl:with-param name="MoreText" select="$MoreText"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="(string-length(substring-before($TextData, ' ')) &gt; 0 or string-length(substring-before($TextData, '  ')) &gt; 0)">
      <xsl:value-of select="concat(substring-before($TextData, ' '), $MoreText)" disable-output-escaping="yes"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$TextData" disable-output-escaping="yes"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The OddEven template:

<!-- Begin OddEven Template -->
<xsl:template name="OddEven" match="Row[@Style='OddEven']" mode="itemstyle">
	<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="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>
	<xsl:variable name="Created">
		<xsl:value-of select="ddwrt:FormatDateTime(string(@ArticleStartDate) ,1033 ,'d MMMM')" />
	</xsl:variable>
	<xsl:variable name="LinkTarget">
		<xsl:if test="@OpenInNewWindow = 'True'" >_blank</xsl:if>
	</xsl:variable>
 
<!-- First row -->
<xsl:variable name="CurPosition" select="count(./preceding-sibling::*)" />
<xsl:if test="count(preceding-sibling::*)=0"> 
<div class="CQWP-Header">Latest news</div>
</xsl:if>

<!-- Create var for alternate classes -->
<xsl:variable name="OddOrEven" >
	<xsl:choose>     
		<xsl:when test="($CurPosition mod 2 = 0)">        
			<xsl:text>CQWP-Odd</xsl:text>     
		</xsl:when>
		<xsl:otherwise>
			<xsl:text>CQWP-Even</xsl:text>
		</xsl:otherwise>     
	</xsl:choose>
</xsl:variable>

<!-- All rows between the first and the last -->
<div id="linkitem" class="{$OddOrEven}">
	<xsl:value-of select="$Created"/>
	
		<a href="{$SafeLinkUrl}" target="{$LinkTarget}" title="{@LinkToolTip}" >
			<xsl:call-template name="FirstNWords">
        		<xsl:with-param name="TextData" select="$DisplayTitle"/>
       			<xsl:with-param name="WordCount" select="7"/>
        		<xsl:with-param name="MoreText" select="'...'"/>
      		</xsl:call-template>
		</a>
</div>

<!-- Last row -->
<xsl:if test="count(following-sibling::*)=0">
	<div class="CQWP-Footer">
		<div class="CQWP-Archive">
			<a href="#">More news »</a>
		</div>
	</div>
</xsl:if>
</xsl:template>

Dont forget to add the following namespace attribute to the root xsl:stylesheet element in the head of the document, when use the ddwrt formating for the date.

Here is the CSS:

.CQWP-Header, .CQWP-Archive {
width:400px;
border-width:1px;
height:20px;
padding-top:5px;	
padding-bottom:0px;
color:#333;
font-family:Cambria, Georgia;
border-style:solid none none none;
border-width:1px;
border-color:#bdbdbd;   
}
.CQWP-Header{
font-size:12px; font-weight:bold;
padding-left:10px;
border-style:solid none dotted none;
border-color:#bdbdbd;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e2e2e2', endColorstr='#ebebeb'); 
background: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#e2e2e2)); 
background: -moz-linear-gradient(top,  #ebebeb,  #e2e2e2);
}
.CQWP-Archive {
margin-top:-1px; font-size:11px;
padding-right:10px; text-align:right;
border-style:dotted none solid none;
border-color:#bdbdbd;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ebebeb', endColorstr='#e2e2e2'); 
background: -webkit-gradient(linear, left top, left bottom, from(#e2e2e2), to(#ebebeb));
background: -moz-linear-gradient(top,  #e2e2e2,  #ebebeb);
}
.CQWP-Odd, .CQWP-Even {
border-style:none none solid none;
border-width:1px; border-color:#e5e5e5;
height:22px; padding-top:7px;	
padding-bottom:0px; padding-left:10px;
color:#333; width:390px;
font-family:Cambria, Georgia; font-size:11px
}
.CQWP-Odd {background-color:#f7f7f7;}
.CQWP-Even {background-color:#fff;}
.CQWP-Odd a:link, .CQWP-Odd a:visited, .CQWP-Even a:link, .CQWP-Even a:visited {
padding-left:25px;color:green;
}

Stay in tune for the next article (Part II) about how to extend this format with jQuery. Thanks dropping by!