Improving link resolving in DD4T

When you use DD4T to create a web site, there are two ways to handle links. One is to write out a hyperlink. This is done by calling the LinkFactory and passing it the current component. For this purpose, all you need to know is the component’s URI.

The other option is to write out content from the linked component itself. In this case, it helps if you have the complete linked component at your disposal, URI, title, fields, metadata, etc.

It’s very neat to have the linked component inside your model, without having to retrieve it dynamically. However, there is a price to pay: the XML that you publish becomes much bigger because it incorporates the linked components as well.  This slows down publishing and may slow down your broker database.

Also, it does not always stop at one level of links. Sometimes, you may feel the need to follow a chain of links, for 2, 3 or more levels. To make this manageable, DD4T uses a mechanism called ‘LinkLevels’: by setting a parameter ‘LinkLevels’, you tell the DD4T templates how deep they should go when following links.

This is a rather rough principle though. In reality, there are some fields which you never want to follow (for example if you know up front that you will write them out as hyperlinks anyway). The LinkLevels mechanism however does not distinguish between fields, so you will get these linked components in the broker anyway!

This diagram shows how it works:

link levels - old

 

With link level set to two, a total of 7 components are published. Only 2 are needed completely, and for 3 we only need the URI. So most of the content is rendered to XML, published and stored in the broker for no reason at all.

Configuring links per field

To do anything about this, we need to have a way to configure link resolving behaviour on a per-field basis. At the recent MVP retreat I had the pleasure of talking to Jaime Santos Alcón. Jaime is a real GUI extension guru. He showed me how easy it is to add custom properties to a schema field in Tridion (at least, he made it look easy!).

With this in mind, and a lot of helpful code by Jaime to get met started (see http://jaimesantosalcon.blogspot.nl/2013/10/adding-extended-information-to-schema_28.html), I created a GUI extension of my own: the Trivident DD4T Editor. This editor adds one little checkbox to the schema definition screen:

dd4t editor

 

When the property ‘Follow link when rendering XML (dd4t)’ is checked, this information is stored in the ExtensionXml of the schema field. It is then picked up by the DD4T template code to determine whether or not it should follow a link.

 

Now, the diagram would look a bit different:

link levels

 

As you can see, there are a lot less components in the broker now, which will improve publisher performance and reduce the amount of data in the broker database.

Installing and configuring the new solution

To install, download trivident.dd4t.editor 1.0.1 and follow the instructions in the README.txt.

 

After the installation you should first enable one or more schema fields to ‘Follow link when rendering XML’. Note that you will only see the checkbox for component link and multimeda link fields!

Next, open your existing templates (component + page) in the template builder and set the parameter ‘Use field setting to determine whether or not to follow links’ to ‘yes’ (okay, the name is a bit too long, please suggest a better one in a comment!).

follow link levels per field

You’ll notice that in the example, the LinkLevels are set to 3. Note that the LinkLevels parameter is not obsolete in this new solution. Rather, it is treated as a ‘Max Link Level’. This is necessary because otherwise you may still acumulate too much XML by following links.

The configuration above means: “follow fields which are configured to be followed, but stop when you reach level 3”.

 

Download

Installation instructions for the GUI extension are in the README.txt inside the zip.

To download the latest DD4T templates, go to https://github.com/dd4t/DD4T.TridionTemplates/releases.

Thanks to Jaime Santos Alcón.

 

 

 

 

Splitting the navigation

Every site has navigation: top menus, side menus (do they still exist?), bread crumbs, sitemaps, etc. The common approach in a Tridion implementation is to derive the navigation from the structure groups.

This is commonly done as follows:

  • Create a Navigation page template in Tridion which traverses the entire structure
  • Leave out some structure groups because you don’t really want them in the navigation
  • Write out this information as XML
  • Consume the XML from the web pages to show the navigation items

 

To change the navigation, all the editors have to do is add/change/delete a structure group and republish the Navigation system page (which uses the Navigation page template).

If the web site runs .NET, chances are the navigation will be published in the form of a SiteMap. For this article I will assume this is the case, but the same principles apply when you’re working with Java.

 

Help, my page is live!

This is how it has been done for years, and by and large it works fine. Performance is good, it is easy to understand and easy to manage, and best of all: it is a low-tech solution which works (in slight variations) on every target platform (java, .NET, even ASP).

Still, I do hear complaints about this solution every once in a while. They usually come from organizations with a big web site, with lots of changes to the navigation structure, and many different editors working to maintain it all. The complaint goes like this:

I am working on a new section of the site which is not yet ready to be published, and a colleague goes and publishes the navigation, making my own new section visible before its time!

This is – of course – an understandable and reasonable complaint. My job is to help Tridion users, so I thought of a solution. I wanted to keep the advantages of the ‘navigation xml’ approach, but tweak it just a little bit to avoid problems like the one quoted above.

The problem lies not in the ‘xml approach’ per se, but rather in the fact that there is only one XML page which carries the information about navigational structure from the CMS to the web site.

Normally (at least in the organizations I work for), the editors (or Content Managers, or whatever they call themselves) will divide the work along the lines of sections of the site:

  • Products versus services
  • Business versus consumers
  • Sales versus support
  • etc.

If we could just split up this information into chunks, we would have a solution! One chunk for ‘products’, managed by the people who are responsible for products, another one for ‘services’, managed by the services crowd.

From a technical angle I would prefer to minimize the changes to the solution. So: split up the navigation page at the last possible moment, and paste the chunks  back together againat the earliest possible moment. In a picture, that would look like this:

splitting up navigation

To achieve this, we need to make two changes:

  • Change the template so that it stops iterating over the structure if a ‘subnavigation’ page is found, and add a link to the subnavigation to the main navigation structure
  • Change the SiteMapProvider which reads the SiteMap information from your navigation page, so that it follows the links to the ‘subnavigation’ pages and merges them into one big SiteMap

The main SiteMap when generated by the template would look something like this:

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> 
  <siteMapNode id="tcm:6-247-64" url="/business" title="Business">
    <siteMapNode id="tcm:6-246-64" url="/business/service1.html" 
      title="An interesting service" />
    <siteMapNode id="tcm:6-147-64" url="/business/service2.html" 
      title="Some other service" />
  </siteMapNode>
  <siteMapNode id="tcm:6-301-64" url="/consumer" title="Consumer" 
    subnavigation="/consumer/navigation.xml">
</siteMap>

As you can see, we have two structure groups on the top level: business and consumer. The consumer branch does not contain any pages, but it does specify a subnavigation url: /consumer/navigation.xml.

This consumer navigation looks like this:

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> 
  <siteMapNode id="tcm:6-301-64" url="/consumer" title="Consumer">
    <siteMapNode id="tcm:6-346-64" url="consumer/product1.html" 
      title="An interesting product" />
    <siteMapNode id="tcm:6-347-64" url="/consumer/product2.html" 
      title="Some other product" />
  </siteMapNode>
</siteMap>

When the navigation is requested in the web application, however, a class called DistributedSitemapProvider makes sure that the whole structure is merged again, and the XML looks like this:

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0"> 
  <siteMapNode id="tcm:6-247-64" url="/business" title="Business">
    <siteMapNode id="tcm:6-246-64" url="/business/service1.html" 
      title="An interesting service" />
    <siteMapNode id="tcm:6-147-64" url="/business/service2.html" 
      title="Some other service" />
  </siteMapNode>
 <siteMapNode id="tcm:6-301-64" url="/consumer" title="Consumer">    
   <siteMapNode id="tcm:6-346-64" url="consumer/product1.html"         
     title="An interesting product" />
   <siteMapNode id="tcm:6-347-64" url="/consumer/product2.
     title="Some other product" />
  </siteMapNode>
</siteMap>

 

As you can see, the subnavigation replaces the node in the main navigation. The end result is a completely standard sitemap which you can use normally.

The mechanism is iterative: you can have subnavigations on any level, and you can nest one subnav inside another as well.

 

Download

Download source code: Distributed Sitemap

The template class should be built and uploaded to Tridion. It creates a TBB called Distributed Sitemap which should be included in a page template.

The web app code should be included in your web application and should run on any ASP.NET site.

 

 

Inside DD4T: Resizing images on the fly

In a previous post, I explained how you can use the DD4T.Web library to serve binary files (like images, documents, etc) directly from the broker database. This was done with the BinaryDistributionModule.

There is an additional benefit to reap from this: the BinaryDistributionModule is able to resize your images on the fly. Here’s how.

Let’s say that you have a JPG image published to the broker database, with the URL  /images/ouroffice_tcm3-4954.jpg. If you use a browser to request this image, the binary data is extracted from the broker database by the module, and stored on the file system. You should see something like this:

ouroffice

 

Now when you request the URL /images/ouroffice_tcm3-4954_w150.jpg, this is what you get:

ouroffice_w150

 

You may be thinking “that’s easy, they simply uploaded a smaller version into Tridion”. But a look at the URL reveals that this cannot be the case: the URI (the ‘3-4954’ bit) is the same, which means that there is only one image in the CMS. Actually, all that’s different between those URLs is the string “_w150” at the end of the filename, right before the file extension.

Substitute for variants

The traditional approach to having thumbnail versions of images is by creating a variant in your template code. But that doesn’t make much sense with DD4T. An implementation using this framework hardly touches Tridion templates at all, and instead focuses completely on the web application. Hence it made sense to implement ‘thumbnailing’ on the web application side as well.

So how does this work in a Razor view? Let’s first look at the typical way to display an image:

<img src="@Model.Fields["image"].LinkedComponentValues[0].Multimedia.Url" />

The Multimedia.Url property contains the (local) url of the image you’re trying to show, e.g.  /images/ouroffice_tcm3-4954.jpg. Somehow we need to insert this ‘_w150’ string into this. Fortunately DD4T offers a helper method which comes to the rescue:

<img src="@Model.Fields["image"].LinkedComponentValues[0].Multimedia.Url.ResizeToWidth(150)" />

When this snippet is requested by the browser, it looks like this:

<img src=" /images/ouroffice_tcm3-4954_w150.jpg" />

Besides ResizeToWidth there is also a ResizeToHeight and even ResizeToWidthAndHeight, which takes two integers as parameters.

 

 

 

Inside DD4T: Handling Binary Files

A new feature in DD4T is the Web library (DD4T.Web.dll). It contains some functions that are useful in any .NET web site, whether they use MVC or not. It offers – for example – a way to serve binaries (images, PDFs, etc) straight out of the broker database.

Why would you want to do that, might you ask? Of course, Tridion has been used for ages to deliver binaries directly to the file system. Binaries are rarely dynamic in nature, so storing them as static files is actually a good idea!

Well, yes and no. Yes, storing binaries on the file system is great for performance, but it has a big downside: you cannot just plug in a new server into your web farm anymore, since it would not contain these binary files. Also, your developers who are so used to running the entire web app from within their IDE, would miss out on the images if they are only published to a central presentation environment.

If you work with DD4T, that does not mean you MUST serve the binary files from the broker database. It is okay  to serve them from the file system as well!

 

Serving binaries with the BinaryDistributionModule

The DD4T.Web library contains a BinaryDistributionModule. Purpose of this HttpModule is to make sure the requested binary is available on the file system, so IIS can serve it. Here’s how it works:

  • If the binary is not on the file system, it is retrieved from the broker database and stored as a file
  • If the binary is already on the file system, the timestamp of the file is compared against the last publish date of the binary in the broker database. If the file is stale, it is replaced. If the binary is no longer present in the broker, the file is removed (resulting in a 404, which is what you would expect if you attempt to view a file which has been unpublished).

To configure the BinaryDistributionModule, add the following XML code to the system.webServer node in your Web.config:

<modules runAllManagedModulesForAllRequests="true">
  <add name="BinaryModule" 
       type="DD4T.Web.Binaries.BinaryDistributionModule" />
 </modules>

DD4T.Web: Publication Resolving

The next DD4T release (1.30) which will be out shortly, features a new .NET library: DD4T.Web. This library contains functionality which can be used within a .NET web application, regardless of whether you are using MVC or not. In a short series I will introduce the most important functionality in this library. This episode: Publication Resolving.

 

DD4T is all about URLs. When the request comes in, the framework looks in the Tridion broker database for pages with a matching URL. Or rather: a matching path. For example: if the URL is http://www.acme.com/products/foobar.html, the path is /products/foobar.html.

But what if there are more matches? This is entirely possible because of Tridion’s BluePrinting model. Imagine the Acme corporation decides to launch a German web site, with the base URL http://www.acme.de. They manage this site in a Tridion publication which is a child of their ‘dotcom publication’ (which manages the www.acme.com site). Given the nature of BluePrinting, the German page about their ‘Foobar’ product, will have the url http://www.acme.de/products/foobar.html, and the path of this page is /products/foobar.html.

As you see, the path is identical to the path in the English publication. So how can DD4T tell which page to serve? This is done by the PublicationResolver. This is a very simple class, whose job it is to find out which publication the current request belongs to. In the example above, the PublicationResolver could look at the host name and return the correct publication id: if the host name is ‘www.acme.de’, it should return the id of the German publication, if it’s ‘www.acme.com’ it returns the id of the DotCom publication.

Resolving the publication based on the host name is an obvious choice in many cases, but there are alternative scenarios. For example: you could look at the preferred browser language and base the publication id on that. So: if my browser language is German, I will see the German site, regardless of the host name. Technically, I wouldn’t need the www.acme.de domain at all (although I don’t think Google will like it if the same URL can return a page in different languages!)

And then there is the simplest form of all: you can simply decide that all the requests in your web application should use the same publication id. This is actually the default behavior. DD4T comes with one implementation of the PublicationResolver interface, the DefaultPublicationResolver, and it simply looks in the Web.config for an appSetting called ‘DD4T.PublicationId’. This id is then used to look up all the pages, components and binaries. The downside is of course that you need to set up a separate web application for each language that you’re supporting. Not very efficient and tough to manage!

 

Implement your own resolver

It’s very easy to write your own publication resolver.

  • First, create a class library project in Visual Studio.
  • Add a class which implements the DD4T.ContentModel.Contracts.Resolvers.IPublicationResolver.  
  • Right-click the word IPublicationResolver and select ‘implement interface’.

Your code now looks like this:

using System;
using System.Web;
using DD4T.ContentModel.Contracts.Resolvers;

namespace Trivident.DD4T.Examples.PublicationResolvers
{
   public class HostNamePublicationResolver : IPublicationResolver
   {
      public int ResolvePublicationId()
      {
      }
   }
}

As you see, this interface defines only one method: ResolvePublicationId(). In that method, we will check the host name in the URL and use it to determine the publication id. In a very simple (not to say a moronic) form, the code might look like this:

using System;
using System.Web;
using DD4T.ContentModel.Contracts.Resolvers;

namespace Trivident.DD4T.Examples.PublicationResolvers
{
   public class HostNamePublicationResolver : IPublicationResolver
   {
      public int ResolvePublicationId()
      {
         switch (HttpContext.Current.Request.Url.Host)
         {
            case "www.acme.de":
               return 17;
            case "www.acme.com":
               return 16;
         }
         throw new InvalidOperationException(string.Format("unknown hostname '{0}", HttpContext.Current.Request.Url.Host));
      }
   }
}

In a real life implementation you would probably want to get rid of the hardcoded IDs, but the point is clear, I hope.

Using  it in your application

The PublicationResolver is a property of the factories (PageFactory, ComponentFactory, BinaryFactory, LinkFactory). If you’re using a dependency injection framework (like MEF or Unity) you can simply configure it as a dependency of those factories. Otherwise, you can set it manually (in code). In your PageController for example:

 

private IPageFactory _pageFactory = null;
public override ContentModel.Factories.IPageFactory PageFactory
{
   get
   {
      if (_pageFactory == null)
      {
         _pageFactory = base.PageFactory;
         _pageFactory.PublicationResolver = new HostNamePublicationResolver();
      }
      return _pageFactory;
   }
}

 

Using DD4T without publication resolving

It’s possible to use DD4T without doing any publication resolving. In that case the publication is not used when looking up the page in the broker database. This works if all the paths in your system are unique. So in our Acme example, the URLs of the English and German ‘foobar’ pages should be:

  • http://www.acme.com/products/foobar.html (English)
  • http://www.acme.com/de/products/foobar.html (German)

In this case, DD4T can and will always find the correct page.

If ‘the business’ can live with this style of URL, then go for it. It saves you the trouble of resolving anything!

To use this approach, just use the default publication resolver (this requires no action) and remove the key ‘DD4T.PublicationId’ from the Web.config.