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" />

Tuesday, 1 March 2011

.NET Reflector replacement

All good stories must come to an end ... RedGate's .NET reflector tool is no exception to that rule. I've been addicted to that little gem from the day it was launched, allowing me to find workarounds for all kinds of SharePoint issues by digging into the SharePoint assemblies.
But alas, RedGate has decided to remove the free version & force existing users to buy their tool (even if you're only using an outdated, plugin-free version to browse assemblies ;)) ...

Now I'm not going to turn this post into a rant on the hypocrisy of suddenly charging people for a tool that has been freeware for years. I'm sure they had their reasons for this and their current price of $35 isn't that wallet-shattering either.  Considering the amount of options it gives you, it's still reasonably priced.

Still I'm a cheapskate and I didn't use all of Reflector's advanced options, so I went looking for a free replacement that allowed me to browse the code of existing .NET assemblies.
And behold, it looks like there's a promising alternative: ILSpy!

Overview of its features:
  • Assembly browsing
  • IL Disassembly
  • Decompilation to C#
  • Saving of resources
  • Search for types/methods/properties (substring)
  • Hyperlink-based type/method/property navigation
  • Base/Derived types navigation
  • Navigation history
  • BAML to XAML decompiler
  • Save Assembly as C# Project
What are you waiting for?  Head over to their site and download the latest version ;)

UPDATE
As mentioned by prantlf in the comments, there are even more alternatives:

All of these products are still in beta, so I don't think it's fair that I do a review on them. I've tested them all out and lets say they each have their pros and cons ...
But as they're all free products (for now), I can only suggest that you try them out yourselves and see which one you like best ;)