Tuesday, April 8, 2008

Super Simple Feature for Hiding the Search Scope DropDown

So I need to hide the scope drop down that shows up next to the search box in my SharePoint MasterPage. 

Me:  "That's gotta be simple...  Just open up SharePoint designer and set some property on the search control." 

SharePoint: "Silly little boy!  Say hello to my little friend...err... DelegateControl!"

<Enter Jesse into world of SharePoint delegate controls>

Foolishness aside, the concept behind the DelegateControl is actually pretty useful, and because of it I was able to roll a very straightforward feature permitting me to hide the scope drop down in my MasterPage.  And I didn't even have to write one line of code... a true ode to developer sloth.

The feature....

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
   3: <Feature  Id="085B7E09-1D3E-41a7-9FEE-0C88F4394920"
   4:           Title="Custom Basic Search Control Feature"
   5:           Description="A feature for a search control that hides the Scope drop-down."
   6:           DefaultResourceFile="spscore"
   7:           Version="1.0.0.0"
   8:           Scope="WebApplication"
   9:           xmlns="http://schemas.microsoft.com/sharepoint/">
  10:     <ElementManifests>
  11:         <ElementManifest Location="searcharea.xml"/>
  12:     </ElementManifests>
  13: </Feature>

 


SearchArea.xml...



   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   3:     <Control 
   4:         Id="CustomSmallSearchInputBox" 
   5:         Sequence="1"
   6:         ControlClass="Microsoft.SharePoint.Portal.WebControls.SearchBoxEx" ControlAssembly="Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
   7:     <Property Name="GoImageUrl">/_layouts/images/gosearch.gif</Property>
   8:     <Property Name="GoImageUrlRTL">/_layouts/images/goRTL.gif</Property>
   9:     <Property Name="GoImageActiveUrl">/_layouts/images/gosearch.gif</Property>
  10:     <Property Name="GoImageActiveUrlRTL">/_layouts/images/goRTL.gif</Property>
  11:     <Property Name="DropDownMode">HideScopeDD</Property>
  12:     <Property Name="SearchResultPageURL">/_layouts/osssearchresults.aspx</Property>
  13:     <Property Name="ScopeDisplayGroupName"></Property>
  14:     <Property Name="FrameType">None</Property>
  15:     </Control>
  16: </Elements>

Two important things to note here... the Id of the control (CustomSmallSearchInputBox) and the DropDownMode (there are actually a bunch of different values you can set here).  So, install your feature, reference the CustomSmallSearchInputBox in your MasterPage and you're ret' to go:



   1: <asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server">
   2:       <SharePoint:DelegateControl runat="server" ControlId="CustomSmallSearchInputBox"/>
   3: </asp:ContentPlaceHolder>

Theoretically speaking, you could roll your feature with the same ID as the SharePoint feature (SmallSearchInputBox), set the Sequence attribute to something super low, install your feature, and have your customized version start showing up throughout the site.  I myself prefer to keep my changes more modular.

WebPart File is not Overwritten After First Solution Deployment

We setup a nightly build process for a recent client which automagically builds and deploys a SharePoint solution.  Soon after it was implemented we realized that our (one) WebPart wasn't updating in the WebPart gallery of our target site.  In other words, the developers were checking in changes to the .webpart file in TFS, that .webpart file was being deployed to the target site, the feature was being reactivated without issue, but the .webpart file in the WebPart Gallery List remained unchanged. 

I suspect that this may have something to do with how we were doing successive deployments (using deploy/install instead of upgrade?), but I figured I'd come up with a solution to the problem within the feature itself.  Pretty simple, actually.  I wrote a piece of feature receiver code that, on feature deactivating, programmatically removes the WebPart file from the Web Part Gallery.

The Feature:

   1: <Feature
   2:   Id="7CAB976B-018D-4ec8-B1A2-354ED7645795"
   3:   Title="AIS Sample Hello World WebPart"
   4:   Description="Basic WebPart example."
   5:   Hidden="FALSE"
   6:   Scope="Site"
   7:   ImageUrl="actionsettings.gif"
   8:   ReceiverAssembly="AIS.SharePoint.Utilities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a961f5844c0e1ceb"
   9:   ReceiverClass="AIS.SharePoint.Utilities.EventReceivers.AisWebPartFeaturReceiver"
  10:   xmlns="http://schemas.microsoft.com/sharepoint/">
  11:   <ElementManifests>
  12:     <ElementManifest Location="ProvisionedFiles.xml"/>
  13:     <ElementFile Location="HelloWorld.WebPart" />
  14:   </ElementManifests>
  15:     <Properties>
  16:         <Property Key="WebPartFileName" Value="HelloWorld.WebPart"/>
  17:     </Properties>  
  18: </Feature>


The Code:



   1: public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
   2: {
   3:     try
   4:     {
   5:         SPFeatureProperty prop = properties.Feature.Properties["WebPartFileName"];
   6:         if (prop != null)
   7:         {
   8:             string filenameUpper = prop.Value.ToUpper();
   9:             SPWeb web = ((SPSite)properties.Feature.Parent).OpenWeb();
  10:             SPList webPartList = web.Lists["Web Part Gallery"];
  11:             int id = -1;
  12:             foreach (SPItem item in webPartList.Items)
  13:             {
  14:                 if (item["Name"].ToString().ToUpper() == filenameUpper)
  15:                 {
  16:                     id = item.ID;
  17:                     break;
  18:                 }
  19:             }
  20:  
  21:             if (id >= 0)
  22:             {
  23:                 webPartList.Items.DeleteItemById(id);
  24:                 System.Diagnostics.EventLog.WriteEntry(this.ToString(), "Successfully removed webpart file from webpart gallery.", System.Diagnostics.EventLogEntryType.Information);
  25:             }
  26:         }
  27:  
  28:     }
  29:     catch (Exception e)
  30:     {
  31:         System.Diagnostics.EventLog.WriteEntry(this.ToString(), "An error occurred during feature deactivation." + Environment.NewLine + e.ToString(), System.Diagnostics.EventLogEntryType.Error);
  32:     }
  33:  
  34: }

I obviously could have been a little more fancy with how I queried the list, etc, but I think this gets the point across...  If anyone has any input on items in a list not updating, please feel free to chime in.  I've seen similar behavior when deploying stylesheets, masterpages, etc, as part of a feature.


Hope this helps somebody!


UPDATE:  Props to my buddy Oskar for pointing me to the Windows Live Writer Code Snippet Plug-In.  And, uh... Do people still say 'Props'???