Implementing custom folder statistics in SharePoint 2010 using c#

In one scenario this past week, I needed to display the total number of files and folders within any folder.  These numbers needed to be recursive – so if a document is three folders deep within the current folder, that still counts.   Same with folders.  I also needed to show a date representing the modified date of the MOST RECENT item or folder, again recursive to the last level.

I found a way to achieve this using a single CAML query and some fancy XSL parsing!

The folder and document counts would be displayed like this:

image

I’m already using other mechanisms to get lists of folders for display.  The XSL-driven styling shown is probably the topic of a future post.  The challenge was, how to get those counts and dates…

I started with an SPQuery.  The following recursively query pulls the basic information required, within the folder specified.


SPQuery query = new SPQuery();
query.Folder = folder;
query.Query = string.Empty;
query.RowLimit = uint.MaxValue;
query.ViewFields = "<FieldRef Name='Modified' Nullable='True'/><FieldRef Name='ContentTypeId' Nullable='True'/>";
query.ViewFieldsOnly = true;
query.ViewAttributes = "Scope='RecursiveAll'";
SPListItemCollection items = parentList.GetItems(query); 

string itemsXml = items.Xml; 

The trick here is the ViewAttributes property.  There are a few different ways to run an SPQuery, and this was the Scope that gave me the results I needed.  Note that an SPListItemCollection may be accessed as API objects OR XSL, and I chose to pull out my results using XSL.

Our query result is something like this (if you look closely, you’ll see that we have two folders and two documents here):


<rs:data ItemCount="4">
  <z:row ows_Modified="2012-05-31 12:34:01" ows_ContentTypeId="0x01200082B0C5829FE047A1BF58F68DA1DAB12500C7BCD99F82ACA340A0131D59CE62371B" ows__ModerationStatus="0" ows__Level="1" ows_ID="1" ows_Created_x0020_Date="1;#2012-05-31 12:34:01" ows_PermMask="0x7fffffffffffffff" ows_FileRef="1;#depts/it/Lists/CTADocuments/mygroup" />
  <z:row ows_Modified="2012-05-31 12:36:51" ows_ContentTypeId="0x01200082B0C5829FE047A1BF58F68DA1DAB12500C7BCD99F82ACA340A0131D59CE62371B" ows__ModerationStatus="0" ows__Level="1" ows_ID="2" ows_Created_x0020_Date="2;#2012-05-31 12:36:51" ows_PermMask="0x7fffffffffffffff" ows_FileRef="2;#depts/it/Lists/CTADocuments/mygroup/another one" />
  <z:row ows_Modified="2012-05-31 17:49:45" ows_ContentTypeId="0x010100EB5D8789C471B04E80E2A7481607C23B" ows__ModerationStatus="0" ows__Level="1" ows_ID="9" ows_Created_x0020_Date="9;#2012-05-31 17:49:45" ows_PermMask="0x7fffffffffffffff" ows_FileRef="9;#depts/it/Lists/CTADocuments/Just_the_Essentials_Publishing.master" />
  <z:row ows_Modified="2012-06-07 17:05:11" ows_ContentTypeId="0x010100EB5D8789C471B04E80E2A7481607C23B" ows__ModerationStatus="0" ows__Level="1" ows_ID="10" ows_Created_x0020_Date="10;#2012-06-07 17:05:11" ows_PermMask="0x7fffffffffffffff" ows_FileRef="10;#depts/it/Lists/CTADocuments/mygroup/another one/junk.txt" />
</rs:data> 

In this case it was more straight-forward to take the raw XSL and get what I needed using lambda expressions.  For example, to get document counts I applied an understanding of how content type id’s work.  All document content type id’s which inherit from Document (with id 0x0101), start with that number:


const string docCtID = "0x0101"; 
const string ctypeIDElement = "ows_ContentTypeId"; 
documentCount = rowElements
    .Where(dt => dt.Attribute(ctypeIDElement).Value.StartsWith(docCtID))
    .Count(); 

Putting it all together

Finally, we can wrap all of this logic into a single, fairly dense method.   And achieve our goal using only one query!


    const string docCtID = "0x0101";
    const string folderCtID = "0x0120";
    const string ctypeIDElement = "ows_ContentTypeId";
    const string modIDElement = "ows_Modified";
    internal static void GetFolderStats(this SPFolder folder, SPList parentList, out string modDate, out int documentCount, out int folderCount)
    {
        SPQuery query = new SPQuery();
        query.Folder = folder;
        query.Query = string.Empty;
        query.RowLimit = uint.MaxValue;
        query.ViewFields = "<FieldRef Name='Modified' Nullable='True'/><FieldRef Name='ContentTypeId' Nullable='True'/>";
        query.ViewFieldsOnly = true;
        query.ViewAttributes = "Scope='RecursiveAll'";
        SPListItemCollection items = parentList.GetItems(query); 

        //get query results and read them as xml. 
        XDocument doc;
        using (TextReader tr = new StringReader(items.Xml))
        {
            doc = XDocument.Load(tr);
        } 

        var rowElements = doc.Root.Element(XName.Get("data", "urn:schemas-microsoft-com:rowset")).Elements();
        documentCount = rowElements
            .Where(dt => dt.Attribute(ctypeIDElement).Value.StartsWith(docCtID))
            .Count();
        folderCount = rowElements
            .Where(dt => dt.Attribute(ctypeIDElement).Value.StartsWith(folderCtID))
            .Count(); 

        if (items.Count == 0)
        {
            modDate = folder.Item.Value(MODIFIED);
        }
        else
        {
            var dtAttList = rowElements
                .Attributes()
                .Where(dt => dt.Name == modIDElement)
                .ToList();
            dtAttList.Sort((a, b) => string.Compare(b.Value, a.Value));  //sort on values, most recent at top
            modDate = dtAttList[0].Value;
        }
    } 

2 comments

Leave a Reply to Michael Cancel reply

Your email address will not be published. Required fields are marked *