arrow Created with Sketch. Insights Blog

Apr 26 / 2018

Workstate Codes: Sitecore – Adding Key Value Pairs on Item Creation

Christian Duvall (@christianduvall)
Vice President, Enterprise Services


At Workstate, we always strive to give the user the smoothest experience possible. In the case of Enterprise Web Content Management Systems, it's particularly important to customize and enhance the content editors' experience to whatever degree is possible, as it's largely dictated by the platform. Fortunately, with platforms like Sitecore, there are nearly an infinite number of ways to enhance your users' experience.

In this blog, we'll discuss how to customize a content editor's experience to automatically add a series of meta tags to an item based on some things we already knew at the time of creation. While the solution is fairly straight forward, there are nuances to consider for this implementation that make it worth sharing.


Features of the Code

  1. Adding a key-value pair collection to an item, to allow for meta tags (name, content) to be captured.
  2. Hooking into the item:created event handler to modify the item once the user inserts it.
  3. Ensuring the item is the type we want to edit.
  4. Creating default values to be added for each item.
  5. Creating a token replacement pattern, to provide contextual data to each tag.
  6. Cleaning up and storing the data on the meta tag field to prevent the "Do you want to save the changes to the item?" message when adding these values.

Configuring the Template

Create or configure your chosen template with a Name Value List field that will hold your data.  For ours, we're going to add a field that will store the Open Graph (og:) specific tags for our post.

You'll now have a spot on your Sitecore item to add one or more new tags in a key-value pair format.

Hooking the Events

Now that you have a template setup to handle the custom fields, we can add our tags when a user inserts a new instance of the item.  This is done by hooking into Sitecore's item:created event.

First, we'll create a handler class that will be configured to handle the event.

public class MetaTagEventHandler
{
    // we'll use this to make sure we're in the right context.
    public string Database => "master"; 
    public void OnItemAdded(object sender, EventArgs args)
    {
    }
}

Next, we'll hook the event in our configuration.  Since we're using Helix, our configuration will set int he App_Config/Include/Feature folder of our Feature project.

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set">
    <sitecore>
        <events>
            <event name="item:created">
                <handler type="Workstate.Sitecore.Feature.Blog.EventHandlers.MetaTagEventHandler, Workstate.Sitecore.Feature.Blog" method="OnItemAdded"></handler>
            </event>
        </events>
    </sitecore>
</configuration>

After a deploy, our event will be firing every time a new item is created.

Adding Our Meta Tags

Now that we have a working handler, it's time to get the job done.  

public void OnItemAdded(object sender, EventArgs args)
{
    // If no args, return.
    var eventArgs = args as Sitecore.Events.SitecoreEventArgs;
    if (args == null)
        return;
    
    // If there's no item attached, return.
    var itemCreatedEventArgs = Sitecore.Events.Event.ExtractParameter(eventArgs, 0) as ItemCreatedEventArgs;
    if (itemCreatedEventArgs?.Item == null)
      return;
    var item = itemCreatedEventArgs.Item;
    // Scope this to only affect master database changes.
    if (item?.Database != null && string.CompareOrdinal(item?.Database?.Name, this?.Database) != 0)
        return;
    // If the item is a blog post.
    if (string.Equals(item.TemplateID.ToString(), "{your-blog-post-template-guid}", StringComparison.OrdinalIgnoreCase))
    {
        // This is the collection of default tags we want to create.
        var openGraphTags = new NameValueCollection
        {
            {"locale", "en_US"},
            {"type", "article"},
            {"title", "{title}"},
            {"site_name", "{site-name}"},
        };
        // This is the replacement tokens that we'll generate to fill in the tags.
        // In real-world cases, the data will be grabbed from various spots within your content tree, such as a settings,
        // your context item, or your data source.
        var replacements = new SortedList<string, string>
        {
            { "{title}", item.Name},
            { "{site-name}", "Workstate Insights Blog"},
            { "{twitter}", "@workstate"}
        };
        try
        {
            // The currently accepted way to enter your edit context.
            item.Editing.BeginEdit();
            // Grab your meta-tag field.
            var field = item.Fields["{your-meta-tag-field-guid}"];
            if (field != null)
            {
                var defaults = new NameValueCollection();
                // Replace our mustache'd values with the ones we want to use.
                foreach (var key in openGraphTags.AllKeys)
                {
                    var val = replacements.Aggregate(openGraphTags[key], (c, v) => c.Replace(v.Key, v.Value));
                    defaults.Add(key, val);
                }
                var cleanTags = new NameValueCollection();
                foreach (var tag in defaults.AllKeys)
                {
                    // This is important.  If you add an empty tag, it will cause a prompt to the user to save the
                    // record, even though it's not interactive.  No, setting "silent" on EndEdit does not suppress
                    // this message.
                    if (!string.IsNullOrEmpty(defaults[tag]))
                    {
                        // Same as above, this will prevent the save prompt. This is picky, you can't URL encode, else
                        // the spaces will turn into "+", which is not an acceptable character.
                        var value = Uri.EscapeDataString(defaults[tag]);
                        cleanTags.Add(tag, value);
                    }
                }
                field.Value = StringUtil.NameValuesToString(cleanTags, "&");
            }
        }
        finally
        {
            // Setting this to silent will actually cause the save event not to properly fire.
            item.Editing.EndEdit(silent: false);
        }
    }
}

In Conclusion

This should be all you need to add dynamic values on item creation.  

After reviewing the code, you may see a few areas and comments that stand out. Here are a few considerations when you're performing your own implementation: 

  1. Always make sure you constraint your business logic to the item type you want to affect.  Using the Template ID is a great way to do this, because it's available in the master database and does not require additional completeness of your item to validate.
  2. The data in the Name Value List is serialized in a query-string format (e.g., key=value&key2=value) before saving.  Because of that, you must escape your data before converting it to a values string.  We accomplish that through use of the Uri.EscapeDataString() method.
  3. If you try to use "silent", the edit event will not get fired in this situation.  If your data is being formatted properly, you should not get prompted, but it's worth noting that some funky stuff can happen if you try this during creation.
  4. Much of the code above can be pulled out into helpers, which will allow you to extend this solution to build other types of default meta tags as well.
  5. For more information about Sitecore Events, check out the Sitecore Community Documentation and the Sitecore Development Network pages on the subject.


Like to learn more about customizing your Sitecore implementation? We'd love to talk.