How to to retrieve and display Social Ratings in Content Query web part

Overview

In this post we will continue the discussion of accessing Social Data in Content Query web part that we started in the previous post. But this time we are going to customize Content Query web part in order to retrieve and display Social Ratings.  Our goal here to retrieve and display social ratings using the same way that they are being displayed  in List View.

When Ratings is enabled in List settings, the rating field is rendered in List View as shown below

ListViewWithRatings

and the customized Content Query web part for displaying Ratings control  will look like this

CBQWithSocialRatings

Prerequisites:

Implementation

So, lets start with the implementation of XSL stylesheet for rendering the Ratings control.

Social Ratings fields in SharePoint 2010

SharePoint 2010 comes with the following fields for Social Ratings:

  • AverageRating
  • RatingCount

For rendering AverageRating field in List View the  TEMPLATE\LAYOUTS\XSL\fldtypes_ratings.xsl is used. We will utilize templates from this file, but in order to make it work properly with CQWP, some modifications should be applied.

Ratings XSL stylesheet for CQWP

Templates definitions for the client side initialization and for rendering of of Ratings manager

<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">
<xsl:param name="PopupAverageRatingLabel" />
<xsl:param name="PopupRatingCountLabel" />
<xsl:param name="PopupUserRatingLabel" />
<xsl:param name="PopupSubmitMessage" />
<xsl:param name="PopupDataNotAvailableMessage" />
<xsl:param name="PopupDataNotRatedMessage" />
<xsl:param name="ClickToRateMessage" />
<xsl:param name="CurrentAverageUnrated" />
<xsl:param name="CurrentAverageRating0_5Stars" />
<xsl:param name="CurrentAverageRating1_5Stars" />
<xsl:param name="CurrentAverageRating1_0Stars" />
<xsl:param name="CurrentAverageRating2_0Stars" />
<xsl:param name="CurrentAverageRating2_5Stars" />
<xsl:param name="CurrentAverageRating3_0Stars" />
<xsl:param name="CurrentAverageRating3_5Stars" />
<xsl:param name="CurrentAverageRating4_0Stars" />
<xsl:param name="CurrentAverageRating4_5Stars" />
<xsl:param name="CurrentAverageRating5_0Stars" />
<xsl:param name="SubmitNewRatingOpenerMessage" />
<xsl:param name="SubmitInstruction" />
<xsl:param name="Rate1StarMessage" />
<xsl:param name="Rate2StarMessage" />
<xsl:param name="Rate3StarMessage" />
<xsl:param name="Rate4StarMessage" />
<xsl:param name="Rate5StarMessage" />
<xsl:param name="IncreaseRatingInstruction" />
<xsl:param name="DecreaseRatingInstruction" />
<xsl:param name="EscapeInstruction" />
<xsl:param name="PopupWaitMessage" />
<xsl:template name="PrintAverageRating" >
<xsl:param name="thisNode" select="."/>
<xsl:variable name="url">
<xsl:call-template name="OuterTemplate.GetSafeLink">
<xsl:with-param name="UrlColumnName" select="'LinkUrl'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="title">
<xsl:choose>
<xsl:when test="string($thisNode/@Title)!=''">
<xsl:value-of select="$thisNode/@Title"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$thisNode/@FileLeafRef.Name"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="ratingValueLocalized">
<xsl:choose>
<xsl:when test="$thisNode/@*[name()=current()/@Name]=''">0</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$thisNode/@*[name()=current()/@Name]"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="ratingsData" select="ddwrt:GetRatingsData(string($thisNode/@ID))"/>
<xsl:variable name="controlId" select="$thisNode/@ID"/>
<xsl:variable name="ratingValue">
<xsl:value-of select="translate($ratingValueLocalized, ',','.')"/>
</xsl:variable>
<xsl:if test="not($ratingsData/ItemContentTypeContainsField) or $ratingsData/ItemContentTypeContainsField/node()='true'">
<xsl:variable name="ratingClass">
<xsl:choose>
<xsl:when test="number($ratingValue) &lt;= .25">ms-rating_0</xsl:when>
<xsl:when test="number($ratingValue) &lt;= .75">ms-rating_0_5</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 1.25">ms-rating_1</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 1.75">ms-rating_1_5</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 2.25">ms-rating_2</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 2.75">ms-rating_2_5</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 3.25">ms-rating_3</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 3.75">ms-rating_3_5</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 4.25">ms-rating_4</xsl:when>
<xsl:when test="number($ratingValue) &lt;= 4.75">ms-rating_4_5</xsl:when>
<xsl:otherwise>ms-rating_5</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="altMessage">
<xsl:choose>
<xsl:when test="$ratingClass='ms-rating_0'"><xsl:value-of select="string($CurrentAverageUnrated)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_0_5'"><xsl:value-of select="string($CurrentAverageRating0_5Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_1'"><xsl:value-of select="string($CurrentAverageRating1_0Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_1_5'"><xsl:value-of select="string($CurrentAverageRating1_5Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_2'"><xsl:value-of select="string($CurrentAverageRating2_0Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_2_5'"><xsl:value-of select="string($CurrentAverageRating2_5Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_3'"><xsl:value-of select="string($CurrentAverageRating3_0Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_3_5'"><xsl:value-of select="string($CurrentAverageRating3_5Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_4'"><xsl:value-of select="string($CurrentAverageRating4_0Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_4_5'"><xsl:value-of select="string($CurrentAverageRating4_5Stars)"/></xsl:when>
<xsl:when test="$ratingClass='ms-rating_5'"><xsl:value-of select="string($CurrentAverageRating5_0Stars)"/></xsl:when>
</xsl:choose>
<xsl:if test="$ratingsData/FeatureActivated/node()='true' and $ratingsData/IsUserAnonymous/node()='false'">
<xsl:text> </xsl:text>
<xsl:value-of select="string($SubmitNewRatingOpenerMessage)"/>
</xsl:if>
</xsl:variable>
<span id="RatingsCtrl_{$controlId}">
<a class="ms-currentRating" href="javascript:;">
<img class="{$ratingClass}" src="{$ratingsData/RatingImageStripUrl/node()}" alt="{$altMessage}"/>
</a>
<xsl:if test="$ratingsData/FeatureActivated/node()='true' and $ratingsData/IsUserAnonymous/node()='false'">
<span class="ms-submitRating">
<img tabindex='0' alt='{string($Rate1StarMessage)} {string($SubmitInstruction)} {string($IncreaseRatingInstruction)} {string($EscapeInstruction)}' class='ms-rate1star'/>
<img tabindex='0' alt='{string($Rate2StarMessage)} {string($SubmitInstruction)} {string($IncreaseRatingInstruction)} {string($DecreaseRatingInstruction)} {string($EscapeInstruction)}' class='ms-rate2stars'/>
<img tabindex='0' alt='{string($Rate3StarMessage)} {string($SubmitInstruction)} {string($IncreaseRatingInstruction)} {string($DecreaseRatingInstruction)} {string($EscapeInstruction)}' class='ms-rate3stars'/>
<img tabindex='0' alt='{string($Rate4StarMessage)} {string($SubmitInstruction)} {string($IncreaseRatingInstruction)} {string($DecreaseRatingInstruction)} {string($EscapeInstruction)}' class='ms-rate4stars'/>
<img tabindex='0' alt='{string($Rate5StarMessage)} {string($SubmitInstruction)} {string($DecreaseRatingInstruction)} {string($EscapeInstruction)}' class='ms-rate5stars'/>
</span>
</xsl:if>
</span>
<xsl:if test="$ratingsData/FeatureActivated/node()='true' and $ratingsData/IsUserAnonymous/node()='false'">
<script type="text/javascript">
function RatingsControlLoader_<xsl:value-of select="$controlId"/>()
{
ratingsManager.CreateControl('<xsl:value-of select="ddwrt:EcmaScriptEncode($url)"/>', '<xsl:value-of select="ddwrt:EcmaScriptEncode($title)"/>', 'RatingsCtrl_<xsl:value-of select="$controlId"/>');
}
ExecuteOrDelayUntilEventNotified(RatingsControlLoader_<xsl:value-of select="$controlId"/>, 'RatingsManagerLoaded');
</script>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="emit_RatingsInitialization">
<xsl:variable name="ratingsData" select="ddwrt:GetRatingsData(string(-1))"/>
<xsl:if test="$ratingsData/FeatureActivated/node()='true' and $ratingsData/IsUserAnonymous/node()='false'">
<script src="/_layouts/Ratings.js?rev={$ratingsData/CurrentBuildVersion/node()}" type="text/javascript" defer="defer"></script>
<script type="text/javascript">
var ratingsManager = null;
function RatingsManagerLoader()
{
var ratingsData = new RatingsCommonData(
'<![CDATA[<img src="/_layouts/images/loading16.gif"/> ]]><xsl:value-of select="string($PopupWaitMessage)"/>',
'<xsl:value-of select="string($PopupAverageRatingLabel)"/>',
'<xsl:value-of select="string($PopupRatingCountLabel)"/>',
'<xsl:value-of select="string($PopupUserRatingLabel)"/>',
'<xsl:value-of select="string($PopupSubmitMessage)"/>',
'<xsl:value-of select="string($PopupDataNotAvailableMessage)"/>',
'<xsl:value-of select="string($PopupDataNotRatedMessage)"/>',
'<xsl:value-of select="string($ClickToRateMessage)"/>',
'<xsl:value-of select="$ratingsData/SessionID/node()"/>',
'<xsl:value-of select="$ratingsData/WebID/node()"/>',
'<xsl:value-of select="ddwrt:EcmaScriptEncode($SiteUrl)"/>',
'<xsl:value-of select="$ratingsData/SiteID/node()"/>',
'<xsl:value-of select="$ratingsData/NewRatingIconUrl_EcmaScriptEncoded/node()"/>',
'<xsl:value-of select="$ratingsData/EmptyRatingIconUrl_EcmaScriptEncoded/node()"/>'
);
ratingsManager = new RatingsManager(ratingsData);
NotifyEventAndExecuteWaitingJobs('RatingsManagerLoaded');
}
ExecuteOrDelayUntilScriptLoaded(RatingsManagerLoader, 'ratings.js');
</script>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

view raw
RatingsStyle.xsl
hosted with ❤ by GitHub

Main XSL stylesheet

Custom ContentQueryMain.xsl is used, in which  the template for client side initialization of Ratings control is invoked

Source

Item XSL stylesheet

Custom ItemStyle.xsl is used, in which  the template for rendering Ratings control per page is invoked

Source

Ratings XSL Parameters

Parameter values are retrieved from resource file  and are passed via ParamaterBindings web part property

<ParameterBindings>
<ParameterBinding Name="PopupAverageRatingLabel" Location="Resource(sps,Ratings_PopupAverageRatingLabel)" />
<ParameterBinding Name="PopupRatingCountLabel" Location="Resource(sps,Ratings_PopupRatingCountLabel)"/>
<ParameterBinding Name="PopupUserRatingLabel" Location="Resource(sps,Ratings_PopupUserRatingLabel)"/>
<ParameterBinding Name="PopupSubmitMessage" Location="Resource(sps,Ratings_PopupSubmitMessage)"/>
<ParameterBinding Name="PopupDataNotAvailableMessage" Location="Resource(sps,Ratings_PopupDataNotAvailableMessage)"/>
<ParameterBinding Name="PopupDataNotRatedMessage" Location="Resource(sps,Ratings_PopupDataNotRatedMessage)"/>
<ParameterBinding Name="ClickToRateMessage" Location="Resource(sps,Ratings_ClickToRateMessage)"/>
<ParameterBinding Name="CurrentAverageUnrated" Location="Resource(sps,Ratings_CurrentAverageUnrated)"/>
<ParameterBinding Name="CurrentAverageRating0_5Stars" Location="Resource(sps,Ratings_CurrentAverageRating0_5Stars)"/>
<ParameterBinding Name="CurrentAverageRating1_5Stars" Location="Resource(sps,Ratings_CurrentAverageRating1_5Stars)"/>
</ParameterBindings>

Let’s summarize.

In order to configure CQWP  for displaying Social Ratings , the following steps should be accomplished:

References

How to retrieve and display social comments in Content Query web part

Introduction

Using social comments in Publishing pages is pretty common scenario for SharePoint Publishing Sites. In comparison to page fields (Page Content, Title, Page Image and etc.)  that are inserted into the “slots” of the page layout and associated with the content type of the publishing page, social comments are stored separately.  Social comments are stored in separate DB and the comment entry is associated by the page Url on which comment has been given.

Let’s take a look at several approaches how to display social comments for Publishing pages in Content Query web part as shown on picture below.

Prerequisites: to enable social comments in Publishing pages the custom page layout with Note Board web part is used  for demonstration here.

CBQ_Comments

Extending Content By Query web part

In this approach the social comments are retrieved and saved in results before it is sent to the XSL transform using  ProcessDataDelegate delegate of Content Query web part. For retrieving social comments, SocialCommentManager class is utilized, which represents the entry point in  SharePoint object model (OM) that exposes methods to do work with social comments or notes.

So, in order to attach method for processing of social comments to a delegate   ProcessDataDelegate we need to subclass Content Query web part as demonstrated below:

/// <summary>
/// Adding social comments to the results of Content Query web part
/// </summary>
[ToolboxItem(false)]
public class SocialCBQ : ContentByQueryWebPart
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.ProcessDataDelegate = ProcessSocialData;
}
private DataTable ProcessSocialData(DataTable data)
{
var context = SPServiceContext.GetContext(SPContext.Current.Site);
scm = new SocialCommentManager(context);
data.Columns.Add("PageUrl", typeof(string));
data.Columns.Add("PageComments", typeof(int));
foreach (DataRow row in data.Rows)
{
var fileRefVal = new SPFieldLookupValue((string)row[SPBuiltInFieldId.FileRef.ToString("B")]);
string pageUrl = SPContext.Current.Site.MakeFullUrl("/" + fileRefVal.LookupValue);
var pageComments = scm.GetCount(new Uri(pageUrl));
row["PageUrl"] = pageUrl;
row["PageComments"] = pageComments;
}
return data;
}
private SocialCommentManager scm;
}

view raw
SocialCBQ.cs
hosted with ❤ by GitHub

And the final step is to customize template for rendering item (ItemStyle.xsl) in order to render social columns  (comment count per page in our case) in Content Query web part:

<div class="comments">
Comments: <span class="comments" pageurl="{@PageUrl}"><xsl:value-of select="@PageComments" /></span>
</div>

Utilizing SharePoint Web Services  for retrieving social comment in CQWP

In this approach we are not going to subclass Content Query web part, but implement social comments retrieving and binding on the client side.

SharePoint Web Services exposes Social Data Service that provides methods for remote clients to Create, Read, Update, and Delete (CRUD) social data.

The following JavaScript code demonstrates how to utilize SharePoint Web Services for retrieving comment count per page and to bind it to  comments item template (ItemStyle.xsl)

function CountCommentsOnUrl(url,result)
{
var soapEnv =
"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance&#39; xmlns:xsd='http://www.w3.org/2001/XMLSchema&#39; xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'&gt; \
<soap:Body> \
<CountCommentsOnUrl xmlns='http://microsoft.com/webservices/SharePointPortalServer/SocialDataService'&gt; \
<url>" + url + "</url> \
</CountCommentsOnUrl> \
</soap:Body> \
</soap:Envelope>";
$.ajax({
pageurl: url,
result: result,
url: "/_vti_bin/SocialDataService.asmx?op=CountCommentsOnUrl",
type: "POST",
dataType: "xml",
data: soapEnv,
contentType: "text/xml; charset=\"utf-8\"",
success: function(data, status, xhr){
if(this.result !== undefined) {
var commentCount = $('CountCommentsOnUrlResponse', data).find('CountCommentsOnUrlResult').text();
this.result(commentCount);
}
}
});
}
$(function() {
$('span.comments').each(function() {
var pageUrl = $(this).attr('pageurl');
var comment = $(this);
CountCommentsOnUrl(pageUrl,function(commentCount){
comment.text(commentCount);
});
});
});

Code excerpt for comment item from ItemStyle.xsl:

<div class="comments">
Comments: <span class="comments" pageurl="{@LinkUrl}"></span>
</div>

References

Beyond the Slideshow web part capabilities in SharePoint 2010

Overview

In one of the previous post we have started the discussion of customizing   Slideshow web part, in particular it was demonstrated  how to provide additional filtering for picture items. This time we’re going to go further and consider different scenarios of using SharePoint SlideShow capabilities. We deliberately do not consider the use of  third party libraries for Slideshow or creating custom web parts, but try to build new experience using existing tools only.

1. Customize the display for Slideshow control: display additional picture properties

Suppose in addition to standard properties, custom properties should be displayed  from Picture Library in Slideshow web part.

PictureLibCustomFields

The following  fields are retrieved  from Picture Library for Slideshow:

So, our goal is to display slideshow using custom layout as shown below

CarsSlideshow

Solution:

Solution consist of the following parts:

  • load custom picture properties, see loadCarPicturesAdditionalInfo method for details
  • Slideshow library (imglib.js) methods overriding for slideshow object initialization, picture  changing and displaying
function SlideshowObjectInitializer() {
ChangePic = (function(ChangePicOrig) {
return function() {
var ssObj = arguments[0]; //SlideShow object
if(typeof ssObj.additionalInfo != "undefined")
ChangePicOrig.apply(this, arguments);
};
})(ChangePic);
ShowPic = (function(ShowPicOrig) {
return function() {
ShowPicOrig.apply(this, arguments); //call original ShowPic
var ssObj = arguments[0]; //SlideShow object
ShowAdditionalPicInfo(ssObj);
};
})(ShowPic);
function ShowAdditionalPicInfo(ssObj)
{
var curPicIdx=ssObj.index; //current picture index
if(ssObj.additionalInfo.length == 0)
return;
var picEntry = ssObj.additionalInfo[curPicIdx];
var ssobj_ext_cell = '<td>';
ssobj_ext_cell += '<div style="font-size:22px;"><span style="font-weight:bold">Car Model:</span>' + picEntry.CarModel + '</div>';
ssobj_ext_cell += '<div style="font-size:22px;"><span style="font-weight:bold">Car Description:</span>' + picEntry.CarDesc + '</div>';
ssobj_ext_cell += '</td>';
var ssobj_row = jQuery(ssObj.cell).closest('tr');
if(ssobj_row.find('td').length > 1) {
ssobj_row.find('td:nth-child(2)').replaceWith(ssobj_ext_cell);
}
else
ssobj_row.append(ssobj_ext_cell);
}
SlideshowObject = (function(SlideshowObjectOrig) {
return function() {
SlideshowObjectOrig.apply(this, arguments);
var ssobj = this;
ExecuteOrDelayUntilScriptLoaded(function(){
loadCarPicturesAdditionalInfo(function(picEntries){
ssobj.additionalInfo = picEntries;
ChangePic(ssobj);
});
},'SP.js');
};
})(SlideshowObject);
}
function loadCarPicturesAdditionalInfo(cbPicsResults) {
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle("Pictures");
var viewXml = '<View></View>';
var query = new SP.CamlQuery();
query.set_viewXml(viewXml);
var items = list.getItems(query);
context.load(items,"Include(CarModel,CarDesc)");
context.add_requestSucceeded(onLoaded);
context.add_requestFailed(onFailure);
context.executeQueryAsync();
function onLoaded() {
var picEntries = [];
var itemsCount = items.get_count();
for (i = 0; i < itemsCount; i++) {
var item = items.itemAt(i);
var picEntry = item.get_fieldValues();
picEntries.push(picEntry);
}
cbPicsResults(picEntries);
}
function onFailure() {
cbPicsResults(null);
}
}

view raw
SSObj_customprops.js
hosted with ❤ by GitHub

Usage

Save JavaScript code to the file and embed it into the page with Slideshow web part  as demonstrated below

<script src="/_layouts/SE/imglibutils.js" type="text/javascript"></script>
<script type="text/javascript">
ExecuteOrDelayUntilScriptLoaded(SlideshowObjectInitializer, 'imglib.js');
</script>

2. Customize the display for Slideshow control: display original pictures

In Slideshow web part, picture is initialized with field value that contains the Url of  Web image (EncodedAbsWebImgUrl  field). Unfortunately it is not supported to configure in web part what image type (original, web or thumbnail) should be displayed in Slideshow The solution that demonstrated below allows to display original images instead of web images in Slideshow web part.

Solution:

In order to specify original images, method overriding for picture initialization is used:

function SlideshowObjectInitializer() {
ShowPic = (function(ShowPicOrig) {
return function() {
var ssObj = arguments[0]; //SlideShow object
var curPicIdx=ssObj.index; //current picture index
ShowPicOrig.apply(this, arguments); //call original ShowPic
//apply some changes to display original picture in SlideShow control
ssObj.image.src = ssObj.linkArray[curPicIdx]; //display original image instead of web image
//change picture & container size to auto instead of fixed (by default web image size is used)
ssObj.image.setAttribute('height','100%');
ssObj.image.setAttribute('width','100%');
var cell = ssObj.cell;
cell.style.width = 'auto';
cell.style.height = 'auto';
cell.style.display = '';
var pcell = ssObj.cell.parentNode;
pcell.style.width = 'auto';
pcell.style.height = 'auto';
};
})(ShowPic);
}
ExecuteOrDelayUntilScriptLoaded(SlideshowObjectInitializer, 'imglib.js');

Usage

One of the most simple way to apply these changes is to embed specified JavaScript code via Content Editor web part (CEWP), for more details please follow this article.

  • Save JavaScript code to the file, for example in SlideshowObjectInitializer.txt and upload it to SiteAssets Library
  • Add CEWP on page where Slideshow web part is located and in the Content Editor tool pane, under Content Link property, type /SiteAssets/SlideshowObjectInitializer.txt

3. Slideshow List View

Slideshow List View is only available for Pictures Library. It is pretty common scenario when another types of repositories are used for storing images, for example Assets Library. Let’s look at how to create a Slideshow  List View for images stored in Assets Library.

Solution:

Slideshow List View

<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 x d asp __designer SharePoint ddwrt2"
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">
<xsl:import href="/_layouts/xsl/main.xsl"/>
<xsl:output method="html" indent="no"/>
<xsl:template match="View" mode="full" ddwrt:ghost="always">
<tr class="ms-viewheadertr"></tr>
<tr>
<td>
<!– Slideshow control –>
<table border="0" style="table-layout: fixed">
<tbody>
<tr>
<td >
<div id="slidecontrol_cell" style="display: table-cell; vertical-align: middle; text-align: center;">
<span style="vertical-align: middle; height: 100%; display: inline-block"></span>
<a id="slidecontrol_link" target="_blank">
<img id="slidecontrol_curr" style="opacity: 0.9900000000000007; border: 0px; vertical-align: middle;" alt=""/>
</a>
</div>
</td>
</tr>
<tr>
<td style="height: 75px">
<div style="height: 75px; opacity: 0.9900000000000007;" id="slidecontrol_text">
<span id="slidecontrol_title" class="ms-slideshow-title"></span>
<br/>
</div>
</td>
</tr>
<tr>
<td style="text-align: center">
<a id="btn_prev" >
<img border="0" style="position: relative; cursor: hand" onmouseover="HiliteButton()" onmouseout="DemoteButton()" src="/_layouts/images/plprev1.gif" alt="Previous Image"/>
</a>
<a id="btn_pp" >
<img border="0" style="position: relative; cursor: hand" onmouseover="HiliteButton()" onmouseout="DemoteButton()" src="/_layouts/images/plpause1.gif" id="slidecontrol_playpause" alt="Pause"/>
</a>
<a id="btn_next">
<img border="0" style="position: relative; cursor: hand" onmouseover="HiliteButton()" onmouseout="DemoteButton()" src="/_layouts/images/plnext1.gif" alt="Next Image"/>
</a>
</td>
</tr>
</tbody>
</table>
<img id="slidecontrol_next" width="0" height="0" style="visibility: hidden" />
<img id="slidecontrol_prev" width="0" height="0" style="visibility: hidden" />
<img id="slidecontrol_pp" width="0" height="0" style="visibility: hidden" />
</td>
</tr>
<script type="text/javascript">
var slidecontrol_slideshowObject;
ExecuteOrDelayUntilScriptLoaded(function(){
slidecontrol_slideshowObject = createSlideControl('slidecontrol_');
},'SP.js');
</script>
<xsl:apply-templates mode="footer" select="." />
</xsl:template>
<xsl:template name="SlideshowViewOverride" mode="RootTemplate" match="View" ddwrt:dvt_mode="root">
<script type='text/javascript' src="/_layouts/1033/imglib.js"></script>
<script type='text/javascript' src="/_layouts/yaspp/slideshow.js"></script>
<!–script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script–>
<script type="text/javascript">
</script>
<xsl:call-template name="View_Default_RootTemplate"/>
</xsl:template>
</xsl:stylesheet>

Slideshow control initialization and retrieving picture entries from Assets Library

//initialize SlideshowObject control
function createSlideControl(slidecontrolId)
{
loadPicturesInfo(function(picEntries) {
var slidecontrol_slideshowObject = new SlideshowObject(slidecontrolId, picEntries.src, picEntries.fullImageSrc, picEntries.title, picEntries.description, picEntries.height, picEntries.width, 15, 1.0);
ChangePic(slidecontrol_slideshowObject);
InitSlideControlNavButtons(slidecontrol_slideshowObject);
});
}
//
function InitSlideControlNavButtons(slideshowObject) {
var btn_prev = document.getElementById("btn_prev");
AddEvtHandler(btn_prev, "onclick", function() { NextPrevious(slideshowObject, false);});
var btn_pp = document.getElementById("btn_pp");
AddEvtHandler(btn_pp, "onclick", function() { PlayPause(slideshowObject);});
var btn_next = document.getElementById("btn_next");
AddEvtHandler(btn_next, "onclick", function() { NextPrevious(slideshowObject, true);});
}
//retrieve picture entries for SlideObject control
function loadPicturesInfo(cbPicsResults) {
var context = new SP.ClientContext.get_current();
var web = context.get_web();
var list = web.get_lists().getByTitle(ctx.ListTitle);
var viewXml = '<View></View>';
var query = new SP.CamlQuery();
query.set_viewXml(viewXml);
var items = list.getItems(query);
context.load(items,"Include(Title,EncodedAbsWebImgUrl,EncodedAbsUrl,ImageWidth,ImageHeight)");
context.add_requestSucceeded(onLoaded);
context.add_requestFailed(onFailure);
context.executeQueryAsync();
function onLoaded() {
var widthArray = [];
var heightArray = [];
var pictureArray = [];
var linkArray = [];
var titleArray = [];
var descriptionArray = [];
var itemsCount = items.get_count();
for (i = 0; i < itemsCount; i++) {
var item = items.itemAt(i);
var picEntry = item.get_fieldValues();
titleArray.push(picEntry.Title);
pictureArray.push(picEntry.EncodedAbsWebImgUrl);
linkArray.push(picEntry.EncodedAbsUrl);
descriptionArray.push('');
widthArray.push(picEntry.ImageWidth);
heightArray.push(picEntry.ImageHeight);
}
picEntries = {title:titleArray,description:descriptionArray,fullImageSrc: linkArray,src:pictureArray, width:widthArray,height:heightArray};
cbPicsResults(picEntries);
}
function onFailure() {
cbPicsResults(null);
}
}

view raw
slideshow.js
hosted with ❤ by GitHub

4. Aggregate picture items and render as Slideshow

The last example demonstrates how to display slideshow for pictures aggregated  from site collection.  For aggregating pictures from site collection, Content Query web part is utilized.

Solution

OuterTemplate.SlideShowBody template is intended for rendering Slideshow control in CQWP

<xsl:template name="OuterTemplate.SlideShowBody">
<xsl:param name="Rows" />
<xsl:param name="FirstRow" />
<xsl:param name="LastRow" />
<script type="text/javascript">
function loadScript(url, callback)
{
// adding the script tag to the head as suggested before
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// then bind the event to the callback function
// there are several events for cross browser compatibility
script.onreadystatechange = callback;
script.onload = callback;
// fire the loading
head.appendChild(script);
}
function InitSlideControlNavButtons(slideshowObject) {
var btn_prev = document.getElementById("btn_prev");
AddEvtHandler(btn_prev, "onclick", function() { NextPrevious(slideshowObject, false);});
var btn_pp = document.getElementById("btn_pp");
AddEvtHandler(btn_pp, "onclick", function() { PlayPause(slideshowObject);});
var btn_next = document.getElementById("btn_next");
AddEvtHandler(btn_next, "onclick", function() { NextPrevious(slideshowObject, true);});
}
<xsl:text>var widthArray = [</xsl:text>
<xsl:for-each select="$Rows">
<xsl:value-of select="@ImageWidth" />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
<xsl:text>];</xsl:text>
<xsl:text>var linkArray = [</xsl:text>
<xsl:for-each select="$Rows">
<xsl:value-of select="concat('&quot;',@LinkUrl,'&quot;')" />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
<xsl:text>];</xsl:text>
<xsl:text>var heightArray = [</xsl:text>
<xsl:for-each select="$Rows">
<xsl:value-of select="@ImageHeight" />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
<xsl:text>];</xsl:text>
<xsl:text>var pictureArray = [</xsl:text>
<xsl:for-each select="$Rows">
<xsl:value-of select="concat('&quot;',@LinkUrl,'&quot;')" />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
<xsl:text>];</xsl:text>
<xsl:text>var titleArray = [</xsl:text>
<xsl:for-each select="$Rows">
<xsl:value-of select="concat('&quot;',@Title,'&quot;')" />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
<xsl:text>];</xsl:text>
<xsl:text>var descriptionArray = [</xsl:text>
<xsl:for-each select="$Rows">
<xsl:value-of select="concat('&quot;',@Description,'&quot;')" />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
<xsl:text>];</xsl:text>
loadScript("/_layouts/1033/imglib.js", function()
{
var slidecontrol = new SlideshowObject('slidecontrol_', pictureArray, linkArray, titleArray, descriptionArray, heightArray, widthArray, 15, 1.0);
ChangePic(slidecontrol);
InitSlideControlNavButtons(slidecontrol);
});
</script>
<xsl:call-template name="SlideShowControl" >
</xsl:call-template>
</xsl:template>
<xsl:template name="SlideShowControl">
<table border="0" style="table-layout: fixed">
<tbody>
<tr>
<td >
<div id="slidecontrol_cell" style="display: table-cell; vertical-align: middle; text-align: center;">
<span style="vertical-align: middle; height: 100%; display: inline-block"></span>
<a id="slidecontrol_link" target="_blank">
<img id="slidecontrol_curr" style="opacity: 0.9900000000000007; border: 0px; vertical-align: middle;" alt=""/>
</a>
</div>
</td>
</tr>
<tr>
<td style="height: 75px">
<div style="height: 75px; opacity: 0.9900000000000007;" id="slidecontrol_text">
<span id="slidecontrol_title" class="ms-slideshow-title"></span>
<br/>
</div>
</td>
</tr>
<tr>
<td style="text-align: center">
<a id="btn_prev" >
<img border="0" style="position: relative; cursor: hand" onmouseover="HiliteButton()" onmouseout="DemoteButton()" src="/_layouts/images/plprev1.gif" alt="Previous Image"/>
</a>
<a id="btn_pp" >
<img border="0" style="position: relative; cursor: hand" onmouseover="HiliteButton()" onmouseout="DemoteButton()" src="/_layouts/images/plpause1.gif" id="slidecontrol_playpause" alt="Pause"/>
</a>
<a id="btn_next">
<img border="0" style="position: relative; cursor: hand" onmouseover="HiliteButton()" onmouseout="DemoteButton()" src="/_layouts/images/plnext1.gif" alt="Next Image"/>
</a>
</td>
</tr>
</tbody>
</table>
<img id="slidecontrol_next" width="0" height="0" style="visibility: hidden" />
<img id="slidecontrol_prev" width="0" height="0" style="visibility: hidden" />
<img id="slidecontrol_pp" width="0" height="0" style="visibility: hidden" />
</xsl:template>

Usage

  • Configure source properties (List Type: Picture Library, Content Type:  Picture)PicturesCQWP
  • Specify additional fields for retrieving via  CommonViewFields property. For more details how to display custom fields in a CQWP, follow this article
<property name="CommonViewFields" type="string">Description,Text;ImageWidth,Integer;ImageHeight,Integer;Title,Text;</property>

view raw
CQWP_Pictures.xml
hosted with ❤ by GitHub

  • Replace OuterTemplate.Body template with OuterTemplate.SlideShowBody in ContentQueryMain.xsl. For more details how to customize XSL for the Content Query Web Part follow this article

Conclusion

The main idea of this post  was to demonstrate how to combine different components available in SharePoint 2010 in order to build new functionality without creating new one from scratch (i.e.: new web parts) or using third party components.

Rendering Content Query Web Part results in Table Layout

It is known that OOB Content Query Web Part (CQWP)  renders results using Lists for arranging items as shown below

In ASP.NET WebForms for Web Control DataList there is a possibility to specify layout rendering mode using property RepeatLayout, what if the similar functionality would be available in CQWP?

So, our  goal to extend CQWP, i.e. in addition to List Layout rendering mode, lets implement functionality for rendering Content Query Web Part results in Plain Old Table Layout.
In this approach, we would like to achieve the following options:

  • Possibility to easily arrange results in columns
  • Specify items direction (horizontal or vertical)

Solution Structure

Content Query Web Part Class

using System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint.Publishing.WebControls;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.WebPartPages;
namespace CQWPWithTableLayout.WebControls
{
[ToolboxItem(false)]
public class CBQTableLayout : ContentByQueryWebPart
{
#region Control Lifecycle
protected override void CreateChildControls()
{
try
{
base.CreateChildControls();
}
catch (Exception ex)
{
throw;
}
}
protected override void ModifyXsltArgumentList(ArgumentClassWrapper argList)
{
argList.AddParameter("RepeatDirection", string.Empty, RepeatDirection);
argList.AddParameter("RepeatColumns", string.Empty, RepeatColumns);
base.ModifyXsltArgumentList(argList);
}
/// <summary>
/// Return the tool panes that configure this <see cref="T:Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart"/> object..
/// </summary>
[SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
public override ToolPart[] GetToolParts()
{
return new ToolPart[3]
{
new ContentByQueryToolPart(),
new WebPartToolPart(),
new CBQTableLayoutToolPart()
};
}
#endregion
#region Properties
[Category("TableLayout")]
[Personalizable(PersonalizationScope.Shared), DefaultValue(3), WebBrowsable(true)]
public int RepeatColumns { get; set; }
[Category("TableLayout")]
[Personalizable(PersonalizationScope.Shared), DefaultValue(RepeatDirection.Horizontal), WebBrowsable(true)]
public RepeatDirection RepeatDirection { get; set; }
#endregion
}
}

Tool Part Class Implementation with the ability to specify Table Layout properties

using System;
using System.Globalization;
using System.Web.UI.WebControls;
namespace CQWPWithTableLayout.WebControls
{
public class CBQTableLayoutToolPart : Microsoft.SharePoint.WebPartPages.ToolPart
{
public CBQTableLayoutToolPart()
{
Init += InitToolPart;
}
private void InitToolPart(object sender, EventArgs e)
{
Title = "Table Layout Settings";
_targetWebPart = ParentToolPane.SelectedWebPart as CBQTableLayout;
if (_targetWebPart == null)
throw new Exception("Wrong web part type error");
}
protected override void CreateChildControls()
{
CreateTableLayoutSection();
PopulateTableLayoutSection();
base.CreateChildControls();
}
public override void ApplyChanges()
{
this.ApplySettingsSectionChanges();
base.ApplyChanges();
}
private void CreateTableLayoutSection()
{
_columnsBox = new TextBox {MaxLength = 2};
_directionBox = new DropDownList();
var mainTable = new Table { CellPadding = 2, CellSpacing = 2 };
mainTable.Style["border-collapse"] = "collapse";
//Repeat Columns
AddRepeatColumnsProperty(mainTable);
//Repeat Direction
AddRepeatDirectionProperty(mainTable);
Controls.Add(mainTable);
}
private void AddRepeatColumnsProperty(Table section)
{
var rowHeader = new TableRow();
var cellHeader = new TableCell { Text = "Repeat Columns" };
rowHeader.Cells.Add(cellHeader);
section.Rows.Add(rowHeader);
var rowItem = new TableRow();
var cellItem = new TableCell();
cellItem.Controls.Add(_columnsBox);
rowItem.Cells.Add(cellItem);
section.Rows.Add(rowItem);
}
private void AddRepeatDirectionProperty(Table section)
{
var rowHeader = new TableRow();
var cellHeader = new TableCell { Text = "Repeat Direction" };
rowHeader.Cells.Add(cellHeader);
section.Rows.Add(rowHeader);
var rowItem = new TableRow();
var cellItem = new TableCell();
cellItem.Controls.Add(_directionBox);
rowItem.Cells.Add(cellItem);
section.Rows.Add(rowItem);
}
private void PopulateTableLayoutSection()
{
_columnsBox.Text = _targetWebPart.RepeatColumns.ToString(CultureInfo.InvariantCulture);
var directions = Enum.GetValues(typeof(RepeatDirection));
_directionBox.DataSource = directions;
_directionBox.DataBind();
_directionBox.Text = _targetWebPart.RepeatDirection.ToString();
}
protected void ApplySettingsSectionChanges()
{
_targetWebPart.RepeatColumns = int.Parse(_columnsBox.Text);
_targetWebPart.RepeatDirection = (RepeatDirection)Enum.Parse(typeof(RepeatDirection), _directionBox.Text);
}
private TextBox _columnsBox;
private DropDownList _directionBox;
private CBQTableLayout _targetWebPart;
}
}

Web Part manifest file

<?xml version="1.0" encoding="utf-8"?>
<webParts>
<webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
<metaData>
<type name="CQWPWithTableLayout.WebControls.CBQTableLayout, CQWPWithTableLayout, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f96554baea0809a2" />
<importErrorMessage>Cannot import this Web Part.</importErrorMessage>
</metaData>
<data>
<properties>
<property name="Title" type="string">Content Query (Plain Old Table Layout)</property>
<property name="Description" type="string">Displays a dynamic view of content from your site.</property>
<property name="ChromeType">TitleOnly</property>
<property name="ChromeState">Normal</property>
<property name="ItemLimit" type="int">15</property>
<property name="SortBy" type="string">{8c06beca-0777-48f7-91c7-6da68bc07b69}</property>
<property name="SortByDirection" type="Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart+SortDirection,Microsoft.SharePoint.Publishing,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c">Desc</property>
<property name="GroupStyle" type="string">DefaultHeader</property>
<property name="ItemStyle" type="string">Default</property>
<property name="ServerTemplate" type="string"></property>
<property name="MainXslLink" type="string" >/Style Library/XSL Style Sheets/ContentQueryMainTableLayout.xsl</property>
<property name="RepeatColumns" type="int">0</property>
<property name="RepeatDirection" type="System.Web.UI.WebControls.RepeatDirection, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">Vertical</property>
</properties>
</data>
</webPart>
</webParts>

Pay attention that we provide custom XSLT for processing of the CQWP, for more details see How to: Customize XSL for the SharePoint Content By Query Web Part.

Main XSLT style sheet for rendering results in Table Layout

<xsl:stylesheet
version="1.0"
exclude-result-prefixes="x xsl cmswrt cbq"
xmlns:x="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cmswrt="http://schemas.microsoft.com/WebPart/v3/Publishing/runtime"
xmlns:cbq="urn:schemas-microsoft-com:ContentByQueryWebPart">
<xsl:output method="xml" indent="no" media-type="text/html" omit-xml-declaration="yes"/>
<xsl:param name="cbq_isgrouping" />
<xsl:param name="cbq_columnwidth" />
<xsl:param name="Group" />
<xsl:param name="GroupType" />
<xsl:param name="cbq_iseditmode" />
<xsl:param name="cbq_viewemptytext" />
<xsl:param name="cbq_errortext" />
<xsl:param name="SiteId" />
<xsl:param name="WebUrl" />
<xsl:param name="PageId" />
<xsl:param name="WebPartId" />
<xsl:param name="FeedPageUrl" />
<xsl:param name="FeedEnabled" />
<xsl:param name="SiteUrl" />
<xsl:param name="BlankTitle" />
<xsl:param name="BlankGroup" />
<xsl:param name="UseCopyUtil" />
<xsl:param name="DataColumnTypes" />
<xsl:param name="ClientId" />
<xsl:param name="Source" />
<xsl:param name="RootSiteRef" />
<xsl:param name="CBQPageUrl" />
<xsl:param name="CBQPageUrlQueryStringForFilters" />
<xsl:param name="RepeatDirection" />
<xsl:param name="RepeatColumns" />
<xsl:variable name="BeginTable" select="string('&lt;table class=&quot;dfwp-list&quot;&gt;')" />
<xsl:variable name="EndTable" select="string('&lt;/table&gt;')" />
<xsl:variable name="BeginTableRow" select="string('&lt;tr&gt;')" />
<xsl:variable name="EndTableRow" select="string('&lt;/tr&gt;')" />
<xsl:variable name="BeginTableCell" select="string('&lt;td class=&quot;dfwp-item&quot;&gt;')" />
<xsl:variable name="EndTableCell" select="string('&lt;/td&gt;')" />
<xsl:template match="/">
<xsl:call-template name="OuterTemplate" />
</xsl:template>
<xsl:template name="OuterTemplate">
<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row" />
<xsl:variable name="RowCount" select="count($Rows)" />
<xsl:variable name="IsEmpty" select="$RowCount = 0" />
<div id="{concat('cbqwp', $ClientId)}" class="cbq-layout-main">
<xsl:if test="$cbq_iseditmode = 'True' and string-length($cbq_errortext) != 0">
<div class="wp-content description">
<xsl:value-of disable-output-escaping="yes" select="$cbq_errortext" />
</div>
</xsl:if>
<xsl:choose>
<xsl:when test="$IsEmpty">
<xsl:call-template name="OuterTemplate.Empty" >
<xsl:with-param name="EditMode" select="$cbq_iseditmode" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="OuterTemplate.Body">
<xsl:with-param name="Rows" select="$Rows" />
<xsl:with-param name="FirstRow" select="1" />
<xsl:with-param name="LastRow" select="$RowCount" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</div>
<xsl:if test="$FeedEnabled = 'True' and $PageId != ''">
<div class="cqfeed">
<xsl:variable name="FeedUrl1" select="concat($SiteUrl,$FeedPageUrl,'xsl=1&amp;web=',$WebUrl,'&amp;page=',$PageId,'&amp;wp=',$WebPartId,'&amp;pageurl=',$CBQPageUrl,$CBQPageUrlQueryStringForFilters)" />
<a href="{cmswrt:RegisterFeedUrl( $FeedUrl1, 'application/rss+xml')}">
<img src="\_layouts\images\rss.gif" border="0" alt="{cmswrt:GetPublishingResource('CbqRssAlt')}"/>
</a>
</div>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.Empty">
<xsl:param name="EditMode" />
<xsl:if test="$EditMode = 'True' and string-length($cbq_errortext) = 0">
<div class="wp-content description">
<xsl:value-of disable-output-escaping="yes" select="$cbq_viewemptytext" />
</div>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.Body">
<xsl:param name="Rows" />
<xsl:param name="FirstRow" />
<xsl:param name="LastRow" />
<xsl:value-of disable-output-escaping="yes" select="$BeginTable" />
<xsl:choose>
<xsl:when test="$RepeatDirection = 1">
<xsl:call-template name="OuterTemplate.CallVerticalLayoutTemplate">
<xsl:with-param name="Rows" select="$Rows" />
<xsl:with-param name="FirstRow" select="$FirstRow" />
<xsl:with-param name="LastRow" select="$LastRow" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$Rows">
<xsl:variable name="CurPosition" select="position()" />
<xsl:call-template name="OuterTemplate.CallHorizontalLayoutTemplate">
<xsl:with-param name="CurPosition" select="$CurPosition" />
<xsl:with-param name="LastRow" select="$LastRow" />
</xsl:call-template>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of disable-output-escaping="yes" select="$EndTable" />
</xsl:template>
<xsl:template name="OuterTemplate.CallVerticalContainerTemplate">
<xsl:param name="RowPosition" />
<xsl:param name="RowsCount" />
<xsl:param name="ColsCount" />
<xsl:if test="$RowPosition &lt;= $RowsCount">
<xsl:value-of disable-output-escaping="yes" select="$BeginTableRow" />
<xsl:variable name="CurPosition" select="$RowPosition" />
<xsl:variable name="AllRows" select="/dsQueryResponse/Rows/Row" />
<xsl:for-each select="$AllRows">
<xsl:variable name="ColPosition" select="position()" />
<xsl:choose>
<xsl:when test="$ColPosition != 1 and $ColPosition &lt;= $ColsCount">
<xsl:variable name="CurPositionAbs" select="$CurPosition + $RowsCount * ($ColPosition – 1)" />
<xsl:if test="$CurPositionAbs &lt;= (count($AllRows))">
<xsl:call-template name="OuterTemplate.CallItemTemplate">
<xsl:with-param name="CurPosition" select="$CurPositionAbs" />
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$ColPosition &lt;= $ColsCount">
<xsl:call-template name="OuterTemplate.CallItemTemplate">
<xsl:with-param name="CurPosition" select="$RowPosition" />
</xsl:call-template>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:value-of disable-output-escaping="yes" select="$EndTableRow" />
</xsl:if>
<xsl:if test="$RowPosition &lt;= $RowsCount">
<xsl:call-template name="OuterTemplate.CallVerticalContainerTemplate">
<xsl:with-param name="RowPosition">
<xsl:value-of select="$RowPosition + 1"/>
</xsl:with-param>
<xsl:with-param name="RowsCount">
<xsl:value-of select="$RowsCount"/>
</xsl:with-param>
<xsl:with-param name="ColsCount">
<xsl:value-of select="$ColsCount"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.CallHeaderTemplate">
<xsl:apply-templates select="." mode="header">
</xsl:apply-templates>
</xsl:template>
<xsl:template name="OuterTemplate.CallVerticalLayoutTemplate">
<xsl:param name="Rows" />
<xsl:param name="FirstRow" />
<xsl:param name="LastRow" />
<!–Calc Cols & Rows–>
<xsl:choose>
<xsl:when test="$RepeatColumns = 0 or $RepeatColumns = 1">
<xsl:variable name="ColsCount" select="1" />
<xsl:variable name="RowsCount" select="$LastRow" />
<xsl:call-template name="OuterTemplate.CallVerticalContainerTemplate">
<xsl:with-param name="RowPosition" select="1" />
<xsl:with-param name="RowsCount" select="$RowsCount" />
<xsl:with-param name="ColsCount" select="$ColsCount" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="ColsCount" select="$RepeatColumns" />
<xsl:variable name="RowsCount" select="floor(($LastRow + $RepeatColumns – 1) div $RepeatColumns)" />
<xsl:call-template name="OuterTemplate.CallVerticalContainerTemplate">
<xsl:with-param name="RowPosition" select="1" />
<xsl:with-param name="RowsCount" select="$RowsCount" />
<xsl:with-param name="ColsCount" select="$ColsCount" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="OuterTemplate.CallHorizontalLayoutTemplate">
<xsl:param name="CurPosition" />
<xsl:param name="LastRow" />
<xsl:choose>
<xsl:when test="$RepeatColumns = 0">
<xsl:if test="$CurPosition = 1">
<xsl:value-of disable-output-escaping="yes" select="$BeginTableRow" />
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$CurPosition mod $RepeatColumns = 1">
<xsl:value-of disable-output-escaping="yes" select="$BeginTableRow" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
<xsl:call-template name="OuterTemplate.CallItemTemplate">
<xsl:with-param name="CurPosition" select="$CurPosition" />
</xsl:call-template>
<xsl:choose>
<xsl:when test="$RepeatColumns = 0">
<xsl:if test="$CurPosition = $LastRow">
<xsl:value-of disable-output-escaping="yes" select="$EndTableRow" />
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$CurPosition mod $RepeatColumns = 0">
<xsl:value-of disable-output-escaping="yes" select="$EndTableRow" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="OuterTemplate.CallItemTemplateDummy">
<xsl:param name="CurPosition" />
<xsl:value-of disable-output-escaping="yes" select="$BeginTableCell" />
<xsl:value-of disable-output-escaping="yes" select="$CurPosition" />
<xsl:value-of disable-output-escaping="yes" select="$EndTableCell" />
</xsl:template>
<xsl:template name="OuterTemplate.CallItemTemplate">
<xsl:param name="CurPosition" />
<xsl:value-of disable-output-escaping="yes" select="$BeginTableCell" />
<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="/dsQueryResponse/Rows/Row[position() = $CurPosition]" mode="itemstyle">
</xsl:apply-templates>
<!–<xsl:value-of disable-output-escaping="yes" select="$CurPosition" />–>
</xsl:otherwise>
</xsl:choose>
<xsl:value-of disable-output-escaping="yes" select="$EndTableCell" />
</xsl:template>
<xsl:template name="OuterTemplate.CallFooterTemplate">
</xsl:template>
<xsl:template name="OuterTemplate.GetSafeLink">
<xsl:param name="UrlColumnName"/>
<xsl:if test="$UseCopyUtil = 'True'">
<xsl:value-of select="concat($RootSiteRef,'/_layouts/CopyUtil.aspx?Use=id&amp;Action=dispform&amp;ItemId=',@ID,'&amp;ListId=',@ListId,'&amp;WebId=',@WebId,'&amp;SiteId=',$SiteId,'&amp;Source=',$Source)"/>
</xsl:if>
<xsl:if test="$UseCopyUtil != 'True'">
<xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
<xsl:with-param name="UrlColumnName" select="$UrlColumnName"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.GetTitle">
<xsl:param name="Title"/>
<xsl:param name="UrlColumnName"/>
<xsl:param name="UseFileName" select="0"/>
<xsl:choose>
<xsl:when test="string-length($Title) != 0 and $UseFileName = 0">
<xsl:value-of select="$Title" />
</xsl:when>
<xsl:when test="$UseCopyUtil = 'True' and $UseFileName = 0">
<xsl:value-of select="$BlankTitle" />
</xsl:when>
<xsl:otherwise>
<xsl:variable name="FileNameWithExtension">
<xsl:call-template name="OuterTemplate.GetPageNameFromUrl">
<xsl:with-param name="UrlColumnName" select="$UrlColumnName" />
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<xsl:when test="$UseFileName = 1">
<xsl:call-template name="OuterTemplate.GetFileNameWithoutExtension">
<xsl:with-param name="input" select="$FileNameWithExtension" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$FileNameWithExtension" />
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="OuterTemplate.FormatColumnIntoUrl">
<xsl:param name="UrlColumnName"/>
<xsl:variable name="Value" select="@*[name()=$UrlColumnName]"/>
<xsl:if test="contains($DataColumnTypes,concat(';',$UrlColumnName,',URL;'))">
<xsl:call-template name="OuterTemplate.FormatValueIntoUrl">
<xsl:with-param name="Value" select="$Value"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($DataColumnTypes,concat(';',$UrlColumnName,',URL;')))">
<xsl:value-of select="$Value"/>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.FormatValueIntoUrl">
<xsl:param name="Value"/>
<xsl:if test="not(contains($Value,', '))">
<xsl:value-of select="$Value"/>
</xsl:if>
<xsl:if test="contains($Value,', ')">
<xsl:call-template name="OuterTemplate.Replace">
<xsl:with-param name="Value" select="substring-before($Value,', ')"/>
<xsl:with-param name="Search" select="',,'"/>
<xsl:with-param name="Replace" select="','"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.Replace">
<xsl:param name="Value"/>
<xsl:param name="Search"/>
<xsl:param name="Replace"/>
<xsl:if test="contains($Value,$Search)">
<xsl:value-of select="concat(substring-before($Value,$Search),$Replace)"/>
<xsl:call-template name="OuterTemplate.Replace">
<xsl:with-param name="Value" select="substring-after($Value,$Search)"/>
<xsl:with-param name="Search" select="$Search"/>
<xsl:with-param name="Replace" select="$Replace"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($Value,$Search))">
<xsl:value-of select="$Value"/>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.GetSafeStaticUrl">
<xsl:param name="UrlColumnName"/>
<xsl:variable name="Url">
<xsl:call-template name="OuterTemplate.FormatColumnIntoUrl">
<xsl:with-param name="UrlColumnName" select="$UrlColumnName"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="cmswrt:EnsureIsAllowedProtocol($Url)"/>
</xsl:template>
<xsl:template name="OuterTemplate.GetColumnDataForUnescapedOutput">
<xsl:param name="Name"/>
<xsl:param name="MustBeOfType"/>
<xsl:if test="contains($DataColumnTypes,concat(';',$Name,',',$MustBeOfType,';'))">
<xsl:value-of select="@*[name()=$Name]"/>
</xsl:if>
</xsl:template>
<xsl:template name="OuterTemplate.GetPageNameFromUrl">
<xsl:param name="UrlColumnName"/>
<xsl:variable name="Url">
<xsl:call-template name="OuterTemplate.FormatColumnIntoUrl">
<xsl:with-param name="UrlColumnName" select="$UrlColumnName"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="OuterTemplate.GetPageNameFromUrlRecursive">
<xsl:with-param name="Url" select="$Url"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="OuterTemplate.GetPageNameFromUrlRecursive">
<xsl:param name="Url"/>
<xsl:choose>
<xsl:when test="contains($Url,'/') and substring($Url,string-length($Url)) != '/'">
<xsl:call-template name="OuterTemplate.GetPageNameFromUrlRecursive">
<xsl:with-param name="Url" select="substring-after($Url,'/')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$Url"/>
</xsl:otherwise>
</xsl:choose>
</xsl: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: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>
</xsl:stylesheet>

Results

Below is shown Web Part configured  to display results in 4 columns and horizontal mode layout

CBQTableLayoutHorizontal2