Link Attributes Extension: Template Building Block

Earlier we talked about customizing an existing default popup in Tridion, and the post gave birth to the start of our Link Attributes GUI Extension.  As a recap, we were creating an extension that would allow us to add custom link attributes onto our hyperlinks added by the Hyperlink button.  We decided that the GUI Extension would add hash parameters to the link’s href attribute, and that’s pretty much where the post left off.  Today we will be adding a Template Building Block that will be responsible for stripping out the hash parameters that were added by the GUI Extension and converting them to attributes on the link element itself.

In another post we mentioned using HtmlAgilityPack in our Template Building Blocks, so although the regex for this task would be fairly simple, I’ll be using that library in our TBB.  If you are going to be using this extension and this TBB yourself, you’ll have to add the HtmlAgilityPack DLL to the GAC on your Tridion servers as well as reference it in your TBB project.

Next you will want to create a new class in your TBB project and add the following code:

using System;
using System;
using HtmlAgilityPack;
using Tridion.ContentManager.Templating;
using Tridion.ContentManager.Templating.Assembly;

namespace ContentBlooom.TemplateBuildingBlocks
{
    /// <summary>
    /// This TBB is responsible for converting the linkAttr- hash params created by the GUI Extension  into actual
    /// attributes on the link element that they've been placed.
    /// </summary>
    [TcmTemplateTitle("Link Attributes Converter")]
    public class LinkAttributesConverter : ITemplate
    {
        public void Transform(Engine engine, Package package)
        {
            bool outputModified = false;
            Item outputItem = package.GetByName(Package.OutputName);
            string outputString = outputItem.GetAsString();

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(outputString);
            doc.OptionOutputOriginalCase = true;

            var linksWithAttributes = doc.DocumentNode.SelectNodes("//a[contains(@href, 'linkAttr-')]");
            if (linksWithAttributes == null)
            {
                return;
            }
            
            foreach (var link in linksWithAttributes)
            {
                string url = link.Attributes["href"].Value;
                string hashString = url.Substring(url.IndexOf("#") + 1);
                string[] hashParams = hashString.Replace("&amp;", "&").Split('&');
                
                bool hasLinkAttributes = false;

                url = url.Substring(0, url.IndexOf("#"));
                hashString = String.Empty;

                foreach (string hashParam in hashParams)
                {
                    if (hashParam.StartsWith("linkAttr-"))
                    {
                        // If its a link attribute, add it as an attribute and then remove it from the hash string
                        hasLinkAttributes = true;
                        outputModified = true;

                        string[] rule = hashParam.Split('=');
                        string attributeKey = rule[0];
                        string attributeValue = rule[1];

                        link.Attributes.Add(attributeKey.Replace("linkAttr-", String.Empty), attributeValue);
                        url = url.Replace(hashParam, String.Empty);
                    }
                    else
                    {
                        // Keep any existing hash info there...
                        hashString += hashString.Length == 0 ? "#" : "&amp;";
                        hashString += hashParam;
                    }
                }
                if (hasLinkAttributes)
                {
                    link.Attributes["href"].Value = url + hashString;
                }
            }

            if (outputModified)
            {
                package.Remove(outputItem);
                outputItem.SetAsString(doc.DocumentNode.OuterHtml);
                package.PushItem(Package.OutputName, outputItem);
            }
        }
    }
}

Simple, no? You’ll want to put this TBB directly after your Building Block that provides the output (your DWT, Razor… whatever). Or even possibly as the first item in your Default Finish Actions. Make sure to create a component that has some links created with our modified Link popup from the earlier article, and run and execute.

Output prior to Link Attribute Converter:

<article>
    <p>Testing link attributes and what not and other stuff.</p>  
    <p>Should include a <a href="tcm:14-103941#linkAttr-newLink=test" title="a title">component link</a> as well as a normal <a href="http://www.example.com#linkAttr-custom=check" title="blah">http type of link</a>.</p>
    <p>We also need to see how it plays with <a href="http://www.test.com#blah=meh&amp;linkAttr-t=v">links with hashes</a> already.</p>
    <p><a href="mailto:e@mail.com#a=b&amp;c=d&amp;linkAttr-mailed=them" title="title">Multi hashed Link</a></p>
 </article>

And our output after our TBB:

 <article>
     <p>Testing link attributes and what not and other stuff.</p>  
     <p>Should include a <a href="tcm:14-103941" title="a title" newlink="test">component link</a> as well as a normal <a href="http://www.example.com" title="blah" custom="check">http type of link</a>.</p>
     <p>We also need to see how it plays with <a href="http://www.test.com#blah=meh" t="v">links with hashes</a> already.</p>
     <p><a href="mailto:e@mail.com#a=b&amp;c=d" title="title" mailed="them">Multi hashed Link</a></p>
 </article>

Not Quite Done

Our extension is not quite done just yet. Stay tuned once more as we add some enhancements and fixes to our little extension, such as the ability to add more than just one link attribute!

Searching and Modifying Output In Tridion Template Building Blocks With HtmlAgilityPack

In your Tridion career, you’ve probably written countless of C# Template Building Blocks. You’ve probably rolled your own custom Link Resolver, your own “Add or Extract Binaries From…”, your own cleanup templates… dozens of Building Blocks where the goal was to search for specific html patterns and attributes or modify the output in some way. And, if you’re like me, you’ve probably had to write numerous regex expressions to accomplish your tasks. I’ll admit that I’m no regex ninja or anything, its usually through trial and error that I get my regular expressions working correctly. Recently however, I attempted to write an expression that handled nested elements that could go any number of levels deep, with possibility to have several different variations of the pattern that I was looking for. My regex skills was just not good enough, and I thought for sure there must be a better way to parse the html output string of these patterns.

The search was short, but I found exactly what I was looking for: Html Agility Pack

Using HtmlAgilityPack in your Tridion Template Building Block is simple… just reference the DLL, and make sure you install the DLL into the GAC on the CMS and Publishing servers.

Now that we are set up and ready to run, let’s go ahead and write some code that will take the output from the package, and load it into an HtmlDocument.  Your code should look something like the following:

using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Tridion.ContentManager.Templating;
using Tridion.ContentManager.Templating.Assembly;
using HtmlAgilityPack;

namespace CodedWeapon.Samples
{
    [TcmTemplateTitle("HtmlAgilityPack Tester")]
    public class HtmlAgilityPackTester : ITemplate
    {
        private TemplatingLogger _logger = null;

	protected TemplatingLogger Logger
	{
		get
		{
			if (_logger == null) 
                            _logger = TemplatingLogger.GetLogger(this.GetType());

			return _logger;
		}
	}

        public override void Transform(Engine engine, Package package)
        {
            Item outputItem = package.GetByName(Package.OutputName);
            string outputString = outputItem.GetAsString();

            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(outputString);
        }
    }
}

If you’re familiar with XmlDocument, then HtmlDocument should not look so strange to you. All we are doing in the above is grabbing the output, and passing the output (as a string) into our HtmlDocument instance. Simple enough right? And now for the fun… seeing how simple it is to use this library to get the exact elements that we are looking for. Since a lot of the time we work with links, I’ll show some examples of grabbing some anchor tags.

Grabbing Every Anchor

Lets say we want to grab every anchor element present on the output:

HtmlNodeCollection nodes = doc.DocumentNode.Descendants("a");

You can loop over each of the nodes and perform any necessary operations that you want:

foreach (var node in nodes)
{
    Logger.Debug("Found node with url: " + anchor.Attributes["href"].Value); 
}

Just like with XmlDocument, you can also use XPath:

HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a");

Querying for Specific Elements

You can use XPath to search for the specific elements that you are looking for. For example, if we wanted to match all links with a specific url:

HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a[@href=\"http://www.example.com\"]");

Or what if we wanted to grab every link that actually contains a title attribute:

HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a[@title]");

Or better yet… what if we want check any node that does not contain the title attribute at all?

HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes("//a[not(@title)]")

If you’re not a fan of XPath, then you’ll be happy to know that you can also use LINQ like in this following example where we are looking for any link that has an href attribute of “#”:

var nodes = doc.DocumentNode.Descendants()
    .Where(n => n.Attributes["href"] != null && n.Attributes["href"].Value.Equals("#"));

Modifying Output

Normally when we’re searching for elements it’s because we may need to modify the markup in someway, either by adding attributes, removing elements, etc. In the following example, we are adding a custom attribute onto our anchors that contain a value of “#” in the href attribute:

foreach (var node in doc.DocumentNode.SelectNodes("//a[@href=\"#\"]"))
{
    node.Attributes.Add("data-my-custom-attribute", "true");
}

But what if we have a more complicated requirement where we need to strip a <tcdl:ComponentPresentation> tag (but ensure that we keep the inside markup). One may be tempted to try the following:

foreach (var node in doc.DocumentNode.Descendants("tcdl:ComponentPresentation"))
{
    node.ParentNode.RemoveChild(node, true); // this strips the tcdl tag while preserving the children... but its modifying the collection...
}

package.Remove(outputItem);
outputItem.SetAsString(doc.DocumentNode.OuterHtml);
package.PushItem(Package.OutputName, outputItem);

However, while running the above, you’ll get an error that says Collection was modified; enumeration operation may not execute. This is because you cannot modify the collection (add/remove) while enumerating it. But, we can keep a record of the nodes and operate on them after:

List nodesToChange = new List();
foreach (var node in doc.DocumentNode.Descendants("tcdl:ComponentPresentation"))
{
    nodesToChange.Add(node);
}

// Now we strip the wrapping tags...
foreach (var node in nodesToChange)
{
    node.ParentNode.RemoveChild(node, true);
}

package.Remove(outputItem);
outputItem.SetAsString(doc.DocumentNode.OuterHtml);
package.PushItem(Package.OutputName, outputItem);

Hopefully this will end the regex blues out there for anyone who may be struggling!

Client Templating For Tridion – Part I

As mentioned in my previous post, the MVP’s actually got down to grind out some work and code during the MVP Retreat. Nuno gives a nice overview of the open source projects in his post. I joined the Client Side Templating team, which was led by Will Price and also included Angel Puntero and Mihai Cădariu. The following post(s) will give some context to the details of that project.

Just show me the code…

If you don’t want to be bored by reading and just want to check out the project itself, feel free to check out the project site’s wiki pages and code.

Current Templating Models

Publish Time Rendering

Currently Tridion works out of the box with a templating model that gets resolved at publish time. If you have used DWT or the Razor Mediator, you have been using this such model. This is great for publishing static content, but what happens when you want to start diving into dynamic content? Sure, you can publish dynamic component presentations. With DCP’s, your presentations are still being resolved at publish time, but you can pull the content in dynamically on your pages via the Content Delivery API.

Dynamic Rendering or Request Time Rendering

Even with using the DCP’s, your development process can still end up feeling very inflexible and hacky at times. That’s where such frameworks such as CWA or DD4T come to the rescue. These awesome frameworks offer an MVC application side templating approach to your projects. Your templates actually live on the application server, and are rendered dynamically at request time. With CWA, your page views are dynamic while your component presentations are still rendered at publish time, and then your views pull the data and presentations in. With DD4T, templating is completely removed from the CMS side of things, and both the page and component views are handled by the application. These frameworks offer a much larger flexability in the way you can architect your projects, and even some added bonuses. For example, with DD4T (and using ASP.NET MVC), you can do your template development inside of Visual Studio using Razor and get the best out of the Intellisense tools. There’s no better feeling than getting drop down help and documentation as you code along with immediate errors and warnings without having to compile the project.

But Why Client Side Templating?

You’ve probably seen a lot of client side frameworks becoming extremely popular as of late. That’s because the benefits of letting the client’s browser handle some (or sometimes all) of the templating for you has numerous benefits. The number one benefit of course is the reduction costs. If you think about it, besides images and other binaries of course, the bulk of the size of your requests isn’t necessarily the data you are serving, but rather the markup itself. Most of your pages probably have the exact same markup, and only the data its relaying is different. Say if you had a 1000 Product pages. If your visitor visits each of those pages, your server has had to render the exact same markup for that page, and serve it, 1000 times, even though its pretty much the exact same page. But what if instead you only loaded the markup in the form of a template once, cache it, and for each of the 1000 pages only send your customer the raw json data needed for the page? You’ve now not only saved on bandwidth costs, you’ve saved on processing that your server would normally have to do for template rendering, and you’ve most likely decreased your web applications load time. Awesome, right?

CT4T

And that’s where CT4T comes into the picture. It’s a set of tools and API’s to easily add client side templating to your projects. The best part is, because its just JavaScript, its flexible enough to allow you to mix and match the different publishing models to meet your project needs. You can continue using your DD4T or CWA project if you wish to and just augment it with some client side goodness. Or you can mix a bit of old fashioned static pages published from Tridion with client side templating. Or you could be daring and create a completely new single page web application. The choices are yours.

To Be Continued…

In my next post I will discuss a bit about the architecture and decisions that were made so far. Remember, this is an open source community project, and if you feel like this is something that interests you, or better yet something that your client would love to have, I encourage you to contribute and join in. The CT4T project is still in its infancy and has a long way to go, and you have the chance to participate to create something great.

Join Us!

Razor Mediator 4 Tridion Version 1.2 Released

Hi readers! My apologies for the lack of blog writing over the past month, but things have been extremely busy on the development front. After much feedback and help with testing from colleagues, I am pleased to announce the release of the Razor Mediator Version 1.2. I put it up for submission to SDL Tridion World yesterday, but if you can’t wait, you can also grab it at its Google Code Project Site. There are a lot of fixes, updates, and new features added to this version, so you can probably expect some more posts coming soon with even more examples and tutorials than what is in the new updated documentation. So, what are some of these new updates and features you ask?

Ability to Import!

That’s right, you heard correctly. You can now import other Razor Template Building Blocks or even external Razor Templates stored in text files on the CMS Server to your templates. You can do this globally via the configuration to allow you to import templates to all of your Razor Templates (or even to all of your Razor Templates in one publication), or at the Template level via the @importRazor(“PathToYourTemplate”) command.

Updated GetComponentPresentationsByTemplate and GetComponentPresentationsBySchema

These methods have been updated to accept multiple parameters. So, that means you can now do something like:

@foreach (var cp in GetComponentPresentationsByTemplate("Template One Name", "Template Two Name", "Template Three Name")) {

}

RenderComponentPresentations and RenderComponentPresentationsByTemplate

Two new utility methods have been added to the base template that allows you to just render templates quickly. RenderComponentPresentations() will render all ComponentPresentations on the Page, while RenderComponentPresentationsByTemplate(param string[] templateNames) will only render the ComponentPresentations given the passed template names.

<div id="allTemplates">
    @RenderComponentPresentations()
</div>
<div id="someTemplates">
    @RenderComponentPresentationsByTemplate("Template One Name", "Template Two Name")
</div>

Better Compile Error Message

When you receive a compile error upon saving your Razor Template, you will now be shown a more informative message that also displays the line of code in question. Remember though, that the line of code that is shown is the generated C# code, and not your actual Razor code. This means that “<span>@blasadsfdsaf</span>” would show the line as being “Write(blasadsfdsaf);”. Also, the error displayed to the user in Tridion will no longer include Warning messages.

Index, IsFirst, and IsLast Properties

To further help you write cleaner code, the properties “Index”, “IsFirst” and “IsLast” has been added to the ComponentPresentationModel, ComponentModel, KeywordModel, and DynamicItemFields classes. For ComponentPresentationModels, these properties are automatically set when accessing the ComponentPresentations via the ComponentPresentations property of the base template, or by either of the GetComponentPresentationsByTemplate() or GetComponentPresentationsBySchema() methods.

@foreach (var cp in ComponentPresentations) {
    @if (cp.IsFirst) {
        <div>@cp.Component.Title is the first item.
    } else if (cp.IsLast) {
        <div>@cp.Component.Title is the last item.
    }
}

For ComponentModel and KeywordModel, these properties are automatically set when accessing them via the DynamicItemFields (that is, when they are set as a multi-valued field of course).

@foreach (var kw in Component.Fields.SomeKeywords) {
    <div class="@(kw.Index % 2 == 0 ? "alt1" : "alt2")">@kw.Title</div>
}
@foreach (var comp in Fields.SomeComponents) {
    @if (comp.IsLast) {
        <span>We only wanted the last ComponentLink item!</span>
    }
}

These properties are automatically set for DynamicItemFields when it is accessed via the DynamicItemFields as a multi-valued EmbeddedSchemaField.

@foreach (var embeddedFields in Fields.SomeEmbeddedFields) {
    <div class="@(embeddedFields.IsLast ? "last" : String.Empty)">@embeddedFields.Address (@embeddedFields.ZipCode)</div>
}

Quick Access to Debug Writing

Pre version 1.2, the Razor Mediator documentation said that you could write logging statements like @Log.Debug(“Your Message”). This was actually incorrect and would have thrown an error… you would of had to write them as @{ Log.Debug(“Your Message”); }. As of version 1.2, you can now do @Debug(“Your Debug”), @Info(“Your Info”), @Warning(“Your Warning”), and @Error(“Your Error”).

Of Fixes and More

There are a couple other minor goodies that have been added, as well as some critical fixes and updates that include caching and thread safety (for publishing) fixes. If you are already using a previous version of the Razor Mediator, I would definitely recommend updating to version 1.2. You can view the Change Log on the Google Code project for a full list of all the updates and fixes made in this release.

Version 1.3?

Yes, a Version 1.3 is now in the works, with even more helpers/utilities to make your templating life a bit less difficult, and some more features to help empower your abilities. And of course any more bugs or issues that you report to me. Much thanks to you for your feedback!

Razor Mediator Version 1.1 Released

I am happy to announce that the Razor Mediator version 1.1 has been submitted to SDL to be updated on SDL Tridion World. In the meantime, you can grab the v1.1 installer at its Google Code project site. So what exactly has been updated since version 1.0?

KeywordModel

Just like the other Tridion items that have been wrapped in Model classes, the Keyword class has finally been wrapped as well.  This means you can easily access a Keyword’s metadata just like the other objects as well.

@Fields.SomeKeywordField.Metadata.SomeMetaField

All fields that would normally return a KeywordField will now return a KeywordModel (or List<KeywordModel> for multi-valued fields).  The @Models.GetKeyword(string) method also returns a KeywordModel.

GetComponentPresentationsBySchema(string schemaName)

A new utility method has been added to the base template class to allow you to easily grab all Component Presentations filtered by the component’s schema title.

@foreach (var cp in GetComponentPresentationsBySchema("Article Schema")) {
    <text>@cp.RenderComponentPresentation()</text>
}

DynamicPackage

This feature was listed in version 1.0 but wasn’t actually used.  All calls to the @Package property was really just returning the Tridion Package instance.  So, what exactly can you do with DynamicPackage?  You can easily grab package items and parameter values in your templates using dot notation.  For backwards compatibility, GetByName(string name), GetByType(ContentType type), and GetValue(string name).  The following would all work assuming there was either a package item of a string type named “ItemName” or a parameter field with xml name of “ItemName”.

@Package.ItemName
@Package.GetByName("ItemName").GetAsString()
@Package.GetValue("ItemName")

And some fixes…

Besides the several new features, some fixes were also added.  For a complete list of fixes, feel free to check out the Change Log page.  If you come across more issues, please let me know so that I can get them fixed ASAP. And if you have some cool ideas for possible features that you want to see in the Razor Mediator, shoot them over as well.

What’s Next?

I’m planning on putting an easy way to extend your Razor templates using reusable helper methods and functions.  So look forward to version 1.2 coming out sometime in the hopefully near future, as well as another blog post to show you examples of creating helper methods in your current templates!

Tridion Dreamweaver Template Delimiter Escaping

This was a question that popped up in the forums recently, so I thought I would share on how to escape Dreamweaver delimiters. That is, actually outputting “@@someValue@@” or “${someValue}” in your DWT’s without having them parsed.

The following samples will all escape the delimiters properly.

@@"@" + "@" + "test" + "@" + "@"@@
@@"$" + "{" + "test" + "}"@@
@@"@" + "@test@" + "@"@@
@@"$" + "{test}"@@

And will output:

@@test@@
${test}
@@test@@
${test}

As a side note, trying to escape the delimiters using the “${ }” delimiter will not work the same. The closest I got to actually displaying the correct output was by using ${"$" + "{" +"test}}. Yep, the odd number of quotes was needed… if you add the last quote (as it looks like it should need), it’ll actually output the last quote. Odd, no?

I hope that helps anyone who’s in need to do this sort of escaping from their DWT’s!