Friday, December 5, 2008

Content Query Web Part (CQWP) and Audience feature – The Bug & work around

Content Query Web Part (CQWP), the much used web part in SharePoint supports feature called Audience. This isnt the audience feature supported by WebPart framework, where in a web part is shown or hidden, but CQWP can apply audience rules for content it displays. However the bug in the product is in the order these rules get applied.

CQWP & Audiencing – The Bug:
CQWP supports filtering data based on user's audience groups. E.g If you configure web part to fetch items from /mysite/pages list, it would apply audience rules and fetch you pages belonging to audience groups you are part of ( any one of it). This works in conjunction with other features of the web part

a) Query: User can select queries such as Title contains 'xyz'
b) Sort Order
c) Item Limit

However the order it applies is the cause of concern. As you would see in the image, audience rule gets set after ItemLimit rule is applied.









The impact is evident. An user can see anywhere between 0 to ItemLimit items based on his audience, though there could be lots of relevant items. Ideally the Audience rule should be set prior to Item Limit is applied.
We encountered this issue in our project and user experience was frustrating. Author configures a page to display Top 5 news items and actual news items displayed in CQWP varied anywhere between 0 to 5, though there were enough news items for all audience.
Solution:
Embarking on journey of workaround ( Sharepoint offers lot such opportunities) we considered following options
1) Can I influence this in the xslt? Answer is NO. The xslt gets called towards the end only for rendering purpose and this made sense
2) No other choice except to extend the web part to our needs. Below is the code snippet which helped to acheive this
public class AudienceContentByQueryWebPart: ContentByQueryWebPart
{
int userSelectedItemLimit ;
protected override void OnInit(EventArgs e)
{
this.ProcessDataDelegate += new ProcessData(ProcessData);
base.OnInit(e);
}
protected override void CreateChildControls()
{
//Store user selected ItemLimit and set it to -1. This is equivalent to user not setting any item limit
userSelectedItemLimit = this.ItemLimit;
this.ItemLimit = -1;

base.CreateChildControls();
}

// ProcessData is called after all rules are applied, including audience rule and before
// xslt is called.
private new DataTable ProcessData(DataTable dt)
{
// Reset the itemlimit to whatever user selected
this.ItemLimit = userSelectedItemLimit;


if (userSelectedItemLimit > 0)
{
//If number of rows greater than than the user given item limit
while (dt.Rows.Count > userSelectedItemLimit)
{
//Removing the rest of the rows
dt.Rows.RemoveAt(dt.Rows.Count - 1);
}
}
return dt;
}
}
Idea is to defer the ItemLimit rule after audiencing rule is applied. Steps were

1. Set ItemLimit = -1, forcing CQWP to avoid applying item limit. This ensures all results eligible for users, after applying audience rules are available.

2. CQWP allows you to hookup your event delegate called 'ProcessData' which gets called just before the xslt is applied. In ProcessData delegate, manually trim down the results to the user set ItemLimit.

Now all users would see the itemlimit number of items as long as enough data exists.
Ideally I would expect the webpart to apply audience rules along with filter queries. This would ensure faster performance. Hopefully Microsoft addresses this in future releases.

Thursday, December 4, 2008

Ouput Cache - Blob Cache fails when there are 10k+ items with custom permissions

Whenever you site has more that 10,000+ items which have custom permissions - meaning they dont inherit from parent site, your output caching would break. Following would be the log entry in event viewer. When blob cache is enabled images wont appear properly in the page and would exhibit random behavior.

[COMException (0x80004005): Cannot complete this action.Please try again.] Microsoft.SharePoint.Library.SPRequestInternalClass.GetAllAclsForCurrentSite(String bstrWebUrl, Int32 lMaxAcls) +0 Microsoft.SharePoint.Library.SPRequest.GetAllAclsForCurrentSite(String bstrWebUrl, Int32 lMaxAcls) +210[SPException: Cannot complete this action.Please try again.] Microsoft.SharePoint.Library.SPRequest.GetAllAclsForCurrentSite(String bstrWebUrl, Int32 lMaxAcls) +379 Microsoft.SharePoint.SPReusableAcl.GetAllReusableAclsForSite(SPSite site, Int32 maxAclsToReturn) +1185 Microsoft.SharePoint.SPSite.GetAllReusableAcls() +46 Microsoft.SharePoint.Publishing.<>c__DisplayClass3.b__0() +185 Microsoft.SharePoint.SPSecurity.CodeToRunElevatedWrapper(Object state) +73 Microsoft.SharePoint.<>c__DisplayClass4.b__2() +599 Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) +309 Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) +583 Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) +146 Microsoft.SharePoint.Publishing.BlobCache.EnsureAuthenticatedRights(Guid siteID) +593 Microsoft.SharePoint.Publishing.BlobCache.RewriteUrl(Object sender, EventArgs e) +3745 System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +177

Unfortunately no solution for this. Be watchful when working with item level permissions and avoid breaking the inheritance.

Saturday, November 15, 2008

Crawled Properties - Which property do they crawl?

If you've explored Search in Sharepoint, you would know that you can create 'Managed Property' which map to Crawled property. Have you ever wondered how the crawled property maps to real fields?

I still dont have the answer for that, but below post shares my painful experience due to lack of the answer. Read on.



As you know,
whenever you run full crawl, Sharepoint crawls entire content, identifies all properties, including your custom fields ( say 'BirthDate' ) and creates a crawled property for the field. All the OOB fields are mapped to crawled properties prefixed with 'ows_', while for custom fields, crawl property with the same name is added.



Our Requirement:
A simple one. Along with page title, bring in the page description in search results displayed. So our plan of action was

a) Create a Managed Property (mp_description) mapping to ows_description ( Description is the field name in Page content type)

b) Search Results Web Part: Configure the Select Columns xml to fetch mp_description.




  • So went about performing the steps and the results - Description was not showing up :-(

  • Explored the default xslt in Search Results web part and found that it isnt generic enough to pick up new select columns added. You need to manually modify it whenever you want to bring additional fields in results display. We had to correct these. Yet no success :-(

  • We thought may be content indexing wasnt good and so wiped off current index and reran the full crawl. Again no success.

  • Now we're sure frustrated. We picked up the MOSS Query Tool (an amazing tool for any developer customizing Search in MOSS) to see what's happening. All results came up with description field empty. Another surprise :-(

  • How about Google? Nope no postings on this.

At this point, it was clear either Search crawl isnt indexing description properly or Search results web part isnt doing its job. We ruled out the later as when experimented with other crawled properties, results were proper.


Now zero'ing on to search crawl, it struck us that "how can be sure that the crawled property we were referring is the one really pointing to Page description".Is there mapping in some config file maintained in 12 hive? A brute search doesnt yield anything.


Now people who have created custom content types would know that you can inherit from existing content types and for fields inherited you can change the display names in your content type. Going on this thought, we went to Page content type, clicked on 'Description' field and from the link got the GUID of the field. Went back to 12 ->Features ->fields, Publishing fields feature a brute search showed that the internal name of description field was indeed 'Comments' !!!!!!!!!!!!!!!! (Page content type derives from Item, which has this field).

Atlast. Remapped our managed property to ows_comments now and guess what, we got page description shown up in the results!!!

Great deception by MOSS, costing us quite number of days to resolve.