Using ECL Images in DXA

This blog post will describe how to use ECL images in DXA. This is based on work I've done with ECL providers for Bynder, MediaBeacon, Amazon S3 etc. In addition I've also released a simple DXA module to render ECL images in a generic way.

Local or remote image?

First thing you need to figure out is if the image should be published to the site or referred by an URL to a remote CDN location. It depends on the DAM or media provider your are using and how you want to handle images on your site. If the image is going to be published by the DXA templates is determined if the ECL provider is returning an URL or not. If the ECL provider returns an empty string an byte stream is requested by the templates and the image asset is published to the site. See below pseudo code from implementation of IContentLibraryMultimediaItem in an ECL provider:

public IContentResult GetContent(IList<ITemplateAttribute> attributes)
{

  // in case we want SDL Tridion to publish the item -> return the content stream for this image
  //
  var stream = MyApi.Download(Assst);
  return ECLProvider.HostServices.CreateContentResult(stream, Asset.Size, MimeType);               
            
  // If not, just return NULL
  //       
  return null;

}

public string GetDirectLinkToPublished(IList<ITemplateAttribute> attributes)
{
  // Return URL if image is residing in a remote CDN   
  //
  return MyApi.GetMediaUrl(Asset);

  // Return empty string if the asset should be published to the site 
  //
  return "";

} 

It is recommended to have this configurable in the ECL provider so the site implementor can select what option that fit's their requirements best.

DXA Semantic mapping

To make DXA to understand your ECL type you need to create (or reuse an existing) media class inheriting from EclItem and annotate it with semantic information about the ECL schema (named 'ExternalContentLibratyStubsSchema[mountpoint ID]'). In addition you need to implement the HTML generation method (ToHtml()/toHhtmlElement()) if you are not using ECL template fragments. You also need to provide MVC data pointing to a default view. See below for some examples in Java and .NET:

Java example:

@SemanticEntity(entityName = "ExternalContentLibraryStubSchemabynder", vocabulary = SDL_CORE, prefix = "i")

public class BynderEclImage extends EclItem {

  @Override
  public boolean isImage() {
    return true;
  }

  @Override

  public HtmlElement toHtmlElement(String widthFactor, double aspect, String cssClass, int containerSize, String contextPath) throws DxaException {

    return img(getUrl())
      .withClass(cssClass)
      .withAttribute("data-aspect", String.valueOf(Math.round(aspect * 100) / 100))
      .withAttribute("width", widthFactor)
      .build();
  }

  @Override
  public MvcData getMvcData() {
    return MvcDataCreator.creator()
      .fromQualifiedName("Bynder:Entity:Image")
      .defaults(DefaultsMvcData.CORE_ENTITY)
      .create();
  }

}

C# example:

[SemanticEntity(CoreVocabulary,"ExternalContentLibraryStubSchemabynder",Prefix = "i")]
public class BynderEclItem : EclItem
{
  public override string ToHtml(string widthFactor, double aspect = 0, string cssClass = null, int containerSize = 0)
  {
     string classAttr = string.IsNullOrEmpty(cssClass) ? string.Empty : string.Format(" class=\"{0}\"", cssClass);
     string widthAttr = string.IsNullOrEmpty(widthFactor) ? string.Empty : string.Format(" width=\"{0}\"", widthFactor);
     string aspectAttr = string.Empty; // string.Format(" data-aspect=\"{0}\"", aspect.ToString(CultureInfo.InvariantCulture));
     return string.Format("<img src=\"{0}\"{1}{2}{3}>", Url, widthAttr, aspectAttr, classAttr);
  }

  public override MvcData GetDefaultView(Localization localization)
  {
     return new MvcData("Bynder:Image");
  }
}

The default view is a simple JSP/Razor view with the following:

JSP:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="dxa" uri="http://www.sdl.com/tridion-dxa" %>

<jsp:useBean id="entity" type="com.sdl.dxa.modules.bynder.BynderEclImage" scope="request"/>
<dxa:media media="${entity}" cssClass="${entity.htmlClasses}"/>

Razor:

@model BynderImage
@Html.Media(Model)

In addition you need to register the view model in the module initialization (in a separate module or included in any other module):

Java:

@Component
@RegisteredViewModels({
@RegisteredViewModel(viewName = "ExternalContentLibraryStubSchemabynder", modelClass = BynderEclImage.class)
})
public class BynderInitializer extends AbstractInitializer {

  @Override
  protected String getAreaName() {
    return "Bynder";
  }
}

C#:

public class BynderAreaRegistration : BaseAreaRegistration

{
  public override string AreaName
  {
     get { return "Bynder"; }
  }

  protected override void RegisterAllViewModels()
  {
     RegisterViewModel("ExternalContentLibraryStubSchemabynder", typeof(BynderEclItem));

     // Or alternatively: ModelTypeRegistry.RegisterViewModel(new MvcData { }, typeof(BynderEclItem));
  }
}

Now it's possible to use your ECL images in DXA as multimedia links in rich texts and in your content views in the same way as normal images.

Generic ECL Image Module

To simplify the setup in my various tests and development of DAM ECL providers I've implemented a simple generic DXA module that can render any ECL image in DXA without any code changes. The module can be found here: https://github.com/NiclasCedermalm/ecl-image-dxa-module 

With the module you just need to specify the different ECL types in the DXA configuration and you're good to go:

.NET:

<add key="ecl-image-types" value="Bynder,MediaBeacon,S3"/>

Java: 

ecl.image.types = Bynder, MediaBeacon, S3

The module uses this configuration to create semantic definitions on the fly using reflection. The module works with both locally published images and remote CDN images.