Tuesday, January 29, 2013

Enforcing SEO-friendly URLs

Sitecore is a very extensible and flexible CMS that allows you to implement and enforce all kinds of business rules. But just because you can, does not always mean you should. URL generation for SEO is one example of what I think should be left to the editors.

The way I've seen SEO-friendly URL enforcement implemented varies for some reason, but generally the rules implemented are something in the line of replacing all spaces in item names with hyphens so that ugly URLs like:
become pretty URLs like:

The concept of enforcing SEO-friendly URLs has been bothering me for a while now. It started while working on an implementation that was not as straight-forward as I expected.  I ended up using development time to discover all the enforcement rules. If you find yourself having to implement some custom hook into the URL resolver or write a hidden service that generates the desired URL, you are asking for trouble. And most importantly, you're doing it wrong!

Sitecore has a built-in model for doing replacements, and it works reasonably well.
Simply add replacement rules in the <encodenamereplacements> … </encodenamereplacements> of the web.config. Then to have this work successfully you would have to make absolutely sure users cannot create item names that have replacement characters in them. If you don't, you are sure to have an item that cannot be resolved at some point.  The illegal characters setting in the Web.config- <setting name="InvalidItemNameChars" … /> allows you to do just that. For example, if you are replacing spaces with hyphens, you would add the replacement rule, and then add the hyphen as an invalid character in an item name.

With all that being said, I feel that developers should stay away from these rules to begin with. Changing the "InvalidItemNameChars" setting could potentially limit the extensibility of your Sitecore solution, disabling you from installing modules that contain now "invalid" item names (or at least causing you headaches when you try to).  I've read blog posts that emphasize that editors in general would prefer that SEO-friendly URLs are enforced so that they wouldn't have to take the time to set them correctly.

If I were an editor though, it would take me less time and less frustration if I could just name my item exactly like it was going to appear in the URL rather than remember all the replacement rules that would go on behind the scenes. It is only fair to your solution and fair to your future users to let SEO-friendly URLs be managed by the user in the user interface. Empower and educate your users rather than restrict them.


Friday, January 25, 2013

Sitecore field value validation: unique value

Someone asked me today if Sitecore can validate whether an item's siblings contained unique values for a specific field. This post will describe how to create a custom field validator that will do just that.

1. Create the custom field validation rule in Sitecore
2. Create a class in a class library project for your code. The class should inherit from Sitecore.Data.Validators.StandardValidator

custom field validation rule

3. The example here has a custom parameter defined for 'parent' item path, which would specify the sub-tree of items against which field values are validated. The query selects all items that have the same field and value for that field as the currently validated one. Starting at the 'parent'.


public class UniqueValueValidator : StandardValidator
    {
      protected override ValidatorResult Evaluate()
        {
            string parent = this.Parameters["parent"];
            string value = base.GetControlValidationValue();
            string query = string.Format("{0}//*[@{1} = '{2}']", parent, this.GetFieldDisplayName(), value);

            foreach (Item item in global::Sitecore.Data.Database.GetDatabase("master").SelectItems(query))
            {
                //skip the current item
                if (item.ID != base.GetField().Item.ID)
                {
                    //set the error message
                    Text = GetText("Value must be unique. Item \"{0}\" contains the same value in field \"{1}\".", new[]{ item.DisplayName, GetFieldDisplayName()});
                    
                    return ValidatorResult.Error;
                }
            }

            return ValidatorResult.Valid;
        }

        protected override ValidatorResult GetMaxValidatorResult()
        {
            return GetFailedResult(ValidatorResult.Warning);
        }

        public override string Name
        {
            get
            {
                return "UniqueValue";
            }
        }
}

It is important to note that having a custom validator in place will not prevent an editor from creating items with duplicate field values. All it will do is provide feedback, so the solution should still be able to handle duplicate field values if they do appear.