Tales from the Evil Empire

Bertrand Le Roy's blog

News


Bertrand Le Roy

BoudinFatal's Gamercard

Tales from the Evil Empire - Blogged

Blogs I read

My other stuff

Archives

Future Orchard Part 2: more Tokens

(c) Bertrand Le Roy 2011This is part 2 for this post

Before I show some more advanced features of Tokens, I should probably say a word about why exactly we think we need this feature. In a CMS, there are many places where you need to build strings from some static but configurable part and a number of values that come from the environment. In the first post in this series, I used the rather silly example of Mad Libs because I thought it was a fun and light-hearted way of explaining the technology. But obviously we are not building the feature to play silly word games, we are building it because we need it to build other cool stuff. Real applications include:

  • e-mail formatting: this does not require full templating but always involves inserting values from various sources (sender, comment URL and text, site name, etc.). Message text should be easy to change by the site administrator.
  • Content post-processing: one can imagine a lightweight post-processing phase on content item bodies, where tokens get dynamically replaced by their values. Having an extensible set of tokens enables modules to add their own stuff.
    UPDATE: I forgot to mention that, but the way we insert images into body from the media picker could use a token that specifies the path of the image in a way that better survives content deployment to another server.
  • URL patterns: this is the Autoroute feature that I will detail in the next post. The idea is that the site administrator can specify an arbitrary pattern of URLs for a content type, without having to touch the code for the affected content types. For example, if you like the style of URLs on this blog, you could specify a pattern such as "/{Content.Container.Owner}/Blog/{Content.Date.Year}/{Content.Date.Month}/{Content.Date.Day}/{Content.Slug}" from the admin and get post URLs like: "/bleroy/Blog/2011/7/26/more-tokens".
  • Many, many others, including your own.

In the URL example above, you may have noticed some deep tokens such as BlogPost.Date.Year. Those can be easily implemented using token chains:

context.For<IContent>("Content")
    .Token("Date",
        content => content.As<ICommonPart>().CreatedUtc.ToShortDateString())
    .Chain("Date", "DateTime",
        content => content.As<ICommonPart>().CreatedUtc)

The date token itself is defined for any content item. If the token chain stops there (Content.Date), the short date string for the creation time of the content item is displayed. If there is more behind it (Content.Date.Year), then the creation date object is going to get fed again into token providers as a DateTime token for further resolution. This could work like this:

context.For<DateTime>("DateTime", () => DateTime.Now)
    .Token("Year", d => d.Year)
    .Token("Month", d => d.Month)
    .Token("Day", d => d.Day)
    /// ... and more

We have effectively described how to resolve the year, month and day tokens, not just the sub-tokens of Content.Date. This means that if I have a comment modification date token for example, as long as it targets "DateTime", this will be re-used.

Also note the default date value of Now if no DateTime exists on the context. This would enable me to do "The current year is {Date.Year}."

But what if I don't know the full list of token names people could use? What if, for example, I want the sub-token after a date to be the format to use on the date object? Well, you don't have to use the syntactic sugar for static tokens that I've used so far, and instead go dynamic:

context.For("DateTime", () => DateTime.Now)
       .Token((token, v) => v.ToString(token));

This opens the door for pretty much any custom dynamic tokens you may dream of. We can now rewrite our original example of a URL pattern like so:

/{Content.Container.Owner}/Blog/{Content.Date.yyyy/MM/dd}/{Content.Slug}

You may be wondering what characters are legal in token names. How would you include a dot or curly braces? Well, just double them.

To conclude this post, I'd like to show one more non-trivial example of token implementation. Here is how we'll surface tokens for content item fields:

if (context.Target == "Content") {
  var forContent = context.For<IContent>("Content");
  foreach (var typePart in
forContent.Data.ContentItem.TypeDefinition.Parts) {
foreach (var partField in typePart.PartDefinition.Fields) { var tokenName = "Fields." + typePart.PartDefinition.Name
+ "." + partField.Name; forContent.Token( tokenName, content => LookupField(content,
typePart.PartDefinition.Name,
partField.Name)
.Storage.Get<string>()); forContent.Chain( tokenName, partField.FieldDefinition.Name, content => LookupField(content,
typePart.PartDefinition.Name,
partField.Name)); } } }

With this provider, you can access fields with tokens looking like {Content.Fields.MyPart.MyField}. If the field itself implements its own tokens, you could do crazy things like {Content.Fields.MyPart.MyField.Some.Crazy.Thing}.

That's it for tokens. Next post, I'll show the preliminary design of Autoroutes, which enables the specification of custom URL patterns such as the one I've used as an example in this post.

Comments

Sebastien Ros said:

This is just awesome ... can't wait to see next part ! And even more to see the one about Projections, I might learn a few things ;)

# July 27, 2011 9:00 PM

Jasper said:

Great post!

It shows how useful tokens can be and that is more than string.Format(). The last example is impressive. Looking forward to see/use it in action.

# July 27, 2011 9:11 PM

George said:

I think Orchard should prove itself as an easy to use CMS by the community before everyday adding more advanced features that may not be used by majority of the users.

# July 28, 2011 9:31 AM

Jon Vee said:

Thanks Bertrand!

I think this will find its way into a lot of SEO uses!  (prepending/postpending categories, hierarchy, etc..)

Stay Awesome!

# July 28, 2011 7:47 PM

Bertrand Le Roy said:

@George: you are entitled to your opinion, but we have some hard evidence that this is already the case (although one can always improve, which we are also going to do). I also know for sure that the features that we are going to build on top of Tokens are in very high demand from the community, and have proven to be extremely useful if not indispensible on other CMS. Furthermore, your comment is too vague to be actionable I'm afraid. Contribute.

# July 28, 2011 7:56 PM

Andrianarivony Léon said:

This is an awesome feature and I can that it's almost our Content authors needed (à la Office TextMerger Field).

This feature will be useful for Newsletter templating but the way how you show it, it's more than this.

We can imagine a content editor (RadEditor, ...) to have a dedicated toolbar filled by the Token Provider.

It's like a "Reusable Content" but more powerfull.

Does it plan that the feature able to query other ContentTypes rather than the "Current Content" ?

# July 29, 2011 4:04 AM

Pierre Henri said:

Really cool!

I couldn't tell for sure, but I assume that you will be providing reflection out-of-the-box!?! And maybe add a setting to turn it off, if some people don't want that (for security reasons).

This would basically allow us to use tokens exactly like in Views (eg: @Model.ContentPart.Comments.Count becomes {Model.ContentPart.Comments.Count} without any extra coding).

I wish there was also some kind of auto-completion experience inside the Dashboard, but I guess that's asking for too much :)

In any case, discovering what tokens are available will be a key aspect of this feature. Maybe you could extend the shape tracer to provide them as well...

# July 29, 2011 4:05 AM

Adam said:

I think it is smart of orchard to focus on the core product and at the same time encourage the plug-in ecosystem to blossom.  

It also sounds like you are taking a lot of inspiration from existing CMS products, which is very smart move

# August 6, 2011 8:40 AM