Displaying Last Modified Contents in SharePoint

SharePoint, as usual, offers lots of ways to do things. This time, I’m going to show how one can get a list of the last modified contents, in any SharePoint version and edition.

We’ll use our good friends DataFormWebPart and SPDataSource, together with some XSLT and some nice tricks. We will be able to specify the number of items to return through the query string, otherwise it will default to 5. I’m going to keep it simple, but you can certainly improve it.

We add a DataFormWebPart with an inline SPDataSource that uses DataSourceMode of  CrossList. This allows querying multiple lists at once. The actual query is specified in the SelectCommand property. In this case, I am specifying it as:

   1: <View><Webs Scope='Recursive'></Webs><Lists ServerTemplate='119'></Lists><RowLimit>{TopN}</RowLimit><Query><Where><And><Contains><FieldRef Name='FileLeafRef'/><Value Type='Text'>.aspx</Value></Contains><Eq><FieldRef Name='FSObjType'/><Value Type='Number'>0</Value></Eq></And></Where><OrderBy><ListProperty Name='Title' Ascending='false'/></OrderBy></Query><ViewFields><ListProperty Name='Title'/><ListProperty Name='ID'/><FieldRef Name='FSObjType'/><FieldRef Name='ID'/><FieldRef Name='Title'/><FieldRef Name='FileRef'/><FieldRef Name='FileDirRef'/><FieldRef Name='FileLeafRef'/><FieldRef Name='Editor'/><FieldRef Name='Modified'/></ViewFields></ViewFields></View>

What it means is:

  • Search recursively all subsites (Scope=’Recursive’);
  • Only search lists and document libraries of type Wiki Page Library (ServerTemplate=’119);
  • Limit the returned results to the value passed in parameter TopN (RowLimit);
  • Only return files (FSObjType = 0) containing .aspx in its name;
  • Return fields List Title (ListProperty Name=’Title’), List ID (ListProperty Name=’ID’), Object Type (FSObjType), ID, Title, File Location (FileRef), File Directory (FileDirRef), File Name (FileLeafRef), Last Modification User (Editor) and Last Modification Timestamp (Modified);
  • Order by the List Title.

The markup for the DataFormWebPart is as follows:

   1: <WebPartPages:DataFormWebPart runat="server" ChromeType="None" ID="recentContents">
   2:     <ParameterBindings>
   3:         <ParameterBinding Name="TopN" Location="QueryString(TopN)" DefaultValue="5"/>
   4:     </ParameterBindings>
   5:     <Xsl>
   6:         <xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal">
   7:           <xsl:output method="html" indent="no"/>
   8:               <xsl:param name="TopN"/>
   9:                           
  10:               <xsl:template match="/">
  11:                 <xsl:variable name="rows" select="/dsQueryResponse/Rows/Row"/>
  12:                 <table>
  13:                     <tr>
  14:                         <th>Name</th>
  15:                         <th>Last Modified At</th>
  16:                         <th>Last Modified By</th>
  17:                     </tr>
  18:                     <xsl:for-each select="$rows">
  19:                           <xsl:variable name="IsNewList" select="ddwrt:NameChanged(@ListProperty.Title, 0)"/>
  20:                           <xsl:if test="$IsNewList != ''">
  21:                               <tr>
  22:                                   <td colspan="3">
  23:                                       <a href="/{substring-after(@FileDirRef, ';#')}" title="{@ListProperty.Title}" target="_blank">
  24:                                           <xsl:attribute name="title">
  25:                                               <xsl:value-of select="@ListProperty.Title"/>
  26:                                           </xsl:attribute>
  27:                                           <xsl:value-of select="@ListProperty.Title"/>
  28:                                       </a>
  29:                                   </td>
  30:                               </tr>
  31:                             <xsl:call-template name="body">
  32:                                   <xsl:with-param name="rows" select="$rows[@ListProperty.Title = current()/@ListProperty.Title]"/>
  33:                             </xsl:call-template>
  34:                           </xsl:if>
  35:                     </xsl:for-each>
  36:                 </table>
  37:               </xsl:template>
  38:                                               
  39:               <xsl:template name="body">
  40:                 <xsl:param name="rows"/>
  41:                 <xsl:for-each select="$rows">
  42:                       <xsl:sort select="ddwrt:FormatDateTime(string(@Modified), 1033, 'yyyyMMdd HHmmss')" order="descending"/>
  43:                     <xsl:call-template name="item"/>
  44:                 </xsl:for-each>
  45:               </xsl:template>
  46:         
  47:               <xsl:template name="item">
  48:                 <tr>
  49:                     <td>
  50:                         <a href="/{substring-after(@FileRef, '#')}" title="{@Title}" target="_blank">
  51:                         <xsl:value-of select="substring-after(substring-before(@FileLeafRef, '.aspx'), '#')"/>
  52:                         </a>
  53:                     </td>
  54:                         <xsl:value-of select="ddwrt:FormatDate(string(@Modified), 1033, 5)"/>
  55:                     </td>
  56:                     <td>
  57:                         <a target="_blank" href="/_layouts/userdisp.aspx?ID={@Editor}">
  58:                                 <xsl:value-of select="substring-after(@Editor, ';#')"/>
  59:                         </a>
  60:                     </td>
  61:                 </tr>
  62:           </xsl:template>
  63:         </xsl:stylesheet>
  64:     </Xsl>
  65:     <DataSources>        
  66:         <SharePoint:SPDataSource runat="server" DataSourceMode="CrossList" SelectCommand="&lt;View><Webs Scope='Recursive'></Webs><Lists ServerTemplate='119'></Lists><RowLimit>{TopN}</RowLimit><Query><Where><And><Contains><FieldRef Name='FileLeafRef'/><Value Type='Text'>.aspx</Value></Contains><Eq><FieldRef Name='FSObjType'/><Value Type='Number'>0</Value></Eq></And></Where><OrderBy><ListProperty Name='Title'/></OrderBy></Query><ViewFields><ListProperty Name='Title'/><ListProperty Name='ID'/><FieldRef Name='FSObjType'/><FieldRef Name='ID'/><FieldRef Name='Title'/><FieldRef Name='FileRef'/><FieldRef Name='FileDirRef'/><FieldRef Name='FileLeafRef'/><FieldRef Name='Editor'/><FieldRef Name='Modified'/></ViewFields></ViewFields></View>" UseInternalName="True" UseServerDataFormat="True">
  67:             <SelectParameters>
  68:                 <WebPartPages:DataFormParameter Name="TopN" ParameterKey="TopN" PropertyName="ParameterValues" runat="server"/>
  69:             </SelectParameters>
  70:         </SharePoint:SPDataSource>
  71:     </DataSources>
  72: </WebPartPages:DataFormWebPart>

The ParameterBindings section includes an entry that gets a TopN parameter from the query string, and if one is not there, sets the default to 5. This parameter is passed to the SPDataSource through a DataFormParameter.

While we are iterating through the results, we call the ddwrt:NameChanged extension function upon the value of ListProperty.Title to check if we have changed to a new list, in which case, we add a new row with just its name. Inside each list, items are obtained where they match the list and sorted by the ddwrt:FormatDateTime function according to its last modification timestamp.

I think that’s it. Hope you find it useful.

                             

1 Comment

Add a Comment

As it will appear on the website

Not displayed

Your website