Thursday 17 March 2011

"This Site" search scope in an anonymous, publicly available site

It's been a while, but I finally encountered a little SharePoint 2007 issue that wasn't properly solved before on these internets :)

Scenario
The setup consists of a MOSS 2007 publishing site, that is publicly available to anonymous users. The site itself has activated the LockDown feature, preventing anonymous users from viewing SharePoint application & admin pages.
Furthermore, the publishing site has a Search Center set up, which is used to display the search results.
Pretty basic setup if you ask me.

Problem
As you would've guessed by the title, the problem lies in the This Site contextual search scope.

Scope dropdown on default search control

Searches on the custom All Sites scope were no problem, those were displayed in a nice results overview in the search center.
But if an anonymous user launched a query on that contextual This Site: ... scope, he would receive a login-prompt. Not quite the functionality you want to see on a public-facing site.

Cause
The cause has already been mentioned in this post. Apparently the default search control uses 2 different URLs to display its search results:
  • Custom scope queries (the ones you manage in site collection admin pages) are redirected to the search center's results pages.
  • Contextual scope queries (like This Site) are redirected to /_layouts/OSSSearchResults.aspx, regardless of the search center settings.

Unfortunately, thanks to the lockdown feature, the /_layouts/OSSSearchResults.aspx page will be blocked for anonymous users ... resulting in a login prompt when a user launches a query on a contextual scope.

Solution
I wouldn't write this post if there was an easy solution :)
Some blogs have already covered some possible workarounds to this, but I wasn't satisfied with their solutions: some involved modifying out-of-the-box SharePoint pages (blasphemy!) or writing a custom search control from scratch.

I went for a workaround based on .NET reflection.
I admit it, it's not that kosher, but at least this approach left most of the original search box's functionality untouched + it's a lot less work than writing a control from scratch.
The following code will subclass the default searchbox and overwrite the contextual scope's result-page URL with the value set in the public SearchResultPageURL property. This will ensure that any query (on both custom and contextual scope) will be redirected to the same page: the search center's results page.

/// 
/// Customized SearchBox that will override the contextual scope's result page
/// (/_layouts/OSSSearchResults.aspx?k=test&cs=This%20Site&...) with the URL 
/// defined in the SearchResultPageURL property.
/// 
public class SearchBox : SearchBoxEx
{
 protected override void CreateChildControls()
 {
  base.CreateChildControls();

  try
  {
   FieldInfo[] fields = typeof(SearchBoxEx).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
   FieldInfo ossSearchResultField = null;
   int i = 0;
   while (ossSearchResultField == null && i < fields.Length)
   {
    if (fields[i].Name.Equals("m_strOssSearchResultsUrl", StringComparison.InvariantCultureIgnoreCase))
    {
     ossSearchResultField = fields[i];
    }
    i++;
   }

   // Field found -> set its value to the search center's URL
   if (ossSearchResultField != null && !string.IsNullOrEmpty(SearchResultPageURL))
   {
    ossSearchResultField.SetValue(this, SearchResultPageURL);
   }
  }
  catch (Exception ex)
  {
   // Do some logging
  }
 }
}
Just add this control to your masterpage instead of the default search control and you'll be able to use contextual scopes in a public-facing, anonymous SharePoint publishing site without getting login-prompts.
Or just use it if you want to make sure that all search results are displayed using the same results page.
<CustomControls:SearchBox ID="SearchBox" 
  RegisterStyles="false" 
  TextBeforeDropDown="..." 
  TextBeforeTextBox="..."
  TextBoxWidth="100"
  UseSiteDefaults="true" 
  DropDownModeEx="ShowDD" 
  SuppressWebPartChrome="true"
  runat="server" />

No comments:

Post a Comment