Custom Field Type: Site Reference (part 1)

Custom Field Type: Site Reference - Custom Field Type using the SharePoint Site Picker

For the last few weeks I’ve been working on a project that strongly relied on meta data. The project is all about finding the right information in a strongly fragmented collaboration environment. One of the parts of the project was publishing news messages related to specific collaboration areas.

The background

The project I mentioned is an Intranet environment meant to support the organization’s collaboration process. It consists of many different areas like Topics, Projects, Departments, etc. Any of these areas can contain a number of sub areas which facilitate many other items like projects, trainings, etc.

Organizing the information wasn’t the only challenge. Next to being able to store the knowledge in a logical way, the customer wanted to improved the awareness of the progress of all the different projects. In order to do that the customer wanted to be able to publish news messages which would contain the updates. Every news message should be related to the specific area so that it can not only be found using search but can also be included in drill-down content aggregations.

What’s in the box?

One of the things SharePoint would offer you out-of-the-box would be to create a News area for every single collaboration site on the Intranet. In the context of a project I worked on however it would have some serious drawbacks.

First of all you would have to create a Publishing Site under every single collaboration site. You could of course merge these two and provide a custom Site Definition, but then the customer wouldn’t be able to use any of the standard templates.

Additionally, as I already mentioned, the collaboration environment is very likely to become very fragmented: consisting of many collaboration sites. While the customer wants the people working on the project to publish some information about the progress of their work, he doesn’t expect it to be many news messages. So would it be really worth it to create a whole new News site for a couple of news messages only?

I considered these and a few more possibilities and then I came up with the following solution: a Site Reference field.

Site Reference field

What I thought it would help the customer was a Custom Field Type using the standard SharePoint Site picker allowing the users to pick any site within the Site Collection and store its URL as metadata so it can be used for search and content aggregations.

Site Reference field step-by-step

Covering the basics

Let’s start off with creating a Custom Field Type. Because we will be storing the URL of a site, let’s derive from the standard SharePoint Text Field Type:

using System;  
using System.Collections.Generic;  
using System.Text;  
using Microsoft.SharePoint;

namespace SiteReferenceFieldType  
{
  public class SiteReferenceFieldType: SPFieldText
  {
    public SiteReferenceFieldType(SPFieldCollection fields, string fieldName) : base(fields, fieldName)
    {
    }

    public SiteReferenceFieldType(SPFieldCollection fields, string fieldName, string displayName)
      : base(fields, fieldName, displayName)
    {
    }
  }
}

Also let’s create a fldtypes_SiteReferenceFieldType.xml file which will provision our Custom Field Type to SharePoint:

<?xml version="1.0" encoding="utf-8" ?>  
<FieldTypes>  
  <FieldType>
    <Field Name="TypeName">SiteReferenceFieldType</Field>
    <Field Name="ParentType">Text</Field>
    <Field Name="TypeDisplayName">Link to a website</Field>
    <Field Name="TypeShortDescription">Link to a website</Field>
    <Field Name="UserCreatable">TRUE</Field>
    <Field Name="ShowInListCreate">TRUE</Field>
    <Field Name="ShowInSurveyCreate">TRUE</Field>
    <Field Name="ShowInDocumentLibraryCreate">TRUE</Field>
    <Field Name="ShowInColumnTemplateCreate">TRUE</Field>
    <Field Name="FieldTypeClass">SiteReferenceFieldType.SiteReferenceFieldType, SiteReferenceFieldType, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6aeba8d1a7d7cac5</Field>
  </FieldType>
</FieldTypes>  

Now let’s check if everything works correctly while it’s still simple and easy to debug:

Custom Field Type Site Reference available on the New Site Column page

Custom Field Type Site Reference added as a List Column

Now let’s add the Site Picker to our custom field.

First of all we need a custom control to override the default TextBox rendering:

using System;  
using System.Collections.Generic;  
using System.Text;  
using Microsoft.SharePoint.WebControls;

namespace SiteReferenceFieldType  
{
  public class SiteReferenceFieldControl : BaseFieldControl
  {
     // code ommitted...
  }
}

This is the place where we will add all the Site Picker logic. Before we start adding the picker functionality we need to register the Field Control with the Field Type:

public class SiteReferenceFieldType: SPFieldText  
{
  // code ommitted...

  public override Microsoft.SharePoint.WebControls.BaseFieldControl FieldRenderingControl
  {
    get
    {
      Microsoft.SharePoint.WebControls.BaseFieldControl fldControl = new SiteReferenceFieldControl();
      fldControl.FieldName = InternalName;
      return fldControl;
    }
  }
}

With doing the above we’ve covered the basics and are now ready to add the picker.

Adding the Site Picker

While you could try to figure this out on your own, Christopher White has done it already for you. All you need to do is to grab the JavaScript snippet from his article and add it to the Custom Field Type:

public class SiteReferenceFieldControl : BaseFieldControl  
{
  protected TextBox SiteUrlValue;
  protected IButtonControl OpenPickerButton;

  /// <summary>
  /// Alt-text for the Launch Picker Button
  /// </summary>
  protected string PickerButtonText = "Pick a website...";

  protected override void CreateChildControls()
  {
    if (Field == null)
    {
      return;
    }

    base.CreateChildControls();

    if (ControlMode == SPControlMode.Display)
    {
      return;
    }

    SiteUrlValue = new TextBox
    {
      ID = "SiteUrlValue",
    };
    OpenPickerButton = new ImageButton
    {
      ImageUrl = "/_layouts/images/cat.gif",
      ImageAlign = ImageAlign.AbsMiddle,
      AlternateText = PickerButtonText
    };

    ((ImageButton)OpenPickerButton).Attributes.Add("onClick", "LaunchSitePicker(); return false;");
    Controls.Add(SiteUrlValue);
    Controls.Add(new LiteralControl("&nbsp;&nbsp;"));
    Controls.Add((ImageButton)OpenPickerButton);
    Controls.Add(new LiteralControl("<br/>"));
  }

  protected override void OnPreRender(EventArgs e)
  {
    if (ControlMode != SPControlMode.Display)
    {
      ScriptLink.Register(this.Page, "PickerTreeDialog.js", true);
      AddSitePickerControlClientScript();
    }

    base.OnPreRender(e);
  }

  private void AddSitePickerControlClientScript()
  {
    EnsureChildControls();
    string script =
    "var lastSelectedSiteSmtPickerId = null; " +
    "function LaunchSitePicker() " +
    "{ " +
    "if (!document.getElementById) return; " +
    "var siteTextBox = document.getElementById('" +
    SPHttpUtility.EcmaScriptStringLiteralEncode(this.SiteUrlValue.ClientID) +
    "'); " +
    "if (siteTextBox == null) return; " +
    "var serverUrl = '" +
    SPHttpUtility.EcmaScriptStringLiteralEncode(
    SPContext.Current.Web.ServerRelativeUrl) +
    "'; " +
    "var callback = function(results) " +
    "{ " +
    "if (results == null || results[1] == null) return; " +
    "lastSelectedSiteSmtPickerId = results[0]; " +
    "siteTextBox.value = results[1] + '/'; " +
    "}; " +
    "LaunchPickerTreeDialog(" +
    "'CbqPickerSelectSiteTitle'," +
    "'CbqPickerSelectSiteText', " +
    "'websOnly','', " +
    "serverUrl, lastSelectedSiteSmtPickerId," +
    "'','','/_layouts/images/smt_icon.gif','', " +
    "callback); " +
    "}";

    this.Page.ClientScript.RegisterClientScriptBlock(
        typeof(SiteReferenceFieldControl),
        "LaunchSitePicker",
        script, true);
  }
}

As you probably noticed on Christopher’s article you need a TextBox to store the value. We’ve already added one. We’ve also added an image button to call the Site Picker.

Let’s have a look at the results so far:

Custom Field Type Site Reference using the SharePoint Site Picker

SharePoint Site Picker appears after clicking on the Site Picker icon in the Site Reference Field Type

Url of the site selected in the SharePoint Site Picker appears as the value of the field

Before you can use this Custom Field Type you have to do one more thing. The underlying Field must be able to retrieve the entered value as well as it must be able to enter the existing value into the Field Control. You can implement it by overriding the Value property of the Field Control:

public override object Value  
{
  get
  {
    EnsureChildControls();
    return SiteUrlValue.Text;
  }
  set
  {
    EnsureChildControls();
    SiteUrlValue.Text = value as string;
  }
}

That’s all. Right now we have a fully working Custom Field Type using the SharePoint Site Picker.

As you have probably noticed it’s not exactly the same control I showed you at the beginning. In my case I took it a bit further. In the next part of this article I will show you how you can make the control ready only and how to support the required field. In the meanwhile you can download the code from CodePlex.

Download Custom Field Type: Site Reference (part 1) (13KB, ZIP)

Technorati Tags: SharePoint,SharePoint 2007,MOSS 2007

Comments

comments powered by Disqus