Using the Content Query Web Part to Aggregate Blog Postings in SharePoint 2010

 

 

image

Recently I had a requirement to aggregate recent blog postings from a number of blog subsites in a SharePoint 2010 web application. Since we were dealing with sites within a single site collection, the content query web part seemed like an obvious candidate to solve the problem. After a bit of work, I was able to get it to display blog postings with a similar look and feel to postings as they are displayed within the blog sites (pictured to the left).

 

 

 

The Challenge

image

image

Without any special configuration (other than specifying which type of list to roll up), the CQWP can be easily made to display blog titles (left). For my needs however, this isn’t sufficient. I want to provide a preview of the blog post and also closely match the look and feel of the posts in a SharePoint 2010 blog site (right).

The first challenge is making the CQWP return the various pieces of specialized information that I need to display. For this example, I am only concerned with pulling back the post title, url, published date, author, and the body. The second challenge is creating the XSL required to transform those fields to match the look and feel of a blog post.

I did a bit of research and was able to find an excellent post by Henry Ong which helped with the first problem. It’s a good read and definitely sent me down the right track. Henry’s post is targeted towards SharePoint 2007 and many of the steps are no longer necessary in SharePoint 2010. Additionally, from reading the comments several people had issues getting the steps to work and also had issues when the blog posts contained formatting. The steps found here should hopefully address both of those problems.

 

Step 1: Adding the CQWP and configuring it via the GUIimage

The first step is simply to add the content query web part to a page and target the query to lists of type “Posts.” This makes the content query web part only pull in blog posts. Since I was already in the settings I also went ahead and made a few other configuration changes like changing the title and the number of items the query returns.

Click Ok to exit out of the settings.

Step 2: Export the CQWP

imageUnfortunately, the GUI doesn’t provide a means to specify additional fields for the CQWP to return, so in order to specify those I have to export the CQWP and modify the file directly. To export the CQWP, I click on the down arrow in the title of the web part and select Export. After the file has downloaded, it can be opened up with notepad (any text editor will do).

As discussed earlier, I need to edit the fields that get returned by the CQWP so that I can display them. To do this, I need to edit the CommonViewFields element within the .webpart file.

Find the line:

  <property name="CommonViewFields" type="string"/>

And change it to:

  <property name="CommonViewFields" type="string">
      ExternalUrl,URL;PublishingPageImage,Image;Body,Description;PublishingPageContent,RichHTML;PublishedDate,   DateTime;Author, User;
  </property>

This specifies the fields and their types that the CQWP should to return. I saved the .webpart file back to the desktop and upload it to the site’s web part gallery.

Step 3: Add a Custom Template to ItemStyles.xsl

ItemStyles.xsl is located in the Style Library document library and can be edited with SharePoint designer. This file contains the various item style templates used by the CQWP and I need to add a new style template to display the blog postings.

At the very top of the ItemStyles.xsl file, I added a new namespace entry (shown in red) because the templates require access to some date manipulation functions.

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

Next I need to add two templates to the .xsl file. The primary template is called BlogPostings which is loosely based off of the template provided in Henry’s post while a secondary template is used in order to strip out html tags from the body of the blogs. This is necessary because I am only showing a short preview of the blog body in the rollup which can lead to malformed HTML if tags end up getting chopped off in the process of creating the preview.

Find the first template entry and paste in this text right before it (approx line 15):

<xsl:template name="removeHtmlTags">
    <xsl:param name="html"/>
    <xsl:choose>
        <xsl:when test="contains($html, '&lt;')">
            <xsl:value-of select="substring-before($html, '&lt;')"/>
            <!-- Recurse through HTML -->
            <xsl:call-template name="removeHtmlTags">
                <xsl:with-param name="html" select="substring-after($html, '&gt;')"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$html"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>
<xsl:template name="BlogPostings" match="Row[@Style='BlogPostings']" 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: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="LinkTarget">
        <xsl:if test="@OpenInNewWindow = 'True'" >_blank</xsl:if>
    </xsl:variable>
    <xsl:variable name="PublishedMonthDay">
        <xsl:value-of select="ddwrt:FormatDateTime(string(@PublishedDate) ,1033 ,'MMMM dd')" />
    </xsl:variable>
    <xsl:variable name="PublishedLong">
        <xsl:value-of select="ddwrt:FormatDateTime(string(@PublishedDate) ,1033 ,'MM-dd-yyyy hh:mm tt')" />
    </xsl:variable>
    <xsl:variable name="Author">
        <xsl:call-template name="OuterTemplate.GetGroupName">
            <xsl:with-param name="GroupName" select="@Author"/>
            <xsl:with-param name="GroupType" select="'Name'"/>
        </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="pureText">
        <xsl:call-template name="removeHtmlTags">
            <xsl:with-param name="html" select="@Body" />
        </xsl:call-template>
    </xsl:variable>
    <xsl:if test="string-length($SafeImageUrl) != 0">
        <div class="image-area-left">
            <a href="{$SafeLinkUrl}" target="{$LinkTarget}">
                <img class="image" src="{$SafeImageUrl}" alt="{@ImageUrlAltText}" />
            </a>
        </div>
    </xsl:if>
    <xsl:if test="position()=1">
        <STYLE>
            .ms-postcalendardateboxtop
            {
            border-bottom: #003d66 1px solid;
            border-left: #00558d 1px solid;
            margin-top: 5px;
            width: 75px;
            background: url(/_layouts/images/calTopBkgd.png) #0072bc repeat-x left bottom;
            height: 13px;
            border-top: #00558d 1px solid;
            margin-right: 7px;
            border-right: #004572 1px solid;
            }
            .ms-postcalendardateboxbottom
            {
            border-bottom: #91959a 1px solid;
            text-align: center;
            border-left: #c1c7cd 1px solid;
            padding-bottom: 2px;
            min-height: 38px;
            padding-left: 0px;
            width: 75px;
            padding-right: 0px;
            display: table;
            font: 1.1em/1.3em verdana;
            max-width: 75px;
            word-wrap: break-word;
            background: url(/_layouts/images/calMainBkgd.png) #f1f1f1 repeat-x left bottom;
            table-layout: fixed;
            color: #65686b;
            border-top: #c1c7cd 1px hidden;
            margin-right: 7px;
            border-right: #9ea3a8 1px solid;
            word-spacing: 10em;
            padding-top: 4px;
            }

            .ms-leftblogdate
            {
            width: 75px;
            padding-right: 10px;
            }
            .ms-blogpostdatecol
            {
            vertical-align: top;
            }
        </STYLE>
    </xsl:if>
    <table xmlns:x="
http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
        <tr class="ms-blogpostdatecol" valign="top">
            <td class="ms-leftblogdate" style="margin-top:15px;">
                <div id="PostDateTopBox" class="ms-postcalendardateboxtop">
                </div>
                <div id="PostDateBottomBox" class="ms-postcalendardateboxbottom" style="overflow-x:hidden;position:relative;top:14px;left:-1px;">
                    <div>
                        <xsl:value-of select="$PublishedMonthDay" />
                    </div>
                </div>
            </td>
            <td class="ms-rightblogpost">
                <table dir="None" cellspacing="0" cellpadding="0" width="100%" border="0">
                    <tbody>
                        <tr>
                            <td class="ms-PostTitle" style="FONT-SIZE: 16pt;">
                                <a href="{$SafeLinkUrl}" target="{$LinkTarget}" title="{@LinkToolTip}">
                                    <xsl:value-of select="$DisplayTitle"/>
                                </a>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <div class="ms-PostFooter">
                    <span class="ms-postfootercolor">by </span>
                    <nobr>
                        <span>
                            <xsl:value-of select="$Author" />
                            <img border="0" height="1" width="3" src="/_layouts/images/blank.gif"/>
                            <img border='0' height='12' width='12' class='ms-imnImg' src='/_layouts/images/blank.gif' alt='' />
                        </span>
                    </nobr>
                    <span class="ms-postfootercolor">
                        on <xsl:value-of select="$PublishedLong"/>
                    </span>
                </div>
                <div class="ms-PostBody" style="margin-top:5px;">
                    <div class="description" style="padding-left:5px; color:#333333;">
                        <xsl:value-of select="substring($pureText, 0, 350)" disable-output-escaping="yes"/>
                        <a href="{$SafeLinkUrl}" target="{$LinkTarget}" title="Read More">...</a>
                    </div>
                </div>
                <div class="ms-PostFooter" style="height:10px;">
                </div>
            </td>
        </tr>
    </table>
</xsl:template>

Save ItemStyles.xsl and be sure to check it back in.

Step 4: Putting the CQWP on the Page and Setting the Item Styleimage

The final step is deleting the CQWP added to the page in step one and replacing it with the modified CQWP I uploaded to the web part gallery in step 2. Once the new web part has been added to the page, the item style must be specified to use the BlogPosting style. Edit the web part and expand the Presentation section. In the Item Style dropdown, there is a BlogPosting style. Select it and click OK to save the changes to the CQWP and then check in the page.

The CQWP should now look something like this:

image

 

 

 

 

 

 

 

6 comments:

NickOhz said...

THANK YOU!!! Works Excellent !

Hannah said...

Thanks for this, you saved me a lot of trial and error!

Rickey Whitworth said...

Awesome job! I was about to create this, you save me lots of work. Here is a huge time saving tip: In SharePoint 2010 you no longer have to save the web part out and edit the web part file to add custom view fields. Instead, you will notice when you are in web part properties, in the presentation area, you have text boxes for Author, PublishedDate, Body, etc. Just enter the field name here. So PublishedDate=Published; Author=Created By; Body=Body. This makes it easy to change which fields feed the different sections. So if you added a field to your blog list called "ShortDescription", you could set Body=ShortDescription; in the web part properties and that would appear instead of the contents of the body field.

Eric Zaluzec said...

Your the man! Other Content Query Web Parts to Aggregate Blog Postings did not work, yours works perfect.

Thanks!

Mike said...

This is really great, but I can't get back the comments for each of the blog posts? Do you have any idea what would keept he comments from showing up?

Eric Stepek said...

Awesome article. Thank you for sharing. One question. I have it mostly working except for the calendar setup off on the left of the title. The blue cap overflows the calendar date body by 10-15px. I verified my code against yours and they match. Any thoughts as to why the skew on the date cap? I can send you a screen shot if needed.