Customizing binary paths in DD4T

Tridion has a rather particular way to deal with so-called binary files (images, PDFs, Word documents etc). When published, these files are placed in one folder (typically called ‘images’). To avoid conflicts between different binaries with the same file name, a unique ID is appended to the name of the file. A typical URL of a published binary file would be something like http://mysite.com/images/my-image_tcm3-123.png.

But what if you want to make your own rules for binary paths? And what if you are using the DD4T framework as well? In this post I will explain how to customize the paths to your binaries in DD4T.

If you use the DD4T framework to develop your SDL web site, binary files are published using the same behaviour as in any other type of Tridion implementation. However, there is a way to modify the behavior: you can configure an alternative structure group to publish the binaries into. That way you could for example have your PDFs use a URL like http://mysite.com/documents/my-pdf_tcm3-124.pdf.

But now (since the DD4T Templates version 2.2.2 were released, in fact) you can also tell DD4T to drop the unique ID (the ‘TCM-URI’) from the path. Just select ‘yes’ for ‘Strip TCM URI from binary paths’) and you’re done. The same PDF would now be published as http://mysite.com/documents/my-pdf.pdf. If you do this, there is a chance of conflicts because the file name is no longer guaranteed to be unique. Use with care, I’d say.

But even with these options, you are still rather limited in what you can do. In reality, many customers require more customization. For example:

  • Publish some (but not all) binaries without the TCM URI in the path
  • Publish binaries into a different structure group depending on the mime type
  • Publish binaries into a different structure group depending on folder metadata
  • Etc.

To make all this possible, we decided to allow customers to define their own logic for binary paths. This can be done by creating a ‘binary path provider’.

What is a binary path provider?

Starting with version 2.2.2, the DD4T templates contain an interface IBinaryPathProvider. This interface defines the following methods:

  • TcmUri GetTargetStructureGroupUri(string componentUri)
  • string GetFilename(Component mmComp, string variantId)

By implementing these methods, you have full control over the binary paths.

An even easier method is to extend the abstract class BaseBinaryPathProvider. This class contains virtual methods which implement the default behavior. You only need to override the methods which you want to change.

The BaseBinaryPathProvider gives you one extra method:

  • bool GetStripTcmUrisFromBinaryUrls(Tridion.ContentManager.ContentManagement.Component component)

If all you want to do is remove the TCM URI from the path (under certain conditions), you can simply override this method (and leave the rest to the base class).

Let’s look at the methods one by one to see what they are supposed to do:

GetStripTcmUrisFromBinaryUrls

Returns a boolean to indicate whether or not the TCM URI should be removed from the path. If the method returns true, the file name of the uploaded binary file is used. If it returns false, the TCM URI of the component and the component template is added. The default implementation returns false.

If your only requirement is to decide wheter or not the binary files have a TCM URI in their URLs, this is the method you want to override.

GetTargetStructureGroupUri

Returns a TcmUri of a structure group, or null if you want to use the default binary path (which is defined on the publication). The default behavior is: return the URI which is configured on the template (using a template parameter), or null if no URI is configured.

Override this method if you want more fine-grained control, e.g. publish binaries in different paths depending on their mime type, or read the target structure group from metadata on the folder that contains the binaries – to name but 2 examples.

Note that there is a relation between ‘stripping the TCM URI from the path’  and ‘specifying a target structure group’. If you decide to strip away the TCM URI, SDL Web can no longer guarantee the uniqueness of the path of the published binary. If 2 binary files have the same filename, and they are both published into the same structure group, publishing will fail. In that case, you need to make sure these files are published into different target structure groups.

GetFilename

If you want full control over the file name, you can implement this method. You could for instance construct the file name based on metadata on the multimedia component.

Note: you can ONLY influence the file name section of the path in this method. If you want to control the directory part of the path, you need to implement GetTargetStructureGroupUri as well.

 

Developing your own binary path provider

  1. Create a Class Library project in Visual Studio
  2. Reference the NuGet package DD4T.Templates.Base (version 2.2.1.74-alpha or higher)
  3. Create a class that extends the abstract class BaseBinaryPathProvider
  4. Override one or more of the methods described above

A code example might make it even clearer:

namespace Acme.SDL.Templates
{
 
  public class FlexibleBinaryPathProvider : BaseBinaryPathProvider
  {
    public FlexibleBinaryPathProvider(Engine engine, Package package) : 
      base(engine, package) { }

    public override bool GetStripTcmUrisFromBinaryUrls(Component component)
    {
      if (component.ComponentType != ComponentType.Multimedia)
      {
        throw new TemplatingException($"Unexpected component type: 
          {component.ComponentType} - while it should have been       
          {ComponentType.Multimedia}");
      }
      if (component.BinaryContent.MultimediaType.FileExtensions.Any(a => 
        a.Replace(".", "").ToLower().Contains("pdf")))
      {
        // this is a PDF, returning TRUE
        return true;
      }
      // this is not a PDF, returning false
      return false;
    }
  }
}

There is one important requirement I haven’t mentioned yet: your class must have a constructor which takes the following 2 arguments:

  • Tridion.ContentManager.Templating.Engine
  • Tridion.ContentManager.Templating.Package

 

Packaging and uploading your binary path provider

In order to access your custom IBinaryPathProvider class, it must be loaded in the AppDomain where your DD4T templates are executed. There are two ways to achieve this:

  1. Add your custom DLL plus all its dependencies to the Global Asssembly Cache.
  2. Merge your custom DLL and all its dependencies into a single DLL using ILMerge

I recommend to go with the second approach. You can set up a post-build action to perform the merge. You need to include the following DLLs:

  • DD4T.ContentModel.Contracts.dll
  • DD4T.ContentModel.dll
  • DD4T.Templates.Base.dll
  • DD4T.Templates.dll
  • DD4T.ContentModel.XmlSerializers.dll
  • DD4T.Serialization.dll
  • Newtonsoft.Json.dll
  • And of course your own custom DLL

Notice that the DD4T.Templates.dll must also be included.

Now you can upload the merged DLL using SDL’s TcmUploadAssembly tool.

Using the binary path provider

To use your new class, you need to add one of the following TBBs to the template:

  • Generate dynamic page
  • Generate dynamic component
  • Generate dynamic component presentation

In the template parameter ‘Custom IBinaryPathProvider class’, enter the fully qualified class name of your class.

Now if you run the template in the template builder (in Debug mode), you should see something like this in the logs:

BinaryPublisher: Found class to override the binary path provider: Acme.SDL.Templates.FlexibleBinaryPathProvider
BinaryPublisher: Instantiated class Acme.SDL.Templates.FlexibleBinaryPathProvider

And lo and behold: your code gets executed.

 

Example implementation

Here is a working example of some of the things you can do with a binary path provider:

 

Happy coding. And by the way: if you ‘d rather wait for the official release before trying out this new feature, we will try to have DD4T 2.2.2 properly released this month.