here and there during my Sitecore development

Trigger field update with custom event handler item:saved

After spending my time trying to implement a custom event handler for one of my clients, I found one tricky bit that I didn’t expect.  I have done a fair amount of custom event handlers but this is the first time that I need to update field’s value with item:saved event.

The Sitecore template has Address, City, State, Zip, Latitude, and Longitude fields.  The Latitude and Longitude fields are used to locate a pin on Google map.  To help content authors, my job is to populate Latitude and Longitude fields automatically if we know the address.

So, the idea is to use custom event handler to generate values in latitude/longitude fields once a location item is saved

These are steps that I take

  1. Create a new custom event handler class
    First, let’s come up with a pseudo code

    • once an item is saved, check if the content template is “location”
    • get the address, city, state, zip, country fields
    • call Google Map API to get latitude/longitude
    • if there is no error, update Latitude/Longitude field in Sitecore and notify Sitecore user.

    Here’s the code

    namespace MyProject
    {
    public class LocationItemEventHandler
    {
    /// <summary>
    /// This custom event auto-populates latitude/longitude co-ordinate when a location item is saved
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    protected void OnItemSaved(object sender, EventArgs args)
    {
    var item = Event.ExtractParameter(args, 0) as Item;
    
    if (item != null)
    {
    if (item.TemplateID.Equals(Constants.LocationTemplateId))
    {
    string errMessage = "";
    string responseCode = "";
    string address = item.Fields["Street Address"].Value;
    string city = item.Fields["City"].Value;
    string state = item.Fields["State"].Value;
    string zipCode = item.Fields["Zip Code"].Value;
    string country = item.Fields["Country"].Value;
    var coordinate = GeocodeUtility.GetCoordinates(out errMessage, out responseCode,
    Constants.GoogleMapAPIKey, address, city, state,
    zipCode, country);
    if (responseCode == "200")
    {
    string latitude = coordinate.Latitude.ToString();
    string longitude = coordinate.Longitude.ToString();
    
    try
    {
    var latitudeField = item.Fields["Latitude"];
    var longitudeField = item.Fields["Longitude"];
    using (new SecurityDisabler())
    {
    item.Editing.BeginEdit();
    latitudeField.SetValue(latitude, true);
    longitudeField.SetValue(longitude, true);
    Sitecore.Context.ClientPage.ClientResponse.Alert(
    string.Format(
    "Fields updated automatically\r\nLatitude: {0}\r\nLongitude: {1}",
    latitude, longitude));
    item.Editing.EndEdit();
    }
    }
    catch (Exception exception)
    {
    Log.Error(exception.Message, this);
    }
    }
    }
    }
    }
    }
    }
    
  2. Bind this custom event handler in item:saved event in the web.config. Note that if you have multiple Sitecore instances (Content Management/Content Delivery), you only need to update Content Management web.config since item:saved event won’t take place in Content Delivery anyway.
    <event name="item:saved">
    <handler type="Sitecore.Links.ItemEventHandler, Sitecore.Kernel" method="OnItemSaved" />
    <handler type="Sitecore.Tasks.ItemEventHandler, Sitecore.Kernel" method="OnItemSaved" />
    <handler type="Sitecore.Globalization.ItemEventHandler, Sitecore.Kernel" method="OnItemSaved" />
    <handler type="Sitecore.Rules.ItemEventHandler, Sitecore.Kernel" method="OnItemSaved" />
    <!-- custom event handler for location -->
    <handler type="MyProject.LocationItemEventHandler, MyAssemblyName" method="OnItemSaved" />
    </event>
    

After testing, I found out that I ran into an infinite loop 🙂  Basically, when I update Latitude and Longitude fields, another item:saved event occurs. To solve this issue, I used SynchronizedCollection to keep track of item.  If the collection contains the item, exit the loop immediately.  If not, add item to the collection, update fields, and then remove the item from the collection

Here’s the updated code

namespace MyProject
{
public class LocationItemEventHandler
{
private static readonly SynchronizedCollection<ID> MProcess = new SynchronizedCollection<ID>();

/// <summary>
/// This custom event auto-populates latitude/longitude co-ordinate when a location item is saved
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
protected void OnItemSaved(object sender, EventArgs args)
{
var item = Event.ExtractParameter(args, 0) as Item;

if (item != null && !MProcess.Contains(item.ID))
{
if (item.TemplateID.Equals(Constants.LocationTemplateId))
{
string errMessage = "";
string responseCode = "";
string address = item.Fields["Street Address"].Value;
string city = item.Fields["City"].Value;
string state = item.Fields["State"].Value;
string zipCode = item.Fields["Zip Code"].Value;
string country = item.Fields["Country"].Value;
var coordinate = GeocodeUtility.GetCoordinates(out errMessage, out responseCode,
Constants.GoogleMapAPIKey, address, city, state,
zipCode, country);
if (responseCode == "200")
{
string latitude = coordinate.Latitude.ToString();
string longitude = coordinate.Longitude.ToString();

MProcess.Add(item.ID);

try
{
var latitudeField = item.Fields["Latitude"];
var longitudeField = item.Fields["Longitude"];
using (new SecurityDisabler())
{
item.Editing.BeginEdit();
latitudeField.SetValue(latitude, true);
longitudeField.SetValue(longitude, true);
Sitecore.Context.ClientPage.ClientResponse.Alert(
string.Format(
"Fields updated automatically\r\nLatitude: {0}\r\nLongitude: {1}",
latitude, longitude));
item.Editing.EndEdit();
}
}
catch (Exception exception)
{
Log.Error(exception.Message, this);
}
finally
{
MProcess.Remove(item.ID);
}
}
}
}
}
}
}
Advertisements

One response

  1. This is a common issue that comes up when the item saved event is raised. Thanks for sharing your particular solution.

    August 31, 2011 at 1:06 PM

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s