Tuesday, December 30, 2014

Application Life Cycle Management for SDL Tridion Projects using .NET Technologies

In a Previous Post I covered in a Technology Agnostic way a set of considerations for Application Life Cycle Management for SDL Tridion Projects. In this post I will be using the same terms but using .NET Technologies.

In this blog post I’ve selected Team Foundation System (TFS) suite in order to fit the different tools and roles for a proper Application Life Cycle Management. The following diagram shows the ALM for Agile projects but using TFS tools suite

TFS
Controls the set of Work Items as well as Bugs and Tasks

Excel
Integrates with TFS in order to show Status Reports and Work Items status

TFS Version Control
Source code control

MS Build
Automated builds (Continuous Integration)

NUnit
Unit Testing

Release Management System
Packaging and deployment




click to open original image


SDL Tridion integration with Portal Servers

One very common use case during enterprise implementations is integrating SDL Tridion with Portal servers. There are several approaches out there, this article will show 2 options that can be use perform such integration.
Integrating with Portal servers is not an straight forward exercise since Portals control the overall page rendering as well as page life cycle, in that sense SDL Tridion becomes to be a data (content) repository that is consumed in certain manner. Portals can consume formatted or unformatted data from Tridion in the format of XML, JSON or HTML chunks, selecting the content format is very important since it will determine design and styling responsibilities.

Thinking as a Software Architect I prefer to specialize responsibilities instead of sharing responsibilities between different systems. Having said that I prefer sending unformatted data to the Portal Server and passing the Content formatting and styling to the Portal server itself.

Based on different approaches I’ve seen so far I have determined two different options

Option 1: Integrated through the Content Delivery Web Service


click to see original image

  • Data (Content) is consumed via the Content Delivery Web Service
  • Binaries are consumed via the Content Delivery Web Service or via a Network Shared location (NAS)
  • Content is sent unformatted using JSON or XML  (no styling)


Option 2: Building Meta Files


click to see original image


  • A deployer extension to be developed in order to build a Meta Page File containing Content Positioning as well as relevant metadata is generated during publishing
  • The Portal Server will consume the Page Meta File by mapping the Portal Page name with the Page Meta File name File Name
  • The Meta File Name will contain unformatted data in XML or JSON format (No Styling)
  • Binaries are consumed from a Network Shared  location (NAS)


The two options shown above are not intended to be considered final solution but to be options to be evaluated during Portal Integrations. Below my considerations to take while deciding the Integration strategy

  • Styling to be controlled in only 1 system (SDL Tridion or the Portal Server). I would suggest to control Styling in the Portal Server so that the HTML Design Team will work with only 1 paradigm
  • Page  Life Cycle to be controlled by the Portal Server
  • SDL Tridion to control editorial content as well as any relevant metadata
  • SDL Tridion to control Binaries
  • SDL Tridion page templates to produce unformatted data that can be consumed by Portals (Option 2)
  • Component Templates to produce unformatted data that can be consumed by Portals (Option 1 and 2)

How to create SDL Tridion Contextual Expressions

Contextual Expressions are intended to be created from external systems like CMA. In this blog post I am showing a basic method to create Contextual Expressions (Expressions Target Groups) without the need of CMA.

Expressions Target Groups should must contain certain Application Data in the following format

TargetGroupExtensionData targetGroupExtensionData = new TargetGroupExtensionData
{
    ContextExpression = contextExpression,
    SyncLabel = version
};

The Context Expression and the SyncLabel properties are mandatory. They should be included while saving the Target Group Application Data

ApplicationDataAdapter appDataAdapter = new ApplicationDataAdapter(TargetGroupExtensionData.ApplicationId, targetGroupExtensionData);
_coreServiceClient.SaveApplicationData(existingTargetGroup.Id, new[] { appDataAdapter.ApplicationData });

The source code is available in my Git Repository.

Application Life Cycle Management for SDL Tridion Projects

In this Article I would like to share what I think is a workable and efficient Application Life Cycle Management for SDL Tridion Projects. We need to consider the different project stages and SDL Tridion dependencies before we define the Strategy to maximize the ALM (Application Life Cycle Management) effectiveness.

Please note that this post is based on Agile projects (implementations) and assuming a mature ALM level during the implementation as well as all of the recommendations are my own opinion and cannot be considered SDL guidelines for Agile projects.

Blueprint and Schemas are not Agile


One common error during Agile implementation is to think that the Blueprint and Schemas can be partially defined and finished, curated and tuned during the different steps (sprints) in the project. Consider the Blueprint and Schemas as your model and infrastructure (when an Civil Engineer designs a House, he delivers a completed blueprint for the house that contains electrical, plumbing and other multiple aspects for the home, then it can be built in different steps (sprints)). Consider the Blueprint and Schemas as the base of your project, I know it will sound archaic but I would suggest an small waterfall to completely define the Blueprint and Schemas (I know that we cannot completely define the Blueprint and Schemas in one single shot, but we have to do our best and be creative in order to consider all the possible scenarios and make them as extensible as possible, you can check the Miguel Migulez blog for Blueprint and Schemas best practices)

click to see original image


Determine a Dev Environment and Process


A typical SDL Tridion Dev Environment for Agile projects must be connected to a Code Repository (Git, SVN, …) as well as a tool for Tasks and Defects management (TFS, JIRA, …) as well as an Deploy Automation tool (Teamcity, Jenkins, …). The following diagram shows how a typical Dev Environment is set up.

click to see original image


In the diagram below we can notice the following elements

Dev Branch
In order to separate the code base it is mandatory to configure a Branch that is intended only for Development. This Branch will contain the latest code for Template Building Blocks, Event Systems, Workflow as well as the Presentation Server pieces. It is important to separate it from the Master branch that is intended only to contain code that has been properly tested and quality assured

Build Server
In order to improve  productivity it is important to accomplish Continuous Integration so that after some code changes as Pushed into the Dev Branch an automated build is performed in the Dev environment. It will improve developers productivity since it will hide tasks like Upload Template Building Blocks, Deploy Event Systems, Deploy Workflows or Presentation server elements

Version Control
All the different Code Branches are controlled and defined in this element, as well as it support Pull and Push requests in order to apply or retrieve code changes.

Work Tasks
All the different Technical Tasks that represent the different User Stories that represents an Sprint

Unit Testing
This is an optional but high recommended step and should be automated during the Continuous Integration (Fail the Build process if a Unit Test fails)

Dev Deliverable
This is the result after a Push operation is performed, it will contain a package containing (Compiled Template Building Blocks, Event Systems, Workflow, ….,  and Presentation Server elements)

Determine a QA Environment and Process


The QA Environment uses the Master Branch coming from the Version Control system. For QA I would not recommend an automated build process (Continuous Integration) instead I would suggest a time basis build process (Nightly Builds for instance) so that we can deliver an stable and testable version of the different components to be tested by the QA team next day.


click to see original image

In the diagram above we can see the following elements

Scheduled Built Artifact
Final deliverable (Artifact) that is built timely basis (Nightly Build)

Version Control
QA deliverables should be built pulling from the Version Control System (Master Branch)

Determine a Preview and Live Environment


As part of the QA process it is recommended to define a central repository for the different tested version of the deliverables, it is called an Artifactory, We will be manually deploying to Preview and Live (Prod) from the Artifactory.

click to see original image


Gluing everything together


The following diagram shows how the different environmetns defined above are glued together, We are also including Content Porter as an important piece in this strategy. Content Porter is responsible to port Tridion Items that doesn’t live in the Artifactory by in SDL Tridion like (Blueprint, Schemas, Taxonomy, Folders, Structure Groups, ……)

click to see original image

Adding razor dotnet style support to the TCDL Transformer

There are plenty of scripting options as well as coding styles involved in a Web Application development. SDL Tridion supports several scripting options that can be configured at 3 different levels, Component Template, Publication Target and Deployer. At the moment this article is being writing SDL Tridion current version is 2013 SP1. This version supports the following script options.

JSP
ASP .NET
VBScript
JScript
REL

This Article is not intended to describe each of the above scripting options but to show how to extend the existing ones and add new coding styles. In order to show it I will describe how ASP .NET scripts are configured and how it can be extended. However the same procedure can be used for other script options. Please notice that this procedure has been tested in Tridion 2013 SP1 and it may stop working on future versions of the product since it is not a documented or supported extension

Configuring ASP .NET


The first Step is to select ASP .NET as the script option in the Publication Target. Consider this configuration as a very high level one. ASP .NET holds different coding styles like “inline coding”, and “controls” that are configured in the cd_deployer_conf.xml

<TCDLEngine>
      <Properties>
            <Property Name="tcdl.dotnet.style" Value="controls"/>
      </Properties>
</TCDLEngine>

The above configuration is instructing to the TCDL Transformer to use the DotNet Language with Controls Coding Style. This will generate an output suitable for ASP .NET Web Forms Web Applications as shown in the following code snippet.

<html>
      <body>
            <nav>
                  <ul>
                        <li><tridion:ComponentLink PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" LinkAttributes="[LinkAttributes]" AddAnchor="[AddAnchor]" /></li>
                        <li><tridion:ComponentLink PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" LinkAttributes="[LinkAttributes]" AddAnchor="[AddAnchor]" /></li>
                        <li><tridion:ComponentLink PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" LinkAttributes="[LinkAttributes]" AddAnchor="[AddAnchor]" /></li>
                        <li><tridion:ComponentLink PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" LinkAttributes="[LinkAttributes]" AddAnchor="[AddAnchor]" /></li>
                  </ul>
            </nav>
            <div class="main">
                        <article>
                              <tridion:ComponentPresentation PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" />
                        </article>
                        <article>
                              <tridion:ComponentPresentation PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" />
                        </article>
                        <article>
                              <tridion:ComponentPresentation PageURI="[PageURI]" ComponentURI="[ComponentURI]" TemplateURI="[TemplateURI]" />
                        </article>
            </div>
      </body>
</html>

All the above transformation are very well known and there is no reason for further explanation. However the intention of this post is to show how to add another coding style so that we can write the same code but  using Razor coding style or any other coding style that may be available.

Create a Razor Code Generator class


SDL Tridion comes with a set of Code Generator classes that will generate code from TCDL to an specific script language like DotNet, JSP, REL, VBScript and JSScript. In this Article I will show how to write a Code Generator class that will transform TCDL into Razor HTML Helpers. This class is implementing the transformation of <tcdl:ComponentPresentation> and <tcdl:Link> tags

/*
* Created by Eric Huiza on 12/28/2014.
*/

package com.tridion.tcdl.codegen;

import com.tridion.tcdl.OutputDocument;
import com.tridion.tcdl.Tag;
import com.tridion.tcdl.TransformContext;
import com.tridion.tcdl.tags.TargetGroupTagHandler;

import java.io.IOException;
import java.io.Writer;
import java.util.List;

public class RazorCodeGenerator implements CodeGenerator {
    protected TransformContext context;
    protected OutputDocument target;

    public RazorCodeGenerator(TransformContext context, OutputDocument output) {
        this.context = context;
        target = output;
    }

    public void generateDynamicComponentPresentation(StringBuffer tagBody, String pageURI, String componentURI, String templateURI) {
        tagBody.append(String.format("@Html.ComponentPresentation(\"%s\", \"%s\", \"%s\")", pageURI, componentURI, templateURI));
    }

    public String generateBinaryLink(String binaryURI, String variantId, String addAnchor, String linkText, String linkAttributes, boolean textOnFail) {
        return String.format("@Html.BinaryLink(\"%s\", \"%s\", \"%s\", \"%s\", \"%s\", %s)", binaryURI, variantId, addAnchor, linkText, linkAttributes, Boolean.toString(textOnFail));
    }

    public String generateComponentLink(String pageURI, String componentURI, String templateURI, boolean addAnchor, String linkText, String linkAttributes, boolean textOnFail) {
        return String.format("@Html.ComponentLink(\"%s\", \"%s\", \"%s\", %s, \"%s\", \"%s\", %s)", pageURI, componentURI, templateURI, Boolean.toString(addAnchor), linkText, linkAttributes, Boolean.toString(textOnFail));
    }

    public String generatePageLink(String pageURI, String addAnchor, String linkText, String linkAttributes, boolean textOnFail, String parameters) {
        return String.format("@Html.PageLink(\"%s\", \"%s\", \"%s\", \"%s\", %s, \"%s\")", pageURI, addAnchor, linkText, linkAttributes, Boolean.toString(textOnFail), parameters);
    }

    public void generatePageHeader() {
    }

    public void generateWAIPageHeader(StringBuffer stringBuffer, String s) {
    }

    public void generateTargetGroupCondition(StringBuffer stringBuffer, String s, String s2, boolean b, String s3, String s4) {
    }

    public String convertTargetGroupOperator(String s) {
        return null;
    }

    public String generateTargetGroupIf(TargetGroupTagHandler.Conditions conditions, String s) {
        return null;
    }

    public String generateIncrementTrackingKey(String s, String s2) {
        return null;
    }

    public void generateTaxonomy(StringBuffer stringBuffer, String s, String s2, boolean b, boolean b2, boolean b3, String s3, String s4) {

    }

    public String getNegationSymbol() {
        return null;
    }

    public String getAlwaysFalseExpression() {
        return null;
    }

    public void generateImport(Writer writer, String s) throws IOException {

    }

    public void generatorPageDirectives(Writer writer, List list) throws IOException {

    }

    public void generateCodeStatements(Writer writer, String s, List list, boolean b) throws IOException {

    }

    public boolean requiresCodeBlocks(Tag tag) {
        return false;
    }
}

Create a New Code Generator Factory


A new Code Generator Factory class needs to be developed in order to recognize the Razor coding style and instantiate the Razor Code Generator we already defined above.

/**
* Created by Eric Huiza on 12/28/2014.
*/

package com.tridion.tcdl.codegen;

import com.tridion.tcdl.OutputDocument;
import com.tridion.tcdl.TransformContext;

public class CodeGeneratorFactory {
    private static final String CODE_GENERATOR_CONTEXT_NAME = "tcdl.internal.codegenerator";
    private static final String TARGET_LANGUAGE_NAME = "tcdl.target.language";
    private static final String JSP_STYLE_NAME = "tcdl.jsp.style";
    private static final String DONET_STYLE_NAME = "tcdl.dotnet.style";

    public CodeGeneratorFactory() {
    }

    public static CodeGenerator getGenerator(TransformContext context, OutputDocument target) {
        CodeGenerator result = (CodeGenerator)context.getProperty(CODE_GENERATOR_CONTEXT_NAME, null);
        if(result == null) {
            String language = (String)context.getProperty(TARGET_LANGUAGE_NAME, "");
            if(language.equalsIgnoreCase("jscript")) {
                result = new JScriptCodeGenerator(context, target);
            }
            else if(language.equalsIgnoreCase("vbscript")) {
                result = new VBSCodeGenerator(context, target);
            }
            else if(language.equalsIgnoreCase("jsp"))
            {
                if(((String)context.getProperty(JSP_STYLE_NAME, "")).equalsIgnoreCase("tags")) {
                    result = new JSPTagCodeGenerator(context, target);
                }
                else {
                    result = new JSPCodeGenerator(context, target);
                }
            }
            else if(language.equalsIgnoreCase("none")) {
                result = new NullCodeGenerator();
            }
            else if(language.equalsIgnoreCase("dotnet")) {
                if(((String)context.getProperty(DONET_STYLE_NAME, "")).equalsIgnoreCase("controls")) {
                    result = new ASPNETCodeGenerator(context, target);
                }
                else if(((String)context.getProperty(DONET_STYLE_NAME, "")).equalsIgnoreCase("native_interop")) {
                    result = new JuggerNETCodeGenerator(context, target);
                }
                else if (((String)context.getProperty(DONET_STYLE_NAME, "")).equalsIgnoreCase("razor")) {
                    result = new RazorCodeGenerator(context, target);
                }
                else {
                    result = new DotNETCodeGenerator(context, target);
                }
            }
            else {
                result = new DotNETCodeGenerator(context, target);
            }
            context.setGlobalProperty(CODE_GENERATOR_CONTEXT_NAME, result);
        }
        return result;
    }
}

Configure Razor Coding Style


Set Razor to the tcdl.donet.style property

<TCDLEngine>
      <Properties>
            <Property Name="tcdl.dotnet.style" Value="razor"/>
      </Properties>
</TCDLEngine>


Override the JVM Class Loading process


Since I created a new CodeGeneratorFactory class, it is necessary to instruct the JVM to load the overridden CodeGeneratorFactory instead of the out of the box one. In order to do so, there are plenty of options like using the Boot Class Path option to force a class to be loaded first. In this scenario I opted for an easier one which is to copy the class files in the “config” folder. Classes living inside the “config” folder will be loaded before the ones in the “lib” folder. It needs to be done the Deployer Application.

C:\INETPUB\WWWROOT\DEPLOYERS\REFERENCE_STAGING\BIN\CONFIG
|   cd_deployer_conf.xml
|   cd_storage_conf.xml
|   logback.xml
|  
\---com
    \---tridion
        \---tcdl
            \---codegen
                    CodeGeneratorFactory.class
                    RazorCodeGenerator.class


Publishing using Razor coding style


The result of this operation is shown as following

<html>
      <body>
            <nav>
                  <ul>
                        <li>
                              @Html.ComponentLink("[PageURI]", "[ComponentURI]", "[TemplateURI]", "[AddAnchor]", "[LinkText]", "LinkAttributes", "TextOnFail")
                        </li>
                        <li>
                              @Html.ComponentLink("[PageURI]", "[ComponentURI]", "[TemplateURI]", "[AddAnchor]", "[LinkText]", "LinkAttributes", "TextOnFail")
                        </li>
                        <li>
                              @Html.ComponentLink("[PageURI]", "[ComponentURI]", "[TemplateURI]", "[AddAnchor]", "[LinkText]", "LinkAttributes", "TextOnFail")
                        </li>
                  </ul>
            </nav>
            <div class="main">
                        <article>
                              @Html.ComponentPresentation("[PageURI]", "[ComponentURI]", "[TemplateURI]")
                        </article>
                        <article>
                              @Html.ComponentPresentation("[PageURI]", "[ComponentURI]", "[TemplateURI]")
                        </article>
                        <article>
                              @Html.ComponentPresentation("[PageURI]", "[ComponentURI]", "[TemplateURI]")
                        </article>
            </div>
      </body>
</html>