Tuesday, May 5, 2015

Sitecore: Custom Standard Value Tokens

Overview

In Sitecore, all developers are familar with the concept of standard values.  Standard values are default values and behaviors that can be set for content items that derive from data templates.  You can define the default values for fields or you can assign behaviors such as insert options.  I will assume that the audience who reads this is already familiar with this concept and knows how to create and work with standard values.

Standard Value Token Variables

Standard values can be defined using static values such as a number or a string or they can be token variables that will be replaced by computed values at item creation time.  The default installation of Sitecore already includes a set of tokens that satisfy most applications and purposes. 
  • $name: The item name
  • $id: The item ID
  • $parentid: The item ID of the parent item
  • $parentname: The item name of the parent item
  • $date: The system date
  • $time: The system time
  • $now: The combination of system date and time
Tokens are strings that start with the "$" symbol.  When a content item is created in the content tree, the pipeline that gets invoked is:



Here, you can see that there are processors to perform basic checks and determine if token variables are detected before actually performing any substitutions.  But what if we want to define custom tokens?  Where do we inject that logic in this process?  Well, there are two possibilities.  One approach would be to inject a processor at the end of the pipeline where more custom tokens are defined and then perform additional substitutions on the new custom tokens, hence adding a "patch" to the way the pipeline works.  A better, leaner approach is to actually modify the "core" of it all to include the custom tokens and to perform substitutions as if everything came out of the box.

Taking Apart The Pipeline




First, let's use a decompiler to inspect what's happening in the expandInitialFieldValue pipeline.  In the ReplaceVariables processor, you will see that there is a call to another class that does all the work.  This class is define in the <settings> section of web.config.



Lets look at this class and see what is does and whether or not we can subclass it.  Knowing how flexible Sitecore is, the asnwer is probably yes.  Again, lets use a decompiler.  Of particular interest are the following three methods.







The execution order is Replace > ReplaceValues > ReplaceWithDefault with Replace being a virtual method while the others are not.  Fortunately for us, this means we can easily override the combined logic with a custom subclass of our own. 



In our custom class, we have to override the Replace method with the same or similar code.  Then we need two local private versions of ReplaceValues and ReplaceWithDefault.  We can use same or similar code for ReplaceWithDefault but the ReplaceValues method is where you would define your custom tokens and also tell Sitecore what to do with it.  For example, let's say you want to replace the custom "$test" token with the string "hello" this would be the resulting code.



That is all there is to define custom token variables for Sitecore standard values.  All the work is done in the ReplaceValues method. 

Custom Tokens For Date Fields

Now let's explore some of the more complex field types besides just the single-line string field.  A Sitecore date field renders as a datepicker in the content admin.  If you define the standard value for a date field,  you can pick a default date and all items will inherit this default date.  Did you know you can also replace the standard value in the date field by typing in a token name instead of using the datepicker?



If you use any of the default tokens such as "$name" or "$id" the resulting value of the field would be an empty string because the date field looks for a valid ISO-formatted datetime string such as "20150329T000000".  The only way to have a string in this format rendered is by defining a custom token with custom logic. 

Let's say the scenario is that we want the date field value to reflect the item name, we could define the "$itemnameasdate" token to be replaced like this.



Basically, if the item name is in a date format that is expected, then we convert the value to an ISO-formatted datetime string and replace the token with this string, otherwise the field will have no value.

Please keep in mind that all tokens should start with a unique string or substitutions will be invalid.  For example, if the item name is "testitem", the "$name" token will be replaced with "testitem".  If a custom token is name "$nameasdate", the substituted valued would be "testitemasdate" because a straight string.Replace method is called and your custom token replace logic will never be invoked.

Custom Tokens For Integer Fields

Integer fields are very tricky when attempting to set standard values.  You can set static integer values as the default values, no problem there.  Since token variables are string values, does this mean we are out of luck?  Kind of.  If we define a field as type Integer, we are only allowed to use integers in the standard value for that field.  This would mean we are out of luck when attempting to put down "$name" as the token to be replaced.





What can we do?  Well, behind the scenes all field values are stored as strings.  The UI is the only part that performs validations and barks at you when attempting to enter a non-integer value in the field.  So one unconventional practice is to first define the field as a string field, enter the token as the standard value, and then change the field type back to integer.  The token sticks!  Now you have a string token in an integer field as the standard value!


Next, we have to add the logic to perform the token replacement.  Let's take for example a data template that defines what a year is and we try to fill in the year integer field with the value of the item name.  If the item name can be interpreted as an integer, then it is a valid year, otherwise leave blank.  I am sure there can be more validations performed on this value to determine if the number is a year but let's keep it simple for now.  We can use this snippet of code.



There are no guarantees for this kind of workaround but at least it works as of 7.x now.  In the future if Sitecore decides to validate token names in standard values, then this code will not work but nothing will break and existing data will no be affected.  Since it is a standard value, only new items will be affected and there will be no default values set.

Final Words

Please keep in mind that if the class definition changes in future revisions of Sitecore and the Sitecore.Kernel assembly, the code in your subclass probably has to be updated to reflect the new base class code.  The code in these classes rarely change so it is not a concern but please just be aware of the possibility.