Wednesday, March 26, 2008

Enhanced Lookup Field (continued)

This is an enhancement or continuation of my old post where I was showing how easily you can teach the lookup field to show field values of the source items instead of just name.

This may be pretty handy in the scenarios with more than one content languages in Sitecore. Let's imagine that we would like to have a lookup field that is used as a "country selector" pulling out those items from the "global" repository:


Then let's imagine that we have 2 more content languages besides English in the system (Spanish and Ukrainian) and that our "Country" template that our source items are created from has only one field defined that is not shared across languages and versions called "Title":

This value is going to be translated into all the languages:


In other words, the "Title" field will have different values depending on the selected language.

Now it should make a lot of sense to "teach" our lookup field to retrieve those language specific values and show them in the drop down list instead of item names.

In order to do that, we will have to customize the lookup field by inheriting from the default one. The code of the custom lookup field is shown below:

   1: using System;
   2: using System.Collections;
   3: using System.Web.UI;
   5: using Sitecore;
   6: using Sitecore.Data.Items;
   7: using Sitecore.Diagnostics;
   8: using Sitecore.Globalization;
   9: using Sitecore.Resources;
  10: using Sitecore.Shell.Applications.ContentEditor;
  11: using Sitecore.Text;
  13: namespace SCUSAINC.Shell.Applications.ContentEditor
  14: {
  15:     public class CustomLookup : LookupEx
  16:     {
  17:         // the name of the field that we have defined for the "Template field" template
  18:         private const string sourceFieldName = "SourceFieldName";
  19:         protected string itemlanguage;
  21:         protected override void OnLoad(EventArgs args)
  22:         {
  23:             if (!Sitecore.Context.ClientPage.IsEvent)
  24:             {
  25:                 Item contextItem = Sitecore.Context.ContentDatabase.Items[this.ItemID];
  27:                 foreach (TemplateFieldItem tfItem in contextItem.Template.OwnFields)
  28:                 {
  29:                     // do the necessary checks
  30:                     if (tfItem.Source == this.Source &&
  31:                         tfItem.InnerItem != null &&
  32:                         tfItem.InnerItem.Fields[sourceFieldName] != null &&
  33:                         tfItem.InnerItem.Fields[sourceFieldName].Value != string.Empty)
  34:                     {
  35:                         // setting the FieldName property that will store the field name of the data source item
  36:                         // that we will consume in the "GetItemHeader" method
  37:                         FieldName = tfItem.InnerItem.Fields[sourceFieldName].Value;
  38:                         break;
  39:                     }
  40:                 }
  41:             }
  42:             base.OnLoad(args);
  43:         }
  45:         protected override string GetItemHeader(Item item)
  46:         {
  47:             Assert.ArgumentNotNull(item, "item");
  48:             if (this.FieldName.StartsWith("@"))
  49:             {
  50:                 return item[this.FieldName.Substring(1)];
  51:             }
  52:             if (this.FieldName.Length > 0)
  53:             {
  54:                 if (ItemLanguage != null)
  55:                 {
  56:                     // return the language specific value of the source item's field
  57:                     return Sitecore.Context.ContentDatabase.GetItem(item.ID, Language.Parse(ItemLanguage))[this.FieldName];
  58:                 }
  59:             }
  60:             return item.DisplayName;
  61:         }
  63:         // this method is used to retrieve the currently selected content language
  64:         // note that there is no code that sets this value, it is being set from outside by Content Editor
  65:         public string ItemLanguage
  66:         {
  67:             set { this.itemlanguage = value; }
  68:             get { return this.itemlanguage; }
  69:         }
  70:     }
  71: }

This should be compiled and the binary should be copied over to the bin directory. Then you should do two configuration steps and create the field reference in Sitecore:
1. Define the namespace of the control in the <controlSources> section:
<source mode="on" namespace="SCUSAINC.Shell.Applications.ContentEditor" assembly="CustomFields" prefix="custom" />
- the "namespace" attribute  is equaled to the one defined in your class.
- the "assembly" attribute  is obviously the name of the DLL that you compile.
- the "prefix" is something that you define but it will be used in the "field type" item creation step #3 below.

2. Open up "/App_Config/FieldTypes.config" and add the reference to your custom field:
<fieldType name="custom lookup" type="Sitecore.Data.Fields.LookupField,Sitecore.Kernel" />
- the "name" attribute will be the same as the "field type" item name in the last step below.
- the "type" attribute should be the same as above.

3. Create an "field type" item under "/sitecore/system/Field types". The easiest way to do that is to duplicate the existing "lookup" item and change the "Control" field.
Note that you should name the item as the value of the "name" attribute set on the second step.lookup4
This field will contain the value of the prefix attribute set on the first step and the name of the class that you have previously compiled followed by the colon.

Well, enough boring stuff. Here is the result in Spanish:

and in Ukrainian:

Hope you find it useful.


Lars Nielsen said...

This is nice.

I think you should post the source to our Shared Source coordinator, Jimmie and make it a OS project.

Anonymous said...

How do I use XSLT to display the selected value? Thank you.

gijimmyj said...

Alex - Great post! I'm trying to do something similar and thought you might have an idea. I want to update a rich text field value on the item when a user changes a lookup value (not when user saves). That way we could set default text based on the selected target. Is it possible to capture the onchange event of a custom lookup?

Nilkanth said...

Great post. I am trying to do the similar thing. I am trying to update list of select-able items in custom field based on value of another droplink field.