Me & My SharePoint [FrontEnd]

© Christian Stahl – All about SharePoint branding & customizations

Extending the DVWP with jQuery search, sort and pagination — May 2, 2013

Extending the DVWP with jQuery search, sort and pagination

In this post I’ll show you the quick steps how to extend the Data View Web Part (DVWP) with help of a jQuery plugin named DataTables.
The DVWP can connect to a SharePoint list and you can use it for example to display content in tabular view from a SharePoint list. Let’s say you want to create a news rollup that reads from the pages library with some special styling and condition, the DVWP is one of the options for this task. There’s a lot you can do with only XSLT, HTML and CSS but sometimes you need to add jQuery for some specific functions.

In this example I’ll show how to extend a basic DVWP news rollup with search-as-you type, sort function and pagination.

In order for jQuery DataTables to be able to function correctly, the HTML for the dataview table must be laid out in a well formed manner with the ‘thead’ and ‘tbody’ sections declared. Remember that use filters and limit number of object by server side in the DVWP especially if you’re dealing with large data sets, the datatable.js is there to serve a user friendly presentation layer.

The steps

  1. Create the DVWP
  2. Add the HTML that’s needed for the datatable plugin
  3. Add jQuery and CSS in separate files and reference this from your custom master page

jQuery
What you need for the plugin:

$(document).ready(function() {
$('#example').dataTable({
"oLanguage": {
"sInfo": "_START_ - _END_ of _TOTAL_ pages",
"sSearch": "Search:",
"sZeroRecords": "",
"sInfoEmpty": "No results..",
"sInfoFiltered": "",
"oPaginate": {
"sFirst": "<<",
"sLast": ">>",
"sNext": ">",
"sPrevious": "<"
}},
"sPaginationType": "full_numbers",
"iDisplayLength": 4,
"bLengthChange": false
});
var oTable = $('#example').dataTable();
oTable.fnSort([[0,'desc']]);
});

References
The references in your master page should looks like this:

<SharePoint:CssRegistration name="/Style Library/DVWP/Styles/MSDN.css" After="corev4.css" runat="server"/>
<script type="text/javascript" src="/Style Library/DVWP/Scripts/jquery-1.9.0.min.js"></script>
<script type="text/javascript" src="/Style Library/DVWP/Scripts/DataTables-1.9.4/media/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="/Style Library/DVWP/Scripts/addOns.js"></script>

Download
Download the aspx including the DVWP, jQuery and CSS taken from this example. The news pages in this example have the path /press/nyheter/ and the page library is named Sidor. Change the name and URL in the DVWP as you prefer as well as the select query. Do also download the DataTable plugin here.

I like the DVWP, it’s easy to work with and there’s a lot of ways to extend it, however this plugin works great with REST, librarys such as SPServices or in custom code. You can even use it in a custom format (template) for Content Query Web Part as well, maybe I’ll write a post about how to do this later on.

This technique works great in SharePoint 2007, 2010 and 2013, in foundation or server, online or OnPrem.

/ Christian

Use jQuery & cookies in SharePoint 2010 — August 31, 2012

Use jQuery & cookies in SharePoint 2010

In this article I’ll show you how to use Cookies in jQuery for your SharePoint Designer 2010 no code applications.

With help of a cookie you can write information to the user’s computer and this can be useful if you need to store stuff like visitors preferences, for example if the user has checked a check box or not at some page. Cookies can be very useful in many ways, but in some situations you need to write such information to the server like a database instead, cookies should not be used for critical information or any kind of sensitive information especially at a public faced site. However a cookie can be a great choice for many tasks in your front end development.

Some examples of the usefulness of a cookie

  • Change color schemes
  • Hide / Show elements at the page
  • Resizing fonts, scale up or down
  • Form, auto save inputs in fields, selected check box or radio button
  • Accordion / tabs, remember last state of collapsed/opened
  • Navigation, remember the last location
  • Welcome message, for example if you want to show a popup only once at specific date at the start page.

jQuery doesn’t support cookies natively but you can use a super small plugin called ‘jquery.cookie.js’  that contains all kind of basic functions and options you’ll need for your scripting. This plugin is also included in jQuery UI development-bundle.

Jquery.cookies gives a couple of options like:

  • Expires
    The lifetime for the cookie. You can use a number of days or a specific date. If omitted, the cookie is a session cookie
  • Path
    Where the cookie is valid. If you want it to be valid at every site in the site collection, just use a slash /, if you want it to be valid at let’s say only the news site set e.g /news/

Get going

Let’s start with a simple example just to get going, the following example will set the top row to a red or green background color depending of which link you click. Let’s say you clicked at green one and then reload the page, the green background color will remain. If you restart the browser, the top row will be white again. This script do not have any expired date, therefore, the cookie will expire automatically when you close the browser.

Let’s try this out:

Download jquery.cookies.js and create a reference to this file in your custom master, don’t forget to include an reference to latest jQuery API as well.

HTML
Put this somewhere in your custom master page

<!-- Session -->
<div class="Wrapper">
	<div class="Red">Red</div>
	<div class="Pipe">|</div>
	<div class="Green">Green</div>
</div>

jQuery
Put this in an external JS file referenced from your master page

$(document).ready(function() {

// Click red
$('.Red').click(function() {
$('.Wrapper').css("background-color","red");
$.cookie('Wrapper', 'red');
});

// Click green
$('.Green').click(function() {
$('.Wrapper').css("background-color","green");
$.cookie('Wrapper', 'green');
});

// Cookies
var Wrapper = $.cookie('Wrapper');
if (Wrapper == 'red') {
$('.Wrapper').css("background-color","red");
};
if (Wrapper == 'green') {
$('.Wrapper').css("background-color","green");
};

});

CSS
Put this in an external CSS file referenced from your master page

.Wrapper{
width:100%;height:16px;
background-color:#fff;color:#000;
}
.Red{
cursor:pointer;padding-left:5px;
float:left;
}
.Green{
cursor:pointer;float:left;
}
.Pipe{
float:left;padding:0px 5px 0px 5px
}

Maybe you want to set the cookie to a life span of 2 days, take a look at the jQuery script and the part where you set the name and the property:

$.cookie('Wrapper-', 'Green-');

Just add a path and a value; a number or a specific date, this will create the cookie

$.cookie('Wrapper-', 'Green-', {path: '/', expires: 2});

Take a look at the cookie in your disk, if you are using IE, you will find it through this path

C:\Users\...\AppData\Local\Microsoft\Windows\Temporary Internet Files

The file will contain the name of the cookie, the property and the path like this

Wrapper-Green-ecmtest67/108815182001923024639810215318430245996*

How to use cookies in a Data View web part

If we take this a step further and try to use cookies in a typical news roller DVWP. In this example, If you click at the icon next to the header for a news item that section will disappear, a kind of ‘I Have Read This’ function. At the bottom of the web part there’s a reset link that will delete the cookie.

The HTML part of the DVWP, it’s important to use @ID for the id, classes and href as the script shows

<div class="NewsRow {@ID}">
<div class="NewsContentContainer">			
<div class="NewsTitle">
<div class="NewsHider"><a href="{@ID}" id="{@ID}"><img alt="" src="/Style%20Library/Custom/Images/IhaveReadThis.png" width="22" height="22" /></a></div>
<div class="NewsTitleInner"><a href="/news/Pages/{@LinkFilename}"><xsl:value-of select="@Title" /></a></div>
<div class="GeneralClearBoth"></div>
</div>		
</div>
<div class="NewsAuthorCreatedWrap">
<span class="NewsAuthor">By <xsl:value-of select="@PublishingContact.title" />,</span> <span class="NewsCreated"><xsl:value-of select="@ArticleStartDate" /></span>
</div>
<div class="NewsContent">
<xsl:call-template name="FirstNWords">
<xsl:with-param name="TextData" select="$DescText"/>
<xsl:with-param name="WordCount" select="15"/>
<xsl:with-param name="MoreText" select="'...'"/>
</xsl:call-template>
</div>
</div>

The jQuery goes here

jQuery(function ($) {
var removedLinks = 'removedLinks'
,Options = {
expires: 7,
path: '/news'
},
c=$.cookie(removedLinks)||'#DebuggingCode'
$(c).remove();
$(".NewsHider a").click(function (e) {
e.preventDefault();
var LinkClass = '.'+ $(this).attr('id'),
removeLinksClasses=c.split(',')
$(LinkClass).remove()
removeLinksClasses.push(LinkClass)
c=removeLinksClasses.join(',') 
$.cookie(removedLinks, c, Options)
});
$('#NewsResetCookie').click(function(){
$.cookie(removedLinks,'',{expires:-1,path:'/news'})
})
});

If you prefer to use this in the ItemStyle.xsl and use it as a format for the Content Query web part, it works fine as well. If you want to take a look at a video of this, take a look here: http://youtu.be/kKEE_yktk8c

Hopes this give some ideas or inspiration for your cookie scripting adventures!
/ Christian

Comment functionality for news pages – Part II — July 18, 2012

Comment functionality for news pages – Part II

This article is the last one of two describing how to implement comment functionality into the news pages in SharePoint Server 2010. You’ll find Part I here. If you followed the examples in the Part II and have implemented the comment functionality it’s time to create one or more news rollup web part:

  • A web part that display the latest news pages and the number of related comments
  • A web part that display recently commented news pages
  • A web part that display the Display most commented news pages

First step – create a news rollup DVWP

When Lists and libraries are connected with a lookup you have the possibility to join content from both sources into a single Data View Web Part (joined sub view). This is useful if you want to create a news rollup that shows the latest news including the number of comments for each news item. In my examples I use the Page library in the root like the custom list NewsComments. You can use this in a sub site for news if you prefer.
I wanted to strip out HTML from the body text and limited body text by a number of words, so I used the strip HTML and the Count Word templates; a Codeplex project called SPXSLT created by my friend Marc D Anderson.

  • Open SPD and create a new folder direct in the root level and give it the name ’SPD-WebParts’.
  • Create a new ASPX page and give it the name RollupNews.aspx, check it out and open this with SPD.
  • Paste all the content from my file with the same name, you’ll find the download link at the end of this blog post.
  • Save the file and hit F12 for preview.
  • Check in and publish the file.
  • You can save this DVWP to the site gallery or to your disc if you want to implement the web part with the browser.

Next step – create a recently comments DVWP

This web part displays the most recent commented news pages. If the comment is created today, ‘New!’ will be displayed otherwise not.

  • Create a new ASPX page, save into the folder SPD-WebParts and give it the name LatestCommentedNews.aspx, check it out and open it up.
  • Paste all the content from my file with the same name, you’ll find the download link at the end of this blog post.
  • Save the file and hit F12 for preview. Check in and publish the file.
  • You can save this DVWP to the site gallery or to your disc if you want to implement the web part with the browser.

Next step – create a most comments DVWP

This web part display the most commented news pages. In order to group this, I have used the PageID column for the grouping. Furthermore I want to sort the web part by number of Items but of some reason I haven’t yet found the perfect way to accomplish this in XSLT, so I leave this as an open question to you readers! If you found a pure XSLT way for this I’ll update this blog post. In the meantime I’ll go for the jQuery TableSorter plugin. This plug is useful in different scenarios when you need client side sorting with all kind of data and it a works great if you need secondary ‘hidden’ sorting or for example just sorting a table without page refresh. You’ll find the file included in the download, see the link at the end of this blog post.

  • Create a new ASPX page, save it save into the folder SPD-WebParts and give it the name MostCommentedNews.aspx, check it out and open it up.
  • Paste all the content from my file with the same name, you’ll find the download link at the end of this blog post.
  • Save the file and hit F12 for preview. Check in and publish the file.
  • You can save this DVWP to the site gallery or to your disc if you want to implement the web part with the browser.

To activate the sort order, add a reference in your custom master page to the tablesorter file and add the following line of script into an external JS file or into the head section of your master page:

<script type="text/javascript">
$(document).ready(function() {
// Custom sort DVWP news sort by most comments
$('.tablesorter').tablesorter({sortList:[[0,0]] });
});
</script>

Download the files

If you got questions about this or maybe ideas for more functions or web parts related to this, pleas drop a comment.
Download my example files here

Thanks!
/ C

Comment functionality for news pages – Part I — June 30, 2012

Comment functionality for news pages – Part I

If you want to implement comment functionality into the news pages in SharePoint Server 2010, a no code solution with Data Views and portion of jQuery could be a perfect option for you. Alternative you can use the OOTB Note board web part or even use a blog site to get comment functionality but these options limits you if you need to customize such solution in comparison to data views powered with XSLT, jQuery and CSS.

My concept for this is basically to use two connected lists. In the following example I’ll use the Page library in the root site and a custom list called NewsComments. The NewsComments lists have a lookup column (PageID) that connects the ID column in the Pages library. You can do this at a news sub site as well if you prefer.
I am using a data view form web part for the comment textbox in a page layout that sends the comment text, the current page ID, the page title and the page URL to the NewsComments list. Into this Page layout that serves as a template for the news pages, I have created a DVWP that displays all comments related to the current page. Do get this relation I use a parameter with a server variable and a XSL filter that matches current page name to the column in NewsComments list that stores the page names. I do also use some jQuery to grab and populate information on the page and the list.
When you connecting lists with a lookup it gives you the possibility to join content from both lists in a Data View Web Part (joined subview), this can also be useful if you want to create a news rollup that shows the latest news including the number of comments per news item. I’ll write more about such web parts in the next blog.

First step – create the list and a page layout

  • Create a list in the root site of the site collection by the type of Custom List named NewsComments. Create following columns:

    • A column named PageID by the type Lookup. Select Pages in ’Get information from’ and select ID for ’In this column’. Set it to required and click OK.
    • A column named PageTitle by the type Single Line of Text and click OK.
    • A column named Comments by the type multiple lines of text with 5 rows, specify this as Plain text and click OK.
  • Open SPD and create a new page layout and give it the name ’NewsPages’. Don’t forget to give the file a Title. You can copy an existing page layout. Open the page and add or remove the content fields you like the page to have, you can use just Page Content and Title. Add some additional markup; give the elements class names that the jQuery functions needs. Download my example files down below and copy the markup. In my example I have created a Page layout based on the Article Page layout, if you are using a content type like the Enterprise Wiki you have to add tag prefix in the top of the page layout that match the fields you put on the page.
  • Save and publish the file in _catalogs > masterpage folder at the root site. Make sure that it’s available at the root site as a selectable page layout so you can specify this page layout when you create a new page in the root site.
  • Now use the browser and create some pages at the root site, for example name the first on to test1 and the URL will be /Pages/test1.aspx. Use your new page layout as template.

Next step – create a comment in the list

Open the NewsComments list and create a new item, type the URL name of one of the pages you have created in the tile field, for example test1.aspx. Type the filename of the page in the Title field, select the ID of this page, type the title of the page in the PageTitle column and create a comment. You’ll need to do this just to make sure the comments can be connected.

You have now created the basics and were now move the two DVWPs that will display the comments for the pages and make the comment box available. Download my example files if you get stuck, you can just copy the DVWPs and the jQuery and paste them in your page layout.

Next step – create a Data View Web Part that only display comments related to the current news page.

  • Open SPD and the page layout that you just created with SharePoint Designer. Create a DVWP (display Item Form) that get the information from the NewsComments list. Add this DVWP at the bottom of the page just below the last content field. Use no paging, show all objects and show only the column Comments.
  • Create a new parameter as in the image below. The parameter is required to match the current page’s URL name to what caught up in NewsComments list’s Title field.
  • Change the dsQueryRespoinse/Rows variable. If you have a language other than English don’t forget to change name of Pages. Locate the following line:

    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row"/>
    

    change to:

    <xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[@Title=substring-after($ReferPageURL, 'Pages/')]"/>
    
  • Save the page and verify that only the comments appear per page according to what you entered in the NewsComments list.
  • Clean up the DVWP, och add the some markup for presentation.
  • Verify that everything works so far.

Next step – create a form web part

  • Add a DFWP (New Item Form) below the other data view, that get the information from the NewsComments list.
  • Use only following columns: PageID, PageTitle, Title and Comments

Clean the DFWP and give it some markup. As for the other DVWP, it’s important that you use the appropriate markup when jQuery and CSS makes use of class names.

Next step – create the jQuery you need for the comment box in the page layout

The purpose of the following jQuery is to get the page’s ID populated in the dropdown and to populate the Page’s title and URL into the select fields. I have also included a function that show characters left and set a limit to 140 characters.

<script type="text/javascript">
// Limit characters
function limitChars(textid, limit, infodiv){
var text = $('#'+textid).val();    
var textlength = text.length;
if(textlength > limit) {
$('#' + infodiv).html(''+limit+' limit reached');
$('#'+textid).val(text.substr(0,limit));
return false;
}
else {
$('#' + infodiv).html(''+ (limit - textlength) +' characters left');
return true;
}}
$(function(){
$(".FormFieldNewsComments > span > textarea[title='Comments']").keyup(function(){
limitChars(".FormFieldNewsComments > span > textarea[title='Comments']", 140, "LimitInfo");
})
});
	
$(document).ready(function() {
var loc = window.location.href.substr(window.location.href.lastIndexOf("/")+1); // Get the page filename
var PageTitle = $('.PageTitleDiv').html(); // Get the page title
$(".FormFieldNewsTitle > span > input[title='Title']").val(loc); // insert page filename in a CSS hidden formfield
$(".FormFieldPageTitle > span > input[title='PageTitle']").val(PageTitle);	// insert pages title in a CSS hidden formfield
var HiddenInput = $("input[title='PageID']").attr("optHid"); // Lagrar ID till attributet optHid
$("select[title='PageID']").val(_spPageContextInfo.pageItemId); // If less than 20 items i Pages bibl, lägger in ID nr som en selected i DD
$("input[title='PageID']").val(_spPageContextInfo.pageItemId);  // If 20 or more items in Pages lib
$("input[id='" + HiddenInput +"']").attr("value",(_spPageContextInfo.pageItemId)) // Set ID as selected if 20 or more items in Pages lib
});
</script>

Next step – add a few more functions

I added a few additional functions in this example which I think may be helpful and that is the ability to remove own comments and to show number of comments for the page. If you download my example files, this is already included in the DVWPs.

Show the number of comments

<xsl:if test="count($Rows) = 0">No comments yet..</xsl:if>
<xsl:if test="count($Rows) = 1"><xsl:value-of select="$dvt_RowCount" /> Comment</xsl:if>
<xsl:if test="count($Rows) &gt;= 2"><xsl:value-of select="$dvt_RowCount" /> Comments</xsl:if>

Add the following parameter to your XSL style sheet

<xsl:param name="UserID" />

Create these parameter for the rowview template

<xsl:param name="KeyField">ID</xsl:param>
<xsl:param name="KeyValue" select="ddwrt:EscapeDelims(string(@ID))"/>
<xsl:param name="Mode">view</xsl:param>

Match the author to the logged in user

<xsl:if test="@Author.title = $UserID">
<a href="javascript: {ddwrt:GenFireServerEvent(concat('__cancel;__delete={',$KeyField,'=',$KeyValue,'};__commit'))}">DELETE</a>
</xsl:if>

If you want to create a link to the comment list that only administrators should see you can use the DDWRT:IfHasRights() function with an appropriate value

<xsl:if test="ddwrt:IfHasRights(33554432)"><a href="/Lists/NewsComments/AllItems.aspx"><img src="/Style Library/Branding/Images/EditIcon.gif" alt="" /></a></xsl:if>

Next step – branding

The functions for this are now completed and it’s time to give this some visual awesomeness! Download my example files where you find CSS and an image that should get you going.

More to do

There are a lot of things to keep in mind to get this up and I hope you can take this as a base for your own solution and find inspiration. There are many ways to extend this and maybe I’ll publish this to Codeplex for the support and possibilities to further extensions. Maybe it could be an idea to integrate this function with my friend Marc D Anderson’s SPservices SocialDataServices

Download the files

Download my example files here

Part II

In the next blog I’ll show you how to create a news rollup out of this, a joined DVWP that displays the latest news and the number of related comments.

Create a Microblog for SharePoint 2010 — February 11, 2012

Create a Microblog for SharePoint 2010

This post is about how to create a Microblog with SharePoint Designer 2010 and the use of DVWPs, CSS and jQuery. A Microblog can be used for quick communication with team members in short sentences at the Intranet. This example works just like a SharePoint blog site, posts and comments are stored in separate lists that are linked by a lookup and team members can add posts and anyone can reply to posts. This Microblog is a basic no code solution and if you are looking for more advanced social features like hash tagging, direct messages, document uploading or integration of my site you should check out tools like NewsGator or Yammer among more. If you are looking for a basic comment function, maybe the OTB web part Note board could be an option. But let’s look into how you can use the DWVP for a Microblog.

Let’s try it out

  • Open the browser and create two custom lists in the root site of your Site Collection
    • Name it ListPosts
    • Name it ListComments
  • ListPosts
    • Create a column:
    • Name: Content – Multi lines of text with three lines for editing and plain text type.
    • Go to list settings, click the Created By column and change the presentation for this field to Name (with picture).
    • Add a new Item in the list with a title and a comment.
  • ListComments
    • Create two columns:
    • Name: PostsID – type Lookup. Get information from ListPosts in the ID column. Do not allow multiple values.
    • Name:  Comments – Multi lines of text with three lines for editing and plain text type.
    • Go to list settings, click the Created By column and change the presentation for this field to Name (with picture).
    • If you don’t use English language, rename the the (Title) column to Title, another way is just to set this column as optional if you prefer.
    • Add a new Item in the list with a title and a comment. Type a title, select PostID according to the ID from the ListPosts item you added and add a comment.

Create a Linked Data Source for the posts and comments lists

  • Open the root site with SPD and click at site objects in the left navigation at click Data Sources. Click at Linked Data Source in the ribbon. Give it the name MicroBlog in the General tab and click at configure Linked Source in the Source tab.
  • Select ListPosts and ListComments and add them to the right pane. If you can’t see the list you have to update SPD. Click at the next button and select Join and click at Finish and OK.
  • Open the root site with SPD and create a new folder named SPD-WebParts
  • Create a new ASPX page called MicroBlog and save it in the folder you created, open the page with SPD. IF you download my pages and will copy and paste you don’t need to do more now, but if you want to set this up by your self you can attach the page to your custom master page. Check it out, click at the Style tab in the ribbon, the button Attach and attach it as a custom master page. In the design view, click at the right arrow for the PlaceHolderMain place holder and click at create custom content.
  • Create a new ASPX page called MicroBlogForm and save it in the same folder as Microblog.aspx. Repeat the steps as you just did for the MicroBlog.aspx.

Create two ASPX pages

In the next step it’s time to create two DVWPs for MicroBlog.aspx, the first is for inserting posts and the second is for displaying current posts and their comments. If you want to build this by yourself you can just add a Display Item Form DVWP and use the Linked Sources and then insert field from the Comments as a Joined sub view. You will also have to create a New Item Form DVWP for the second page meant for inserting comments.

If I would describe all the steps from now including styling and jQuery this blog would be too long and for this reason I have prepared this so it’s just to download the pages and do some copy and paste. If you export or upload the DVWPs as web parts the CSS and the jQuery will not follow the web part. so cut the CSS from the MicroBlogForm page and paste this into an external CSS file. The same goes for the jQuery stuff in both pages, cut the scripts and paste them into the head section of your custom master or into an external JS file.

Read more about Linked data sources at the Microsoft Office SPD pages.

Insert the Dataviews into the ASPX pages

MicroBlog.txt

  • Open this page and copy the content into MicroBlog.aspx and save the page

MicroBlogForm.txt

  • Open this page and copy the content into MicroBlogForm.aspx and save the page

CSS

Copy this CSS into your custom CSS file

/* --- MicroBlog --- */
.PostBodyWrapper {width:100%;}
.PostWrapper {margin-left:5px}
.PostSeparator {border-bottom:1px #f1f1f1 solid; border-top:1px #fff solid; margin:5px 5px 5px 5px}
.PostImg {float:left; width:50px; height:50px; padding-right:10px}
.PostContentWrapper {background-color: #fff; height:50px;}
.PostEditor {font-weight:bold; color: #0072bc}
.PostContent {}
.PostLink {}
.PostComment {}
.PostCreatePost {padding:10px}
.FormFieldMicroBlogWrapper {margin-left:5px; width:100%; }
.FormFieldMicroBlog > span > textarea {
background-color: transparent; width:95%;
border:1px solid #ccc;        
overflow:hidden; overflow-y: hidden; overflow-x: hidden;   
font-size:14px; color:#999;
font-family:Arial, Helvetica, sans-serif;
padding:10px 20px 0px 10px;
margin-right:20px
}
.PostBtn {padding-right:3px; float:left}
.PostBtn Input {
width: auto;
padding: 5px 10px 5px 10px;
background: #617798;
border: 0;
font-size:13px;
color: #fff;
cursor: pointer!important;
float:right
}
.CommentWrapper {margin-left:60px; margin-right:5px; background-color: #f7f7f7; padding:10px; border-bottom:1px #fff solid}
.CommentImg {width:50px; float:left;}
.CommentContentWrapper {}
.CommentEditor {font-weight:bold; float:left}
.CommentEnd {margin-left:60px; margin-right:5px; border-top:1px #ccc solid; border-bottom:1px #fff solid; margin-bottom:20px}
.CommentContent {}
.CommentLink {}
.CommentNoComment {border-bottom:1px #fff solid; border-top:1px #fff solid; margin:0px 0px 15px 0px}
/* --- End MicroBlog --- */

Jquery

Copy this jQuery into your custom master or into a referenced JS file

$(document).ready(function() {
$(function() {
// Remove autofocus
$("textarea[title='Content']").blur();
// Set defaulttext
var defaultText = 'What are you working on?';
$("textarea[title='Content']")  
// Handle blur effect  
.val(defaultText)
.focus(function() {
if ( this.value == defaultText ) this.value = ''
})
.blur(function() {
if ( !$.trim( this.value ) ) this.value = defaultText
});});
});

Image Paths

If you not have activated the user profiles and my site, you have to change the image path in the MicroBlog file. You can create an image library in the root site and store the user images there if you want. Just name the images same as the Users ID. The path can looks like:

<img src=”/RootImages/{@Author.id}.jpg” alt=”” width=”50″ height=”50″ />

Ready to test

Finally it’s time to try this out, open your browser and to to the MicroBlog page

Behind the scenes

Some notes if you needs to change something

  • You have to reference the latest jQuery file in your custom master page
  • Save the two web parts into the web part gallery, or export them as files and you can use them at any page in the site collection, not only at the root site.
  • I have set a RowLimit variable to 5 In MicroBlog.aspx, change this as you prefer.
  • I changed the variable that Joins the list to get rid of the unwanted characters
    <xsl:variable name=”Rows” select=”../../../ListComments/Rows/Row[substring-before(@PostsID., ‘;#’) = normalize-space($dvt_ParentRow/@ID)]”/>
  • The link ‘Reply’ sends the current posts ID as a query string over to the next page (the dialogue). I have used a bit jQuery go get this ID from the URL in order to prepopulate a hidden drop down (the lookup column), and as you may know if there are 20 or more items in a list and you use lookup to this list, the select drop down will be converted to an input drop down and this needs to be solved as well.
  • In the dialog I used a SharePoint:SaveButton as I think is an easy way to close a dialog when click the save button.

The jQuery used in the MicroBlogForm that sets the Post ID into the hidden dropdown.

$(document).ready(function() {
var loc = window.location.href.substr(window.location.href.lastIndexOf("/")).replace('/MicroBlogForm.aspx?CID=', '').replace('&IsDlg=1', ''); 
var HiddenInput = $("input[title='PostsID']").attr("optHid");$("select[title='PostsID']").val(loc);	
$("input[title='PostsID']").val(loc);	
$("input[id='" + HiddenInput +"']").attr("value",(loc))
$('input[title="Title"]').val(loc); 
$(".FormFieldShow > span > textarea").focus(); 
});

More to do

If someone’s profile picture should be broken or missing, you can use jQuery to replace a broken image with an shadow image:

$(window).bind('load', function() { 
$('img.resizeme').each(function() { 
    if((typeof this.naturalWidth != "undefined" && 
        this.naturalWidth == 0 )  
        || this.readyState == 'uninitialized' ) { 
        $(this).attr('src', '/_layouts/images/O14_person_placeHolder_32.png'); 
    } 
}); 
})

Enjoy, and drop a comment if you have something to ask
/ Christian

How to customize a grouped view in the Data View web part — September 18, 2011

How to customize a grouped view in the Data View web part

How about to use a Data View Web Part (DWWP) to display a grouped view of news pages by year and month? Let’s improve the findability and give the users a simply way to browse the news pages in a news archive page. This technique could be useful for any kind of item in a list; you could for example do the same to display documents from a library or items in an announcement list.

Some things we need to do:

  • Create two calculate columns that will serve us when the items is created or published; this is an easy way to create the grouping.
  • Create a click function (expand / collapse) for the whole row, for the year and month in the group header.
  • Show number of items for each month
  • Sort the month by their actual numbers but display the month by their names

Let’s start!

  • Open and the site that containing the news pages with SharePoint Designer 2010, in the example it’s a news site based on the publishing site template in SharePoint 2010 server.
  • Add two new calculated columns to the Pages library. You can do this in SharePoint Designer or by the browser. Name the first calculated column to ArticleYear with data type Single line of text. Us the formula: =TEXT([Article Date],”YYYY”) name the next calculated column ArticleMonth with data type Single line of text. Us the formula: =TEXT([Article Date],”MM”). In this example I use a column called Article Date and I use this column in the actual page layout for the news pages. Be sure to include this column in your page layout and set dates to the news pages in the library. If you don’t want to use this column you can use the inbuilt column Created instead. If you’re using another language on the site than english, you may have to written the formulas a bit different, for example if you use the Swedish language you have to use ; instead of a comma character.
  • Create a new aspx page with SharePoint Designer and open this page, save this somewhere in the site. Place the cursor inside the form tag and click Insert in the ribbon. Click at Display Item Form button and select Pages.
  • Before you’ll get into the code view, do the settings for the DVWP you’ll need with help of the ribbon buttons. Set the paging, filter, sort and grouping like this:
    • Paging eq display All Items
    • Add / Remove columns. Display this columns: Title, Article date, ArticleYear and ArticleMonth
    • Filter eq article date Not Null
    • Sort order eq Article Date desc
    • Group ArticleYear by asc, show group header and make it collapse
    • Group ArticleMonth by asc, show group header and make it collapse

At this point you’ll have the basic stuff needed to get going with the UI customizations. Now you it’s your turn to do the branding and start work on functionality. If you want to see my specific example, you can download the aspx page, the CSS and the image (see the bottom of this blog post) where you’ll see the HTML structure, how to set onclick at rows, how you todisplay month names how to count a nodeset to display numer of news items per each month. In this page you’ll also how you can display month names. When you done, you can save the web part as to a file and upload this to the news site and put in a news archive page.

A quick view of things you can do more

Display month names

<xsl:choose>
 <xsl:when test="$fieldvalue='01'">January</xsl:when>
 ---
 <xsl:when test="$fieldvalue='12'">December</xsl:when>
 <xsl:otherwise></xsl:otherwise>
</xsl:choose>

Get number of items (news) per each month:

<xsl:value-of select="count($nodeset)"/>

Onclick event for the rows

<tr class="YearRow" id="group{$groupid}" style="display:{$displaystyle}" onclick="javascript:ExpGroupBy(this);return false;">

If you consider to use divs instead of tables, keep in mind that the ExpGroupBy function that lives in the Core.js needs to be overridden to match you structure.

If you like to have this grouped DVWP connected to a a list instead of a Page library, no problems! The only thing that will be different is the link to the actual item, try rewrite the link like this:

<a href="{@FileDirRef}/DispForm.aspx?ID={@ID}"><xsl:value-of select="@Title" /></a>

One thing more, if you want to use this as an archive for news pages and don’t want to display the current year you can change the Rows varible to this:

<xsl:variable name="Rows" select="/dsQueryResponse/Rows/Row[not(contains(@ArticleStartDate,substring(ddwrt:TodayIso(),1,4)))]"/>

Download the files here

/ Christian

Image slideshow with CQWP in SharePoint 2010 — August 9, 2011

Image slideshow with CQWP in SharePoint 2010

This post is a follow up to the past 3 part series ‘Display News pages with CQWP’. In the last post (Part 3) I wrote about how to slide the news items one by one from right to left with the Content By Query Web Part (CQWP) and with help of jQuery.

In this post I will show how to use a similar technique but we’re now going to create an image slideshow that reads images from an image library in SharePoint. To achieve this I have used a slideshow plugin that support many types of effects, it also supports stuff like pause on hover, auto fit, click triggers and much more.


This blog do not cover how to configure the CQWP or the basics of modifying ItemStyle.xsl ContentQueryMain.xsl or other XSL files that describes the CQWP, we´re going straight to the core for the fun stuff which means XSLT, CSS and jQuery. Please read the previous posts for more information about options for created date, the count word function and the layout.

Some things to consider:

  • By this example you need to modify ContentQueryMain.xsl. Changes in this file will affect all templates in ItemStyle.xsl, therefor I recommend you to create a copy of ContentQueryMain.xsl and ItemStyle.xsl and modify these copies separately.
  • If you prefer you can create a provisioning module with Visual Studio 2010, a feature that pushes the copies and the web part into proper folders, but this is a SPD blog, so I will show how to do this by SharePoint Designer 2010.
  • When JS is used for DOM manipulation, all the content could show up shortly when the page loads, before the DOM is fully loaded. This can be solved by CSS or jQuery, in this example I have simply used a fixed height for the container where the images displays.
  • The images will be displayed from the thumbnail directory in the Picture Library, so you don’t need to think about loading time for full size images.
  • Content Query web part is only available in SharePoint Server.

Let´s go!

Now create a standard Picture Library in SharePoint and upload a couple of images, type a title for each image. Go to a page in your SharePoint environment and add a Content Query Web Part (CQWP). Open the tool pane. Expand Query and click ‘Show items from the following list’. Select the picture library you just created. Click the OK button. Verify that you see the images onto the page, and then click the Web Part arrow and select Export. Save it on the desktop. You can now delete the Content Query Web Part on the page and close the page. Later on we going to do some changes in this file and upload it, let’s come back to that.

Download jQuery Cycle Plugin here and the latest jQuery, upload the files to Style library.

Open your custom master page and type the proper reference to latest jQuery and to the cycle plugin, for example

<!-- Custom Scripts -->
<script type="text/javascript" src="/Style Library/Scripts/jquery-1.6.min.js"></script>
<script type="text/javascript" src="/Style Library/Scripts/jquery.cycle.all.js"></script> 

In the head section of the master page, copy and paste this script, remember that this plugin allows lots of cool effects more than this

<!-- Image Cycle -->
<script type="text/javascript">
$(document).ready(function() {
    $('.slideshow').cycle({
		fx: 'fade', 
		speed: 3000,
		timeout:3000,
		random:  1
	});
});
</script>

Copy and paste this CSS classes into your custom CSS file

/* --- CYCLE JQUERY --- */
.slideshow {position:relative;height:170px;overflow: hidden;}
.slideshow img {display:block;height:143px;padding:5px;border:1px solid #ccc;background-color:#eee;}
.SlideshowText {padding-left:5px;margin-bottom:10px;height:20px;}

Publish the master page and the CSS file and create a copy of ItemStyle.xsl check it out and rename this to ItemStyleImageCycle.xsl. In this file you’ll need only one template, so open jthe file and delete all its content. Copy and paste the following xslt stylesheet.

<xsl:stylesheet 
  version="1.0" 
  exclude-result-prefixes="x d xsl msxsl cmswrt"
  xmlns:x="http://www.w3.org/2001/XMLSchema" 
  xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" 
  xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"
  xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:msxsl="urn:schemas-microsoft-com:xslt">
  <xsl:param name="ItemsHaveStreams">
    <xsl:value-of select="'False'" />
  </xsl:param>
  <xsl:variable name="OnClickTargetAttribute" select="string('javascript:this.target=&quot;_blank&quot;')" />
  <xsl:variable name="ImageWidth" />
  <xsl:variable name="ImageHeight" />
 
<!-- CycleImage Template -->
    <xsl:template name="CycleImage" match="Row[@Style='CycleImage']" 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="SafeImageUrl">
            <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
                <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
            </xsl:call-template>
        </xsl:variable>
            <xsl:if test="string-length($SafeImageUrl) != 0"> 
                    <a href="{$SafeLinkUrl}">
                      <xsl:if test="$ItemsHaveStreams = 'True'">
                        <xsl:attribute name="onclick">
                          <xsl:value-of select="@OnClickForWebRendering"/>
                        </xsl:attribute>
                      </xsl:if>
                      
                      <xsl:if test="$ItemsHaveStreams != 'True' and @OpenInNewWindow = 'True'">
                        <xsl:attribute name="onclick">
                          <xsl:value-of disable-output-escaping="yes" select="$OnClickTargetAttribute"/>
                        </xsl:attribute>
                      </xsl:if>  
                 <img class="image" src="{$SafeImageUrl}" title="{@ImageUrlAltText}"><br />
                 <div class="SlideshowText"><xsl:value-of select="@Title"/></div>
                        <xsl:if test="$ImageWidth != ''">
                          <xsl:attribute name="width">
                            <xsl:value-of select="$ImageWidth" />
                          </xsl:attribute>
                        </xsl:if>
                        <xsl:if test="$ImageHeight != ''">
                          <xsl:attribute name="height">
                            <xsl:value-of select="$ImageHeight" />
                          </xsl:attribute>
                        </xsl:if>
                        
                      </img>
                    </a>
            </xsl:if>
    </xsl:template> 
</xsl:stylesheet>

When you have checked in and published this file, create a copy of ContentQueryMain.xsl. check it out and rename this to ContentQueryMainImageCycle.xsl. Open this file and copy and paste the following xsl stylesheet.

<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" />
    

	<!--No UL start-->
  <xsl:variable name="BeginList" select="string()" />
	<!--No end UL-->
  <xsl:variable name="EndList" select="string()" />
	<!--No LI start-->
  <xsl:variable name="BeginListItem" select="string()" />
	<!--No LI end-->
  <xsl:variable name="EndListItem" select="string()" />
  
  
<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" />
      

		<!--Div and class - no UL and id-->
      <xsl:variable name="BeginColumn1" select="string('&lt;div class=&quot;slideshow&quot; style=&quot;width:')" />
      <xsl:variable name="BeginColumn2" select="string('%&quot; &gt;')" />
      <xsl:variable name="BeginColumn" select="concat($BeginColumn1, $cbq_columnwidth, $BeginColumn2)" />
		<!--End Div - No end UL-->
      <xsl:variable name="EndColumn" select="string('&lt;/div&gt;')" />
      
      <xsl:for-each select="$Rows">
            <xsl:variable name="CurPosition" select="position()" />
            <xsl:if test="($CurPosition &gt;= $FirstRow and $CurPosition &lt;= $LastRow)">
                <xsl:variable name="StartNewGroup" select="@__begingroup = 'True'" />
                <xsl:variable name="StartNewColumn" select="@__begincolumn = 'True'" />
                <xsl:choose>
                    <xsl:when test="$cbq_isgrouping != 'True'">
                        <xsl:if test="$CurPosition = $FirstRow">
                            <xsl:value-of disable-output-escaping="yes" select="$BeginColumn" />
                        </xsl:if>
                    </xsl:when>
                    <xsl:when test="$StartNewGroup and $StartNewColumn">
                        <xsl:choose>
                            <xsl:when test="$CurPosition = $FirstRow">
                                <xsl:value-of disable-output-escaping="yes" select="$BeginColumn" />
                                <xsl:call-template name="OuterTemplate.CallHeaderTemplate"/>
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:call-template name="OuterTemplate.CallFooterTemplate"/>
                                <xsl:value-of disable-output-escaping="yes" select="concat($EndColumn, $BeginColumn)" />
                                <xsl:call-template name="OuterTemplate.CallHeaderTemplate"/>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:when>
                    <xsl:when test="$StartNewGroup">
                        <xsl:call-template name="OuterTemplate.CallFooterTemplate"/>
                        <xsl:call-template name="OuterTemplate.CallHeaderTemplate"/>
                    </xsl:when>
                    <xsl:when test="$StartNewColumn">
                        <xsl:choose>
                            <xsl:when test="$CurPosition = $FirstRow">
                                <xsl:value-of disable-output-escaping="yes" select="$BeginColumn" />
                            </xsl:when>
                            <xsl:otherwise>
                                <xsl:value-of disable-output-escaping="yes" select="concat($EndColumn, $BeginColumn)" />
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:when>
                    <xsl:otherwise>
                    </xsl:otherwise>
                </xsl:choose>
                <xsl:call-template name="OuterTemplate.CallItemTemplate">
                    <xsl:with-param name="CurPosition" select="$CurPosition" />
                </xsl:call-template>
                <xsl:if test="$CurPosition = $LastRow">
                  <xsl:if test="$cbq_isgrouping = 'True'">
                    <xsl:call-template name="OuterTemplate.CallFooterTemplate"/>
                  </xsl:if>
                  <xsl:value-of disable-output-escaping="yes" select="$EndColumn" />
                </xsl:if>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
   
   
   
   

    <xsl:template name="OuterTemplate.CallHeaderTemplate">
      <xsl:value-of disable-output-escaping="yes" select="$BeginListItem" />
      <xsl:apply-templates select="." mode="header">
        </xsl:apply-templates>
      <xsl:value-of disable-output-escaping="yes" select="$BeginList" />
    </xsl:template>
    <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:apply-templates>
              </xsl:otherwise>
          </xsl:choose>
      <xsl:value-of disable-output-escaping="yes" select="$EndListItem" />
    </xsl:template>
    <xsl:template name="OuterTemplate.CallFooterTemplate">
      <xsl:value-of disable-output-escaping="yes" select="$EndList" />
      <xsl:value-of disable-output-escaping="yes" select="$EndListItem" />
    </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>

Save and publish these files. Let´s return to the file you have exported to your desktop, open it with notepad. There’s three properties in this file you need to change

property name=”ItemXslLink”
property name=”MainXslLink”
property name=”Xsl”

Change this three properties:

<property name="ItemXslLink" type="string">/Style Library/XSL Style Sheets/ItemStyleImageCycle.xsl</property>

<property name="MainXslLink" type="string">/Style Library/XSL Style Sheets/ContentQueryMainImageCycle.xsl</property>

<property name="Xsl" type="string">&lt;xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:cmswrt="http://schemas.microsoft.com/WebPart/v3/Publishing/runtime" exclude-result-prefixes="xsl cmswrt x" &gt; &lt;xsl:import href="/Style Library/XSL Style Sheets/Header.xsl" /&gt; &lt;xsl:import href="/Style Library/XSL Style Sheets/ItemStyleImageCycle.xsl" /&gt; &lt;xsl:import href="/Style Library/XSL Style Sheets/ContentQueryMainImageCycle.xsl" /&gt; &lt;/xsl:stylesheet&gt;</property>

Save the file, close it and go to a page in your SharePoint environment. Now, add and upload this file as a web part from your desktop, it will be displayed as Content Query in the imported Web Part folder. Once you got it on the place, check in the page or stop editing.

There are a couple of steps here, so please drop a comment if you got any questions or get stuck. By the way, it’s not only images that can be cycled, in this way you can achieve this for any kind of list objects.

/ Christian

Display News pages with CQWP – Part III — July 18, 2011

Display News pages with CQWP – Part III

This blog shows how to create a news ticker with Content By Query Web Part (CQWP), how you can make items slide from right to left with help of SharePoint Designer 2010. In this example you will modify ItemStyle.xsl and ContentQueryMain.xsl and use a portion CSS.
In Part 1 in this series I wrote about CQWP and how to create a custom template in ItemStyle.xsl and how to use CSS for the styles. In Part 2 I wrote about how to display the latest 5 news by category and how to separate them by tabs.

Click at the image below to activate the video:

This blog do 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 for the fun stuff which means ItemStyle.xsl, ContentQueryMail.xsl, CSS and jQuery.
Please read the previous posts in this series for more related information.

Some things to consider when the slider template is to be built:

  • Modify the structure of ContentQueryMain can affect all other templates in ItemStyle, when you do this you need therefore to customize a copy of ContentQueryMain. In this example were just adding a class name so you don’t need to do that now.
  • Alternative to customize the xsl files you can create modul with Visual Studio, a feature that copies your customized files to style library including a modified version of the CQWP to the web part gallery.

Let´s go!

Download the jQuery plugin called easySlider by CSS globe. Make a reference in your custom master page to this easySlider. Do also reference to latest jQuery in your custom master page.

<script type="text/javascript" src="/Style Library/Scripts/jquery-1.6.min.js"></script>
<script type="text/javascript" src="/Style Library/Scripts/easySlider1.7.js"></script>

Paste following script in the head section of your master page.

<script type="text/javascript">
$(document).ready(function(){	
	
	$(".slider").easySlider({
		auto: true,
		continuous: true,
		nextId: "slider1next",
		prevId: "slider1prev",
		prevText: 		'',
		nextText: 		'',
		speed: 	1000, // 800
		pause:	3000, // 2000
	});
	
});	
</script>

Check out ItemStyle.xsl. Paste following templates into ItemStyle. This example contains two templates, the first one is for limit number of words for the header and the next is the actual sliding template. Publish the file when you’re done.

<!-- 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> 

<!-- Sliding News Template -->
<xsl:template name="SlidingNews" match="Row[@Style='SlidingNews']" 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="@Created" />
	</xsl:variable>
	<xsl:variable name="LinkTarget">
		<xsl:if test="@OpenInNewWindow = 'True'" >_blank</xsl:if>
	</xsl:variable>
	
	<div class="SlidingNewsWrap CQWP-Content">		
		<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> 
	
</xsl:template>

If you want the date to be formatted you can use for example this xsl:value-of select=”ddwrt:FormatDateTime(string(@Created) ,1033 ,’dd MMMM’)” instead of xsl:value-of select=”@Created”, if you use the ddwrt function you have to include xmlns:ddwrt=”http://schemas.microsoft.com/WebParts/v2/DataView/runtime&#8221; in the XSL:Stylesheet defintion in the top of the Itemstyle file.

Check out ContentQueryMain.xsl. Open the file and locate the div with the class name cbq-layout-main. Add the class slider after the cbq-layout-main class.
Don’t forget a blank space between. Publish the file when you’re done.

<div id="{concat('cbqwp', $ClientId)}" class="cbq-layout-main slider">

Here goes the CSS

/* --- QCWP Slider --- */
.CQWP-Content {
width:390px; height:22px;
padding:7px 0px 0px 10px;
font-family:Cambria, Georgia;
font-size:11px;
border-style:none none solid none;
border-width:1px;
border-color:#e5e5e5;  
}
.CQWP-Content a:link, .CQWP-Content a:visited {
color:green;
}
.slider ul, .slider li, .slider li{ 
width:900px;
overflow:hidden;
}

Stay in tune for the next post in this series, I will wrote about how to cycle images from a image library this time. Please drop a comment if you got any questions, Christian

Display News pages with CQWP – Part II — June 10, 2011

Display News pages with CQWP – Part II

In my previous blog (Part 1) I wrote about the Content By Query Web Part (CQWP) and the post showed how to create a custom template in ItemStyle.xsl and how to use CSS for styles. This blog (Part 2) shows how to display the latest 5 news per type of news at the start page and how to extend the web part with jQuery in order to separate different types of news with tabs. In the next part (Part III) I plan to write about how to create a jQuery news roller that loops the news one by one for the CQWP.

Click at the image below to activate the video:

This blog do 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 for the fun stuff which means ItemStyle.xsl, CSS and jQuery. Please read the previous post for more information about options for created date, the count word function and the footer.

Some things to consider when the format is to be built:

  • A site column associated to the content type for the page layout
  • 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, with tabs for different types of news
  • Footer, with the link More news

Let’s go!

In this example I have two types of news pages, Internal and External. I added a new site column I called ‘NewsCategory’, a choice type column to the Article Page content type. Finally I added this as a content field into the page layout I used for my news pages in the site collection.

When this is done and you have created some example news and it’s time to open up Itemstyle.xsl and create a new template. You will find the complete templates in the end of this blog. Let’s start with how to create the header with the tabs into the template. I used UL and LI for this.

<!-- First row -->
<xsl:variable name="CurPosition" select="count(./preceding-sibling::*)" />
<xsl:if test="count(preceding-sibling::*)=0"> 
<ul class="CQWP-Header tabs">	
	<li id="NewsLink1"><a href="#">Internal</a></li>
	<li id="NewsLink2"><a href="#">External</a></li>
</ul>
</xsl:if>

We also need to create output for dates and titles. I have separated internal news and external news in two DIVs with different class names, Internal and External. This will be useful when we need to create click events in jQuery for the tabs.

<!-- Internal -->
<xsl:if test="@NewsCategory = 'Internal'">
<div class="Internal CQWP-Content">
		<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> 
</xsl:if>
<!-- External -->
<xsl:if test="@NewsCategory = 'External'">
<div class="External CQWP-Content">
		<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> 
</xsl:if>

After this it´s time to create functions for the tabs. When the page loads, only Internal news should be displayed, then we need to take care of the click functions for the tabs. In this example I have used FadeTo as effect for the click. Another challenge is to create a background color for an selected tab, as you may know CSS got no pseudo element for ‘selected’, so in this example I have done this with jQuery by adding a class to an element that have been clicked.

Insert this snippet into your custom master page, don´t forget to include a reference to latest jQuery as well.

<script type="text/javascript">
$(document).ready(function() {
 
 // Show and Hide content
  	$(".Internal").show();
  	$(".External").hide();	
  
 // On Click Event first link
 	$('#NewsLink1').click(function() {
    		$('.Internal').fadeTo("normal",0.95);
    		$('.External').hide();
    	return false; 
  	});
 // On Click Event second link
 	$('#NewsLink2').click(function() {
    		$('.External').fadeTo("normal",0.95);
    		$('.Internal').hide();
    	return false; 
  	});

  // Activate the first tab and add the active class + remove & add the active class
	$("ul.tabs li:first").addClass("active").show(); 
	$("ul.tabs li").click(function() {
		$("ul.tabs li").removeClass("active"); 
		$(this).addClass("active"); 
	});

});
</script>

After this it´s time to set the styles with CSS. Copy these classes and paste them in your custom CSS file attached to the master page.

.CQWP-Header, .CQWP-Archive  { 
color:#333;
font-family:Cambria, Georgia;
border-width:1px;
border-color:#bdbdbd;
}
.CQWP-Header {
width:397px;
height:25px;
padding-top:0px;	
padding-bottom:0px;
padding-left:3px;
font-size:12px;
font-weight:bold;  
border-style:solid none dotted none;
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 {
width:390px;
height:20px;
margin-top:-1px;		
padding-top:5px;
padding-right:10px;	
padding-bottom:0px;
font-size:11px;
text-align:right;
border-style:dotted none solid none;
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-Content {
width:390px;
height:22px;
padding-top:7px;	
padding-bottom:0px;
padding-left:10px;
font-family:Cambria, Georgia;
font-size:11px;
color:#333;
border-style:none none solid none;
border-width:1px;
border-color:#e5e5e5;  
background-color:#fff;  
}
.CQWP-Content a:link, .CQWP-Content a:visited {
padding-left:25px;color:green;
}
#NewsLink1	{float:left;margin-right:6px;list-style: none;}
#NewsLink2	{list-style: none;}

ul.tabs {
list-style: none;
border: 1px solid #fff;
}
ul.tabs li {
float:left;
margin:0;
padding:0;
height:24px;
line-height:24px;
border-top:1px solid #e3e3e3;
overflow:hidden;
}
ul.tabs li a {
text-decoration: none;
color:#000;
display:block;
padding:0px 10px 5px 10px;
border-top:1px solid #e3e3e3;
outline: none;
}
ul.tabs li a:hover {
background:#ccc;
border-top:1px solid #e3e3e3;
}	
ul.tabs li.active, ul.tabs li.active a:hover   {
background:#fff;
border-top:1px solid #e3e3e3;
}

Let’ s put this all together, here’s the complete templates for you:

<!-- Begin the 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>

<!-- Begin NewsCategory Template -->
<xsl:template name="NewsCategory" match="Row[@Style='NewsCategory']" 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 ,'dd 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"> 
<ul class="CQWP-Header tabs">	
	<li id="NewsLink1"><a href="#">Internal</a></li>
	<li id="NewsLink2"><a href="#">External</a></li>
</ul>
</xsl:if>
<!-- Internal -->
<xsl:if test="@NewsCategory = 'Internal'">
<div class="Internal CQWP-Content">
		<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> 
</xsl:if>
<!-- External -->
<xsl:if test="@NewsCategory = 'External'">
<div class="External CQWP-Content">
		<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> 
</xsl:if>
<!-- 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>

Stay tune for part III and please drop a comment if you got any questions, Christian

Display News pages with CQWP – Part I — May 28, 2011

Display News pages with CQWP – Part I

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!