Tag Archives: Search

I’m speaking at SharePoint Connections Amsterdam 2013

SharePoint Connections Amsterdam is now in its third year and has established itself as one of the key annual SharePoint conferences in Europe. Running across two days in November It has an awesome line-up of speakers covering a huge range of topics from IT Pro, Dev (and one track which aims to cover both), Business, End User and even an Office 365 track.

This year SharePoint Connections Amsterdam 2013 will take place at the Meervaart Theatre in Amsterdam on the 19th & 20th November 2013 and I have been honoured to be invited to be one of the speakers in the IT Pro / Dev track.

My session will be all about the new Search capabilities in SharePoint 2013, so if you want to know more about Result Sources, Result Types, Query Rules and Display Templates then this is the session for you!

I’m speaking on the Tuesday at 1:25pm in Room B (either side of sessions by Joel Oleson and Spencer Harbar so no pressure eh?) Edit  – Agenda Change .. I’m speaking on Tuesday at 4:30pm in Room B

For more information about the venue, hotels, the speakers, and (most importantly) the sessions and agenda head on over and grab yourself a ticket (before they all go!)

Sign up for the event here and use discount code SP333 for 10% off when you book!

Memory Leak in SharePoint 2013 (Preview) Search

Any of you who has setup their own SharePoint 2013 box (and cried at the Hardware requirements) will be aware of a process which is chewing up your RAM like nothing else

noderunner.exe

There will be four of these running (you can see them in Task Manager) and these basically represent the four major topology services for the FAST Search engine which now powers SharePoint 2013 search services.

The problem is the current implementation (the Preview aka “Beta” build) has a memory leak! This was confirmed by a TechNet blog post (https://blogs.technet.com/b/mpriem/archive/2012/09/05/sharepoint-2013-preview-hungry-search-service.aspx) who also described two potential workarounds to alleviate the stress that noderunner.exe puts on your system:

Jose Vigenor from MS beta support pointed to two options to contain these processes:

  1. Use Set-SPEnterpriseSearchService -PerformanceLevel Reduced to reduce the CPU impact the search service has on your test environment.
  2. Modify the C:\Program Files\Microsoft Office Servers\15.0\Search\Runtime\1.0\noderunner.exe.config so that it can only consume X amount of RAM.
    Change the value at <nodeRunnerSettings memoryLimitMegabytes=”0″ /> to any amount of RAM you like to contain the memory leak.

Be careful when you implement this though, Paul Hunt (aka @cimares) has his own blog post (which is where I found the link above by the way!) where he encountered some “Out of Memory” exceptions when this is configured a little too tightly!

Search Core Results Web Part with Dynamic Date and User Profile Tokens

If you just want the goodies then you can get them here:

The Big Fat Disclaimer – This has not been thoroughly tested for a production environment. I have also removed references in my snippets below to caching and error handling to try and keep it brief. The downloadable version uses both caching and error handling, but it is still really just a proof of concept and you should TEST it before you deploy it! I take no responsibility if your production servers blow up!

I must have seen this requirement dozen of times on different projects, having search results which either:

  • Filter using a User Profile Property of the current user
  • Filter using a dynamic date range (e.g. using the “TODAY” token)
  • Specify the Sort-By (which is normally restricted to either “Relevance” or “Modified Date”

The requirement for functionality of this nature come up extremely frequently on Intranet projects. For example:

“Show News Articles from the past 7 days which filter based on the user’s location”
“Show events coming up in the next 3 months”
“Show discussions / wiki entries / blog posts which include the current user’s Ask Me About values”

Example
image
FixedQuery used in the Web Part:
Author:[UPP-PreferredName] AND Write:[TODAY-180]..[TODAY] AND ContentType:Event

Well .. on my current client project these very requirements came up .. so this time I decided to knock together the basics of the web part in my spare time and then “donate” it to the project… and this post describes how I built it, what goes on under the hood, and also includes both the source code as well as a downloadable WSP package with the working Web Part in it.

Step 1 – Extending the Search Core Results Web Part
So .. to get us started, lets kick off by creating our actual Web Part. I am going to be extending the Search CoreResultsWebPart (link to MSDN).
This is easy enough to achieve by simply creating a new Web Part in Visual Studio and inheriting from the CoreResultsWebPart class. This will make sure our web part gets all of the functionality and properties that the normal Search Results web part does without any additional effort.

   1:  [ToolboxItemAttribute(false)]
   2:  public class ExtendedSearchWebPart : CoreResultsWebPart
   3:  {
   4:   
   5:  }

That is the easy bit …

Step 2 – Overriding the Query and SortOrder
Now the next bit to tackle is how to override the actual query that gets executed. Well the best place to do this is to override the ConfigureDataSourceProperties method. This method gets called before the query is actually executed against the Search engine itself.

You can then leverage the CoreResultsWebPart.DataSource property (which is of type CoreResultsDataSource). This is what allows all of the magic to happen.

   1:  protected override void ConfigureDataSourceProperties()
   2:  {
   3:      // only perform actions when we are trying to show search results
   4:      // i.e. not when you're in Design Mode
   5:      if (this.ShowSearchResults)
   6:      {
   7:          // call the base web part method
   8:          base.ConfigureDataSourceProperties();
   9:   
  10:          // get the data source object
  11:          CoreResultsDatasource dataSource = this.DataSource as CoreResultsDatasource;
  12:   
  13:          // override the query being executed
  14:          dataSource.Query = "Author:\"Martin Hatch\"";
  15:   
  16:          // remove the original sort order
  17:          dataSource.SortOrder.Clear();
  18:          dataSource.SortOrder.Add("Title", Microsoft.Office.Server.Search.Query.SortDirection.Ascending);
  19:      }
  20:  }

So lets talk through the code above.

First off we want to make sure we are only executing our custom code when we are actually trying to retrieve search results. This is a fail-safe block as some instances this will be false (such as when you are editing the web part or if you are viewing the “Design” view in SharePoint Designer). We also need to call the base method (as you typically would when overriding a method call!).

Then things get interesting. Line 11 has us create our “CoreResultsDataSource” object from the local “DataSource” property. This has two properties which we are modifying:

Query (line 14) – This allows us to change or completely override the query which is being executed. This will be the entire query including the Fixed Query, Appended Query and whatever the user typed into their search box (if you are using this on a Search Results page). In my example above I am simply overriding the result so that it just searches for items created by “Martin Hatch” (me!)

SortOrder (lines 17 and 18) – This allows us to override the sort order, allowing us to select ANY indexed Search Property you want (I expect excluding rich text fields of course!). In my example, I am sorting by Title in Ascending order.
This can then be easily extended to provide custom Web Part properties to allow the Sort functionality to be specified by the page editor.

Step 3 – Making it re-usable Part 1 – Dynamic Date ranges
 So now that we can override the query easily we can move on to adding some of the good stuff. I decided to go with a relatively simple Token Replacement function using a simple [TODAY] token to represent the current date:

  • [TODAY] (todays date)
  • [TODAY+7] (today plus 7 days)
  • [TODAY-7] (today minus 7 days)

So .. how do we code this in? Well .. I am quite lazy and don’t really get on with regular expressions (if you are reading this and you are a RegEx guru.. by all means download the source code, refactor it and send it back, cheers!).

So I started off by creating a bunch of class level constants which I would use to recognise the tokens that we are looking for above:

private const string TODAY_PLACEHOLDER = "[TODAY]";
private const string TODAY_ADD_STARTSTRING = "[TODAY+";
private const string TODAY_SUBTRACT_STARTSTRING = "[TODAY-";
private const string TOKEN_ENDSTRING = "]";

The following code can then be swapped out for our ConfigureDataSourceProperties method.

   1:  protected override void ConfigureDataSourceProperties()
   2:  {
   3:      // only perform actions when we are trying to show search results
   4:      // i.e. not when you're in Design Mode
   5:      if (this.ShowSearchResults)
   6:      {
   7:          // call the base web part method
   8:          base.ConfigureDataSourceProperties();
   9:   
  10:          // get the data source object
  11:          CoreResultsDatasource dataSource = this.DataSource as CoreResultsDatasource;
  12:   
  13:          // get the current Fixed Query value from the web part
  14:          string strQuery = this.FixedQuery;
  15:   
  16:          // swap out the exact "today" date
  17:          if (strQuery.IndexOf(TODAY_PLACEHOLDER) != -1)
  18:          {
  19:              strQuery = strQuery.Replace(TODAY_PLACEHOLDER, DateTime.UtcNow.ToShortDateString());
  20:          }
  21:   
  22:          // perform all of the "Add Days" calculations
  23:          while (strQuery.IndexOf(TODAY_ADD_STARTSTRING) != -1)
  24:          {
  25:              strQuery = CalculateQueryDates(strQuery, TODAY_ADD_STARTSTRING, true);
  26:          }
  27:   
  28:          // perform all of the "Remove Days" calculations
  29:          while (strQuery.IndexOf(TODAY_SUBTRACT_STARTSTRING) != -1)
  30:          {
  31:              strQuery = CalculateQueryDates(strQuery, TODAY_SUBTRACT_STARTSTRING, false);
  32:          }
  33:   
  34:          // swap out the Fixed Query for our Calculated Query
  35:          dataSource.Query = dataSource.Query.Replace(this.FixedQuery, strQuery);
  36:      }
  37:  }

This then calls the CalculateQueryDates support method which I put together:

   1:  private static string CalculateQueryDates(string strQuery, string startStringToLookFor, bool AddDays)
   2:  {
   3:      try
   4:      {
   5:          // get the index of the first time this string appears
   6:          int firstIndex = strQuery.IndexOf(startStringToLookFor);
   7:   
   8:          // get the text which appears BEFORE this bit
   9:          string startString = strQuery.Substring(0, firstIndex);
  10:   
  11:          // get the text which appears AFTER this bit
  12:          string trailingString = strQuery.Substring(firstIndex);
  13:          int endIndex = trailingString.IndexOf(TOKEN_ENDSTRING);
  14:          if (endIndex + 1 == trailingString.Length)
  15:          {
  16:              // there is nothing else after this
  17:              trailingString = "";
  18:          }
  19:          else
  20:          {
  21:              trailingString = trailingString.Substring(endIndex +1);
  22:          }
  23:   
  24:          // find the number of days
  25:          string strDays = strQuery.Substring(firstIndex + startStringToLookFor.Length);
  26:          strDays = strDays.Substring(0, strDays.IndexOf(TOKEN_ENDSTRING));
  27:          int days = int.Parse(strDays);
  28:   
  29:          // re-construct the query afterwards
  30:          if (AddDays)
  31:          {
  32:              strQuery = startString + DateTime.UtcNow.AddDays(days).ToShortDateString() + trailingString;
  33:          }
  34:          else
  35:          {
  36:              // subtract days
  37:              strQuery = startString + DateTime.UtcNow.AddDays(0 - days).ToShortDateString() + trailingString;
  38:          }
  39:   
  40:          return strQuery;
  41:      }
  42:      catch (FormatException ex)
  43:      {
  44:          throw new FormatException("The format of the [TODAY] string is invalid", ex);
  45:      }
  46:      catch (ArgumentNullException ex)
  47:      {
  48:          throw new FormatException("The format of the [TODAY] string is invalid. Could not convert the days value to an integer.", ex);
  49:      }
  50:  }

So you should be able to see we are using simple String.IndexOf() method calls to find out if our Tokens are present.

If they are then we simply calculate the DateTime value based on the static DateTime.UtcNow property and use String.Replace() methods to swap out these into our query text.

When we are using [TODAY+X] or [TODAY-X] we simply use DateTime.UtcNow.AddDays(X) or DateTime.UtcNow.AddDays(0-X) and use the same String.Replace() method.

The search syntax is exactly the same as it was previously, and the Keyword Syntax is very powerful.

Example: Using [TODAY] Token query syntax

Write:[TODAY] – this will return all items that were modified today
Write>[TODAY-7] – this will return all items that were modified in the past week
Write:[TODAY-14]..[TODAY-7] – this will return all items that were modified between 2 weeks ago and 1 week ago

So we already have a powerful and reusable search component .. but there is more!

Step 4 – Making it re-usable Part 2 – Dynamic User Profile Properties
The next one is to allow us to pull in User Profile Properties so that we can start doing searches based on the current user’s profile values.
For this we needed to create new replacable Tokens, for which I decided to use:

  • [UPP-{User Profile Property Internal Name}]
  • [UPP-PreferredName] (swaps out for the users name)
  • [UPP-SPS-Responsibility] (swaps out for their “Ask Me About” values)
  • etc ..

So .. we add another class level constant (same as we did for our DateTime tokens)

private const string USER_PROFILE_PROP_STARTSTRING = "[UPP-";

We can then use this in our code, in exactly the way we did before (using String.IndexOf(), String.SubString() and String.Replace() methods).

So we add the following additional code to our ConfigureDataSourceProperties method;

   1:  if (dataSource.Query.IndexOf(USER_PROFILE_PROP_STARTSTRING) != -1 &&
   2:      UserProfileManager.IsAvailable(SPServiceContext.Current))
   3:  {
   4:      string strQuery = dataSource.Query;
   5:   
   6:      while (strQuery.IndexOf(USER_PROFILE_PROP_STARTSTRING) != -1)
   7:      {
   8:          strQuery = ReplaceUserProfilePropertyTokens(strQuery);
   9:      }
  10:   
  11:      if (strQuery != dataSource.Query)
  12:      {
  13:          dataSource.Query = strQuery;
  14:      }
  15:  }

This uses the additional method call ReplaceUserProfilePropertyTokens which is shown below:

   1:  private static string ReplaceUserProfilePropertyTokens(string strQuery)
   2:  {
   3:      // retrieve the current user's Profile
   4:      UserProfileManager upm = new UserProfileManager(SPServiceContext.Current);
   5:      UserProfile profile = upm.GetUserProfile(false);
   6:   
   7:      if (profile == null)
   8:      {
   9:          throw new ApplicationException("The current user does not have a User Profile");
  10:      }
  11:   
  12:      // extract the user profile property name from the token
  13:      int startIndex = strQuery.IndexOf(USER_PROFILE_PROP_STARTSTRING);
  14:      string strPropertyName = strQuery.Substring(startIndex + USER_PROFILE_PROP_STARTSTRING.Length);
  15:      strPropertyName = strPropertyName.Substring(0, strPropertyName.IndexOf(TOKEN_ENDSTRING));
  16:   
  17:      string strToReplace = strQuery.Substring(startIndex);
  18:      strToReplace = strToReplace.Substring(0, strToReplace.IndexOf(TOKEN_ENDSTRING) + 1);
  19:   
  20:      try
  21:      {
  22:          // get the value
  23:          UserProfileValueCollection propertyValue = profile[strPropertyName];
  24:          string strValues = String.Empty;
  25:   
  26:          foreach (object propValue in propertyValue)
  27:          {
  28:              if (propValue.ToString().IndexOf(" ") == -1)
  29:              {
  30:                  strValues += propValue.ToString() + " OR ";
  31:              }
  32:              else
  33:              {
  34:                  strValues += "\"" + propValue.ToString() + "\" OR ";
  35:              }
  36:          }
  37:   
  38:          if (strValues.Length > 0)
  39:          {
  40:              strValues = strValues.Substring(0, strValues.Length - 4);
  41:          }
  42:   
  43:          // swap the value out in the query
  44:          strQuery = strQuery.Replace(strToReplace, strValues);
  45:   
  46:      }
  47:      catch (ArgumentException ex)
  48:      {
  49:          throw new FormatException("The User Profile Property specified in your UPP token does not exist", ex);
  50:      }
  51:      return strQuery;
  52:  }

So there are a few things to point out here which might trip you up:

  • We are using the UserProfileManager.IsAvailable() method to find out if we have a user profile service application provisioned and assigned to the current Web Application.
  • At the moment this code throws an error if the current user doesn’t have a User Profile. You may want to handle this differently for your environment?
  • Handling of multi-value fields. At the moment all we do is take the string values and concatenate them with “OR” in the middle. So if you had “Value1; Value2” as your property value the Token replacement would put “Value1 OR Value2” as the search query.

As long as the content editors are aware of the behaviour this allows us to create quite complex queries.

Example if we now used the Fixed Query:

([UPP-SP-Responsibility]) AND Write:[TODAY-14]..[TODAY]

Then for a user who’s “Ask Me About” properties were “SharePoint” and “IT Administration” then the resulting Search Query would be:

(SharePoint OR “IT Administration”) AND Write:13/05/2012..17/05/2012

If another user comes along whose “Ask Me About” property was just set to “Marketing” then the resulting Search Query would be:

(Marketing) AND Write:13/05/2012..17/05/2012

This is without changing any of the web part properties, and allows us to drive dynamic content from a single web part to our entire user base.

Hopefully you can see that this is incredibly powerful and flexible.

Step 5 – Making it re-usable Part 3 – Controllable Sort By
The final step is to allow our content editors to control the “Sort By” functionality. The default OOTB webpart only allows us to sort by “Relevance” or “Last Modified”, which is fine when you are looking at general search results, but when you are building custom components (such as news, links or event feeds) you typically want to control the order by date or title or something a little more usable for the specific component.

So this bolt-in allows you to control the Sort By. First off we need to add some Web Part Properties so that the user can modify their values:

   1:  [Personalizable(PersonalizationScope.Shared)]
   2:  [WebBrowsable(true)]
   3:  [WebDescription("Sort by this managed property")]
   4:  [WebDisplayName("Managed Property")]
   5:  [Category("Sort Override")]
   6:  public string OrderByProperty { get; set; }
   7:   
   8:  [Personalizable(PersonalizationScope.Shared)]
   9:  [WebBrowsable(true)]
  10:  [WebDescription("Sort direction")]
  11:  [Category("Sort Override")]
  12:  public Microsoft.Office.Server.Search.Query.SortDirection SortDirection { get; set; }

This will provide the Web Part property editing functionality:
image

Once we have done that, we can add the following code to our ConfigureDataSourceProperties method (yes .. this method really is where all of the grunt work goes on in this web part!)

   1:  // if OrderByProperty is not set, use default behavior
   2:  if (!string.IsNullOrEmpty(OrderByProperty))
   3:  {
   4:      // change the sortorder
   5:      dataSource.SortOrder.Clear();
   6:      dataSource.SortOrder.Add(OrderByProperty, SortDirection);
   7:  }

And that is all there is to it.

Step 6 – Enjoy!
So congratulations if you made it this far. I know this was a long blog post but thought it was worth walking through it properly.

If you have any questions, feedback or questions then please get in touch using the comments, and here are links to the downloads (which are also referenced at the top of this blog post)

Some notes about the “final” version:

  • The code structure is slightly different because the DateTime [TODAY] queries are cached using Web Part Properties for better performance
  • the [TODAY] token is case sensitive!
  • There is an extra “Debug Mode” checkbox in the Web Part Properties which when enabled spits out the entire query being executed at the bottom of the search results.
  • Code contains an “Editor Part” .. this just clears out the Cache value when the web part properties are modified

Usage Summary:

Tokens you can use are:

  • [TODAY]
  • [TODAY+X] (add X days)
  • [TODAY-X] (remove X days)
  • [UPP-{Internal Name of User Profile Property}]

Example Usage

Sample user has:
Name: Martin Hatch
Ask Me About: SharePoint; Solution Architecture; Code

ContentType:Event AND ([UPP-SPS-Responsibility]) AND Write:[TODAY-7]..[TODAY]
becomes
ContentType:Event AND (SharePoint OR “Solution Architecture” OR Code) AND Write:12/05/2012..17/05/2012
Returns all events which were updated within the past week, and contain the current user’s “Ask Me About” values.

Author:[UPP-PreferredName] IsDocument:1
becomes
Author:”Martin Hatch” IsDocument:1
Returns all documents written by the current user

Author:[UPP-PreferredName] Write>=[TODAY-14]
becomes
Author:”Martin Hatch” Write>=02/05/2012
Returns all content created by the current user and updated within the past 2 weeks

RCWP Part 3 – Edit Web Part using a Ribbon modal dialog

Also check out the other part of the series:

This follows on from Part 1 (where we created a “Related Content Web Part”) and Part 2 (where we added a contextual tab to the Ribbon).

This post summarised the final part of this Web Part (which we completed in the final session of the day of SPRetreat last Saturday).

We wanted to provide a pop-up window, accessed through our new Contextual Tab in the Ribbon, which allowed us to easily modify some web part properties.

The basis of this was quite straight-forward, and it certainly starts off quite easy.
We created a new Application Page (RCWP_SetFieldValue.aspx) which would contain the code to update our Web Part Properties.

In this file we added a simple ASP.Net Label, Drop Down List and button.

<asp:Content ID=”Main” ContentPlaceHolderID=”PlaceHolderMain” runat=”server”>
<p>
    This allows you to set the field value for the <strong>Related Content Web Part</strong>
</p>
<asp:Label runat=”server” ID=”lblChoice” Text=”Select Field:” AssociatedControlID=”ddlFields” /><br />
<asp:DropDownList runat=”server” ID=”ddlFields” /><br />
<asp:Button runat=”server” ID=”btnNike” Text=”Just Do It!” />
</asp:Content>

Back in Part 2 we created a JavaScript file which was use for the “Command” events for our Buttons (yes .. I told you we’d be looking at that again!).

Here we are going to modify one of the Buttons so that it throws up a SharePoint Modal Dialog with our Application Page in it.

The code below is modified from the original MS Blog Article I referenced in Part 2 (called “How to create a Web Part with a Contextual Tab”).

if (commandId === ‘CustomContextualTab.GoodbyeWorldCommand’) {
            //alert(‘Good-bye, world!’);
            var options = {
                url: ‘/_layouts/SPR3/RCWP_SetFieldValue.aspx,
                title: ‘Set Field’,
                allowMaximize: false,
                showClose: true,
                width: 800,
                height: 600
            };
            SP.UI.ModalDialog.showModalDialog(options);
        }

I have basically changed the JavaScript for the “GoodbyeWorldCommand” button so that it does something different.

I am using the new SP.UI.ModalDialog namespace in the SharePoint ECMAScript to pop up a modal dialog window.

(Note – I also changed the display text to “Set Field” .. and deleted the other button to clean up the ribbon a bit)

But don’t forget that our Application Page is running from _layouts … it’s in a completely different place to our Web Part so this really isn’t enough for our page to work. In order to do anything else our Layouts page would need the following information:

  • The Page that the web part is on (URL)
  • Which Web Part to update on that page (Web Part ID)

The URL of the current page is easy enough using JavaScript (location.href) but the Web Part ID … this represented a new challenge.

How do you get the server-side Web Part ID through JavaScript?

This problem took the entire final hour of the day (Session 5) and took quite a bit of research and web searching. Eventually (after a few suggestions) we hit upon the answer:

Back in Part 2 we created a JavaScript file which was used to register our Contextual Tab. The JavaScript file that registers the Contextual Tab contains a reference to a “PageComponentId”.

getId: function ContextualTabWebPart_CustomPageComponent$getId() {
    return this._webPartPageComponentId;
}

Thes pecific instance of our Web Part had a “PageComponentID” of “WebPartWPQ2” and after some digging we found it in the Source of the page!

<div WebPartID=”866ef42d-6626-45e0-af9c-a00467ed2666″ WebPartID2=”1ad9529a-5e86-4e7c-9d4d-022a1fa6e6c0″ HasPers=”false” id=”WebPartWPQ2″ width=”100%” class=”ms-WPBody noindex ms-wpContentDivSpace” allowRemove=”false” allowDelete=”false” style=”” >

The attribute that REALLY stands out though is the WebPartID:

WebPartID=”866ef42d-6626-45e0-af9c-a00467ed2666″

This is clearly a GUID value, referring to the server-side Web Part ID for that instance of the Web Part.
So .. how do we get this to our dialog.. well, good old trusty document.GetElementById() (we could have used JQuery, but I didn’t want to have to install the framework .. and don’t forget .. I only had 1 hour to get this working at SPRetreat!!)

Using this information, I could modify my JavaScript to retrieve these values, and pass them through to my Modal Dialog.

// get the Web Part DIV element
            var element = document.getElementById(this._webPartPageComponentId);
            // extract the Web Part ID (as a GUID object)
            var wpID = element.attributes[“WebPartId”];
            // pass through the URL and Web Part ID
            var options = {
                url: ‘/_layouts/SPR3/RCWP_SetFieldValue.aspx?wpID=’ + wpID.nodeValue + ‘&url=’ + location.href,
                title: ‘Set Field’,
                allowMaximize: false,
                showClose: true,
                width: 800,
                height: 600
            };
            SP.UI.ModalDialog.showModalDialog(options);

Note – as the Web Part ID is of type HTML Attribute, we need to use the “NodeValue” property instead of toString();

So .. first off, in our Application Page we can use the URL to retrieve the fields from the page’s Content Type and populate our Drop Down List.

protected void Page_Load(object sender, EventArgs e)
        {
            TargetUrl = Request.QueryString[“url”];
            // remove any query strings
            if (TargetUrl.IndexOf(“?”) != -1)
            {
                TargetUrl = TargetUrl.Substring(0, TargetUrl.IndexOf(“?”));
            }
            if (!Page.IsPostBack)
            {
                ddlFields.Items.Clear();
                SPFile file = this.Web.GetFile(TargetUrl);
                foreach (SPField field in file.Item.Fields)
                {
                    if (!field.Hidden)
                    {
                        ListItem item = new ListItem(field.Title, field.StaticName);
                        ddlFields.Items.Add(item);
                    }
                }
            }
            btnNike.Click += new EventHandler(btnNike_Click);
        }

I did a bit of string manipulation on the URL to make sure we trim out any URL query strings, and then use that to retrieve an SPFile object.

We then just iterate through the SPListItem.Fields collection, adding any fields that are not hidden.

Note – we are using an ASP.Net ListItem object in the Drop Down List, so that we can use the Display Name in the drop-down, but store the Static Name as the value .. it’s the Static Name we need to save to our Web Part!

The next bit is under our Click event. We can now use the URL to get the SPLimitedWebPartManager for the page, and pass through the Web Part ID property, and it would retrieve the instance of my Web Part (allowing me to set the field value).

protected void btnNike_Click(object sender, EventArgs e)
       {
           // get Web Part ID
           wpID = Request.QueryString[“wpID”];
           // retrieve the Web Part Panager for the URL
           SPFile file = this.Web.GetFile(TargetUrl);
           SPLimitedWebPartManager wpm = file.GetLimitedWebPartManager(PersonalizationScope.Shared);
           // get the safely-casted web part object
           RelatedContentWebPart.RelatedContentWebPart wp =
               wpm.WebParts[new Guid(wpID)] as RelatedContentWebPart.RelatedContentWebPart;
           if (wp != null)
           {
               // set the web part property, and save settings
               wp.FieldName = ddlFields.SelectedValue;
               wpm.SaveChanges(wp);
           }
           // close the modal dialog
           this.Context.Response.Write(“<script type=’text/javascript’>window.frameElement.commitPopup();</script>”);
           this.Context.Response.End();
       }

So .. we should be done…

Build / Deploy / Test

So .. a long journey but worth it, five different 1 hour sessions and a great day at #SPRetreat .. but definitely worthwhile, and a new “Related Content Web Part” to boot!

A massive thanks to Andrew Woodward (21Apps) and Ben Robb (CScape) for organising the event, the venue and the food! (great food!!!)

Source Code

Sorry it took so long for me to get it all online, I was very busy then went on holiday. You can find all of the source code downloadable from my SkyDrive here:

https://cid-60f12a60288e5607.office.live.com/self.aspx/SPRetreat/SPR3.zip

RCWP Part 2 – Web Part with Ribbon Contextual-Tab

Also check out the other part of the series:

This follows on from Part 1, where we created a “Related Content Web Part” (RCWP) which would dynamically return search results based on the value of a field on the current page.

This post summarises the second-phase of that Web Part (which we started in session 4 of SPRetreat) to provide an easier method for the Content Editors to cherry-pick which field they wanted to use for the keyword search value. Seeing as me and my session 4 partner hadn’t done anything with Contextual Tabs before, we decided to give that a crack.

I’m not going to cover every single details of this. There are 2 fantastic articles:
Chris O’Brien (MVP) has a great 4-Part series titled “SharePoint 2010 Ribbon Customisation series” which is a must-read if you want to understand more about Ribbon development work.

There is also an excellent step-by-step guide on the Microsoft SharePoint Developer Documentation Blog called “How to create a Web Part with a contextual tab”.

In my example, I used the Microsoft blog’s example code, but there are plenty of good tutorials for this on the web. I’m not going to copy the same information in the blog article here, but the summarised core points are as follows:

  • Your Web Part must implement the Interface IWebPartPageComponentProvider(Microsoft.SharePoint.WebControls).
  • The Interface involves a WebPartContextualInfo property, which you use to register you “contextual” event
  • You will need a JS file to stored your “actions”
  • You will need XML configuration to register the definition of your “tab” and it’s controls.

So first off, we need to modify our web part to implement the Interface IWebPartPageComponentProvider:

public class RelatedContentWebPart : CoreResultsWebPart,
    IWebPartPageComponentProvider
{
    // omitted for clarity
}

Now, before we implement the Interface we need to setup a few methods and properties.
First up is the XML schema for both the Tab Template and the Tab itself. It’s a lot of XML so I’m not going to post it here, but I basically copied the XML provided on the MS Blog post, so you should find it easy enough to go and fetch it from there or just use my Full Source Code (in Part 3).

A snippet of the first part is below;

private string contextualTab = @”
   <ContextualGroup Color=””Magenta””
     Command=””CustomContextualTab.EnableContextualGroup””
     Id=””Ribbon.CustomContextualTabGroup””
     Title=””Related Content Web Part””
     Sequence=””502″”
     ContextualGroupId=””CustomContextualTabGroup””>
          <Tab
              Id=””Ribbon.CustomTabExample””
              Title=””Filtering Settings””
              Description=””This holds my custom commands!””
// remainder omitted for clarity

You can see that we have created a string called “contextualTab” that references a “ContextualGroup”. This has a colour, as well as a unique ID that we need to create. The Title will appear in the ribbon as the “contextual” group, which will span across multiple tabs (if you have multiple tabs in the same “contextual group” of course!).

Note – I have changed the title and description of my Tab and the Contextual Group, but everything else is the same as the MS Blog!

We also have a standard Ribbon “Tab” declaration, with an Id value which we will need later) as well as the Tab Title and Description.

Further down in the XML is a reference to the “Buttons” that we are going to have on our tab.
(Note – the code below is an exact copy of the “MS Blog Article” code .. we will be changing this later on!)

<Button     Id=””Ribbon.CustomTabExample.
    CustomGroupExample.GoodbyeWorld””
    Command=””CustomContextualTab.GoodbyeWorldCommand””
    Sequence=””15″”
    Description=””Show the Goodbye World text””
    LabelText=””Goodbye World!””
    TemplateAlias=””cust1″”/>

Make a note of where these button controls are, because we’re going to need to modify this in Part 3 of this post!

There is also another string created called “contextualTabTemplate” which you can use for the layout configuration.

private string contextualTabTemplate = @”
          <GroupTemplate Id=””Ribbon.Templates.CustomTemplateExample””>
            <Layout
              Title=””OneLargeTwoMedium”” LayoutTitle=””OneLargeTwoMedium””>
              <Section Alignment=””Top”” Type=””OneRow””>
                <Row>
                  <ControlRef DisplayMode=””Large”” TemplateAlias=””cust1″” />
                </Row>
              </Section>
// remainder omitted for clarity

So … now we have all of our XML defined we need to create a JavaScript file. The MS Blog article includes a JS file called “CustomContextualTabPageComponent.js” which deploys directly to the 14\Template\Layouts\ folder.
This contains a whole load of JavaScript for the Tab, Group and Button items in your XML file.

Important Note – The Command and Enable scripts use in your XML are in this JS file. If you start changing names you will need to manually keep these files in check.. they are XML and JS files so no compile errors or warnings! … except this one 😉

Probably the most important section in this file is that which handles the “Command” event for your buttons:

handleCommand: function ContextualTabWebPart_CustomPageComponent
    $handleCommand(commandId, properties, sequence)
{
    if (commandId ===
        ‘CustomContextualTab.HelloWorldCommand) {
            alert(‘Hello, world!’);
    }
    if (commandId ===
        ‘CustomContextualTab.GoodbyeWorldCommand’) {
            alert(‘Good-bye, world!’);
    }
}

Again, this code has been copied from the MS Blog, but keep a note of it .. we will be modifying this too in Part 3!

So .. we have our XML schema, and we have our JS file created, but we haven’t actually DONE anything with them yet.

So lets create a method to register our new Contextual Tab:

private void AddContextualTab()
        {
            //Gets the current instance of the ribbon on the page.
            Microsoft.Web.CommandUI.Ribbon ribbon = SPRibbon.GetCurrent(this.Page);
            //Prepares an XmlDocument object used to load the ribbon extensions.
            XmlDocument ribbonExtensions = new XmlDocument();
            //Load the contextual tab XML and register the ribbon extension.
            ribbonExtensions.LoadXml(this.contextualTab);
            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild, “Ribbon.ContextualTabs._children”);
            //Load the custom templates and register the ribbon extension.
            ribbonExtensions.LoadXml(this.contextualTabTemplate);
            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild, “Ribbon.Templates._children”);
        }

So .. this method is basically using the SPRibbon object to get a reference to the Ribbon on the current page. It is then loading in our XML schema to register a new contextual tab).
(Eventually we will end up calling this method from the PreRender() event).

Now we are going to need to register our JavaScript, and strangely enough we are going to do that with yet more JavaScript! (yes .. when you are working with the Ribbon JavaScript really is word of the day!)

public string DelayScript
        {
            get
            {
            string webPartPageComponentId = SPRibbon.GetWebPartPageComponentId(this);
            return @”
            <script type=””text/javascript””>
            //<![CDATA[
            function _addCustomPageComponent()
            {
                var _customPageComponent = new ContextualTabWebPart.CustomPageComponent(‘” + webPartPageComponentId + @”‘);
                SP.Ribbon.PageManager.get_instance().addPageComponent(_customPageComponent);
            }
            function _registerCustomPageComponent()
            {
                SP.SOD.registerSod(“”CustomContextualTabPageComponent.js””, “”\/_layouts\/CustomContextualTabPageComponent.js””);
                SP.SOD.executeFunc(“”CustomContextualTabPageComponent.js””, “”ContextualWebPart.CustomPageComponent””, _addCustomPageComponent);
            }
            SP.SOD.executeOrDelayUntilScriptLoaded(_registerCustomPageComponent, “”sp.ribbon.js””);
            //]]>
            </script>”;
            }
        }

The really important part of this is the first line:

SPRibbon.GetWebPartPageComponentId(this);

This gets a unique reference for the instance of your Web Part, on the current page, in the context of the Ribbon. This is used to effectively identify your Web Part when someone clicks on it!
The other really important bit is the last line:

SP.SOD.executeOrDelayUntilScriptLoaded(_registerCustomPageComponent, “”sp.ribbon.js””);

This is the new “Script On Demand” (SOD) method which allows us to tell SharePoint not to try loading our JavaScript until the SP.Ribbon.JS has already been processed!
Once that has been done we need to implement our WebPartContextualInfo method:

public WebPartContextualInfo WebPartContextualInfo
        {
            get
            {
                // create objects for the contextual web part tab
                WebPartContextualInfo info = new WebPartContextualInfo();
                WebPartRibbonContextualGroup contextualGroup = new WebPartRibbonContextualGroup();
                WebPartRibbonTab ribbonTab = new WebPartRibbonTab();
                //Create the contextual group object and initialize its values.
                contextualGroup.Id = “Ribbon.CustomContextualTabGroup”;
                contextualGroup.Command = “CustomContextualTab.EnableContextualGroup”;
                contextualGroup.VisibilityContext = “CustomContextualTab.CustomVisibilityContext”;
                //Create the tab object and initialize its values.
                ribbonTab.Id = “Ribbon.CustomTabExample”;
                ribbonTab.VisibilityContext = “CustomContextualTab.CustomVisibilityContext”;
                //Add the contextual group and tab to the WebPartContextualInfo.
                info.ContextualGroups.Add(contextualGroup);
                info.Tabs.Add(ribbonTab);
                // fetch dynamic component info for the current page’s Ribbon control
                info.PageComponentId = SPRibbon.GetWebPartPageComponentId(this);
                return info;
            }
        }

Now lets take a quick look through some of this code first. The first thing you should notice is that there are 3 new classes provided in the OOB API specifically for providing contextual tabs (from Web Parts):

  • WebPartContextualInfo
  • WebPartRibbonContextualGroup
  • WebPartRibbonTab

These objects contains all of the core functionality that you will need to handle the interaction between selecting your Web Part in edit mode, and the Ribbon dynamically popping up your Tab when that happens.
The RibbonContextualGroup and RibbonTab objects then get given both an ID property and a VisibilityContext. These values refer to the same pointers in the XML schema definition for the Tab, Group and Controls that we are going to register (which we created earlier).
The final part of the puzzle is to add these to the Web Part’s load stack (in this case, the Pre-Render method):

protected override void OnPreRender(EventArgs e)
{
            base.OnPreRender(e);
            this.AddContextualTab();
            ClientScriptManager clientScript = this.Page.ClientScript;
            clientScript.RegisterClientScriptBlock(this.GetType(), “ContextualTabWebPart”, this.DelayScript);
}

So, we are registering our contextual tab (using the AddContextualTab method we created earlier).
We then dynamically add a “lazy load” reference to our “DelayScript” string, which will register all of the JavaScript functions that we are using.

Build / Deploy / Test
There you have it … a contextual tab in edit mode!

Make sure you check back for:

  • RCWP Part 3 – Edit Web Part using a Ribbon modal dialog
  • (Full source code for the solution will be published in Part 3)

    RCWP Part 1 – SPRetreat and the Related Content Web Part

    I’m sat on the train after a great day of SPRetreat (followed by SharePint of course!), superbly organised by Andrew Woodward (21Apps) and Ben Robb (CScape). It was a really good day of innovative ideas, problem solving, chewing the fat (and the occasional dirty joke… you know who you are!).

    The core challenge thrown down for the day involved trying to provide cross-site (collection) “related information”, effectively a “cross-pollination” function, using SharePoint 2010. There were some great ideas and a lot of top effort into involving the Managed Metadata Service, Custom Search API work and some cracking Scrum / Agile design processes.

    We had 5 sessions of 1 hour each, and my efforts for the day mostly revolved around delivering a “Related Content Web Part”, which could use Search to show other content from any data source which is in some way related to the information on the current page.

    In this post I’m going to walk through how my efforts of the day culminated in the “Related Content Web Part” but ended up being a generic “Dynamic Field Driven Search” web part, basically allowing a set of search results to be displayed based on the value of a field that is stored in the publishing page (i.e. content type) that contains the web part (which derived from the Search CoreResultsWebPart).

    In the final sessions we though about how to improve this, and ended up building a custom contextual ribbon interface to surface SP Modal Dialogs to allow easy updating of core web part properties.
    Core Functionality

    The functionality is really split into 3 major sections:

    (Full source code for the solution will be published in Part 3)

    RCWP Part 1 – Extending the Core Results Web Part
    This is one of the nicest new “features” of SharePoint 2010. They have stopped sealing all of the Web Parts (and Web Part Connections)that are used for the results pages in SharePoint Search solutions. This means it is now much easier for you to extend and add-value to these web parts without having to throw out all of the OOB functionality.
    The reason for using the CoreResultsWebPart was simple;

    • Using search is fast, efficient, cross farm and highly configurable.
    • The core results web part using XSLT for rendering, so easy to design the output.
    • Leveraging an OOB web part means we get loads of added functionality for free! (like specifying which scope we want to use).

    In this solution, we extended the CoreResultsWebPart to create our own Web Part. Simple create a new Visual Studio 2010 “Web Part” item and set it to inherit from “CoreResultsWebPart” (you will need to add a reference to Microsoft.Office.Server.Search.dll).

    [ToolboxItemAttribute(false)]
    public class RelatedContentWebPart : CoreResultsWebPart
    {
    }

    In terms of functionality we need to do 1 thing:

    Override the Query programmatically, based on the metadata of the current page.

    This involved a few steps. First off, we need to identify which field we want to be “targeting”. For this we created a simple string field, exposed as a Web Part Property.

    private string fieldName = “Title”;
    ///<summary>
    /// The field on the current page we want to use for filtering
    ///</summary>
    [WebBrowsable(true)]
    [Personalizable(PersonalizationScope.Shared)]
    [WebDisplayName(“Field Name”)]
    [SPWebCategoryName(“Filter SPR”)]
    [WebPartStorage(Storage.Shared)]
    [Description(“Which field on the current page do you want to use for filtering?”)]
    public string FieldName
    { get { return fieldName; } set { fieldName = value; } }

    Then we needed to override the query itself. This is simply done by setting the “FixedQuery” property of the CoreResultsWebPart. The trick here is you need to set this in the “OnInit” event, otherwise it won’t take effect (if you try to place it in OnLoad, CreateChildControls or any other later method then it won’t have any effect!).

    protected override void OnInit(EventArgs e)
    {
        this.FixedQuery = “keyword string value”;
        base.OnInit(e);
    }

    Finally, we need to make sure we are pulling out the field value of the current list item, based on our custom field. For this we used a simple set of SPContext combined with some defensive programming to make sure we don’t get any NullReferenceException errors. So change the “OnInit” event to the following:

    protected override void OnInit(EventArgs e)
    {
        if (!string.IsNullOrEmpty(fieldName))
        {
            if(SPContext.Current.ListItem.Fields.
                ContainsFieldWithStaticName(fieldName))
            {
                this.FixedQuery =
                    SPContext.Current.ListItem[fieldName].ToString();
            }
        }
        base.OnInit(e);
    }

    After that … Build / Deploy and the web part was working!

    The code could obviously be re-factored a little bit, but on the whole it’s all working 🙂
    Make sure you check back for:

  • RCWP Part 2 – Web Part with Ribbon Contextual-Tab
  • RCWP Part 3 – Edit Web Part using a Ribbon modal dialog
  • (Full source code for the solution will be published in Part 3)

    The call to SearchServiceInstance.Provision failed … resolved!

    This was a very annoying one. I had previously configured my development virtual machine to disable Search, Indexing, Document Conversion .. all those pesky services that sap RAM and CPU which aren’t always needed for development work.

    The problem came when I wanted to turn them back on again. I was creating a new SSP and got the rather spurious error "no indexers".
    This was a little odd, but I quickly realised that it meant the Index service was disabled. (annoying, because I only wanted an SSP for user profile development, but c’est la vie).

    So I went to start the Windows SharePoint Services Search and *wham*… "Error"
    I had a bit of a poke around the log files and found a reference to:

    The call to SearchServiceInstance.Provision ("<name of server>") failed.

    Well .. it took me a while to work this out but I did finally crack it. You see, I was doing this on a local virtual machine, so I rarely if ever use the full domain name. I found that I could only start the Search Service using the fully qualified "Domain\UserName" designation.
    If you use just the "Username" then it didn’t work and you got the odd error above!

    Very strange, another one for the archives I guess.

    People search errors when My Site is running on SSL

    This was a great gotcha that I picked up duing a client project handover.
    They reported issues with search, and I found a load of errors in the crawl logs regarding accessing the mysite.
    You will find that this issue occurs when you configure your My Sites to run under SSL (HTTPS), and the problem is that the People Search isn’t really interested in the My Site content, it is actually interested in the Profile Database (which is another thing most people forget to switch on .. make sure your Profile Database has an import schedule setup!)
    Basically, you need to set the Content Source URL to the following:
    sps3s://<mysiteurl>/
    So if your mysite is running under https://mysite.company.com/ then you need to set your Content Source URL to:
    sps3s://mysite.company.com/
    Personally I found this pretty confusing, so I actually  split out the Profile Database content into a separate Content Source, and configured the People Scope to only retrieve items from that specific Content Source.
    Happy searching!
    Update
    Just a quick update, because this is another “People Search” problem. Even if you don’t use My Sites, you should still switch on and use the MySite web application and settings (You can stop people from creating “My Sites” by disabling Self Service Site Creation at the Web Application level).
    The reason is that the My Site web application is used for People Search URLs and will automatically dsiplay information from the Profile Database (which is basically the user’s “public profile”) even if they don’t have a My Site created!