Current Articles | RSS Feed RSS Feed

Randomly Displayed Content in SharePoint

Share on Twitter Twitter | Share on Facebook Facebook | Submit to Digg digg it |  Add to delicious  delicious |  Share on LinkedIn LinkedIn 

By Justin Botchek

On the most recent SharePoint site that I've worked on, I was tasked with finding a way to randomly select pieces of content to display on the site homepage after each page refresh.  I've seen more complicated methods to implement this using javascript, but found the functionality can be easily accomplished using only two lines of xsl code within an existing SharePoint data view webpart.  These lines are:

<xsl:variable name="highlight" select="ddwrt:Random(1,count($Rows))" />

<xsl:for-each select="$Rows[position()=$highlight]">

For this example I will be manipulating code that has been automatically generated by SharePoint Designer to display a simple multiple item data view.  In a multiple item view, each item from the data source is collected via the Rows parameter.   By default the data view will loop through all rows until every item has been displayed, but in this case we only want it to randomly select one of those items for display.  Thankfully there is a bulit-in XSL function we can use to choose that random number for us.  SharePoint also tracks each row item with a value called ‘position()'; so in order to randomly pick a row we simply need to generate a random number between one and the total number of rows and then filter the results by that position value.

Normally, a generic multiple item data view generated by SharePoint Designer will look something like this:

To randomly select one item to display, we search through the data view code for where it cycles through each row.  It will be in a template block that looks like this:

<xsl:template name="dvt_1.body">                                                                                                                                                <xsl:param name="Rows"/>                                                                                                                                                   <xsl:for-each select="$Rows">                                                                                                                                                        <xsl:call-template name="dvt_1.rowview"/></xsl:for-each></xsl:template>

Directly above the line shown in bold, we will want to add in the line to select the random position value:    

<xsl:variable name="highlight" select="ddwrt:Random(1,count($Rows))" />         

This creates a variable ‘highlight' and assigns a random value to it using ‘ddwrt:Random' between 1 and the total number of rows ‘count($Rows))'.   Finally, we will want to modify the line in bold so that it looks like this:

<xsl:for-each select="$Rows[position()=$highlight]">

This applies a filter ‘[position()=$highlight]' to the xsl for-each statement so that it will only display rows with a position equal to the randomly selected value from the previous line.  After this change, save the page changes and reload the page with your data view.  The data view shown above would look something like this:

You will notice that the data view now only displays one entry, and after a couple page refreshes it will cycle randomly through the other items.  Real world applications of this could be to display a different random ‘fun fact' on a site home page whenever someone visits, or even to draw random user names out of a SharePoint ‘hat'.  

Update 3/31/2010:

I've been asked how to display a set of randomly selected items, instead of just one, so I thought I'd update my post with an example of how this could be done.  This may not be the most elegant or efficient way to approach the problem, but it worked for me when pulling a relatively small set of entries.

First I moved the code to generate the random number into a template so I could easily call it multiple times.  I placed this just before the dvt_1.body template definition:

<xsl:template name="randomNumber">

          <xsl:param name="Rows"/>

          <xsl:value-of select="ddwrt:Random(1,count($Rows))" />   

</xsl:template>

Next, I created variables within the body template to store position values for the number of rows I wanted displayed.  For each variable I called the randomNumber template showed above to get a value. 

<xsl:variable name="value1">

<xsl:call-template name="randomNumber">                                                                           <xsl:with-param name="Rows" select="$Rows"/>

</xsl:call-template>                                                          

</xsl:variable>

If I wanted five rows displayed, for example, I would have five sets of this code with the variables labeled  value1 through value5.

I then created another variable after the others to store my new set of rows, just to make it easier to reference.

<xsl:variable name="selectedRows" select="$Rows[position()=$value1 or position()=$value2]"/>

Here I'm pulling out two rows, but additional 'or' statements should be added for each value variable created previously.  This by itself would work great, except there is nothing to ensure that the values returned are all unique.  To work around the issue, I added an xsl choose statement and checked the count of the selectedRows variable.  If it was less then 2, in the above example, then not all the values were unique. In this case I would call the body template again until all unique values were returned.  I replaced the dvt_1.rowview template call with a choose statement that looked like this:

<xsl:choose>                                                                                                          

          <xsl:when test="count($selectedRows) &lt; 2">                                                                       <xsl:call-template name="dvt_1.body">

                              <xsl:with-param name="Rows" select="$Rows"/>

                    </xsl:call-template>

</xsl:when>                                                                                                  <xsl:otherwise>

<xsl:for-each select="$selectedRows">                                                                        <xsl:call-template name="dvt_1.rowview"/>

                    </xsl:for-each>

          </xsl:otherwise>

</xsl:choose>

This could be cumbersome to write or resource intensive when trying to pull large sets of unique values, but for a small set like 5 it worked great.  Note that you will get a stack overflow error if an infinite loop is created by checking for a selectedRows count greater than the number of values available or missing any of the or statements for each value in the row filter.

Comments

Great stuff! This is such a creative solution to the proposed problem. Keep up the fantastic posts. 
 
For more SharePoint conversation head to http://www.facebook.com/office 
 
Cheers, 
Andy 
MSFT Office Outreach Team
Posted @ Wednesday, October 28, 2009 7:27 PM by Andy
Great work 
 
How do i randomly display 'n' number of items eg n =5  
please can u help me out with this
Posted @ Wednesday, March 31, 2010 6:27 AM by aditya
Hi Aditya, thanks for your comment! I updated my post with one possible solution for this.
Posted @ Wednesday, March 31, 2010 1:48 PM by Justin Botchek
Hi, 
 
I did try the above but it did not display any items. 
What did u mean by replace the dvt_1.rowview template call with a choose statement. 
Did you replace this statement  
<xsl:call-template name="dvt_1.rowview"> 
<xsl:with-param name="ParentPath" select="$ParentPath" /> 
<xsl:with-param name="SrcPos" select="$SrcPos" /> 
</xsl:call-template> 
 
with the choose statement.
Posted @ Thursday, April 01, 2010 2:52 AM by Aditya
Hi Aditya. Yes, the original rowview template call, including the for-each $Rows statement that goes with it, should be replaced with a choose statement like what is shown above. The idea is that you display your subset of rows instead of the full set.  
 
 
 
You might want to try temporarily removing the filters between the [] brackets on the selectedRows variable declaration. That should display all the rows. If it does then check the random values to ensure they are in range. If it doesn't, then try adding the filter directly to the $Rows parameter in your original rowview template call. There should always be at least one row returned since the random numbers will be between 1 and the total number of rows (a valid 'position()').
Posted @ Thursday, April 01, 2010 11:06 AM by Justin Botchek
Post Comment
Name
 *
Email
 *
Website (optional)
Comment
 *

Allowed tags: <a> link, <b> bold, <i> italics

Subscribe by Email

Your email: