Subclassing Content Query Web Part to override CAML query

The Content Query Web Part (CQWP) in MOSS is one of the popular out-of-the-box Publishing components which is used in content management sites, allowing you to get results from different sources.

But sometimes out of the box functionality is not enough to meet your requirements, and you need to customize CQWP. There are several resources describing how to do this – 1, 2, 3, 4

The most important features which are missed in CQWP are:

  • overriding query to use custom CAML
  • enabling web-part connections

To implement these functionality you need to subclass ContentByQueryWebPart class and override several methods.

Below I will describe what exactly need to be done to achieve desired behaviour.

Query Override

To have custom CAML query in your Web part you need to override CreateChildControls base method. You can find samples how to do this in the references above, but it”s done so implicitly, that you can easily spend hours to find out why you code doesn”t work and gives you different errors

The crucial part is in the way sending the query to the base class within overrided CreateChildControls

   1: protected override void CreateChildControls()
   2: {
   3:     this.QueryOverride = customQueryString;
   4:  
   5:     base.CreateChildControls();
   6:  
   7:     QueryOverride = string.Empty;
   8:  
   9:     CommonViewFields = string.Empty;
  10: }

First, you need to set your custom query to the “this.QueryOverride” to inform base class about query – line 3


Second, call base CreateChildControls() allowing your query being executed – line 5


Third and last – CLEAN query and view fields properties – line 7,9. Without this step you will get errors when open your Web Part in edit mode


Enabling Connection


The Content Query WP doesn”t accept any connections by default. You need to implement this too. Everything you need to to is just add ConnectionConsumer attribute. But trick in setting the right attribute which is used in Parameter dialog box, when you connect two web-parts.


   1: [ConnectionConsumer("Another WebPart", "IFilterValues", AllowsMultipleConnections = true)]
   2: public void SetConnectionInterface(IFilterValues filterProvider)
   3: {
   4:     if (filterProvider != null)
   5:     {
   6:         // save provider with values
   7:         providerValues = filterProvider.ParameterValues;
   8:         _filterProviders.Add(filterProvider);  // variable declaration is List<IFilterValues> _filterProviders = new List<IFilterValues>();
   9:         List<ConsumerParameter> parameters = new List<ConsumerParameter>();
  10:         
  11:         // add params
  12:         parameters.Add(new ConsumerParameter("param1",
  13:                 ConsumerParameterCapabilities.SupportsSingleValue |
  14:                 ConsumerParameterCapabilities.SupportsEmptyValue));
  15:         parameters.Add(new ConsumerParameter("param2",
  16:                 ConsumerParameterCapabilities.SupportsMultipleValues |
  17:                 ConsumerParameterCapabilities.SupportsAllValue |
  18:                 ConsumerParameterCapabilities.SupportsEmptyValue));
  19:  
  20:         filterProvider.SetConsumerParameters(new ReadOnlyCollection<ConsumerParameter>(parameters));
  21:  
  22:         }
  23: }

Method name can be any, but pay attention which provider you are using . WebPart providers send data via different provider interfaces, for example when you are working with Filtering web parts (like “Current User Filter” web part) then you need to “listen” your incoming connections via IFilterValues (thx Mutaz for this findings), for other WB it could be IWebPartField or IWebPartRow.


If you are expecting to get data from WP via unsupported provider you connection link will be dimmed


Practical Sample


@d2design kindly asked to provide practical sample, for example how to use current user as a keyword :)


Actually, it”s good example which shows how use both features I described.


To do this you need to have:


  1. ShareServices (SSP) with UserProfile, where current userName is stored
  2. “Current User Filter” WebPart, to get the userName from SSP. It returns you logged user name by default (not necessary actually, as well as SSP, because you could resolve you user name via standard asp.net User class when construct custom query)
  3. Enable WebPart connections as I described above. You need to use IFilterValue interface for “Current User Filter” and store your incoming user name in variable.
  4. Override query to include your userName in resulted custom query.

 


TIPS:


  1. When you are quering content which is not published and approved (like pages) you wont be able to see that content on the page in final view. You data will be available in page edit mode only, because they are treated as “draft” data.
  2. <OrderBy>CAML tag is not parsed by CQWP, you need to remove it from your query and set the related order field of CQWP class

 


Mirror: Subclassing Content Query Web Part to override CAML query

18 Comments »

  1. Haitao Said,

    December 12, 2008@ 4:34 pm      Reply

    Hi Micheal,

    I have created a subclass of CBQ web part and enable my CBQ”s subclass with the ability of using custom CAML. Recently, I want to use a custom CAML which filters all the list item of created by the current user. The CAML is like
    “”

    The problem is, when the page is in edit mode, the CAML works perfectly. But when I exit the edit mode, it cannot returieve any item out.

    What probably is the problem? Can you give me any help?

  2. laflour Said,

    December 16, 2008@ 12:14 pm      Reply

    Where is your root element for CAML?

  3. Haitao Said,

    December 24, 2008@ 7:50 am      Reply

    My root CAML element is . So, the complete CAML is: “”.

    I used this CAML in a SPQuery object. Then I tried to get items from a custom list with this SPQuery object. The fact is that this CAML works perfectly to filter all the items created by the current user in both edit mode and normal mode. So, I guess this problem is something related to CQWP only.

    I googled lots of pages and found one post saying that the caching infrastructure of the CQWP does not cache checked-out items of individual users and we disable cache in edit mode. By following his suggestion, I set the UseCache property of the CQWP to false. This way, it works :)

    Now, the problem is disabling the cache of CQWP because I don”t think this is the right way of doing things. Do you have any ideas on this? Is there is another way of writting the CAML so that we can work around the cache issue?

  4. laflour Said,

    December 24, 2008@ 11:16 am      Reply

    Nope. You need either to disable the cache or don”t use checkout items.

    Actually, it”s not only checkout items, but items which are not published yet, are out of the CQWP results in standard mode. We had such issue before :)

    I found this behavior very logical. Because checked items and not published items are private items, which are in work now, and you should not expose those data in most of cases.

    So, just disable cache or revise your approach for shown data in CQWP

  5. Tee Said,

    April 21, 2009@ 5:21 am      Reply

    Hi Micheal, I tried your code; however, I got the error when I compile it, here is the errors:
    The name ”providerValues” does not exist in the current context
    The name ”_filterProviders” does not exist in the current context
    It seems that it does not recognize these variables:
    providerValues
    _filterProviders
    Could you tell me what need to be fixed to eliminate these errors. Thanks for your help, here is my code:
    [skipped]

  6. laflour Said,

    April 21, 2009@ 5:33 am      Reply

    I posted the code-snippet, what means that you need to write the additional code.

    1) _filterProviders – please read a commends for the line 8. It says how to declare that variable

    2) providerValues – check what filterProvider.ParameterValues returs, and create such field

  7. sudha Said,

    June 21, 2009@ 12:07 pm      Reply

    i want to display the items in CQWP based on logged in user region.

    ex:

    if logged in user belongs to Middel East, i want to display Middle East related news articals to the user.

    user profile property holds the user Region and news artical has the field named as region.
    i want to map these and display the related news articles.

    Please help me out. thanks in advance.

  8. laflour Said,

    June 21, 2009@ 12:37 pm      Reply

    What are the requirements to the “region” detection?
    Do you want to detect it by IP – where user physically locates (but what to do in case of working over proxy or being in business trip),
    or you want to detect the region based on the user”s localizations settings?

    See these links
    1) http://www.west-wind.com/weblog/posts/334.aspx
    2) http://forums.asp.net/t/1052219.aspx

  9. sudha Said,

    June 21, 2009@ 12:57 pm      Reply

    Our company employees located in Geographical areas(USA,UK,INDIA).

    User profiles are dumped from AD to sharepoint.
    that has a property named as Region holds the User Region. Ex: USA,UK,INDIA

    I have news Document libray containing news articles and has one field named region, which indicated this artical related to that perticular region.
    Ex: USA,UK,INDIA

    i want to display the news articals based on loggedIN user region by pulling the region from user profile and map it with the field region of news artical located at news document librey.

    i want to customize the CQWP. plese help me out.

  10. sudha Said,

    June 21, 2009@ 3:28 pm      Reply

    Michael, i am really waiting for your answer. please help me out.

    thanks..

  11. laflour Said,

    June 21, 2009@ 3:39 pm      Reply

    Where exactly you stuck?
    You just read the user”s properties from AD and then construct you CAML query to include that country.

    See this sample http://www.helloitsliam.com/archive/2007/07/03/moss2007-–-redirect-using-profile-property-and-sharepoint-list.aspx

  12. sudha Said,

    June 21, 2009@ 4:15 pm      Reply

    First time i am working on CQWP customization.

    One more question i have,

    How to expose the new properties like grouping and sorting.. how to use the newly exposed property in CAML query..

    Not able to open this link, please send the full URL.

  13. laflour Said,

    June 21, 2009@ 9:34 pm      Reply

    http://tinyurl.com/nuwzf4

  14. Carl Said,

    November 24, 2009@ 5:13 pm      Reply

    Can you provide more details on filtering content in content query. I have enabled the connection on content query webpart and reading the providers field values and i want to filter the content of the content query webpart.

    Thanks in advance.

  15. laflour Said,

    November 24, 2009@ 9:31 pm      Reply

    The way to achieve this is to control the CAML query you overried in your subslassed CQWP

  16. Lee Dale Said,

    March 1, 2010@ 9:20 am      Reply

    Thanks alot for this posts, was doing the same thing but getting a Failed ViewState error and was spending ages trying to figure out what was wrong.

    Didn”t know you had to clear the QueryOverride and CommonViewFields properties.

    Thanks again, very helpful.

  17. Vishal Said,

    August 3, 2010@ 6:28 am      Reply

    Hi Micheal ,
    i have doubt .. whether i can query across site collection which is on different web application ?
    is there any property to set in extended CQWP

  18. Gurbet Sohbet Said,

    September 14, 2010@ 8:58 pm      Reply

    {

    this.QueryOverride = customQueryOverride:

    base.CreateChildControls();

    this.QueryOverride = string.Empty;

    this.CommonViewFields = string.Empty;

    }

    have problems here


RSS feed for comments on this post · TrackBack URI

Leave a Comment

*