Skip Ribbon Commands
Skip to main content

Robin | zevenseas | SharePoint Blog

:

The zevenseas Community > Blogs > Robin | zevenseas | SharePoint Blog > Posts > Extending CQWP by doing a bottom-up aggregation
March 25
Extending CQWP by doing a bottom-up aggregation

You might wonder what bottom up aggregation means in this context.. well.. let me explain this by using the following example:

Let’s say you have a portal with the following structure

  • Main Portal
    • Departments
    • Products
      • Product A
        • Document Library
      • Product B
        • Document Library

and you want to aggregate all the information from the bottom (Product A) to the upper most level (Main Portal). But you don’t want any information from other products or even from Departments. Now by default the CQWP will aggregate everything under a given site/sitecollection.

So.. what to do? First we create our own version of the CQWP by inheriting from it like this:

public class BottomUpAggregationWebPart :
 Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart

To change the result of the CQWP we can manipulate the DataTable that is created by the CQWP by using a delegate like this:

protected override void OnInit(EventArgs e)
{
    this.ProcessDataDelegate += new ProcessData(modifyData);            
    base.OnInit(e);
}
private DataTable modifyData(DataTable dt)
{
    //do something 
    return dt;
}

For every row in the DataTable there is a column called “WebId’. This GUID is , as you might have guessed, the ID of the web where the item belongs to. So, all we need to do is filter out all the rows where the WebId is not part of the parent chain. To determine which web is part of the chain I’ve created the following recursive method:

private void DetermineDepth(SPWeb web, ref List<Guid> webIds)
{
    if (web != null)
    {
        if (web.Url != SPContext.Current.Site.RootWeb.Url)
        {
            webIds.Add(web.ID);
            DetermineDepth(web.ParentWeb, ref webIds);
        }
        else
        {
            webIds.Add(SPContext.Current.Site.RootWeb.ID);
        }
    }
}

Next is the filtering itself which looks this

private DataTable modifyData(DataTable dt)
{
    //Getting a List with all the SPWeb.ID's from the current SPWeb to the RootWeb of the SiteCollection
    List<Guid> webIds = null;
    webIds = new List<Guid>();
    DetermineDepth(SPContext.Current.Web, ref webIds);

    webIds.Sort();

    List<DataRow> rowsToDelete = new List<DataRow>();
    foreach (DataRow row in dt.Rows)
    {
        if (!webIds.Contains(new Guid(row["WebId"].ToString())))
        {
                rowsToDelete.Add(row);
        }
    }

    //Delete the rows
    foreach (DataRow row in rowsToDelete)
    {
        row.Delete();
    }

    return dt;
}

And that’s it! ;)

The only thing to keep in mind is that when you have set the item limit in the presentation settings, this item limit is set before you have the chance to modify the DataTable. So it’s best to to not have an item limit at all or a modified one. If you wish to modify the property in code, here’s the snippet which you can use:

this.ItemLimit = -1;

While I’m covering the subject of the CQWP,  I also want to share some experiences and assumptions that I had when dealing with it. (Please note that for the most time when I’m developing with SharePoint I really tend to stick to WSS ;)

One of the assumptions I had was that the CQWP aggregated based on a given content type OR list type/template in the properties of the webpart. However, as I found out, this is not the case.. it’s an AND operation..  so when setting these properties programmatically it took me a while to figure out which ones to use. But fortunately enough I managed ;)

  • ServerTemplate, the template ID of the list/document library. So 101 for Document Libraries for example
  • ContentTypeName, the name of the ContentType
  • ContentTypeBeginsWithId, the id of the Parent ContentType. Please note here, that in some cases you need to have the Parent of the Parent of the Child ContentType due the fact that when a ContentType is added to a list, it automatically is being marked as a child.

 

To conclude this post, here are some articles that helped get going:

Comments

Keith Dahlby

Great idea! Works nicely with my site collection-relative fix too. ;) Few comments...

1) Any particular reason why do you sort the webIds list? I would probably use a HashSet instead (O(1) add and lookup).

2) Don't like web.IsRootWeb?

3) To reduce memory use, you'll probably want to dispose the parent webs yourself. Could leverage ParentWebId to improve perf and avoid ParentWeb pitfalls:

webIds.Add(web.ID);
if (!web.IsRootWeb && web.ParentWebId != Guid.Empty) {
  using(SPWeb parentWeb = web.Site.AllWebs[web.ParentWebId]) {
    DetermineDepth(parentWeb, ref webIds);
  }
}
System Account on 25/03/2009 21:32

Robin

Hi Keith,

thanks! And yes, I was asking myself the same question about the DepthDetermining function it was leakproof and peformance wise a good option.

Though I'm only using the context.. that's what got me worried.. is it 'ok' to dispose those parentwebs?

The sorting shouldn't be in there anymore.. old code :) And indeed, I should learn the ways of elegant .NET programming like you do :)
System Account on 27/03/2009 04:47

Keith Dahlby

You're right to be worried - it's not OK to dispose your context's ParentWeb. If you have two controls that each wander up the tree with ParentWeb, the same webs will be reused and disposing them would be bad. However, not being able to dispose them means the memory will remain allocated for the life of the request. That is why I suggest using ParentWebId to get your own SPWeb that is safe to eagerly dispose. YMMV

OT: Wrapping comments in pre keeps code pretty, but is distracting for prose. :(
System Account on 27/03/2009 14:14

Robin

Fixed the comments, thanks to XSLT guru Waldek ;)

And thanks for the confirmation about the worrieness that I had! Will update my code using your sample.

System Account on 30/03/2009 08:46

umbilical cord blood banking

Thats great, I never knew before this blog.
System Account on 22/12/2009 21:54

Tires

Thanks for such a nice blog post....i was searching for something like that.
System Account on 19/01/2010 00:54
 

 Statistics

 
Views: 3514
Comments: 6
Tags:
Published:1517 Days Ago