Saturday, August 20, 2011

Using Dreamweaver Field Notation in Tridion C# code

If you've done any Tridion Dreamweaver Templates you have surely come to appreciate the DW notation syntax that Tridion introduced with version 5.3. Compared to what we had to do before to read a field, this syntax was much easier - both on the eye and on the sanity.

If you have no clue what I'm talking about, here's an example of the difference between old-school (pre-2008) template code to read one field from a component, and the new way:

Old School
[%= Component.Fields("summary").value(1)%]
Dreamweaver Notation

Not very different you say? What about reading a value from an embedded field then?
Old School
Dreamweaver Notation

OK, so you agree with me now? If you don't agree yet, then try comparing the code when looping through multivalue collections...

Another great feature of Compound Templating (perhaps the best feature some will say) was the introduction of Template Mediators. This allows us to (out of the box) use .NET-based languages as Template Building Blocks, both using .NET assemblies or c# Fragments. Some of you may even use the Community owned (mostly written and maintained by Yoav Niran) XSLT Mediator that lets you use XSLT Building Blocks in Compound Templates.

So, plenty of good stuff. One thing that grandly p****d me off with the c# implementation was that it was still as complex - if not more - to read a component's field value than it was before with VBScript.

Here's how you would read the same Embedded Summary field using c#:
Component c = (Component)engine.GetObject(package.GetByName(Package.ComponentName));
ItemFields fields = new ItemFields(c.Content, c.Schema);
ItemField embeddedField = (ItemFields)fields["paragraph"];;
TextField textField = embeddedField["embeddedsummary"];
String textFieldValue = textField.Value;

Not really user friendly, is it? Another thing you may notice is that the field Type is also needed when reading a field, so what happens if the field type changes from, say, SingleLineTextField to KeywordField? Yup, your code will not work anymore and needs to be changed (there's a lot to say about Schema changes, but that's for another day).

So, why can't we use a DW-like notation in c#? Well, the short answer is that you can't because Tridion didn't provide it for you. The longer answer is that if you're one of the near 1000 people that downloaded the Dreamweaver Get Extension from Tridion World, you can, and here's how.

  1. Reference Tridion.ContentManager.Extensions.Templating.dll from your Visual Studio Project
  2. Add "using Tridion.ContentManager.Extensions;" to your .cs
  3. Start using the "FieldOutputHandler" class
Here's an example to get you started:
FieldOutputHandler h = new FieldOutputHandler(page.Id, engine, package);
meta.Add("Title", h.GetStringValue("Metadata.SEO.SEOTitle"));
meta.Add("Keywords", h.GetStringValue("Metadata.SEO.SEOKeywords"));
meta.Add("Description", h.GetStringValue("Metadata.SEO.SEODescription"));

This class has a lot of parameters, configuration settings, SiteEdit-related instructions, and also can be used for a lot more than just outputting string values...

For one, it can read _any_ field as a String, so you don't need to worry about the field's type.
Second, it can - just like the Get Extension - read fields from other objects, like Keyword Metadata, publication Metadata, etc, etc.
Third, you can drill down into a linked component's field value, which could also be a linked component, etc, etc.

Here's another example where this handler is being used to loop through values of a configuration component:
for (int x = 0; x < TotalConfigs; x++)
    FieldOutputHandler h = new FieldOutputHandler(PubMeta.GetComponentLinkField("Metadata.Configuration").Values[x].Id, engine, package);

    int varCount = h.GetEmbeddedSchemaField("Fields.Keys").Values.Count;
    for (int i = 0; i < varCount ; i++)
        String name = h.GetStringValue(String.Format("Fields.Keys[{0}].Key", i));
        String value = h.GetStringValue(String.Format("Fields.Keys[{0}].Value", i));
        if (package.GetByName(name) == null)
            package.PushItem(name, package.CreateStringItem(ContentType.Text, value));

Last but not least, I know the class name sucks. I was quite uninspired that day. Have fun with your newly-discovered Tridion powers!

No comments: