Skip to main content

Fixing Missing Token in For This {0}

 I recently encountered an issue where my non-content blocks (site settings, categories, etc.) were producing a "For This" folder with a missing token. This missing token appeared in custom content, as well as add-ons like Geta Categories.


For custom content, two things are required to get a value into that empty token: an XML file under the lang folder, and UIDescriptor. In order to fix an add-on like Geta Categories (which already has the UIDescriptor baked-in), all that is needed is an XML file under the lang folder. This is a pretty simple XML which simply denotes the content type in question and the name you'd like to give it. Like so:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<languages>
  <language name="English" id="en">
    <contenttypes>
      <CategoryData>
        <name>Working Category! </name>
      </CategoryData>
    </contenttypes>
  </language>
</languages>

Simply adding the file for Geta Categories will resolve the issue there, since they've already included the required UIDescriptor in the package. The result will look like this:


For your own custom types, the already-mentioned UIDescriptor is also required. This is a simple bit of code to notify Optimizely what to fill in for what tokens. The XML can be expanded to include multiple types, each with their own user-defined name node. 

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<languages>
  <language name="English" id="en">
    <contenttypes>
      <SettingsBase>
        <name>Setting</name>
      </SettingsBase>
      <CategoryData>
        <name>Category</name>
      </CategoryData>
    </contenttypes>
  </language>
</languages>

The UIDescriptor is few lines of code that inherit from the generic UIDescriptor<T> where T is the content type we want to target. As you can see in the XML the parent node of name should be the content type name so that Optimizely can pair it up correctly. 

    [UIDescriptorRegistration]
    public class SettingsBlockFolderDescriptor : UIDescriptor<SettingsBase>
    {
        public SettingsBlockFolderDescriptor()
        {
            IsPrimaryType = true;
        }
    }


With both pieces in place, we'll now see our XML-defined name appear instead of the empty token, {0}:


Comments

Popular posts from this blog

Config-Per-Site in Multi-Tenant Environments

Recently, a task was given to me at work where we needed a multisite configuration. We all know that multi-environment is as easy as appsettings.<environment_name>.config. What about in multi-tenant environments? You can't have appsettings.site1.config and appsettings.site2.config in your site! Well, not without a little extra work... Allow me to introduce  AddKeyPerFile ; this handy little function that you set up in your Program.cs will enumerate the files in a directory and add them to your Configuration.  "Alright!" you must be thinking "Show me how this miracle function works..." Well, let's get to it! First, you'll want to identify the config sections that will be unique to each site and put them in their own folder. I do this by site name because it makes the most sense by our conventions, if something else works for your practice, these names aren't set in stone.  Now, in order to suck those values into your config, there's one simp

The 1001st Piece in your 1000 Piece Puzzle: .NET Default Interface Functions

I was recently working with a client who wanted a reasonably large subsystem added to Optimizely that would add automated management to their content. While cutting the code for this, I found myself writing similar code across multiple classes. The reason why I had to write it that way was: 1) The client was currently on CMS11 and didn't have access to newer language features; 2) The hierarchy of the classes prevented me from inserting a common ancestor. Thankfully, .NET has expanded the functionality of interfaces, so we can take advantage of those within Optimizely. With .NET 5, Microsoft introduced default implementations on interfaces. Now interfaces can bring along a default implementation. Resulting in all classes that implement the interface can use the default implementation or override it with custom logic. Enough text! Let's code! Original Interface The following code is something that we'd create for an Optimizely experiment: using OptimizelySDK; using Optimizely

You Just Got Vectored! SVG Image Formats

 If you're reading this, then you've come across a need that nearly all Opti developers encounter in their careers; You need to display a vector image format (SVGs and the like) properly, the <IMG> tag just isn't going to cut it anymore. Post like this are a right-of-passage for Opti bloggers. "So," you think "if there are so many other blogs out there on the topic, why should I read yours?" Firstly, you enjoy my familiar and conversational tone of writing. Cary Elwes does the English accent, I do the nerd stuff. Second, you've already come this far, you might as well finish as this point, it's not long, I promise. To that point, and most importantly, I've seen some complex solutions out there, this one is a quick and simple implementation. 100% Guarantee! The VectorFile class: [MediaDescriptor(ExtensionString = "svg")]     public class VectorFile : ImageData     {         public override Blob Thumbnail { get => BinaryData;