Grouping and Limiting Content Query Web Parts

原文链接:http://www.wagesworld.com/a2r/post/Grouping-and-Limiting-Content-Query-Web-Parts.aspx

 

The topic came up quite some time ago on how to limit the results of a Content Query Web Part (CQWP).  Basically when doing a site collection wide query, sometimes you don’t really want to see ALL the results.  It would be nice if you could cap the CQWP to only show the first 5, 10, or n number of results.

sample output

In the image to the right, I am showing up to the first three items in the results and then a link to see the remaining items.  Unfortunately there is no out of the box way to limit the CQWP in this fashion.  You have to dig into the XSL, and this gets a lot trickier than you’d initially suspect.

Since there is no grouping constructs in the CQWP XSL natively, we’ll need to group the results ourselves.  There are a variety of ways to do this but, the simplest method is to use the Muenchian Method to handle the heavy lifting.

Open the ContentQueryMain.xsl file, located in the root collection Style Library –> XSL Style Sheets.  Scroll down past the initial <xsl:stylesheet> element and the associated <xsl:param> elements (approx. line 28).  Add in the following key tag, which creates the item level grouping:

<xsl:key name="matchOnSite" match="Row" use="@ProjectProperty.Title" />

This little gem of XSL says create a custom key called matchOnSite, and make a group for all the Rows that have matching Title attributes.  Note that the attribute ProjectProperty.Title contains the value of your Group (as set in the CQWP webpart properties), and NOT the Title field of the Row.

If you look at the raw XML returned from the CQWP, you’ll see that one row is returned for each item and that it contains a list of attributes which describe the item and the group it belongs to:

 

<Row
     
ListId="{838AA5C9-F166-42AB-B09D-7247D7CBC13C}"
     WebId
="{E1E02C1C-E6B9-4E86-9847-1BD8D29AC6AC}"
     ID
="1"
     Title
=""
     FileRef
="sites/sandbox/Docs/Sales Documents/Test Doc.docx"
     _x007B_1d22ea11_x002D_1e32_x002D_424e_x002D_89ab_x002D_9fedbadb6ce1_x007D_="1"
     Modified
="2008-06-10 14:16:37"
     Author
="Wages, Jason L"
     Editor
="Wages, Jason L"
     Created
="2008-06-10 14:16:37"
     PublishingRollupImage
=""
     _Level
="1"
     Comments
=""
     ProjectProperty.Title
="Document Center"
     LinkUrl
="http://bhihnbweb02/sites/sandbox/Docs/Sales Documents/Test Doc.docx"
     PubDate
="Tue, 10 Jun 2008 14:16:37 GMT"
     ImageUrl
=""
     ImageUrlAltText
=""
     Description
=""
     Style
="LimitTo5"
     GroupStyle
="Band"
     __begincolumn
="True"
     __begingroup
="True">
</Row>

 

I encourage you to examine the raw XML output, as it makes understanding the XSL a lot easier.  An easy way to see the raw XML is to modify the main template in the ContentQueryMain.xsl file like this:

 

<xsl:template match="/">
    
<!-- <xsl:call-template name="OuterTemplate" /> -->
    
<textarea cols="50" rows="50"><xsl:copy-of select="."/></textarea>
</xsl:template>

 

This drops the XML into a text box where you can copy and paste it into your text editor of choice for further analysis.  Ok, so now we’ve got some grouping going on, next thing is to create a template that uses the XML keys to write out the groups.  Add the following template to the ContentQueryMain file:

<xsl:template name="CustomGroupTemplateSimple">
       
<xsl:for-each select="/dsQueryResponse/Rows/Row[count(. | key('matchOnSite', @ProjectProperty.Title)[1])=1]">
        
<b>Header #<xsl:value-of select="position()"/><xsl:value-of select="@ProjectProperty.Title" /> <br/></b>
        
<xsl:for-each select="key('matchOnSite', @ProjectProperty.Title)">
            Item #
<xsl:value-of select="position()" /><xsl:value-of select="@LinkUrl"/> <br/>
        
</xsl:for-each>
    
</xsl:for-each>
</xsl:template>

 

This template contains two loops, which make up the Muenchian Method.  The first for-each statement loops through all the rows where the count of the group header is equal to 1, effectively returning the first row in each group.  The group header is written out, and then the second for-each statement loops through all the Rows where the ProjectProperty.Title matches the current row, resulting in writing out all the items for that group.

Modify the main template in the ContentQueryMain file to call the custom template instead of the default OuterTemplate:

<xsl:template match="/">
    
<!-- <xsl:call-template name="OuterTemplate" /> -->
    
<xsl:call-template name="CustomGroupTemplateSimple"/>
</xsl:template>

 

base output

Save and check the file in and you should see the grouped data.  So, now that the grouping is in place we can add in the checks for limiting each group of results.

Within the second for-each loop we’ll add an <xsl:choose> statement to pick when the list item is less than or equal to 3, when it is equal to 4 (to write out our More link), and when it is greater than 4.

Also, since we are still in the core ContentQueryMain file we can also utilize the existing functions to write out the header and line item links.  This will allow us to still use the main WebPart properties to set the header and item styles.

 

<xsl:template name="CustomGroupTemplateSimple">
       
<xsl:for-each select="/dsQueryResponse/Rows/Row[count(. | key('matchOnSite', @ProjectProperty.Title)[1])=1]">
        
<xsl:call-template name="OuterTemplate.CallHeaderTemplate"/>
        
<xsl:for-each select="key('matchOnSite', @ProjectProperty.Title)">
        
<xsl:sort select="@Modified" order="descending"  />
            
<xsl:choose>
            
<xsl:when test="position()&lt;=3">
                
<xsl:call-template name="OuterTemplate.CallItemTemplate" />
            
</xsl:when>
            
<xsl:when test="position()=4">
                [ See All 
<xsl:value-of select="count(key('matchOnSite', @ProjectProperty.Title))"/> Documents ]
            
</xsl:when>
            
<xsl:otherwise>
            
</xsl:otherwise>
            
</xsl:choose>
        
</xsl:for-each>
    
</xsl:for-each>
</xsl:template>

 

Notice the calls to OuterTemplate.CallHeaderTemplate and simple output OuterTemplate.CallItemTemplate.  These are the built it routines within the CQWP and handle writing the header and item data.  Also, note the I’ve added in the <xsl:sort> element to the inner for-each loop.  A benefit of this technique is that you can sort within each group independantly, by date, by name, etc.  The <xsl:choose> construct writes out the item when the position is <=3 and our link text when it is equal to 4.

At this point you can format the [ See All ] link to point to another page that has a subsite scoped CQWP to display all the items.  Or to a custom ASPX page to show the results.

But since we’ve already got all the results, it would be nice to just display them right on the page using some dynamic HTML.  To do this we’ll use a couple of <div> tags: one to wrap the header and one to wrap the additional items.  When the user clicks on the header DIV, we’ll use some javascript to hide the header div and show the additional items DIV.

However, this does get tricky because we are embedding javascript in with XSL and at the same time doing it across a couple of loops.  Ultimately, the output will look like this:

 

<div id="HEADER_GroupName">
    
<span onclick="SHOW/HIDE">See All Documents</span>
</div>
<div id="LIST_GroupName" style="display:none;">
    Item 4
    Item 5
</div>

 

I’ll throw out the XSL code and then walk through it.

<xsl:template name="CustomGroupTemplateSimple">
       
<xsl:for-each select="/dsQueryResponse/Rows/Row[count(. | key('matchOnSite', @ProjectProperty.Title)[1])=1]">
        
<xsl:call-template name="OuterTemplate.CallHeaderTemplate"/>
        
<xsl:for-each select="key('matchOnSite', @ProjectProperty.Title)">
            
<xsl:sort select="@Modified" order="descending"  />
            
<xsl:if test="position()=4">
                
<div id="CustomGroupLink_{@ProjectProperty.Title}" style="font-size: 8pt; cursor:hand;">
                [
<span onclick="document.getElementById('CustomGroupData_'+'{@ProjectProperty.Title}').style.display='inline';document.getElementById('CustomGroupLink_'+'{@ProjectProperty.Title}').style.display='none';" style="background:#CAE4FF; padding: 0 2 2 2;">See all <xsl:value-of select="count(key('matchOnSite', @ProjectProperty.Title))"/> Documents</span><br/>
                
</div>
                
<xsl:text disable-output-escaping="yes"><![CDATA[<div style="display:none;" id="CustomGroupData_]]></xsl:text><xsl:value-of select="@ProjectProperty.Title"/><xsl:text disable-output-escaping="yes"><![CDATA[">]]></xsl:text>
            
</xsl:if>
            
<xsl:call-template name="OuterTemplate.CallItemTemplate">
                
<xsl:with-param name="CurPosition" select="position()" />
            
</xsl:call-template>
        
</xsl:for-each>
        
<xsl:if test="count(key('matchOnSite', @ProjectProperty.Title))&gt;3">
            
<xsl:text disable-output-escaping="yes"><![CDATA[</div>]]></xsl:text>
        
</xsl:if>
    
</xsl:for-each>
</xsl:template>

 

Inside the second loop, we check to see if we are at position 4.  If so, we write out the HEADER DIV, the [ See All ] link, and also the opening LIST DIV.  Since, XSL doesn’t like unmatched tags, you have to escape this text and put it in a CDATA block.

The the list item is written, and this loops over each group.  After each inner group looping, we check to see if the group had more than 3 items in it, in which case we need to close that LIST DIV.  Again, this needs to be escaped and wrapped in CDATA tags.  The sample code also, has a bit of style\formatting on the [ See All ] link which makes it a little more complex to read.

This method provides some additional functionality for group level sorting and dynamically showing result sets.  However, it does entail modifying the core rendering in the ContentQueryMain file.  Meaning, you can use this method or the default method, but not both.  There are ways to specify a custom XSL for each CQWP, but remember to grab all of the core OuterTemplate templates if you want to use the built in methods for writing out header and item links.

 

posted on 2009-05-30 19:50  王丹小筑  阅读(636)  评论(0)    收藏  举报

导航