Home » Configuration » Configuration Factory (Page 3)

Category Archives: Configuration Factory

Utilize the Strategy Design Pattern for Content Editor Warnings in Sitecore

This post is a continuation of a series of posts I’m putting together around using design patterns in Sitecore implementations, and will show a “proof of concept” around using the Strategy pattern — a pattern where a family of “algorithms” (for simplicity you can think of these as classes that implement the same interface) which should be interchangeable when used by client code, and such holds true even when each do something completely different than others within the same family.

The Strategy pattern can serve as an alternative to the Template method pattern — a pattern where classes have an abstract base class that defines most of an β€œalgorithm” for how classes that inherit from it work but provides method stubs (abstract methods) and method hooks (virtual methods) for subclasses to implement or override — and will prove this in this post by providing an alternative solution to the one I had shown in my previous post on the Template method pattern.

In this “proof of concept”, I will be adding a processor to the <getContentEditorWarnings> pipeline in order to add custom content editor warnings for Items — if you are unfamiliar with content editor warnings in Sitecore, the following screenshot illustrates an “out of the box” content editor warning around publishing and workflow state:

content-editor-warning-example

To start, I am reusing the following interface and classes from my previous post on the Template method pattern:

using System.Collections.Generic;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings
{
    public interface IWarning
    {
        string Title { get; set; }

        string Message { get; set; }

        List<CommandLink> Links { get; set; }

        bool HasContent();

        IWarning Clone();
    }
}

Warnings will have a title, an error message for display, and a list of Sheer UI command links — the CommandLink class is defined further down in this post — to be displayed and invoked when clicked.

You might be asking why I am defining this when I can just use what’s available in the Sitecore API? Well, I want to inject these values via the Sitecore Configuration Factory, and hopefully this will become clear once you have a look at the Sitecore configuration file further down in this post.

Next, we have a class that implements the interface above:

using System.Collections.Generic;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings
{
    public class Warning : IWarning
    {
        public string Title { get; set; }

        public string Message { get; set; }

        public List<CommandLink> Links { get; set; }

        public Warning()
        {
            Links = new List<CommandLink>();
        }

        public bool HasContent()
        {
            return !string.IsNullOrWhiteSpace(Title)
                    || !string.IsNullOrWhiteSpace(Title)
                    || !string.IsNullOrWhiteSpace(Message);
        }

        public IWarning Clone()
        {
            IWarning clone = new Warning { Title = Title, Message = Message };
            foreach (CommandLink link in Links)
            {
                clone.Links.Add(new CommandLink { Text = link.Text, Command = link.Command });
            }

            return clone;
        }
    }
}

The HasContent() method just returns “true” if the instance has any content to display though this does not include CommandLinks — what’s the point in displaying these if there is no warning content to be displayed with them?

The Clone() method makes a new instance of the Warning class, and copies values into it — this is useful when defining tokens in strings that must be expanded before being displayed. If we expand them on the instance that is injected via the Sitecore Configuration Factory, the changed strings will persistent in memory until the application pool is recycled for the Sitecore instance.

The following class represents a Sheer UI command link to be displayed in the content editor warning so content editors/authors can take action on the warning:


namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings
{
    public class CommandLink
    {
        public string Text { get; set; }

        public string Command { get; set; }
    }
}

The Strategy pattern calls for a family of “algorithms” which can be interchangeably used. In order for us to achieve this, we need to define an interface for this family of “algorithms”:

using System.Collections.Generic;

using Sitecore.Data.Items;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern
{
    public interface IWarningsGenerator
    {
        Item Item { get; set; }

        IEnumerable<IWarning> Generate();
    }
}

Next, I created the following class that implements the interface above to ascertain whether a supplied Item has too many child Items:

using System.Collections.Generic;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern
{
    public class TooManyChildItemsWarningsGenerator : IWarningsGenerator
    {
        private int MaxNumberOfChildItems { get; set; }

        private IWarning Warning { get; set; }

        public Item Item { get; set; }

        public IEnumerable<IWarning> Generate()
        {
            AssertProperties();
            if (Item.Children.Count <= MaxNumberOfChildItems)
            {
                return new List<IWarning>();
            }

            return new[] { Warning };
        }

        private void AssertProperties()
        {
            Assert.ArgumentCondition(MaxNumberOfChildItems > 0, "MaxNumberOfChildItems", "MaxNumberOfChildItems must be set correctly in configuration!");
            Assert.IsNotNull(Warning, "Warning", "Warning must be set in configuration!");
            Assert.ArgumentCondition(Warning.HasContent(), "Warning", "Warning should have some fields populated from configuration!");
            Assert.IsNotNull(Item, "Item", "Item must be set!");
        }
    }
}

The “maximum number of child items allowed” value — this is stored in the MaxNumberOfChildItems integer property of the class — is passed to the class instance via the Sitecore Configuration Factory (you’ll see this defined in the Sitecore configuration file further down in this post).

The IWarning instance that is injected into the instance of this class will give content authors/editors the ability to convert the Item into an Item Bucket when it has too many child Items.

I then defined another class that implements the interface above — a class whose instances determine whether Items have invalid characters in their names:

using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern
{
    public class HasInvalidCharacetersInNameWarningsGenerator : IWarningsGenerator
    {
        private string CharacterSeparator { get; set; }

        private string Conjunction { get; set; }

        private List<string> InvalidCharacters { get; set; }

        private IWarning Warning { get; set; }

        public Item Item { get; set; }

        public HasInvalidCharacetersInNameWarningsGenerator()
        {
            InvalidCharacters = new List<string>();
        }

        public IEnumerable<IWarning> Generate()
        {
            AssertProperties();
            HashSet<string> charactersFound = new HashSet<string>();
            foreach (string character in InvalidCharacters)
            {
                if (Item.Name.Contains(character))
                {
                    charactersFound.Add(character.ToString());
                }
            }

            if(!charactersFound.Any())
            {
                return new List<IWarning>();
            }

            IWarning warning = Warning.Clone();
            string charactersFoundString = string.Join(CharacterSeparator, charactersFound);
            int lastSeparator = charactersFoundString.LastIndexOf(CharacterSeparator);
            if (lastSeparator < 0)
            {
                warning.Message = ReplaceInvalidCharactersToken(warning.Message, charactersFoundString);
                return new[] { warning };
            }

            warning.Message = ReplaceInvalidCharactersToken(warning.Message, Splice(charactersFoundString, lastSeparator, CharacterSeparator.Length, Conjunction));
            return new[] { warning };
        }

        private void AssertProperties()
        {
            Assert.IsNotNullOrEmpty(CharacterSeparator, "CharacterSeparator", "CharacterSeparator must be set in configuration!");
            Assert.ArgumentCondition(InvalidCharacters != null && InvalidCharacters.Any(), "InvalidCharacters", "InvalidCharacters must be set in configuration!");
            Assert.IsNotNull(Warning, "Warning", "Warning must be set in configuration!");
            Assert.ArgumentCondition(Warning.HasContent(), "Warning", "Warning should have some fields populated from configuration!");
            Assert.IsNotNull(Item, "Item", "Item must be set!");
        }

        private static string Splice(string value, int startIndex, int length, string replacement)
        {
            if(string.IsNullOrWhiteSpace(value))
            {
                return value;
            }

            return string.Concat(value.Substring(0, startIndex), replacement, value.Substring(startIndex + length));
        }

        private static string ReplaceInvalidCharactersToken(string value, string replacement)
        {
            return value.Replace("$invalidCharacters", replacement);
        }
    }
}

The above class will return an IWarning instance when an Item has invalid characters in its name — these invalid characters are defined in Sitecore configuration.

The Generate() method iterates over all invalid characters passed from Sitecore configuration and determines if they exist in the Item name. If they do, they are added to a HashSet<string> instance — I’m using a HashSet<string> to ensure the same character isn’t added more than once to the collection — which is used for constructing the warning message to be displayed to the content author/editor.

Once the Generate() method has iterated through all invalid characters, a string is built using the HashSet<string> instance, and is put in place wherever the $invalidCharacters token is defined in the Message property of the IWarning instance.

Now that we have our family of “algorithms” defined, we need a class to encapsulate and invoke these. I defined the following interface for classes that perform this role:

using System.Collections.Generic;

using Sitecore.Data.Items;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern
{
    public interface IWarningsGeneratorContext
    {
        IWarningsGenerator Generator { get; set; }

        IEnumerable<IWarning> GetWarnings(Item item);
    }
}

I then defined the following class which implements the interface above:

using System.Collections.Generic;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern
{
    public class WarningsGeneratorContext : IWarningsGeneratorContext
    {
        public IWarningsGenerator Generator { get; set; }

        public IEnumerable<IWarning> GetWarnings(Item item)
        {
            Assert.IsNotNull(Generator, "Generator", "Generator must be set!");
            Assert.ArgumentNotNull(item, "item");
            Generator.Item = item;
            return Generator.Generate();
        }
    }
}

Instances of the class above take in an instance of IWarningsGenerator via its Generator property — in a sense, we are “lock and loading” WarningsGeneratorContext instances to get them ready. Instances then pass a supplied Item instance to the IWarningsGenerator instance, and invoke its GetWarnings() method. This method returns a collection of IWarning instances.

In a way, the IWarningsGeneratorContext instances are really adapters for IWarningsGenerator instances — IWarningsGeneratorContext instances provide a bridge for client code to use IWarningsGenerator instances via its own little API.

Now that we have all of the stuff above — yes, I know, there is a lot of code in this post, and we’ll reflect on this at the end of the post — we need a class whose instance will serve as a <getContentEditorWarnings> pipeline processor:

using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Pipelines.GetContentEditorWarnings;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern
{
    public class ContentEditorWarnings
    {
        private List<IWarningsGenerator> WarningsGenerators { get; set; }

        private IWarningsGeneratorContext WarningsGeneratorContext { get; set; }

        public ContentEditorWarnings()
        {
            WarningsGenerators = new List<IWarningsGenerator>();
        }

        public void Process(GetContentEditorWarningsArgs args)
        {
            AssertProperties();
            Assert.ArgumentNotNull(args, "args");
            Assert.ArgumentNotNull(args.Item, "args.Item");
            
            IEnumerable<IWarning> warnings = GetWarnings(args.Item);
            if(warnings == null || !warnings.Any())
            {
                return;
            }

            foreach(IWarning warning in warnings)
            {
                AddWarning(args, warning);
            }
        }

        private IEnumerable<IWarning> GetWarnings(Item item)
        {
            List<IWarning> warnings = new List<IWarning>();
            foreach(IWarningsGenerator generator in WarningsGenerators)
            {
                IEnumerable<IWarning> generatorWarnings = GetWarnings(generator, item);
                if(generatorWarnings != null && generatorWarnings.Any())
                {
                    warnings.AddRange(generatorWarnings);
                }
            }

            return warnings;
        }

        private IEnumerable<IWarning> GetWarnings(IWarningsGenerator generator, Item item)
        {
            WarningsGeneratorContext.Generator = generator;
            return WarningsGeneratorContext.GetWarnings(item);
        }

        private void AddWarning(GetContentEditorWarningsArgs args, IWarning warning)
        {
            if(!warning.HasContent())
            {
                return;
            }

            GetContentEditorWarningsArgs.ContentEditorWarning editorWarning = args.Add();
            if(!string.IsNullOrWhiteSpace(warning.Title))
            {
                editorWarning.Title = TranslateText(warning.Title);
            }

            if(!string.IsNullOrWhiteSpace(warning.Message))
            {
                editorWarning.Text = TranslateText(warning.Message);
            }

            if (!warning.Links.Any())
            {
                return;
            }
            
            foreach(CommandLink link in warning.Links)
            {
                editorWarning.AddOption(TranslateText(link.Text), link.Command);
            }
        }

        private string TranslateText(string text)
        {
            if(string.IsNullOrWhiteSpace(text))
            {
                return text;
            }

            return Translate.Text(text);
        }

        private void AssertProperties()
        {
            Assert.IsNotNull(WarningsGeneratorContext, "WarningsGeneratorContext", "WarningsGeneratorContext must be set in configuration!");
            Assert.ArgumentCondition(WarningsGenerators != null && WarningsGenerators.Any(), "WarningsGenerators", "At least one WarningsGenerator must be set in configuration!");
        }
    }
}

The Process() method is the main entry into the pipeline processor. The method delegates to the GetWarnings() method to get a collection of IWarning instances from all IWarningGenerator instances that were injected into the class instance via the Sitecore Configuration Factory.

The GetWarnings() method iterates over all IWarningsGenerator instances, and passes each to the other GetWarnings() method overload which basically sets the IWarningGenerator on the IWarningsGeneratorContext instance, and invokes its GetWarnings() method with the supplied Item instance.

Once all IWarning instances have been collected, the Process() method iterates over the IWarning collection, and adds them to the GetContentEditorWarningsArgs instance via the AddWarning() method.

I then registered everything above in Sitecore using the following Sitecore patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getContentEditorWarnings>
        <processor type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern.ContentEditorWarnings, Sitecore.Sandbox">
          <WarningsGenerators hint="list">
            <WarningsGenerator type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern.TooManyChildItemsWarningsGenerator, Sitecore.Sandbox">
              <MaxNumberOfChildItems>20</MaxNumberOfChildItems>
              <Warning type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Warning, Sitecore.Sandbox">
                <Title>This Item has too many child items!</Title>
                <Message>Please consider converting this Item into an Item Bucket.</Message>
                <Links hint="list">
                  <Link type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.CommandLink">
                    <Text>Convert to Item Bucket</Text>
                    <Command>item:bucket</Command>
                  </Link>
                </Links>
              </Warning>
            </WarningsGenerator>
            <WarningsGenerator type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern.HasInvalidCharacetersInNameWarningsGenerator, Sitecore.Sandbox">
              <CharacterSeparator>,&amp;nbsp;</CharacterSeparator>
              <Conjunction>&amp;nbsp;and&amp;nbsp;</Conjunction>
              <InvalidCharacters hint="list">
                <Character>-</Character>
                <Character>$</Character>
                <Character>1</Character>
              </InvalidCharacters>
              <Warning type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Warning, Sitecore.Sandbox">
                <Title>The name of this Item has invalid characters!</Title>
                <Message>The name of this Item contains $invalidCharacters. Please consider renaming the Item.</Message>
                <Links hint="list">
                  <Link type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.CommandLink">
                    <Text>Rename Item</Text>
                    <Command>item:rename</Command>
                  </Link>
                </Links>
              </Warning>
            </WarningsGenerator>
          </WarningsGenerators>
          <WarningsGeneratorContext type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Strategy_Pattern.WarningsGeneratorContext, Sitecore.Sandbox" />
        </processor>
      </getContentEditorWarnings>
    </pipelines>
  </sitecore>
</configuration>

Let’s test this out.

I set up an Item with more than 20 child Items, and gave it a name that includes -, $ and 1 — these are defined as invalid in the configuration file above:

strategy-1

As you can see, both warnings appear on the Item in the content editor.

Let’s convert the Item into an Item Bucket:

strategy-2

As you can see the Item is now an Item Bucket:

strategy-3

Let’s fix the Item’s name:

strategy-4

The Item’s name is now fixed, and there are no more content editor warnings:

strategy-5

You might be thinking “Mike, that is a lot of code — a significant amount over what you had shown in your previous post where you used the Template method pattern — so why bother with the Strategy pattern?”

Yes, there is more code here, and definitely more moving parts to the Strategy pattern over the Template method pattern.

So, what’s the benefit here?

Well, in the Template method pattern, subclasses are tightly coupled to their abstract base class. A change to the parent class could potentially break code in the subclasses, and this will require code in all subclasses to be changed. This could be quite a task if subclasses are defined in multiple projects that don’t reside in the same solution as the parent class.

The Strategy pattern forces loose coupling among all instances within the pattern thus reducing the likelihood that changes in one class will adversely affect others.

However, with that said, it does add complexity by introducing more code, so you should consider the pros and cons of using the Strategy pattern over the Template method pattern, or perhaps even decide if you should use a pattern to begin with.

Remember, the KISS principle should be followed wherever/whenever possible when designing and developing software.

If you have any thoughts on this, please drop a comment.

Employ the Template Method Design Pattern for Content Editor Warnings in Sitecore

This post is a continuation of a series of posts I’m putting together around using design patterns in Sitecore solutions, and will show a “proof of concept” around using the Template method pattern — a pattern where classes have an abstract base class that defines most of an “algorithm” for how classes that inherit from it work but provides method stubs — these are abstract methods that must be implemented by subclasses to “fill in the blanks” of the “algorithm” — and method hooks — these are virtual methods that can be overridden if needed.

In this “proof of concept”, I am tapping into the <getContentEditorWarnings> pipeline in order to add custom content editor warnings for Items — if you are unfamiliar with content editor warnings in Sitecore, the following screenshot illustrates an “out of the box” content editor warning around publishing and workflow state:

content-editor-warning-example

To start, I defined the following interface for classes that will contain content for warnings that will be displayed in the content editor:

using System.Collections.Generic;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings
{
    public interface IWarning
    {
        string Title { get; set; }

        string Message { get; set; }

        List<CommandLink> Links { get; set; }

        bool HasContent();

        IWarning Clone();
    }
}

Warnings will have a title, an error message for display, and a list of Sheer UI command links — the CommandLink class is defined further down this post — to be displayed and invoked when clicked.

You might be asking why I am defining this when I can just use what’s available in the Sitecore API? Well, I want to inject these values via the Sitecore Configuration Factory, and hopefully this will become clear once you have a look at the Sitecore configuration file further down in this post.

Next, I defined the following class that implements the interface above:

using System.Collections.Generic;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings
{
    public class Warning : IWarning
    {
        public string Title { get; set; }

        public string Message { get; set; }

        public List<CommandLink> Links { get; set; }

        public Warning()
        {
            Links = new List<CommandLink>();
        }

        public bool HasContent()
        {
            return !string.IsNullOrWhiteSpace(Title)
                    || !string.IsNullOrWhiteSpace(Title)
                    || !string.IsNullOrWhiteSpace(Message);
        }

        public IWarning Clone()
        {
            IWarning clone = new Warning { Title = Title, Message = Message };
            foreach (CommandLink link in Links)
            {
                clone.Links.Add(new CommandLink { Text = link.Text, Command = link.Command });
            }

            return clone;
        }
    }
}

The HasContent() method just returns “true” if the instance has any content to display though this does not include CommandLinks — what’s the point in displaying these if there is no warning content to be displayed with them?

The Clone() method makes a new instance of the Warning class, and copies values into it — this is useful when defining tokens in strings that must be expanded before being displayed. If we expand them on the instance that is injected via the Sitecore Configuration Factory, the changed strings will persistent in memory until the application pool is recycled for the Sitecore instance.

The following class represents a Sheer UI command link to be displayed in the content editor warning so content editors/authors can take action on the warning:


namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings
{
    public class CommandLink
    {
        public string Text { get; set; }

        public string Command { get; set; }
    }
}

I then built the following abstract class to serve as the base class for all classes whose instances will serve as a <getContentEditorWarnings> pipeline processor:

using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.Pipelines.GetContentEditorWarnings;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Template_Method_Pattern
{
    public abstract class ContentEditorWarnings
    {
        public void Process(GetContentEditorWarningsArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            Assert.ArgumentNotNull(args.Item, "args.Item");
            IEnumerable<IWarning> warnings = GetWarnings(args.Item);
            if(warnings == null || !warnings.Any())
            {
                return;
            }

            foreach(IWarning warning in warnings)
            {
                AddWarning(args, warning);
            }
        }

        protected abstract IEnumerable<IWarning> GetWarnings(Item item);

        private void AddWarning(GetContentEditorWarningsArgs args, IWarning warning)
        {
            if(!warning.HasContent())
            {
                return;
            }

            GetContentEditorWarningsArgs.ContentEditorWarning editorWarning = args.Add();
            if(!string.IsNullOrWhiteSpace(warning.Title))
            {
                editorWarning.Title = TranslateText(warning.Title);
            }

            if(!string.IsNullOrWhiteSpace(warning.Message))
            {
                editorWarning.Text = TranslateText(warning.Message);
            }

            if (!warning.Links.Any())
            {
                return;
            }
            
            foreach(CommandLink link in warning.Links)
            {
                editorWarning.AddOption(TranslateText(link.Text), link.Command);
            }
        }

        protected virtual string TranslateText(string text)
        {
            if(string.IsNullOrWhiteSpace(text))
            {
                return text;
            }

            return Translate.Text(text);
        }
    }
}

So what’s going on in this class? Well, the Process() method gets a collection of IWarnings from the GetWarnings() method — this method must be defined by subclasses of this class; iterates over them; and delegates to the AddWarning() method to add each to the GetContentEditorWarningsArgs instance.

The TranslateText() method calls the Text() method on the Sitecore.Globalization.Translate class — this lives in Sitecore.Kernel.dll — and is used when adding values on IWarning instances to the GetContentEditorWarningsArgs instance. This method is a hook, and can be overridden by subclasses if needed. I am not overriding this method on the subclasses further down in this post.

I then defined the following subclass of the class above to serve as a <getContentEditorWarnings> pipeline processor to warn content authors/editors if an Item has too many child Items:

using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Template_Method_Pattern
{
    public class TooManyChildItemsWarnings : ContentEditorWarnings
    {
        private int MaxNumberOfChildItems { get; set; }

        private IWarning Warning { get; set; }

        protected override IEnumerable<IWarning> GetWarnings(Item item)
        {
            AssertProperties();
            if(item.Children.Count <= MaxNumberOfChildItems)
            {
                return new List<IWarning>();
            }

            return new[] { Warning };
        }

        private void AssertProperties()
        {
            Assert.ArgumentCondition(MaxNumberOfChildItems > 0, "MaxNumberOfChildItems", "MaxNumberOfChildItems must be set correctly in configuration!");
            Assert.IsNotNull(Warning, "Warning", "Warning must be set in configuration!");
            Assert.ArgumentCondition(Warning.HasContent(), "Warning", "Warning should have some fields populated from configuration!");
        }
    }
}

The class above is getting its IWarning instance and maximum number of child Items value from Sitecore configuration.

The GetWarnings() method ascertains whether the Item has too many child Items and returns the IWarning instance when it does in a collection — I defined this to be a collection to allow <getContentEditorWarnings> pipeline processors subclassing the abstract base class above to return more than one warning if needed.

I then defined another subclass of the abstract class above to serve as another <getContentEditorWarnings> pipeline processor:

using System.Collections.Generic;
using System.Linq;

using Sitecore.Data.Items;
using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Template_Method_Pattern
{
    public class HasInvalidCharacetersInNameWarnings : ContentEditorWarnings
    {
        private string CharacterSeparator { get; set; }

        private string Conjunction { get; set; }

        private List<string> InvalidCharacters { get; set; }

        private IWarning Warning { get; set; }

        public HasInvalidCharacetersInNameWarnings()
        {
            InvalidCharacters = new List<string>();
        }

        protected override IEnumerable<IWarning> GetWarnings(Item item)
        {
            AssertProperties();
            HashSet<string> charactersFound = new HashSet<string>();
            foreach (string character in InvalidCharacters)
            {
                if(item.Name.Contains(character))
                {
                    charactersFound.Add(character.ToString());
                }
            }

            if(!charactersFound.Any())
            {
                return new List<IWarning>();
            }

            IWarning warning = Warning.Clone();
            string charactersFoundString = string.Join(CharacterSeparator, charactersFound);
            int lastSeparator = charactersFoundString.LastIndexOf(CharacterSeparator);
            if (lastSeparator < 0)
            {
                warning.Message = ReplaceInvalidCharactersToken(warning.Message, charactersFoundString);
                return new[] { warning };
            }

            warning.Message = ReplaceInvalidCharactersToken(warning.Message, Splice(charactersFoundString, lastSeparator, CharacterSeparator.Length, Conjunction));
            return new[] { warning };
        }

        private void AssertProperties()
        {
            Assert.IsNotNullOrEmpty(CharacterSeparator, "CharacterSeparator", "CharacterSeparator must be set in configuration!");
            Assert.ArgumentCondition(InvalidCharacters != null && InvalidCharacters.Any(), "InvalidCharacters", "InvalidCharacters must be set in configuration!");
            Assert.IsNotNull(Warning, "Warning", "Warning must be set in configuration!");
            Assert.ArgumentCondition(Warning.HasContent(), "Warning", "Warning should have some fields populated from configuration!");
        }

        private static string Splice(string value, int startIndex, int length, string replacement)
        {
            if(string.IsNullOrWhiteSpace(value))
            {
                return value;
            }

            return string.Concat(value.Substring(0, startIndex), replacement, value.Substring(startIndex + length));
        }

        private static string ReplaceInvalidCharactersToken(string value, string replacement)
        {
            return value.Replace("$invalidCharacters", replacement);
        }
    }
}

The above class will return an IWarning instance when an Item has invalid characters in its name — these invalid characters are defined in Sitecore configuration.

The GetWarnings() method iterates over all invalid characters passed from Sitecore configuration and determines if they exist in the Item name. If they do, they are added to a HashSet<string> instance — I’m using a HashSet<string> to ensure the same character isn’t added more than once to the collection — which is be used for constructing the warning message to be displayed to the content author/editor.

Once the GetWarnings() method has iterated through all invalid characters, a string is built using the HashSet<string> instance, and is put in place wherever the $invalidCharacters token is defined in the Message property of the IWarning instance.

I then registered everything above in Sitecore via the following patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <getContentEditorWarnings>
        <processor type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Template_Method_Pattern.TooManyChildItemsWarnings, Sitecore.Sandbox">
          <MaxNumberOfChildItems>20</MaxNumberOfChildItems>
          <Warning type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Warning, Sitecore.Sandbox">
            <Title>This Item has too many child items!</Title>
            <Message>Please consider converting this Item into an Item Bucket.</Message>
            <Links hint="list">
              <Link type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.CommandLink">
                <Text>Convert to Item Bucket</Text>
                <Command>item:bucket</Command>
              </Link>
            </Links>
          </Warning>
        </processor>
        <processor type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Template_Method_Pattern.HasInvalidCharacetersInNameWarnings, Sitecore.Sandbox">
          <CharacterSeparator>,&amp;nbsp;</CharacterSeparator>
          <Conjunction>&amp;nbsp;and&amp;nbsp;</Conjunction>
          <InvalidCharacters hint="list">
            <Character>-</Character>
            <Character>$</Character>
            <Character>1</Character>
          </InvalidCharacters>
          <Warning type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.Warning, Sitecore.Sandbox">
            <Title>The name of this Item has invalid characters!</Title>
            <Message>The name of this Item contains $invalidCharacters. Please consider renaming the Item.</Message>
            <Links hint="list">
              <Link type="Sitecore.Sandbox.Pipelines.GetContentEditorWarnings.CommandLink">
                <Text>Rename Item</Text>
                <Command>item:rename</Command>
              </Link>
            </Links>
          </Warning>
        </processor>
      </getContentEditorWarnings>
    </pipelines>
  </sitecore>
</configuration>

As you can see, I am injecting warning data into my <getContentEditorWarnings> pipeline processors as well as other things which are used in code for each.

For the TooManyChildItemsWarnings <getContentEditorWarnings> pipeline processor, we are giving content authors/editors the ability to convert the Item into an Item bucket — we are injecting the item:bucket command via the configuration file above.

For the HasInvalidCharacetersInNameWarnings <getContentEditorWarnings> pipeline processor, we are passing in the Sheer UI command that will launch the Item Rename dialog to give content authors/editors the ability to rename the Item if it has invalid characters in its name.

Let’s see if this works.

I navigated to an Item in my content tree that has less than 20 child Items and has no invalid characters in its name:

no-content-editor-warnings

As you can see, there are no warnings.

Let’s go to another Item, one that not only has more than 20 child Items but also has invalid characters in its name:

both-warnings-appear

As you can see, both warnings are appearing for this Item.

Let’s now rename the Item:

rename-dialog

Great! Now the ‘invalid characters in name” warning is gone. Let’s convert this Item into an Item Bucket:

item-bucket-click-1

After clicking the ‘Convert to Item Bucket’ link, I saw the following dialog:

item-bucket-click-2

After clicking the ‘OK’ button, I saw the following progress dialog:

item-bucket-click-3

As you can see, the Item is now an Item Bucket, and both content editor warnings are gone:

item-bucket-click-4

If you have any thoughts on this, or have ideas on other places where you might want to employ the Template method pattern, please share in a comment.

Also, if you would like to see another example around adding a custom content editor warning in Sitecore, check out an older post of mine where I added one for expanding tokens in fields on an Item.

Until next time, keep on learning and keep on Sitecoring — Sitecoring is a legit verb, isn’t it? πŸ˜‰

Chain Together Sitecore Functionality Using the Chain-of-responsibility Design Pattern

This post is a continuation of a series of posts I’m putting together around using design patterns in Sitecore solutions, and will show a “proof of concept” around using the chain-of-responsibility pattern — a pattern where objects are linked together and are invoked in a cascading manner.

I decided to revisit a post I wrote over two years ago on chaining together client commands — these are invoked via the Sheer UI framework which drives how the ribbon, item context menu and other things work in Sitecore.

Earlier today — or yesterday depending on where you are — I began my code journey by building the following interface:

using Sitecore.Shell.Framework.Commands;

namespace Sitecore.Sandbox.Invokers.Commands
{
    public interface ICommandInvoker
    {
        bool HasCommand();

        void SetCommand(string commandName);

        void SetNextInvoker(ICommandInvoker nextInvoker);

        bool CanInvoke(CommandContext commandContext);

        void Invoke(CommandContext commandContext);
    }
}

The idea here is instances of classes that implement the interface above — let’s call them processing objects — will encapsulate instances of subclasses of Sitecore.Shell.Framework.Commands.Command — this is defined in Sitecore.Kernel.dll.

Each processing object will be linked to another processing object — I’m calling this other processing object the NextInvoker in code — which is invoked after the previous one.

Since the NextInvoker implements the interface above, it can also have its own NextInvoker thus chaining together a series of classes that implement the ICommandInvoker interface above.

I decided employ another design pattern in this solution — the Null Object pattern — and defined the following class whose instances will serve as a Null Object — I did this to reduce the amount of null checks in code (I should probably devote an entire post on this pattern):

using Sitecore.Diagnostics;
using Sitecore.Shell.Framework.Commands;

namespace Sitecore.Sandbox.Invokers.Commands
{
    public class NullCommandInvoker : ICommandInvoker
    {
        public NullCommandInvoker()
        {
        }

        public bool HasCommand()
        {
            return false;
        }

        public void SetCommand(string commandName)
        {
        }

        public void SetNextInvoker(ICommandInvoker nextInvoker)
        {
        }

        public bool CanInvoke(CommandContext commandContext)
        {
            return true;
        }

        public void Invoke(CommandContext commandContext)
        {
        }
    }
}

Basically, instances of the class above do nothing and can be invoked — why not? They don’t do actually do anything so no harm done, right? πŸ˜‰ — as can be seen in the CanInvoke() method.

I then created the following class whose instances will serve as the default processing objects:

using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Shell.Framework.Commands;

namespace Sitecore.Sandbox.Invokers.Commands
{
    public class CommandInvoker : ICommandInvoker
    {
        private static ICommandInvoker NullCommandInvoker { get; set; }

        private Command Command { get; set; }

        private ICommandInvoker nextInvoker;
        private ICommandInvoker NextInvoker 
        { 
            get
            {
                return nextInvoker ?? NullCommandInvoker;
            }
            set
            {
                nextInvoker = value;
            }
        }

        static CommandInvoker()
        {
            NullCommandInvoker = CreateNullCommandInvoker();
        }

        public CommandInvoker()
        {
        }

        public bool HasCommand()
        {
            return Command != null;
        }

        public void SetCommand(string commandName)
        {
            Assert.ArgumentNotNullOrEmpty(commandName, "commandName");
            Command = GetCommand(commandName);
            Assert.IsNotNull(Command, "commandName", "commandName is not a valid command!");
        }

        public void SetNextInvoker(ICommandInvoker nextInvoker)
        {
            Assert.ArgumentNotNull(nextInvoker, "nextInvoker");
            NextInvoker = nextInvoker;
        }

        public bool CanInvoke(CommandContext commandContext)
        {
            Assert.ArgumentNotNull(commandContext, "commandContext");
            if(!HasCommand())
            {
                return false;
            }
            
            return Command.QueryState(commandContext) == CommandState.Enabled && NextInvoker.CanInvoke(commandContext);
        }

        public void Invoke(CommandContext commandContext)
        {
            Assert.ArgumentNotNull(commandContext, "commandContext");
            if(!HasCommand())
            {
                return;
            }

            Command.Execute(commandContext);
            NextInvoker.Invoke(commandContext);
        }

        protected virtual Command GetCommand(string commandName)
        {
            return CommandManager.GetCommand(commandName);
        }

        private static ICommandInvoker CreateNullCommandInvoker()
        {
            ICommandInvoker nullCommandInvoker = Factory.CreateObject("commandInvokers/nullCommandInvoker", true) as ICommandInvoker;
            Assert.IsNotNull(nullCommandInvoker, "nullCommandInvoker", "nullCommandInvoker must be set correctly in configuration!");
            return nullCommandInvoker;
        }
    }
}

There is a lot going on in the class above, so let me try to briefly capture the main things.

The SetCommand() method in the class above takes in the name of the command and delegates to the GetCommand() method which looks it up using the GetCommand() method on Sitecore.Shell.Framework.Commands.CommandManager in Sitecore.Kernel.dll.

The SetNextInvoker() method will chain the current CommandInvoker instance with another class instance that implements the ICommandInvoker interface — this is the NextInvoker.

The CanInvoke() method basically checks to see if the current CommandInvoker instance has a non-null Sitecore.Shell.Framework.Commands.Command instance set within it; ascertains whether the Sitecore.Shell.Framework.Commands.Command instance is enabled; and determines if the NextInvoker can be invoked.

The Invoke() method calls the Execute() method on the Sitecore.Shell.Framework.Commands.Command instance, and then calls the Invoke() method on the class instance’s NextInvoker.

One thing I’d like to point out is an instance of the Null Object class that was defined above is used when the NextInvoker is not set — this is why a null check is not done in the CanInvoke() and Invoke() methods on the NextInvoker instance.

I then built the following Sitecore.Shell.Framework.Commands.Command subclass which is to be wired-up to a menu option of some type in the Core database:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;

using Sitecore.Collections;
using Sitecore.Configuration;
using Sitecore.Diagnostics;
using Sitecore.Shell.Framework.Commands;
using Sitecore.Web;

using Sitecore.Sandbox.Invokers.Commands;

namespace Sitecore.Sandbox.Shell.Framework.Commands
{
    public class ChainOfResponsibilityCommand : Command
    {
        private ICommandInvoker commandInvoker;
        private ICommandInvoker CommandInvoker 
        { 
            get
            {
                if(commandInvoker == null)
                {
                    commandInvoker = GetCommandInvoker();
                }

                return commandInvoker;
            }
        }

        public override void Execute(CommandContext context)
        {
            if (!CommandInvoker.CanInvoke(context))
            {
                return;
            }

            CommandInvoker.Invoke(context);
        }

        public override CommandState QueryState(CommandContext context)
        {
            if(!CommandInvoker.CanInvoke(context))
            {
                return CommandState.Hidden;
            }

            return CommandState.Enabled;
        }

        private ICommandInvoker GetCommandInvoker()
        {
            ICommandInvoker firstInvoker = CreateNewCommandInvoker();
            IEnumerable<string> commandNames = GetCommandNames(GetParameters());
            if(commandNames == null || !commandNames.Any())
            {
                return firstInvoker;
            }
            
            ICommandInvoker invoker = firstInvoker;
            invoker.SetCommand(commandNames.First());
            commandNames = commandNames.Skip(1).ToList();
            if (!commandNames.Any())
            {
                return firstInvoker;
            }

            foreach(string commandName in commandNames)
            {
                ICommandInvoker nextInvoker = CreateNewCommandInvoker();
                nextInvoker.SetCommand(commandName);
                invoker.SetNextInvoker(nextInvoker);
                invoker = nextInvoker;
            }

            return firstInvoker;
        }

        private IEnumerable<string> GetCommandNames(SafeDictionary<string> parameters)
        {
            string commands = GetCommandsString(parameters);
            char[] delimiters = GetCommandsDelimiters(parameters);

            if (string.IsNullOrWhiteSpace(commands) || delimiters == null || !delimiters.Any())
            {
                return new List<string>();
            }

            return commands.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
        }

        protected virtual string GetCommandsString(SafeDictionary<string> parameters)
        {
            return parameters["commands"];
        }

        private char[] GetCommandsDelimiters(SafeDictionary<string> parameters)
        {
            string delimiters = GetDelimitersString(parameters);
            if (string.IsNullOrWhiteSpace(delimiters))
            {
                return new char[] { };
            }

            return GetDelimitersString(parameters).ToCharArray();
        }

        protected virtual string GetDelimitersString(SafeDictionary<string> parameters)
        {
            return parameters["delimiters"];
        }

        protected virtual SafeDictionary<string> GetParameters()
        {
            XmlNode xmlNode = Factory.GetConfigNode(string.Format("commands/command[@name='{0}']", Name));
            string parametersValue = GetAttributeValue(xmlNode, "parameters");
            if (string.IsNullOrWhiteSpace(parametersValue))
            {
                return new SafeDictionary<string>();
            }
           
            return WebUtil.ParseQueryString(xmlNode.Attributes["parameters"].Value);
        }

        private string GetAttributeValue(XmlNode xmlNode, string attributeName)
        {
            if (xmlNode == null || xmlNode.Attributes[attributeName] == null || string.IsNullOrWhiteSpace(xmlNode.Attributes[attributeName].Value))
            {
                return string.Empty;
            }

            return xmlNode.Attributes[attributeName].Value;
        }

        protected static ICommandInvoker CreateNewCommandInvoker()
        {
            ICommandInvoker commandInvoker = Factory.CreateObject("commandInvokers/defaultCommandInvoker", true) as ICommandInvoker;
            Assert.IsNotNull(commandInvoker, "commandInvoker", "commandInvoker must be set correctly in configuration!");
            return commandInvoker;
        }
    }
}

The Command above reads the list of commands to chain together from Sitecore configuration — this is coming from an attribute on the Command’s configuration element which you will see in the configuration file below — and builds up a linked list of ICommandInvoker instances via the GetCommandInvoker() method.

The QueryState() method simply checks to see if the linked list of ICommandInvoker instances can be invoked, and the Execute() — which also performs the same check as is done in the QueryState() method — ultimately calls the Invoke() method on the first ICommandInvoker instance — this will cascade throughout the entire linked list.

I then wired everything up in the following Sitecore configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <commands>
      <command name="item:MoveRenamePublish" parameters="commands=item:moveto|item:rename|item:publish&amp;delimiters=|"  type="Sitecore.Sandbox.Shell.Framework.Commands.ChainOfResponsibilityCommand, Sitecore.Sandbox"/>
      <command name="item:MoveLastPublish" parameters="commands=item:movelast|item:publish&amp;delimiters=|"  type="Sitecore.Sandbox.Shell.Framework.Commands.ChainOfResponsibilityCommand, Sitecore.Sandbox"/>
    </commands>
    <commandInvokers>
      <defaultCommandInvoker type="Sitecore.Sandbox.Invokers.Commands.CommandInvoker, Sitecore.Sandbox" singleInstance="false" />
      <nullCommandInvoker type="Sitecore.Sandbox.Invokers.Commands.NullCommandInvoker, Sitecore.Sandbox" singleInstance="false" />
    </commandInvokers>
  </sitecore>
</configuration>

I defined two different commands in the above configuration file to test whether the ChainOfResponsibilityCommand class can be reused for multiple Sheer UI commands.

I then set up two different buttons in the ribbon for the two command elements in the configuration file above:

item:MoveRenamePublish:

move-rename-publish-ribbon-core

item:MoveLastPublish:

move-last-publish-ribbon-core

Let’s take this for a spin.

Let’s move, rename and publish the following Sitecore Item:

cat-page-one-move-rename-publish-1

I clicked the button, and was presented with this dialog:

cat-page-one-move-rename-publish-2

I was then prompted with this dialog after the Item was moved to the selected destination:

cat-page-one-move-rename-publish-3

I renamed the Item, and was prompted with the publishing dialog:

cat-page-one-move-rename-publish-4

Ok, that appears to be working. Let’s try out the other Ribbon button. Let’s try it on this Item:

sub-page-one-move-last-publish-1

After the Item was moved, I was prompted with the publishing dialog:

sub-page-one-move-last-publish-2

As you can see, this worked as well.

I will say that although I had fun implementing this solution, it is way more complex than the solution I had built over two years ago in my older post.

This brings up an important point I want to make regarding design patterns: don’t use a design pattern because it may seem like a cool thing to do, or because your crazy developer cousin who always carries around a copy of the Gang Of Four book on design patterns says all solutions should implement them always.

Use them wisely.

if you see an opportunity to use one where it will save time when introducing new features moving forward, or it makes it easy to swap-in/out features, then by all means go for it. Otherwise, the KISS principle is a better “rule of thumb” to follow.

If you have any thoughts on this, please drop a comment.

Augment Functionality in Sitecore Using the Decorator Design Pattern

Over the past few days, I’ve been trying to come up with a good idea for a blog post showing the usage of the Decorator design pattern in Sitecore.

During this time of cogitation, I was having difficulties coming up with a good example despite having had used this pattern in Sitecore on many past projects — I can’t really share those solutions since they are owned by either previous employers or clients.

However, I finally had an “EUREKA!” moment after John West — CTO of Sitecore USA — wrote a blog post earlier today where he shared an <httpRequestBegin> pipeline processor which redirects to a canonical URL for an Item.

So, what exactly did I come up with?

I built the following example which simply “decorates” the “out of the box” ItemResolver — Sitecore.Pipelines.HttpRequest.ItemResolver in Sitecore.Kernel.dll — which is used as an <httpRequestBegin> pipeline processor to figure out what the context Item should be from the URL being requested by looking for an entry in the IDTable in Sitecore (note: this idea is adapted from a blog post that Alex Shyba — Director of Platform Innovation and Engineering at Sitecore — wrote a few years ago):

using Sitecore;
using Sitecore.Data;
using Sitecore.Data.IDTables;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Pipelines.HttpRequest;

namespace Sitecore.Sandbox.Pipelines.HttpRequest
{
    public class IDTableItemResolver : HttpRequestProcessor
    {
        private string Prefix { get; set; }

        private HttpRequestProcessor InnerProcessor { get; set; }

        public override void Process(HttpRequestArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            AssertProperties();
            
            Item item = GetItem(args.Url.FilePath);
            if (item == null)
            {
                InnerProcessor.Process(args);       
                return;
            }

            Context.Item = item; 
        }

        protected virtual void AssertProperties()
        {
            Assert.IsNotNullOrEmpty(Prefix, "Prefix", "Prefix must be set in configuration!");
            Assert.IsNotNull(InnerProcessor, "InnerProcessor", "InnerProcessor must be set in configuration!");
        }

        protected virtual Item GetItem(string url)
        {
            IDTableEntry entry = IDTable.GetID(Prefix, url);
            if (entry == null || entry.ID.IsNull)
            {
                return null;
            }

            return GetItem(entry.ID);
        }

        protected Item GetItem(ID id)
        {
            Database database = GetDatabase();
            if (database == null)
            {
                return null;
            }

            return database.GetItem(id);
        }

        protected virtual Database GetDatabase()
        {
            return Context.Database;
        }
    }
}

What is the above class doing? It’s basically seeing if it can find an Item for the passed relative URL — this is passed via the FilePath property of the Url property of the HttpRequestArgs instance taken in by the Process() method — by delegating to a method that looks up an entry in the IDTable for the URL — the URL would be the key into the IDTable — and return the Item from the context database if an entry is found. If no entry is found, it just returns null.

If null is returned, that pretty much means there is no entry in the IDTable for the given relative URL so a delegation to the Process() method of the InnerProcessor is needed in order to preserve “out of the box” Sitecore functionality for Item URL resolution.

I then replaced the “out of the box” ItemResolver with the above in the following patch include configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <httpRequestBegin>
        <processor patch:instead="*[@type='Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel']" 
                   type="Sitecore.Sandbox.Pipelines.HttpRequest.IDTableItemResolver, Sitecore.Sandbox">
          <Prefix>UrlRewrite</Prefix>
          <InnerProcessor type="Sitecore.Pipelines.HttpRequest.ItemResolver, Sitecore.Kernel" />
        </processor>  
      </httpRequestBegin>
    </pipelines>
  </sitecore>
</configuration>

In the above configuration file, we are setting the “out of the box” ItemResolver to be injected into the class above so that its Process() method can be “decorated”.

Let’s see this in action!

Let’s try this out with the following page Item:

mario-content-editor

In order to see the above <httpRequestBegin> pipeline processor in action, I had to add an entry into my IDTable — let’s make pretend an UrlRewrite module on the Sitecore Marketplace added this entry for us:

url-rewrite-id-table

I loaded up another browser window; navigated to the relative URL specified in the IDTable entry; and then saw the following:

resolved-url

As you can see, it worked.

We can also navigate to the same page using its true URL — the one resolved by Sitecore “out of the box”:

mario-resolved

The above worked because the inner processor resolved it.

Let’s now go to a completely different page Item altogether. Let’s use this one:

cat-page-five-sitecore

As you can see, that also worked:

cat-page-five

If you have any thoughts on this, or have other ideas around using the Decorator pattern in Sitecore, please share in a comment.

Augment Configuration-defined Sitecore Functionality using the Composite Design Pattern

In a couple of my past posts — Synchronize IDTable Entries Across Multiple Sitecore Databases Using a Composite IDTableProvider and
Chain Together Sitecore Client Commands using a Composite Command — I used the Composite design pattern to chain together functionality in two or more classes with the same interface — by interface here, I don’t strictly mean a C# interface but any class that servers as a base class for others where all instances share the same methods — and had a thought: how could I go about making a generic solution to chain together any class type defined in Sitecore configuration?

As a “proof of concept” I came up with the following solution while taking a break from my judgely duties reviewing 2015 Sitecore Hackathon modules.

I first defined an interface for classes that will construct objects using the Sitecore Configuration Factory:

using System.Collections.Generic;
using System.Xml;

namespace Sitecore.Sandbox.Shared
{
    public interface IConfigurationFactoryInstances<TInstance>
    {
        void AddInstance(XmlNode source);

        IEnumerable<TInstance> GetInstances();
    }
}

The following class implements the methods defined in the interface above:

using System.Collections.Generic;
using System.Xml;

using Sitecore.Configuration;

namespace Sitecore.Sandbox.Shared
{
    public class ConfigurationFactoryInstances<TInstance> : IConfigurationFactoryInstances<TInstance> where TInstance : class
    {
        private IList<TInstance> Instances { get; set; }

        public ConfigurationFactoryInstances()
        {
            Instances = new List<TInstance>();
        }

        public virtual IEnumerable<TInstance> GetInstances()
        {
            return Instances;
        }

        public void AddInstance(XmlNode configNode)
        {
            TInstance instance = CreateInstance(configNode);
            if (instance == null)
            {
                return;
            }

            Instances.Add(instance);
        }

        protected virtual TInstance CreateInstance(XmlNode configNode)
        {
            if (configNode == null)
            {
                return null;
            }

            TInstance instance = Factory.CreateObject(configNode, true) as TInstance;
            if (instance == null)
            {
                return null;
            }

            return instance;
        }
    }
}

The AddInstance() method in the class above — along with the help of the CreateInstance() method — takes in a System.Xml.XmlNode instance and attempts to create an instance of the type denoted by TInstance using the Sitecore Configuration Factory. If the instance was successfully created (i.e. it’s not null), it is added to a list.

The GetInstances() method in the class above just returns the list of the instances that were added by the AddInstance() method.

Since I’ve been posting a lot of meme images on Twitter lately — you can see the evidence here — I’ve decided to have a little fun tonight with this “proof of concept”, and created the following composite MediaProvider:

using System.Xml;

using Sitecore.Data.Items;
using Sitecore.Resources.Media;

using Sitecore.Sandbox.Shared;

namespace Sitecore.Sandbox.Resources.Media
{
    public class CompositeMediaProvider : MediaProvider
    {
        private static IConfigurationFactoryInstances<MediaProvider> Instances { get; set; }

        static CompositeMediaProvider()
        {
            Instances = new ConfigurationFactoryInstances<MediaProvider>();
        }

        public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
        {
            foreach (MediaProvider mediaProvider in Instances.GetInstances())
            {
                string url = mediaProvider.GetMediaUrl(item, options);
                if(!string.IsNullOrWhiteSpace(url))
                {
                    return url;
                }
            }

            return base.GetMediaUrl(item, options);
        }

        protected virtual void AddMediaProvider(XmlNode configNode)
        {
            Instances.AddInstance(configNode);
        }
    }
}

The AddMediaProvider() method in the class above adds new instances of Sitecore.Resources.Media.MediaProvider through delegation to an instance of the ConfigurationFactoryInstances class. The Sitecore Configuration Factory will call the AddMediaProvider() method since it’s defined in the patch include configuration file shown later in this post

The GetMediaUrl() method iterates over all instances of Sitecore.Resources.Media.MediaProvider that were created and stored by the ConfigurationFactoryInstances instance, and calls each of their GetMediaUrl() methods. The first non-null or empty URL from one of these “inner” instances is returned to the caller. If none of the instances return a URL, then the class above returns the value given by its base class’ GetMediaUrl() method.

I then spun up three MediaProvider classes to serve up specific image URLs of John West — I found these somewhere on the internet πŸ˜‰ — when they encounter media Items with specific names (I am not advocating that anyone hard-codes anything like this — these classes are only here to serve as examples):

using System;

using Sitecore.Data.Items;
using Sitecore.Resources.Media;

namespace Sitecore.Sandbox.Resources.Media
{
    public class JohnWestOneMediaProvider : MediaProvider
    {
        public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
        {
            if(item.Name.Equals("john-west-1", StringComparison.CurrentCultureIgnoreCase))
            {
                return "http://cdn.meme.am/instances/500x/43030540.jpg";
            }

            return string.Empty;
        }
    }
}

using System;

using Sitecore.Data.Items;
using Sitecore.Resources.Media;

namespace Sitecore.Sandbox.Resources.Media
{
    public class JohnWestTwoMediaProvider : MediaProvider
    {
        public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
        {
            if (item.Name.Equals("john-west-2", StringComparison.CurrentCultureIgnoreCase))
            {
                return "http://cdn.meme.am/instances/500x/43044627.jpg";
            }

            return string.Empty;
        }
    }
}
using System;

using Sitecore.Data.Items;
using Sitecore.Resources.Media;

namespace Sitecore.Sandbox.Resources.Media
{
    public class JohnWestThreeMediaProvider : MediaProvider
    {
        public override string GetMediaUrl(MediaItem item, MediaUrlOptions options)
        {
            if (item.Name.Equals("john-west-3", StringComparison.CurrentCultureIgnoreCase))
            {
                return "http://cdn.meme.am/instances/500x/43030625.jpg";
            }

            return string.Empty;
        }
    }
}

I then registered all of the above in Sitecore using a patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <mediaLibrary>
      <mediaProvider>
        <patch:attribute name="type">Sitecore.Sandbox.Resources.Media.CompositeMediaProvider, Sitecore.Sandbox</patch:attribute>
        <mediaProviders hint="raw:AddMediaProvider">
          <mediaProvider type="Sitecore.Sandbox.Resources.Media.JohnWestOneMediaProvider, Sitecore.Sandbox" />
          <mediaProvider type="Sitecore.Sandbox.Resources.Media.JohnWestTwoMediaProvider, Sitecore.Sandbox" />
          <mediaProvider type="Sitecore.Sandbox.Resources.Media.JohnWestThreeMediaProvider, Sitecore.Sandbox" />
          <mediaProvider type="Sitecore.Resources.Media.MediaProvider, Sitecore.Kernel" />
        </mediaProviders>
      </mediaProvider>
    </mediaLibrary>
  </sitecore>
</configuration>

Let’s see this in action!

To test, I uploaded four identical photos of John West to the Media Library:

four-jw-media-library

I then inserted these into a Rich Text field on my home Item:

rte-jw-times-four

I saved my home Item and published everything. Once the publish was finished, I navigated to my home page and saw the following:

four-jw-home-page

As you can see, the three custom John West MediaProvider class instances served up their URLs, and the “out of the box” Sitecore.Resources.Media.MediaProvider instance served up its URL on the last image.

If you have any thoughts on this, please share in a comment.

Until next time, have a Sitecoretastic day!

Turn Off the Attaching of Files in Emails Sent by Web Forms for Marketers in Sitecore

The other day Michael West pinged me to see if I could help someone in one of the SDN forums via this tweet:

The poster had asked if there were an easy way to disable the attachment of files on emails sent by the Web Forms for Marketers (WFFM) module in Sitecore.

After quickly peeking in the WFFM assemblies, I gave a potential solution — check out the thread to see what it was — though it did not work.

Tonight I looked further into how to accomplish this, and came up with the following solution:

using System;
using System.Net;

using Sitecore.Data;
using Sitecore.Form.Core.Client.Data.Submit;
using Sitecore.Form.Core.Pipelines.ProcessMessage;
using Sitecore.Pipelines;

namespace Sitecore.Sandbox.Form.Submit
{
    public class SendMessage : Sitecore.Form.Submit.SendMessage
    {
        protected override void ExecuteMail(ID form, AdaptedResultList fields)
        {
            ProcessMessageArgs args = new ProcessMessageArgs(form, fields, this.MessageType);
            base.To = base.To.TrimEnd(new char[] { ',', ';' });
            base.LocalFrom = base.From.TrimEnd(new char[] { ',', ';' });
            args.To.Append(base.To.Replace(";", ","));
            args.From = base.From.Replace(";", ",");

            // magically populated by WFFM via XML
            args.IncludeAttachment = IncludeAttachments; 

            args.Mail.Append(base.Mail);
            args.Subject.Append(base.Subject);
            args.Recipient = this.Recipient;
            args.RecipientGateway = this.RecipientGateway;
            args.Host = base.Host;
            args.Port = base.Port;
            args.IsBodyHtml = this.IsBodyHtml;
            args.EnableSsl = this.EnableSsl;
            args.Data.Add("FromPhone", this.FromPhone ?? string.Empty);
            string[] strArray = string.IsNullOrEmpty(base.Login) ? new string[] { string.Empty } : base.Login.Split(new char[] { '\\' });
            if ((strArray.Length > 0) && !string.IsNullOrEmpty(strArray[0]))
            {
                if ((strArray.Length == 2) && !string.IsNullOrEmpty(strArray[1]))
                {
                    args.Credentials = new NetworkCredential(strArray[1], base.Password, strArray[0]);
                }
                else
                {
                    args.Credentials = new NetworkCredential(strArray[0], base.Password);
                }
            }
            if (!string.IsNullOrEmpty(base.CC))
            {
                base.CC = base.CC.TrimEnd(new char[] { ',', ';' });
                args.CC.Append(base.CC.Replace(";", ","));
            }
            if (!string.IsNullOrEmpty(base.BCC))
            {
                base.BCC = base.BCC.TrimEnd(new char[] { ',', ';' });
                args.BCC.Append(base.BCC.Replace(";", ","));
            }
            CorePipeline.Run("processMessage", args);
        }

        // magically populated by WFFM via XML
        private bool IncludeAttachments { get; set; }
    }
}

The class above inherits from Sitecore.Form.Submit.SendMessage in Sitecore.Forms.Custom.dll, and overrides the ExecuteMail() method — this method is declared virtual in Sitecore.Form.Submit.SendMail which is the base class of Sitecore.Form.Submit.SendMessage.

Most of the code in ExecuteMail() above is from the ExecuteMail() method on Sitecore.Form.Submit.SendMessage, though with one minor difference: I have added a boolean property named “IncludeAttachments” which is magically populated by WFFM via an XML property declaration for this class:

wffm-no-attachments

I spun up a form quickly with some File Upload fields, and mapped the above Save Action to it:

form-save-action-no-attachment

I then navigated to my form, attached two images, and clicked the submit button:

attached-two-images

As you can see, the images were not attached to the email:

no-attachments-email

I then went back to my Save Action; set the <IncludeAttachments> XML element’s value to be true; saved the form Item; published it; and resubmitted the form:

with-attachments-email

As you can see, the images were attached this time.

If you have any thoughts on this, please drop a comment.

Leverage the Sitecore Configuration Factory: Inject Value Types as Dependencies

In a previous post, I had mentioned primitive types cannot be injected into classes via Sitecore’s Configuration Factory, and further learned through exploring and experimentation that this applies to all value types in C#.

However, after some digging in Sitecore.Configuration.Factory — this lives in Sitecore.Kernel.dll — I discovered the CreateFromFactoryMethod() method which calls a static method — this method is defined in an XML attribute named “factoryMethod” — on a class defined in the “type” attribute on the XML configuration element being processed, and felt I could take advantage of this hidden gem for injecting value types into my configuration-defined classes.

Despite the fact the method defined in the “factoryMethod” attribute must be static, I decided to define an interface for classes that do value type conversions from strings, and created the following interface (don’t worry, I haven’t completely lost my mind — yes, I did say it requires a static method — but do bear with me until we get further down in this post πŸ˜‰ ):

namespace Sitecore.Sandbox.Configuration
{
    public interface IValueTypesConverter
    {
        bool ConvertToBoolean(string argument);

        char ConvertToCharacter(string argument);

        decimal ConvertToDecimal(string argument);

        double ConvertToDouble(string argument);

        float ConvertToFloat(string argument);

        int ConvertToInteger(string argument);
    }
}

I then created the following class to implement the interface above:

using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Configuration
{
    public class ValueTypesConverter : IValueTypesConverter
    {
        public bool ConvertToBoolean(string argument)
        {
            bool value;
            Assert.ArgumentCondition(bool.TryParse(argument, out value), "argument", "argument is not a boolean!");
            return value;
        }

        public char ConvertToCharacter(string argument)
        {
            char value;
            Assert.ArgumentCondition(char.TryParse(argument, out value), "argument", "argument is not a character!");
            return value;
        }

        public decimal ConvertToDecimal(string argument)
        {
            decimal value;
            Assert.ArgumentCondition(decimal.TryParse(argument, out value), "argument", "argument is not a decimal!");
            return value;
        }

        public double ConvertToDouble(string argument)
        {
            double value;
            Assert.ArgumentCondition(double.TryParse(argument, out value), "argument", "argument is not a double!");
            return value;
        }

        public float ConvertToFloat(string argument)
        {
            float value;
            Assert.ArgumentCondition(float.TryParse(argument, out value), "argument", "argument is not a float!");
            return value;
        }

        public int ConvertToInteger(string argument)
        {
            int value;
            Assert.ArgumentCondition(int.TryParse(argument, out value), "argument", "argument is not an integer!");
            return value;
        }
    }
}

Each method in the class above calls its specific value type’s TryParse() method to convert the passed string to the value type. We ensure the argument was in the correct format: an assertion is done to check if the argument was of the type the method was expecting.

I then created the following adapter which wraps an instance of a class that implements the interface defined above:

using Sitecore.Configuration;

namespace Sitecore.Sandbox.Configuration
{
    public class TypesConverter
    {
        static TypesConverter()
        {
            ValueTypesConverter = Factory.CreateObject("valueTypesConverter", true) as IValueTypesConverter;
        }

        public static bool ConvertToBoolean(string argument)
        {
            return ValueTypesConverter.ConvertToBoolean(argument);
        }

        public static char ConvertToCharacter(string argument)
        {
            return ValueTypesConverter.ConvertToCharacter(argument);
        }

        public static decimal ConvertToDecimal(string argument)
        {
            return ValueTypesConverter.ConvertToDecimal(argument);
        }

        public static double ConvertToDouble(string argument)
        {
            return ValueTypesConverter.ConvertToDouble(argument);
        }

        public static float ConvertToFloat(string argument)
        {
            return ValueTypesConverter.ConvertToFloat(argument);
        }

        public static int ConvertToInteger(string argument)
        {
            return ValueTypesConverter.ConvertToInteger(argument);
        }

        private static IValueTypesConverter ValueTypesConverter { get; set; }
    }
}

The class above creates an instance of the ValueTypesConverter class via the Sitecore.Configuration.Factory.CreateObject() method. I felt defining the class that implements the IValueTypesConverter interface in Sitecore configuration would allow for customization if one would ever need to do so.

Further, all methods in the class above just delegate calls to the ValueTypesConverter instance.

For testing, I created the following class to serve as a <renderField> pipeline processor:

using System.Text;

using Sitecore.Diagnostics;
using Sitecore.Pipelines.RenderField;

namespace Sitecore.Sandbox.Pipelines.RenderField
{
    public class ValueTypesTest
    {
        public void Process(RenderFieldArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            StringBuilder htmlBuilder = new StringBuilder();
            htmlBuilder.AppendFormat("SomeBoolean ==> {0} <br />", SomeBoolean);
            htmlBuilder.AppendFormat("SomeCharacter ==> {0} <br />", SomeCharacter);
            htmlBuilder.AppendFormat("SomeDecimal ==> {0} <br />", SomeDecimal);
            htmlBuilder.AppendFormat("SomeDouble ==> {0} <br />", SomeDouble);
            htmlBuilder.AppendFormat("SomeFloat ==> {0} <br />", SomeFloat);
            htmlBuilder.AppendFormat("SomeInteger ==> {0}", SomeInteger);
            args.Result.FirstPart = htmlBuilder.ToString();
        }

        private bool SomeBoolean { get; set; }

        private char SomeCharacter { get; set; }

        private decimal SomeDecimal { get; set; }

        private double SomeDouble { get; set; }

        private float SomeFloat { get; set; }

        private int SomeInteger { get; set; }
    }
}

The class above defines properties that will be populated magically by Sitecore’s Configuration Factory, and builds a string of HTML to be rendered on the front-end.

I wired everything together in the following Sitecore configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <renderField>
        <processor type="Sitecore.Sandbox.Pipelines.RenderField.ValueTypesTest, Sitecore.Sandbox">
          <SomeBoolean type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToBoolean" arg0="true" />
          <SomeCharacter type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToCharacter" arg0="b" />
          <SomeDecimal type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToDecimal" arg0="0.1" />
          <SomeDouble type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToDouble" arg0="1234.5678" />
          <SomeFloat type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToFloat" arg0=".98765" />
          <SomeInteger type="Sitecore.Sandbox.Configuration.TypesConverter, Sitecore.Sandbox" factoryMethod="ConvertToInteger" arg0="2" />
        </processor>  
      </renderField>
    </pipelines>
    <valueTypesConverter type="Sitecore.Sandbox.Configuration.ValueTypesConverter, Sitecore.Sandbox" />
  </sitecore>
</configuration>

When I loaded my home page, I saw that all value types defined in configuration were accounted for:

value-types-test-rendered

If you have any thoughts on this, or ideas around implementing this in a different way, please share in a comment.

Leverage the Sitecore Configuration Factory: Inject Dependencies Through Class Constructors

In my previous post, I showed how you can leverage Sitecore’s Configuration Factory to inject dependencies into properties of class instances — this is known as Setter injection in the Dependency injection world — and thought I would share another way you can inject dependencies into instances of classes defined in configuration: through class constructors (this is known as Constructor injection).

Suppose we have the following interface for objects that perform some kind of operation on parameters passed to their DoSomeOtherStuff() method:

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public interface ISomeOtherThing
    {
        void DoSomeOtherStuff(SomeProcessorArgs args, string someString);
    }
}

The following dummy class implements the interface above — sadly, it does not do anything:

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public class SomeOtherThing : ISomeOtherThing
    {
        public void DoSomeOtherStuff(SomeProcessorArgs args, string someString)
        {
            // TODO: add code to do some other stuff
        }
    }
}

In my previous post, I defined the following interface for objects that “do stuff”, and will reuse it here:

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public interface ISomeThing
    {
        void DoStuff(SomeProcessorArgs args);
    }
}

I have modified the SomeThing class from my previous post to consume an instance of a class that implements the ISomeOtherThing interface above along with a string instance:

using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public class SomeThing : ISomeThing
    {
        public SomeThing(ISomeOtherThing someOtherThing, string someString)
        {
            Assert.ArgumentNotNull(someOtherThing, "someOtherThing");
            Assert.ArgumentNotNullOrEmpty(someString, "someString");
            SomeOtherThing = someOtherThing;
            SomeString = someString;
        }

        public void DoStuff(SomeProcessorArgs args)
        {
            SomeOtherThing.DoSomeOtherStuff(args, SomeString);
        }

        private ISomeOtherThing SomeOtherThing { get; set; }

        private string SomeString { get; set; }
    }
}

The Sitecore Configuration Factory will magically create instances of the types passed to the constructor of the SomeThing class defined above, and you can then assign these instances to members defined in your class.

As far as I know — I did a lot of digging in Sitecore.Kernel.dll for answers, and some code experimentation — the Sitecore Configuration Factory will only magically inject instances of class types and strings: it will not inject .NET primitive types (if I am incorrect on this, please share in a comment).

I have reused the SomeProcessor class from my previous post — I did not change any code in it:

using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public class SomeProcessor
    {
        public void Process(SomeProcessorArgs args)
        {
            DoSomethingWithArgs(args);
        }

        private void DoSomethingWithArgs(SomeProcessorArgs args)
        {
            Assert.ArgumentNotNull(SomeThing, "SomeThing");
            SomeThing.DoStuff(args);
        }

        private ISomeThing SomeThing { get; set; } // is populated magically via Setter injection!
    }
}

We can then piece everything together using a Sitecore patch configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <somePipeline>
        <processor type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeProcessor, Sitecore.Sandbox">
          <SomeThing type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeThing, Sitecore.Sandbox">
            <param hint="1" type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeOtherThing, Sitecore.Sandbox" />
            <param hint="2">just some string</param>
          </SomeThing>  
        </processor>  
      </somePipeline>
    </pipelines>
  </sitecore>
</configuration>

You must define <param> elements in order to pass arguments to constructors. The “hint” attribute determines the order of the parameters passed to the class constructor, though I believe using this attribute is optional (if I am wrong on this assumption, please share in a comment below).

If you have any thoughts on this, please drop a comment.

Leverage the Sitecore Configuration Factory: Populate Class Properties with Instances of Types Defined in Configuration

I thought I would jot down some information that frequently comes up when I am asked to recommend plans of attack on projects. The first recommendation I always give for any Sitecore project is: define as much as you possibly can in Sitecore configuration. Doing so introduces seams in your code: code that does not have to change when its underlying behavior changes — think interfaces. πŸ˜‰

When defining types in Sitecore configuration, you are leveraging Sitecore’s built-in Dependency Injection framework: Sitecore’s Configuration Factory will magically inject instances of classes — yes, you would define these in configuration files — into properties of classes that are used for your pipeline processors, event handlers, and other Sitecore configuration-defined objects.

For example, suppose we have the following interface for classes that change state on an instance of a phony subclass of Sitecore.Pipelines.PipelineArgs:

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public interface ISomeThing
    {
        void DoStuff(SomeProcessorArgs args);
    }
}

Let’s create a fake class that implements the ISomeThing interface above:

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public class SomeThing : ISomeThing
    {
        public void DoStuff(SomeProcessorArgs args)
        {
            // it would be nice if we had code in here
        }
    }
}

We can then define a class property with the ISomeThing interface type in a class that serves as a pipeline processor:

using Sitecore.Diagnostics;

namespace Sitecore.Sandbox.Pipelines.SomePipeline
{
    public class SomeProcessor
    {
        public void Process(SomeProcessorArgs args)
        {
            DoSomethingWithArgs(args);
        }

        private void DoSomethingWithArgs(SomeProcessorArgs args)
        {
            Assert.IsNotNull(SomeThing, "SomeThing must be set in your configuration!");
            SomeThing.DoStuff(args);
        }

        private ISomeThing SomeThing { get; set; } // this is populated magically!
    }
}

The class above would serve as a processor for the dummy <somePipeline> defined in the following configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <somePipeline>
        <processor type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeProcessor, Sitecore.Sandbox">
          <SomeThing type="Sitecore.Sandbox.Pipelines.SomePipeline.SomeThing, Sitecore.Sandbox" />
        </processor>  
      </somePipeline>
    </pipelines>
  </sitecore>
</configuration>

In the configuration file above, we defined a <SomeThing /> element within the processor element of the <somePipeline> pipeline, and this directly maps to the SomeThing property in the SomeProcessor class shown above. Keep in mind that names matter here, so the name of the configuration element must match the name of the property.

Until next time, have a Sitecoretastic day!