One of the really cool features of the Content Query Web Part (CQWP) is the ability to do dynamic filtering.

  • The two options for dynamic filtering include:
    • PageFieldValue
      • Token allows specify field on page layout and dynamically replaces value of filter with current page’s field value
    • PageQueryString
      • Use URL query string for a value to use in Query

In this post, I will demonstrate how to work with the PageQueryString value and then in a part two blog post I will demonstrate how to work with the PageFieldValue. As a speaker of tech conferences, I will use speakers as my demo content and base it off a custom list. The solution I am providing is something I have implemented for various types of content for customers.

Jacksonville University Example

A real world example of using the CQWP with dynamic filtering would be what I implemented for Jacksonville University’s public facing website (www.ju.edu).

The screenshot below is a partial screenshot of the Employment Opportunities publishing page. The page has a CQWP that is configured to pull the job opps from custom lists and uses custom XSL templates to render the items and provide the link to each item which includes the query string parameter in the URL.

juedurollup

The next screenshot is the Opportunity Details page that has a single CQWP configured to use my custom template and is filtered based on the PageQueryString value. The parameter used is JobID which is populated with the list item’s ID.

http://www.ju.edu/humanresources/Pages/AdminOppDetails.aspx?JobID=112

juedurollup2

Implementation

This solution will work in both O365 and on-prem. For this demo, I am using an O365 Team Site that has the publishing features activated and contains 4 subsites (Australia/New Zealand, Canada, Europe/UK, & US). I created a Speaker content type, site columns, created a list template, and then created a speakers list in each of my subsites as detailed in Using Custom Content Types with Lists.

The Global home page will rollup all the speakers from the subsites and each subsite’s home page displays the speakers based on the subsite.

XSL Style Sheet Templates

I created two different XSL style sheet templates in my DemoItemStyle.xsl file. To learn more about using the CQWP with custom XSL style sheets please see Content Query Web Part – Part One: Using Custom XSL Style Sheets.

Speaker Rollup Pages

14rollup

Global home page rollup

On the home page of the site and the home page of all the subsites, I added a CQWP and configured it to use my SpeakerRollup XSL style sheet template. I took a copy of the existing default (Image left) template and modified it removing what I didn’t want and added new markup.

Line 3: has the variable speakerUrl which is linking to my SpeakerBio.aspx page.
Line 41 & Line 53: build the href to the page appending the querystring value Speaker to equal the speakername variable ?Speaker={$speakername}

<xsl:template name="SpeakerRollup" match="Row[@Style='SpeakerRollup']" mode="itemstyle">
<xsl:variable name="speakername" select="@Title" />
<xsl:variable name="speakerUrl" select="'/sites/demo/Pages/SpeakerBio.aspx'" />
    <xsl:variable name="SafeImageUrl">
        <xsl:call-template name="OuterTemplate.GetSafeStaticUrl">
            <xsl:with-param name="UrlColumnName" select="'ImageUrl'"/>
        </xsl:call-template>
    </xsl:variable>
    <div class="speaker-wrapper">
      <div class="speaker-image item">
          <xsl:if test="string-length($SafeImageUrl) != 0">
              <div class="image-area-top">
                  <a href="{$speakerUrl}?Speaker={$speakername}">
                    <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}">
                      <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>
              </div>
          </xsl:if>
          <div class="speaker-rollup">
              <xsl:call-template name="OuterTemplate.CallPresenceStatusIconTemplate"/>
              <a href="{$speakerUrl}?Speaker={$speakername}">
                <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>
              </a>
              <a href="{$speakerUrl}?Speaker={$speakername}">
                <div class="speaker-link"><xsl:value-of select="$speakername"/></div>
              </a>
          </div>
      </div>
    </div>
</xsl:template>

Global Home Page CQWP Configuration

16rollup

Querying all sites in a site collection.

17rollup

Show items of the Speaker content type.

18rollup

Filtering to only show where Speaker Active = Yes

19rollup

For the global home page, I’m grouping by <Site> in ascending order and sorting items by Title in ascending order.

20rollup

I’m using Large text for the group style (header) and SpeakerRollup for the Item style.

Subsite Home Page CQWP Configurations

15rollup

Europe/UK subsite home page rollup

For the subsite home pages, I used the same settings as the Global except the following.

21rollup

Queried to show items from the subsite level.

22rollup

Did not group items at the subsite level.

Speaker Bio Page

The Speaker Bio page (SpeakerBio.aspx) is a publishing page that I created at the root site collection and added a CQWP on the page. I created an XSL style sheet template called SpeakerBio and configured the CQWP to use this template and and filter based on the PageQueryString value.

When I click on a speaker, the URL will contain the querystring value for other speaker. For example, if I click on Wictor his URL would be the following:

29rollup

https://spglobal.sharepoint.com/sites/demo/Pages/SpeakerBio.aspx?Speaker=Wictor Wilen

23rollup

The template contains logic to show the MVP icon if the speaker has been flagged as an MVP in the list. There’s also logic to show the Blog site, LinkedIn, and Twitter urls based on the value set for each speaker. Wictor does not have a LinkedIn value set therefore only his Blog site and Twitter links are showing up.

XSL style sheet template (SpeakerBio)

 <xsl:template name="SpeakerBio" match="Row[@Style='SpeakerBio']" mode="itemstyle">
   <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="speakerblogsite" select="@BlogSite" />
 <xsl:variable name="speakerlinkedin" select="@LinkedIn" />
 <xsl:variable name="speakertwitter" select="@Twitter" />
 <xsl:variable name="speakername" select="@Title" />		
 <div class="speakerbiowrapper">
       <div class="speakerbio-image image-area-left"> 
             <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="{$speakername}">
               <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>
       </div>
   <div class="speaker-name">
     <xsl:value-of select="$speakername" />
     <xsl:if test="@MVP = 'True'">
               <span class="speaker-mvp">
                	<img src="/sites/demo/SiteAssets/Images/mvp.jpg" />
               </span>
     </xsl:if>
   </div>
   <div class="speaker-bio">
     <xsl:value-of select="@SpeakerBio" disable-output-escaping="yes" />
   </div>
   <div class="speaker-social">
     <xsl:if test="$speakerblogsite != ''">
               <div class="speaker-blog">
                	<b>Blog:</b><xsl:text> </xsl:text> <a href="{$speakerblogsite}"><xsl:value-of select="substring-after($speakerblogsite,',')"/></a>
               </div>
			</xsl:if>			
     <xsl:if test="$speakerlinkedin != ''">
              <div class="speaker-linkedin">
                	<b>LinkedIn:</b><xsl:text> </xsl:text> <a href="http://www.linkedin.com/in/{$speakerlinkedin}"><xsl:value-of select="$speakerlinkedin" /></a>
               </div>
			</xsl:if>			
     <xsl:if test="$speakertwitter != ''">
               <div class="speaker-twitter">
                	<b>Twitter:</b><xsl:text> </xsl:text> <a href="http://www.twitter.com/{$speakertwitter}"><xsl:text>@</xsl:text><xsl:value-of select="$speakertwitter" /></a>
               </div>                
			</xsl:if>			
   </div>
 </div>  
 </xsl:template>

30rollup

https://spglobal.sharepoint.com/sites/demo/Pages/SpeakerBio.aspx?Speaker=Christina Wheeler

24rollup

I’m not a MVP therefore the MVP icon does not show up next to my name. However, I have a value set for Blog, LinkedIn, and Twitter therefore all 3 are showing up.

Speaker Bio Page CQWP Configuration

The PageQueryString value is set on the Additional Filters options and since the URL string built in the SpeakerBio XSL style sheet template is ?Speaker the value set in the web part is [PageQueryString: Speaker].

26Rollup.png

Filtering off the PageQueryString value.

27rollup

Grouping and sorting are set to none and set the limit to 1 item.

28rollup

The Group style is set to Default (which his ignored when no Group by is set. The Item Style is set to use SpeakerBio and the fields to display are mapped to the display name of the fields used in the speaker lists.

Custom Styling

To display the rollup of speakers in columns I added custom CSS to the site.

div.speaker-wrapper {
  float:left;
  display:block;
}
.speaker-rollup, .speaker-image {
  text-align:center;
  border:0px red solid;
  width:300px;
}
.speaker-link {
  text-align:center;
  color:#0072c6;
  font-size: 14px;
  padding-bottom:15px;
}
a:visited {
  color:#0072c6;
}
.speaker-name {
  font-weight:bold;
  font-size:20px;
  padding-bottom:10px;
}
.speaker-bio {
  width:60%;
  font-size:14px;
}
.speakerbio-image {
  margin-right:15px;
  margin-bottom:5px;
}
.speaker-mvp {
  padding-left:10px;
}
.speaker-social {
  padding-top:10px;
}
.speaker-blog, .speaker-linkedin, .speaker-twitter {
  padding-top:10px;
}