Tuesday 10 November 2015

Display multiple random list items in Dataview Web Part

I always like to configure our corporate Intranet to minimise the amount of admin work that needs to be done. One way to keep content looking fresh with minimum manual updating is to put all your text into a SharePoint list, then present it on the page using a Data View Web Part, with an added bit of XSLT scripting to show a random item from the list.

I didn't have much trouble finding a bit of scripting (via Google) to achieve this effect. I ran in to difficulties when I wanted to take this one step further and present several random items from a SharePoint list. However hard I Googled, I just couldn't turn up comprehensive, step-by-step instructions on how to display more than one random item.

The solution turned out to be a combination of two different methods, that I'd picked up in two different locations. As always, as I couldn't find the total solution by Googling alone, I'll explain how to display a number of random items from a list here.

First things first: set up a list. In my case, I wanted to display a set of four large buttons that users can click on to be taken to content in the sub-sites below the top level of the Intranet. So to have a reasonable selection of buttons to choose from, I specified at least eight for each landing page. These were stored in a document library.

As a document library is a just a flavour of list, I decided to keep things simple by using the library to double for the list and added my metadata in extra columns in the Document Library.

So I used the Title field to store the text equivalent (alt attribute) for the button image. Then I added a custom column to hold the destination URL. You could use a Hyperlink-type content type, but I just opted for a Single line of text, to keep things simple. I also added an extra column to hold the value for the tab page the buttons would be appearing on, set up as a Choice.

Then I uploaded all the button images and made sure the metadata was completed correctly.

This is what my Document library looks like when populated with my image buttons.
My next task was to create the DVWP on the tab page to display the four buttons. You almost certainly already know how to do this, so I won't waste your time here. But just in case you are struggling, here's a site that shows you How to insert a DVWP

The next step might not apply to you, but I was looking to manage a button set for each of my main tab pages on our Intranet. So once I had the Dataview Web Part on the page, I needed to filter out the irrelevant buttons. 

In SharePoint Designer, there's a wizard that allows you to Filter, sort and set the number of items displayed.
As I'd already set up a column to hold the name of the tab my button were going to appear on, I just had to click on Filter and set up a criterion for the tab I was working on, in this case "Community".

This allows me to filter out all the buttons except for those tagged as "Community".
Before I did any more work in the wizard, I thought I'd tidy up the HTML of the web part.

The raw DVWP tends to nest tables inside tables which can get messy and complex to work with. Some people prefer to replace the table elements with DIVs, and sometimes I might do that. But here, I thought I'd stick with a simplified table as I wanted to iterate across the row, rather than down the column. So first I removed the extra nested table.

Inside the "rowview" template, first take out the TR, TD and TABLE tags ...
As soon as you do that, the corresponding closing tags are highlighted by SharePoint Designer for you.

... SharePoint Designer then highlights the closing tags for you, making them easy to find and delete.
Now, because I'd knocked out the nested table and I wanted to iterate across the row, I needed to put an opening and closing <TR> tag inside the main table of the DVWP. Like this:

Wrap the dvt_1_body template in TR tags.
Next, I wanted to remove the column that holds the List Column names. You can do this quickly by right-clicking on the column in the Design view and selecting Delete, then Delete Columns from the drop-down menu.

Deleting the table column is very simple.
Next I needed to remove the <TR> tags that render the items from the list down the column. Doing this will cause the items to render across the row, instead. You might want to remove the legacy width attributes while you're at it.

So I removed the unneeded TR tags ...
Next, I needed to get all the "value-of"s into a single table cell and discard the remainder, like this.

Re-arranging the value-of calls and eliminating the unwanted TD tags.
My next job was to get the buttons to render across the row, and to have the ALT attribute inserted, and make each button a link to the relevant content I was highlighting. Rather than explain it, here's the final code ... you should be able to figure out what it's doing for yourself.

Here's the final code ...
It's starting to take shape, now:


Next, I needed to go back to the Wizard and set the Paging to Display All Items.

Select Display all items ...
At this point I was finally ready to apply the bits of XSLT scripting that would do the work of presenting four random items from the list, and would change each time a visitor came back to the page or refreshed.

I'd already previously implemented DVWPs where the script selected one item at random from the list to display. Suitable techniques for this are not difficult to find on the Internet via Google search. I've come across several ways to do this. The one I've most commonly used is:

<xsl:template name="dvt_1.body">
<xsl:param name="Rows"/>
<xsl:variable name="Random" select="ddwrt:Random(1,count($Rows))" />
<xsl:for-each select="$Rows[position()=$Random]">
<xsl:call-template name="dvt_1.rowview" />
</xsl:for-each>
</xsl:template>

... but this only displays one random item from the list. My challenge was to get the DVWP to display four random items. I figured this would be made more difficult because I didn't want to any of the items to be repeated, which would be a possibility with a routine that selected four items randomly, one after another.

But while digging around on Google, I came across several different ways of rendering a random item from a list and came to understand that I might have to combine two different methods to get the effect I wanted.

In the end I settled for a slightly different way of rendering a random item that turned up in several different SharePoint blogs, but this is the one I came back to

<xsl:for-each select="$Rows">
    <xsl:sort select="ddwrt:Random(1, $RowCount)" order="ascending"/>
    <xsl:call-template name="dvt_1.rowview" />
</xsl:for-each>

What this is doing is sorting the items in the list into a random order and rendering the first one. It's a slightly different approach to the one I was using, but it does get me a step closer to where I want to be.

I did get error messages with this saying that the variable wasn't defined, so I added in the line:

<xsl:variable name="RowCount" select="count($Rows)" />

... before the <xsl:for-each= ...> line and it seemed to work okay.

All I had to do next was figure out how to select the first four items in this randomised list and I'd be there. I found something on another blog that selected the first x number of items from a listso I figured I might be able to combine the two elements to get a randomised first four items ... I added the line:

<xsl:if test="position() &lt; 5">

This is saying: display the row if the position is less than five (ie, four, then) ... so the "for-each" section should now look like this:

<xsl:variable name="RowCount" select="count($Rows)" />
<xsl:for-each select="$Rows">
<xsl:sort select="ddwrt:Random(1, $RowCount)" order="ascending" />
<xsl:if test="position()&lt;5">
<xsl:call-template name="dvt_1.rowview" />
</xsl:if>
</xsl:for-each>

Now the buttons will change randomly each time the user lands on the page for the first time, or indeed refreshes the page. The final effect looks like this:

The images display randomly across the row.
Then it changes to this, when you refresh.

Hit refresh and another four images display.
You can probably adapt this method to suit the set of random items you want to display.

Hope this helps someone.