Content Bloom Global Summit 2014

Content Bloom Sign

Our sign outside of our Halifax location

This past week has been such an extraordinary week with the rest of the Bloomers at the Content Bloom Global Summit, our annual company event. While last year they held the event in the charming Belgium spot, this year was hosted at the beautiful Halifax location in Nova Scotia. The events were a week long experience, arriving on Sunday and leaving the following Sunday, filled with socializing, team bonding, training, presentations, workshops, demos, and just great fun. Every person got to give a couple of presentations that they specialize in, and the group itself was very active in participating and asking questions. And with a group like Content Bloom, many great questions and conversations were had for each presentation!

Group photo at Peggy's Cove!

Group photo at Peggy’s Cove!

This year’s topics included Tridion Templating, DD4T, Media Manager, Advanced Tridion Architectures, TCDL, Event System, Core Service API Usage, Workflow, and other cool Tridion items. Other presentations included advanced .NET and Java development, Java for .NET guys, front end development, and overviews of the company and its processes in general.

Rob himself talking about Tridion's TCDL tags at the Halifax office.

Rob himself talking about Tridion’s TCDL tags at the Halifax office.

An outdoor presentation by Primmer on Tridion Workflow after a rocky coastline hike! He was threatened to be thrown in if the presentation sucked.

An outdoor presentation by Primmer on Tridion Workflow after a rocky coastline hike! He was threatened to be thrown in if the presentation sucked.

But of course the week wasn’t just about learning, it was getting to know one another and seeing each other face to face. Our evenings were spent going on runs through hilly forests, going on hikes, diving into lakes and freezing cold ocean waters, exploring the local sites and scenery, beach lounging, barbecuing at Nick’s house, and of course being the group that we are, much much pub crawling! There was also some climbing up something they call The Wave, a concrete sculpture that just beckons you to reach the top of it (even though there are signs saying not to climb). I’m sure it was a hilarious sight for any passerby, a large group in the middle of a slightly drizzly night running up a concrete slope and nearly breaking their necks as they slipped and came tumbling back down. I also attempted to skateboard (or long board?) for the first time ever, which resulted in me doing the splits and learning how not to stop yourself while speeding up down a hill.

Some much needed relaxation on a beach after an intense week of presentations and training!

Some much needed relaxation on a beach after an intense week of presentations and training!

Much thanks to John, Nick, Miles and Oksana for organizing and running the event! It was an incredible experience of a week and an honor to be among such a talented and fun group of peers!

Our last night of the summit at "Your Father's Moustache"

Our last night of the summit at “Your Father’s Moustache”

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!

Reloading/Refreshing Lists in the Shortcuts Area

A recent question on stack exchange regarding the ability to refresh or reload the favorites section using the Anguilla API has sparked this post, where I thought I would expand a little bit on the answer.  What the asker was doing was modifying the favorites (in this case the names), but needed the favorites section to be reloaded in order to reflect these changes. The shortcuts area is within an iFrame, so my first thought was to use some regular JavaScript to reload the iFrame itself via:

$('#FavoritesTree iframe').contentWindow.location.reload(true);

But as the asker of the question realized, this actually causes the entire Tridion GUI to be loaded into the shortcuts area!  Definitely not what we want to do!

Luckily for us, Anguilla has a way for us to unload the lists which will automatically cause them to reload. If we want to refresh all of the lists inside of the shortcuts area, we can do the folllowing:

$models.getItem("cme:userfavs").unloadLists();

So what’s going on in the above statement? $models.getItem("cme:userfavs") retrieves the shortcuts area which is represented by the type Tridion.Cme.Model.FavoritesFolder. This object contains the method unloadLists() which does the magic for us. But suppose you only want to refresh a specific list in the shortcuts area?

$models.getItem("cme:shortcuts").unloadLists();
$models.getItem("cme:custompgs").unloadLists();
$models.getItem("cme:usrtsks").unloadLists();

Again, with each of these items you can just call the unloadLists() method to refresh that specific list. You can even target the child lists of these lists… for example, $models.getItem("cme:chklst"); which would get the Checked-out Items from the My Tasks. If you’re trying to figure out how you can get the identifier to pass to $models.getItem(), a good place to look is the address bar after clicking on the list you wish to target with your code. For example, after clicking on the “Checked-out Items”, you should see “Dashboard.aspx#locationId=cme:chklst” at the end of the address bar. The value of the locationId is what you want to use.

Happy coding everyone!

Back From The Dead

Hello dear friends and fellow Tridionaughts! Too long has it been since my last post, update, and involvement in my beloved Tridion community. But hopefully this post will be the first of many more to come as I crawl forth back from the dead, not as a flesh and brain eating zombie (but how cool would that be right? well… maybe not…), but optimistically as a once again involved blogger and Tridionaught. A lot has happened in my life since my disappearance, but most important of which was the birth of my beautiful daughter Lillian on April 19th. My army of mini-me developers will soon be a reality!

And a farewell to colleagues…

It’s with a heavy heart that I bid farewell to my colleagues and friends at Tahzoo as I travel wide eyed down the road to new and exciting adventures. Leaving behind something that I was a part of for four years was no easy decision, for it was a joy and pleasure to work along side my colleagues who’ve became great friends to me.

And a hello to the new…

With excitement I now say hello and greetings to my new colleagues and family at Content Bloom, a company started and created for similar beliefs and values that I hold myself by fellow Tridionaughts and MVP’s John Winter and Nickoli Roussakov. I’ve had the pleasure of getting to know and talk to John and Nick, as well as some of the other Bloomers, and I am thrilled to be working along side such fellow tech enthusiasts.

I hope to fuel the fires of creativity and innovation with such a company that has the passion and commitment to both technology and the client. Will that mean more blog posts for the Tridion community? Or maybe the birth of Razor Mediator 2.0? Maybe even finishing up some of the blog posts that I’ve started and never got around to wrapping up and publishing.

Or…?