Sitecore URLs With Dashes Instead of Spaces
Posted
Saturday, December 27, 2014 7:15 PM
by
Kevin
One of the SEO best practices everyone should trying to implement on their sites relates to URLs with spaces. I’m sure you’ve seen a URL with an encoded space before - %20. Yuck. Not only does it look bad to your site’s visitors, but search engines don’t particularly like it either. From what I’ve read (e.g. http://goo.gl/mwNpgP), crawlers like Google definitely prefer to see dashes where humans like to see spaces.
I’ve seen a few recommendations for how to deal with this in Sitecore before. If you’re currently using encodeNameReplacements, InvalidItemNameChars, and/or ItemNameValidation - I think I have discovered a better way...
Sitecore generates URLs for items using the name of the item (and its parent items in the content tree), therefore what we really need is for items to be named something like “my-blog-post” instead of “My Blog Post”. Having content authors follow a naming convention is probably too much to ask, and what if they forget? Plus, it’s not particularly friendly for them to look at in the Sitecore content tree either.
What if we could let them name the item however they want and let them continue to see the name they entered in the content tree, but actually have the item named in an SEO friendly way in the database? What mechanism does Sitecore give us to allow an item to be named one thing, but display using a different name in the content tree? Perhaps you’ve seen or used the Display Name before?
Display Name in the Sitecore Ribbon
The essence of my solution to the spaces in URLs problem is to handle the Sitecore events for an item being added or renamed, detect items with a space in their name, rename them to use a dash in place of a space, and copy the original name into the Display Name so that content authors don’t see what we’ve done.
Here’s the complete code for a solution that I’m using on my current project. It makes use of a technique that I mentioned in a previous blog post (see Subscribing to Sitecore Events in a PreApplicationStartMethod) to subscribe to the item:added and item:renamed events, and then performs the above logic. I also threw in a .ToLower() on the item name while I’m at it because apparently search engines prefer to see all lowercase URLs (note - I’m also using the lowercaseUrls attribute on the linkManager, so this shouldn’t really be necessary - up to you if you want to include it in your solution or not).
Oh - that regex you see in there takes care of cases where an item was named with spaces surrounding a dash. Otherwise “Account - Change Password” would become “account---change-password”.
using System;
using System.Text.RegularExpressions;
using System.Web;
using Sitecore.Data.Items;
using Sitecore.Events;
[assembly: PreApplicationStartMethod(typeof([Namespace].[Class]), "Start")]
namespace [Namespace]
{
///
/// Sitecore item:added and item:renamed event handler that ensures
/// item names are created with dashes instead of spaces and that
/// the original name (with spaces) is stored in Display Name.
///
public class [Class]
{
///
/// Start method that registers the Sitecore event handlers.
///
public static void Start()
{
var handler = new [Class]();
Event.Subscribe("item:added", new EventHandler(handler.OnEvent));
Event.Subscribe("item:renamed", new EventHandler(handler.OnEvent));
}
///
/// Called when a Sitecore item is saved or renamed.
///
public void OnEvent(object sender, EventArgs args)
{
// Pull the item from the args.
var item = (Item)Event.ExtractParameter(args, 0);
// Do nothing if the item isn't in the master database.
if (item.Database.Name != "master") return;
// We're only concerned with items under /sitecore/content
// (so not Layouts or Templates or whatever).
if (!item.Paths.Path.StartsWith("/sitecore/content")) return;
// If spaces are found in the name, replace them.
string newName;
if (item.Name == (newName = Regex.Replace(item.Name.ToLower().Replace(' ', '-'), "-{2,}", "-")))
{
// If no spaces were found, do nothing.
return;
}
// Edit the item that was just saved.
item.Editing.BeginEdit();
try
{
// Set the display name to the original name (with spaces).
item.Appearance.DisplayName = item.Name;
// Set the item name to the new name (with dashes).
item.Name = newName;
}
finally
{
item.Editing.EndEdit();
}
}
}
}
I can’t take full credit for this solution as one of my Hanson Dodge Creative co-workers originally proposed the idea (and I think similar code was already in use on another client’s site), but this particular implementation is mostly mine. In the future, I would like to improve this solution in some ways - perhaps implement it as a rules engine action to plug into something like John West mentions in his post here: http://goo.gl/7UAm5W. If you have any suggestions, please pass them along! I can be reached on Twitter @williamsk000 or you can leave a comment here.