Sitecore – Revisiting token substitutions with IFormattable

Most CMSs offer a form of token replacement for commonly used words and phrases within the text that the content editor controls. Sitecore doesn’t deliver this out of the box; it requires some customizations.  This post is extending on the Token Substitution section of Reusing and Sharing Data using a Sitecore 8.2 example site.

Summary:

  • Add a processor to the RenderField pipeline
  • Make use of multiple Dictionary Domains
  • Use IFormattable for token replacement functionality

IFormattable utility

The IFormattable interface provides a way of formatting the value of an object into a string representation. The method, IFormattable.ToString(string, IFormatProvider) must be implemented.  It supplies formatting services for the implementing type.  Inside here, we can plug in some code to perform replacements against the dictionary domain.

Below, the constructor is providing an argument for bringing in the dictionary domain name.

The ToString() method requests the string to format and the formatProvider, which returns a formatting object that has defined the symbols used in converting an object to its string representation.  In this case, it is an instance of the class.  It calls the GetDictionaryDomainValue() method which takes advantage of Sitecore’s Translate.TextByDomain() functionality.

TextByDomain() takes two arguments.  The first is the dictionary domain name and the second is the replacement text to replace.

 public class ContentFieldFormattable : IFormattable
 {
  private readonly string _dictionaryDomain;

  public ContentFieldFormattable(string dictionaryDomain)
  {
     _dictionaryDomain = dictionaryDomain;
  }

  public string ToString(string format, IFormatProvider formatProvider)
  {
     return GetDictionaryDomainValue(format);
  }

  public string GetDictionaryDomainValue(string replacementText)
  {
   return Sitecore.Globalization.Translate
            .TextByDomain(_dictionaryDomain, replacementText);
   }
 }

RenderField pipeline

Configuration file

The GetTokenDictionaryReplacements processor is patched in to the RenderField pipeline.  It contains a <DictionaryDomain> child item which provides the processor with the name of the Sitecore dictionary domain to use.

token-replacement-config

Pipeline processor

The processor has a property, DictionaryDomain, which accepts the child node from the configuration file renderField entry.

The ContentFieldFormattable class is instantiated, passing along the DictionaryDomain; this object serves as the format provider in the string.Format method.

public class GetTokenDictionaryReplacements
 {
    public string DictionaryDomain { get; set; }
    public virtual void Process(RenderFieldArgs args)
    {
      Assert.ArgumentNotNull((object)args, "args");

      if (Sitecore.Context.Site == "shell") return;

      if (Sitecore.Context.PageMode.IsExperienceEditor) return;

      ContentFieldFormattable cff = new ContentFieldFormattable(DictionaryDomain);

      args.Result.FirstPart = (args.Result.FirstPart == null) 
                 ? null 
                 : string.Format(args.Result.FirstPart, cff);
      args.Result.LastPart = (args.Result.LastPart == null) 
                 ? null 
                 : string.Format(args.Result.LastPart, cff);
     }
 }

Token Dictionary setup

A separate dictionary domain is recommended.  Inherit from  /sitecore/templates/System/Dictionary/Dictionary Domain when creating the top node.

sitecore-content-tree
Token Dictionary Domain

Example

The rich text field below has three tokens defined.  With a single call to the IFormattable ToString() method, all values are replaced.

sitecore-rtf-example

Rich text field with tokens

website-sample-page

Site after replacements

Fallback domain dictionary

You can also set a fallback domain on the dictionary domain.  In the event that the value is not defined as a dictionary entry in the initial dictionary domain, a Fallback Domain dictionary may be queried.

sitecore-fallback-dictionary

In the next blog post, I’ll show how to provide the Content Editor with an easy ability to add these to the Rich Text Editor.

References:

https://sitecorecontextitem.wordpress.com/2014/06/06/you-should-probably-start-using-sitecore-dictionary-domains/

 

Advertisements

Sitecore – A case for intercepting the InsertRenderings pipeline

So you wanna modularize and re-use Sitecore items even more than how you were originally Sitecore-trained?

Here’s a perfect opportunity.

How about using the InsertRenderings pipeline? This pipeline determines the presentation components (e.g. sublayouts, renderings, views, etc) that will render on a page during a given request.

Why would you want to intercept the InsertRenderings pipeline?

A case may be that you want to use more than one component together over and over but do not want to assign them to each template or even the base template.
Imagine the 1990’s concept of HTML framesets. Each frame could be populated independently and the content within could be saved and used on any page.  Although this example is clunky and out-dated, it may help visualize the re-use that this post is addressing.

A case for headers and footers

Many sites have headers that aren’t a single component.  In fact, they may be made of components like a logo spot, a primary navigation, secondary navigation, search bar, login area, and perhaps a login status.  If each of these are considered a component (for modularity), it means that each of these, in the correct order must be placed on each page that has a header.
Yes, you could create base template variations of each component combination, true. You’re relying on no content author messing with the standard values of the presentation layer which could potentially remove components.
An alternative to this could be to create one or more items whose presentation layers reflect all the variations of headers and use logic to pick which one is used.

The logic would be contained an extension of the InsertRenderingsProcessor.

public override void Process(InsertRenderingsArgs args){ }

Within the argument of type InsertRenderingsArgs passed into the Process method is Renderings property which is a list of RenderingReference.  The objective of extending the InsertRenderings is to append additional components within this list.

List<RenderingsReference> renderingReferenceList = args.Renderings;

Where do we store a reference to the header?

One idea would be to add an additional property to the <site> node within the web.config and store the site’s header guid there.

You would retrieve the renderings from the guid’s item (guidItem) with:

RenderingReference[] headerRenderings = guidItem.Visualization.GetRenderings(device, false);

Add (AddRange) these renderings to the renderingReferenceList.

args.Renderings.AddRange(headerRenderings.ToList());

TIP: Consider using this for re-usable complex sidebar widgets as well.