SDL Connect 2018

Last week SDL opened the doors of the 2-day SDL Connect conference in Santa Clara, CA. In 2017 they brought back Tridion and included Knowledge Center as Docs in Tridion DX suite. This year they followed suit, by launching new Tridion Sites 9 and new language solutions based on linguistic artificial intelligence (AI) and machine learning (ML). SDL is foreseeing 5 disruptions related to content where AI will play a big differentiating factor:

  • Content will create itself
  • Content will be agile
  • Content will organize itself
  • Content will be secure
  • Content will be your best salesperson

Besides all the announcements it was good to see customers like IKEA, NetApp and Philips on stage explaining their challenges and how SDL helped resolve them. New this year was the SDL Innovation Center where experts answered questions and showed demos of the new SDL products during networking breaks. 

There were a lot of interesting sessions and too much to cover. So, let me show you my favorite topics of SDL Connect 2018.

SDL Tridion Sites 9

The biggest announcement during SDL Connect 2018 was the new version of their flagship product: SDL Tridion Sites 9. This enterprise content management system is part of a bigger suite: Tridion DX. Overview of the newest features is described below.

Enhanced customer experiences:

  • API‑first approach to delivering content across your ecosystem of mobile and digital channels
  • Headless content delivery services enabling server-less based solutions 
  • Dynamic content mashups (marketing and product information) with shared taxonomies
  • Experience optimization for greater personalized relevance

Assisting content management operations:

  • Regions for easy management of content‑rich web pages
  • Quick and easy image editing (cutting and cropping)

Newest technology features:

  • GraphQL-based content service
  • Docker container support for flexible deployment
  • Alibaba Cloud support for websites in China (solves ICP filing/licensing issues)
  • Staged and rolling scenarios for smoother upgrades in the future with zero downtime for users. 
  • SAML 2.0 support for single sign‑on

You can read more about SDL Tridion Sites here:

Global Content Operating Model

The researchers from Forrester recognizes the trend that the GCOM becomes more and more important. The customer journey in the past was often marketing driven and only touching the first few touchpoints. Companies recognize that product information has a great impact on customer experience and loyalty. To be able to provide the right content in the right context companies need a well-organized content supply chain. This research shows that SDL is on the right track combining marketing (Sites) and product information (Docs) in the Tridion DX suite. Another trend is the growing amount of content required for the (translated) omnichannel experience. Translations have always been the core-business for SDL, but they are now showing that AI and ML technology can and should be used to lower the cost and time to market.

Download the free Forrester report from

SDL Machine Translation

Another announcement was related to their translation solutions. SDL has the world's first secure Natural Machine Translation (NMT) solution that is available across on-premises and cloud boundaries. For companies that have content that should be in a self-controlled environment can opt for a fully on-premise version. An interesting feature is a hybrid solution. In the scenarios when on-premise translation capacity is reaching limits (incidental bulk operations), you can send packages to the cloud for translation. Companies that want full flexibility and scalability can use the cloud version.

SDL Content Assistant

SDL has strong AI expertise in NMT which up till now has been used for speeding up translation processes, better quality and lower costs. During SDL Connect they announced a brand new product: SDL Content Assistant. This stand-alone application can assist Editors with AI enabled generated content. Its strong points are creating high-quality content with multiple variants in the right context. CA will be introduced slowly by a closed beta program, but it is exciting to see where SDL is headed.

Apply for the beta program here:

SDL West Coast user group

It was great to participate in the user group session, hosted by Joseph Yu from Wells Fargo. A lot of great discussions about what 'headless' really means and how that works in the real world. Also a great presentation about Content as a Service (CaaS) and how much different it is than Dutch cheese (KaaS) by John Winter. Niclas Cedermalm showed his big plans on the connectors and was taking notes on some valuable feedback.

It was a great conference. Thanks to SDL for having us and thanks to EPAM for allowing me to visit it.

Tridion Sites 9 - Content API with GraphQL

SDL announced with upcoming Tridion Sites 9 that they also release a new version of their Unified Delivery Platform (UDP). The most promising feature of it will be a completely new public-facing Content API. This API is the new endpoint for content delivery for Sites and Docs and help customers enabling more dynamic and cleaner solutions for their channels.

Unified Delivery Platform update

SDL marketing is trying to rename their technology all the time. We have seen it with Tridion to Web to Tridion. And now what once was Content Delivery (CD) was renamed to UDP and now the introduced a new term: Dynamic Experience Delivery (DED). I will keep UDP for now in this article. :-)

Before we dive into Content API, a short highlight of the other key features of UDP 11:

  • Multiple namespace support. Now one for Sites and one for Docs. This can be extended to more areas in the future.
  • Content mashups possible. Mix content from Sites and from Docs in a single web application.
  • Full-text search supported with Elastic Search technology. Index and Query support is expected with Sites 9.1
  • Docker support. Making cloud orchestration for the microservices easier (scaling and failover scenarios).
  • Content API

Content API

The new content API uses GraphQL which has roots in Facebook technology stack. GraphQL has several benefits compared to other query languages:

  • Single endpoint without versioning required
  • Efficient querying: specify the information you need and get data in one single-trip request
  • Stability: changing data doesn't require changing clients
  • Documentation: simple understandable Graph schema with self-documented feature
  • Support: large community resulting in a large number of GraphQL client tools

Since GraphQL can be consumed by a lot of other platforms it is likely that it will replace the OData web services at some point in time.

Example GraphQL query for the Content API

When installing the Content API the default endpoint will be something like https://YOURSERVER/udp/content. Note that there is also another endpoint for developers without caching. One requirement is that the content is published as DXA (DD4T) rendered JSON. So if you have this in your broker database, no republish actions are required at all and you're good to go.

In the GraphQL example below, we request 3 properties of a published page by URL.

  myPageByUrl: page(namespaceId: 1, publicationId: 5, url: "/index.html") {

myPageByUrl is the container where the query result can be found in the response. The namespaceId corresponds with Tridion Sites and the publicationId and url is the rest of the actual search query. In the inner query we declare 3 properties what we want to receive in the response: url, itemId and title.

A possible response from the Content API could be:

	"data": {
		"myPageByUrl": {
			"url": "/index.html",
			"itemId": 123,
			"title" : "000 Home"

More complex GraphQL queries

The given example is fairly straightforward and simple. More complex and useful queries can be made as well. To give you an impression of the almost endless possibilities:

  • Get the binary component by ID and by URL 
  • Get contained ComponentPresentation's by page ID and by URL
  • Get items as paginated results
  • Get first X publications
  • Get category and its children keywords
  • Query for component presentations based on taxonomy
  • Get keywords and structure groups related to the page, find parents and expand children
  • Mashup queries (retrieving content from Sites and Docs namespaces in single request)
  • Get items based on query with custom metadata

Probably during Tridion Developer Summit there will be presentations about the Content API with rich GraphQL demos. TDS is hosted in Amsterdam on 6th and 7th september. It is still possible to register and maybe see you there!

Tridion Hackathon story with Alchemy

Last week I participated in a Hackathon for our client. It all started with a kickoff meeting with business users. They shared their daily experience of the Tridion system and implementation. Likes and dislikes were discussed and also where they lose time working with cms (amount of clicks). It was a fruitful session, I must say. After voting about business value and guestimates on developer effort we got an ordered list of issues to tackle.

Hackathon development phase

After the business users left the developers brainstormed about possible solutions. To name a few:

  • Additional user training
  • Installing existing extensions from the Alchemy Webstore.
  • Developing new Alchemy extensions
  • Automation via the event system
  • Extend DD4T rendering
  • ...

During the next couple of days, we started building. At the end of our available time, we developed 3 very useful Alchemy extensions (and some other improvements). The extensions itself are too much targetted for the customer implementation, but what I can share are some very useful tips and tricks.

Top 5 Alchemy developer tips

#1 - Starting and learning Alchemy

The best way to start with Alchemy development is to read the docs, install the VS project templates.

To learn about development, clone and inspect the BigBox of Samples plugin. Also, browse the Alchemy gallery to see what is there for inspiration. Also to not reinvent the wheel. Most plugins are open source and have a source code link. In case you wonder, how did they do XYZ... If no source available you could opt for downloading the A4T, rename the file to .ZIP and browse it. Javascript is easy, for the server-side code you need a decompiler to peek.

#2 - Custom configuration settings in client-side javascript

It is possible to create custom configuration settings for your extension. By default, you can use them in your serverside controller. Optionally, you can use them in your javascript command file. Then you must not forget to add the client=true attribute in a4t.config:

    <InitialSize client="true">400px</InitialSize>

#3 - Disable Alchemy Webstore popup 

By default everytime you do a build in VS, you will be prompted with an upload-to-alchemy popup window. While developing locally you do not need it and luckily there is a way to turn it off. Right-click the project and unload the project. Then right-click again to edit the project file and search for:


If you remove DEBUG| it will only prompt when building in release mode.

#4 - Deploy on VS build

Peter Kjaer developed various very useful Powershell modules for Tridion. There is a stable Alchemy module available to install, update and uninstall Alchemy extensions. If you install it, you can configure you VS project post-build event to install/update your plugin automatically. Then you only need to refresh the GUI for loading you extension.

#5 - Choose between client- and server-side

Working with the minimal documented Tridion Anguilla javascript framework can be a very time-consuming experience. Sometimes certain logic can be implemented with Tridion Core Service, so use the Controller to implement logic there. It is easier to get things done server side.

Hope this helps. If you have anything to add, please let me know!

Handling rich text content fields in SDL Tridion CMS

In this article, I will explain a little bit about Rich Text Fields in SDL Tridion CMS and how to customize and restrict the usage of styling to some extent with a basic example.

Introduction to RTF fields in Tridion

Tridion supports Rich Text Fields for allowing content managers to create better-looking content. All kinds of HTML elements are supported like the typical Italic, Bold and Underline. By default when enabling RTF features for a text field in your schema design all common tags are allowed:

  • Editing (or copy/paste) as HTML
  • Possible to select some text and apply a style via the buttons in the ribbon bar:

Customization options in Tridion

All this freedom for a content manager may drive front-end developers insane since they need to support all of it with their CSS. Luckily, there are 3 customization options within Schema design to limit the RTF features:

  1. Disable buttons in the ribbon bar
  2. Configure custom styles (matching the class names in CSS)
  3. Filter unwanted HTML (with help of XSLT)

These can be configured per schema content field:

Example case - Transforming HTML

XSLT is a complex but powerful language for transforming XML from one format to another. HTML is just a form of XML and thus XSLT can be used to free RTF content from unwanted tags.

Here, I want to demonstrate how to implement the following requirements:

  1. Make sure all bold texts are using <strong> tag element (and not <b> tag, which is default). This mechanism can later also be applied for converting italic <i> tag to <em> tag, etc.
  2. Only paragraphs, breaks and bold should be allowed in the HTML. This results in the following HTML tags: <p>, <br /> and <strong>

Converting from one HTML tag to another

The 1st requirement can be implemented by inserting the following fragment into the default XSLT:

  <xsl:template match="b">
        <xsl:element name="strong">
          <xsl:apply-templates select="node()"></xsl:apply-templates>

What it does is: when a tag is <b> found, replace it with a <strong> tag and continue XSLT rendering for the inner value of the node.

Filtering unwanted tags

For filtering you basically have two approaches:

  • Black-listing: unwanted tag elements. Use when you want to allow all tags, and exclude a few unwanted tags.
  • White-listing: specify the tag elements you want to keep. Use when the set of allowed tags is known.

In general (as in security), white-listing is the preferred method. Explicitly allowing specific tags gives you the most control over the output and rule out any 'forgotten' tags to appear in the HTML. 

The 2nd requirement can be implemented by using the following fragment in XSLT:

<xsl:template match="/ | node() | @*">
      <xsl:apply-templates select="/ | p | b | br"></xsl:apply-templates>

This fragment is basically processing all X(HT)ML elements and it copies only the allowed tags to the output to be processed further.

Complete XSLT solution

Below the complete file, including a conversion for <i> to <em>.

<xsl:stylesheet xmlns:xsl="" version="1.0">
  <xsl:output omit-xml-declaration="yes" method="xml" cdata-section-elements="script"></xsl:output>
  <xsl:template match="*[      (self::br or self::p or self::div)     and      normalize-space(translate(., ' ', '')) = ''     and      not(@*)     and      not(processing-instruction())     and      not(comment())     and      not(*[not(self::br) or @* or * or node()])     and      not(following::node()[not(         (self::text() or self::br or self::p or self::div)        and         normalize-space(translate(., ' ', '')) = ''        and         not(@*)        and         not(processing-instruction())        and         not(comment())        and         not(*[not(self::br) or @* or * or node()])       )])     ]">
    <!-- ignore all paragraphs and line-breaks at the end that have nothing but (non-breaking) spaces and line breaks -->
  <xsl:template match="br[parent::div and not(preceding-sibling::node()) and not(following-sibling::node())]">
    <!-- Chrome generates <div><br /></div>. Renders differently in different browsers. Replace it with a non-breaking space -->
    <xsl:text> </xsl:text>
  <!-- added bold to strong conversion -->
  <xsl:template match="b">
        <xsl:element name="strong">
          <xsl:apply-templates select="node()"></xsl:apply-templates>
  <!-- added italic to em converter -->
  <xsl:template match="i">
    <xsl:element name="em">
          <xsl:apply-templates select="node()"></xsl:apply-templates>
  <xsl:template match="/ | node() | @*">
      <xsl:apply-templates select="/ | p | b | i | br"></xsl:apply-templates>





Tridion publishing in debug mode

Publishing is the most critical process for Tridion users and when publishing fails (or takes too long) they become unhappy and the sysadmin has to explain why and fix it asap. Here is my Tridion tip for today: how to debug the publishing process.


  • Admin access to machine where Publisher service runs
  • Nobody is publishing from that CMS

Steps for setting up the publisher in DEBUG mode:

  • Stop the Publisher windows service
  • Open command prompt (or Powershell)
  • Navigate to <TRIDION_HOME>\bin\ folder
  • Run 'TcmPublisher.exe -debug'

Now the publisher service is listening in DEBUG mode to the publish queue. Now you can publish the page that is causing issues and see what the publisher is doing under the hood and what is going wrong. 

Example response of publishing page

Below is an example result (modified a bit for readability):

10:33:29.5042 <12152> Retrieved queue message 73 from PublishQueue
10:33:29.5042 <12680> Working on queue message 73
10:33:29.7073 <12680> Extension initialized: CME System Privileges Extensions (Tridion.Web.UI.CME.TcmExtensions.SystemPr
10:33:29.7230 <12680> Extension initialized: CME Event System Extensions (Tridion.Web.UI.CME.TcmExtensions.EventHandlers
10:33:29.7542 <12680> Extension initialized: XPM - TCM Extensions (Tridion.SiteEdit.TcmExtensions.EventHandlers)
10:33:30.4376 <12680> Handling Publish Transaction [tcm:0-2-66560]
10:33:31.6916 <12680> Publishing item [tcm:1-41-64] from publication [tcm:0-1-1] to target [tcm:0-1-65537]
10:33:31.8836 <12680> Storage location: c:\Temp\tcm_0-2-66560.Content\
10:33:32.0197 <12680> Using resolver [Tridion.ContentManager.Publishing.Resolving.PageResolver]
10:33:32.1291 <12680> Resolving the pages [tcm:1-41-64] took 00:00:00.1135300
10:33:32.1447 <12680> Page tcm:1-41-64 resolved to 1 items. Resolving took: 00:00:00.1412808
10:33:32.1760 <12680> Rendering item [tcm:1-41-64] 'Test Page' with template [tcm:1-34-128] 'Default Page Template' in p
ublication target [tcm:0-1-65537] 'Staging'
10:33:32.9103 <12680> Referenced assemblies from the GAC:

10:33:32.9103 <12680> Referenced assemblies from the custom path:

10:33:32.9103 <12680> Source code: /* C# sourcecode removed for readability */
10:33:34.3745 <12680> Rendering of item [tcm:1-41-64] 'Test Page' with template [tcm:1-34-128] 'Default Page Template' i
n publication target [tcm:0-1-65537] 'Staging' took: 00:00:02.1851037
10:33:34.3865 <12680> Adding rendered item to the transport package: item [tcm:1-41-64] 'Test Page' with template [tcm:1
-34-128] 'Default Page Template' in publication target [tcm:0-1-65537] 'Staging'
10:33:34.3905 <12680> Using transport package handler: Tridion.ContentManager.Publishing.Transporting.DefaultPageHandler

10:33:34.3945 <12680> Adding [Page tcm:1-41-64] to the transport package.
10:33:34.3995 <12680> Writing file: c:\Temp\tcm_0-2-66560.Content\Pages\test.html
10:33:34.5353 <12680> Updating the publish transaction with the list of processed items
10:33:34.5509 <12680> Saving the transport package.
10:33:34.5978 <12680> Sending tranport package for transaction [tcm:0-2-66560] with deploy control [Commit] to transport
10:33:34.8514 <12680> Sending information to transport service took: 00:00:00.2454763.
10:33:34.8544 <12680> Transport service reported state [ReadyForTransport] for Publish Transaction [tcm:0-2-66560] with
response: <TransportSummary referenceId="tcm:0-2-66560" state="Ready for transport"><Processing><Context topic="Content
Delivery"><IsRollbackOnFailure>false</IsRollbackOnFailure></Context><Step href="simple" type="Prepare transport" state="
Ready for transport"><RemoteEndpoint connector="DiscoveryService" id="DgHAheAA2zLe19e5+Pe/tQ==" name="Discovery Service
Upload" state="Ready for transport" windowSize="0" /></Step><Step href="simple" type="Transporting" state="None"><Remote
Endpoint connector="DiscoveryService" id="DgHAheAA2zLe19e5+Pe/tQ==" name="Discovery Service Upload" state="None" windowS
ize="0" /></Step><Step href="simple" type="Deployment preparation" state="None"><RemoteEndpoint connector="DiscoveryServ
ice" id="DgHAheAA2zLe19e5+Pe/tQ==" name="Discovery Service Upload" state="None" windowSize="0" /></Step><Step href="simp
le" type="Deploying" state="None"><RemoteEndpoint connector="DiscoveryService" id="DgHAheAA2zLe19e5+Pe/tQ==" name="Disco
very Service Upload" state="None" windowSize="0" /></Step><Step href="simple" type="Deployment committing" state="None">
<RemoteEndpoint connector="DiscoveryService" id="DgHAheAA2zLe19e5+Pe/tQ==" name="Discovery Service Upload" state="None"
windowSize="0" /></Step></Processing></TransportSummary>
10:33:34.9102 <12680> Handling Publish Transaction [tcm:0-2-66560] took: 00:00:04.4792882
10:33:34.9102 <12680> Cache statistics: SimpleCache: 5 Regions, 5 Objects, 11 Hits, 2 Misses
10:33:34.9102 <12680>   Region 'Component': 0 Objects, 0 Hits, 0 Misses.
10:33:34.9102 <12680>   Region 'PublishTransaction': 3 Objects, 6 Hits, 1 Misses.
10:33:34.9102 <12680>   Region 'TargetType': 1 Objects, 4 Hits, 0 Misses.
10:33:34.9102 <12680>   Region 'PublicationTarget': 1 Objects, 1 Hits, 0 Misses.
10:33:34.9258 <12680>   Region 'Tdse': 0 Objects, 0 Hits, 1 Misses.
10:33:35.0352 <12152> Deleting queue message: 73 from PublishQueue.
10:33:35.4883 <6132> Transport service response: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
/* XML response is removed for readability */
10:33:35.5234 <6132> Receiving deployment feedback for publish transaction [tcm:0-2-66560] with status: WaitingForDeploy
10:33:39.7672 <6132> Transport service response: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
/* XML response is removed for readability */

10:33:39.7922 <6132> Receiving deployment feedback for publish transaction [tcm:0-2-66560] with status: Success

What good to know is that this also works for other TCM services as well. After you finished your investigation, don't forget to start the service again.