Showing posts with label context-engine-cartridge. Show all posts
Showing posts with label context-engine-cartridge. Show all posts

Tuesday, December 30, 2014

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.

Friday, October 3, 2014

Adding Expression Target Groups support to the Reference Implementation

Now days “Context” is a key part on almost every SDL Tridion implementation, the Reference Implementation is not a exception, the first version of the Reference Implementation supports Contextual Images Delivery. However it doesn’t support Dynamic Vocabulary (Expression Target Groups)

In this post I am describing the process to extend the Reference Implementation in order to add support for Dynamic Vocabulary. If you need more details on what is a Dynamic Vocabulary, please refer to this blog post Context Expression Extension

Adding Dynamic Vocabulary support for the Reference Implementation involves adding / changing some DD4T and Reference implementation areas.


Publishing Model

1.       Update the IComponentPresentation interface. Add a new property that holds Expressions

namespace DD4T.ContentModel
{
    public interface IComponentPresentation
    {
        IComponent Component { get; }
        IComponentTemplate ComponentTemplate { get; }
        IPage Page { get; set; }
        bool IsDynamic { get; set; }
        string RenderedContent { get; }
        int OrderOnPage { get; set; }
        IList<ICondition> Conditions { get; }
        IList<string> Expressions { get; set; }
    }
}

2.       Update the ComponentPresentation class. Implement the Expressions property

public class ComponentPresentation : IComponentPresentation
{
    [XmlIgnore]
    public IPage Page { get; set; }
    public Component Component { get; set; }
    [XmlIgnore]
    IComponent IComponentPresentation.Component
    {
        get { return Component as IComponent; }
    }
    public ComponentTemplate ComponentTemplate { get; set; }
    [XmlIgnore]
    IComponentTemplate IComponentPresentation.ComponentTemplate
    {
        get { return ComponentTemplate as IComponentTemplate; }
    }
    public string RenderedContent { get; set; }
    public bool IsDynamic { get; set; }

    [XmlIgnore]
    public int OrderOnPage { get; set; }

    public List<Condition> Conditions { get; set; }

    public List<string> Expressions { get; set; }

    [XmlIgnore]
    IList<ICondition> IComponentPresentation.Conditions
    {
        get { return Conditions.ToList<ICondition>(); }
    }

    [XmlIgnore]
    IList<string> IComponentPresentation.Expressions {
        get {
            throw new NotImplementedException();
        }
        set {
            throw new NotImplementedException();
        }
    }
}

3.       Add Context Target Groups TBB. New TBB to be created / added in order to populate the Expressions property with Target Groups / Component Presentations mapping

namespace DD4T.Templates {
    [TcmTemplateTitle("Add Expression Target Groups")]
    public class AddExpressionTargetGroups : BasePageTemplate {
        protected override void TransformPage(Page page) {
            Tcm.Page tcmPage = GetTcmPage();

            int index = 0;
            foreach (var componentPresentation in tcmPage.ComponentPresentations) {
                if (componentPresentation.Conditions != null && componentPresentation.Conditions.Count > 0) {
                    page.ComponentPresentations[index].Expressions = componentPresentation.Conditions.Select(s => s.TargetGroup.Title).ToList();
                }
                index += 1;
            }
        }
    }
}

4.  Update the Render Page Content Composite TBB in order to use Add Expressions Target Group


Context Expression Extension

1.  Create a set of Expressions Target Groups to test the functionality




2.  Apply Context Target Groups to Component Presentations. In this case I have applied them to the Article in the Reference Implementation Home Page



3.  Publish the Expressions Target Groups so that they can be part of the Dynamic Vocabulary



Reference Implementation

1.       Add a new class ExpressionsEngine to Sdl.Web.Tridion. This class will call the JEXL engine in order to validate the expressions while Rendering Items

namespace Sdl.Web.Tridion.Context {
    public static class ExpressionsEngine {
        public static bool EvaluateExpression(string expression) {
            using (ClaimStoreExpressionEngine expressionEngine = new ClaimStoreExpressionEngine(Com.Tridion.Ambientdata.AmbientDataContext.GetCurrentClaimStore(), new ValueConverter())) {
                Java.Lang.Boolean result = expressionEngine.EvaluateBooleanExpression(expression);
                if (result != null) return result.BooleanValue();
                return false;
            }
        }

        public static string WriteExpression(string expression) {
            using (ClaimStoreExpressionEngine expressionEngine = new ClaimStoreExpressionEngine(Com.Tridion.Ambientdata.AmbientDataContext.GetCurrentClaimStore(), new ValueConverter())) {
                return expressionEngine.EvaluateStringExpression(expression);
            }
        }
    }
}

2.       Update the RenderEntity method in the DD4TRenderer class in order to evaluate expressions before entities are written

public override MvcHtmlString RenderEntity(object item, HtmlHelper helper, int containerSize = 0, List<string> excludedItems = null)
{
    var cp = item as ContentModel.ComponentPresentation;
    if (cp.Expressions != null && cp.Expressions.Count > 0) {
        foreach (string expression in cp.Expressions) {
            if (!ExpressionsEngine.EvaluateExpression(expression)) {
                return null;
            }
        }
    }

    var mvcData = ContentResolver.ResolveMvcData(cp);
    if (cp != null && (excludedItems == null || !excludedItems.Contains(mvcData.ViewName)))
    {
        var parameters = new RouteValueDictionary();
        int parentContainerSize = helper.ViewBag.ContainerSize;
        if (parentContainerSize == 0)
        {
            parentContainerSize = SiteConfiguration.MediaHelper.GridSize;
        }
        if (containerSize == 0)
        {
            containerSize = SiteConfiguration.MediaHelper.GridSize;
        }
        parameters["containerSize"] = (containerSize * parentContainerSize) / SiteConfiguration.MediaHelper.GridSize;
        parameters["entity"] = cp;
        parameters["area"] = mvcData.ControllerAreaName;
        foreach (var key in mvcData.RouteValues.Keys)
        {
            parameters[key] = mvcData.RouteValues[key];
        }
        MvcHtmlString result = helper.Action(mvcData.ActionName, mvcData.ControllerName, parameters);
        if (WebRequestContext.IsPreview)
        {
            result = new MvcHtmlString(TridionMarkup.ParseEntity(result.ToString()));
        }
        return result;
    }
    return null;
}


Test the Reference Implementation

1.       Test with an Apple Device


2.       Test with a Non Apple Device


Monday, February 3, 2014

Context Expression Extension

I this post I will cover a brand new feature added in Tridion 2013 SP1 called Context Expression Extension (CEE). CEE is part of Tridion 2013 SP1 and is installed as a GUI Extension that will mark Target Groups as Context Expressions. Additionally it has Content Delivery pieces that will be described later in this post.

CEE integrates SDL Tridion with Campaign Management and Analysis Data (CMA) allowing marketers and data analysts to define Expressions in CMA and push them into Tridion in the form of Target Groups. Context Expression Target Groups are read only and can just be updated in CMA, as a personal quote for future releases it would be nice if we can generate Context Expressions from external systems or within the Tridion user interface.

CEE updates how a Target Group looks like if it recognizes that it contains a Context Expression as you can appreciate in the following pictures.








Context Expressions Target Groups are named in the form of [aspect].[property]













CEE will show an extra tab called Context Expression that shows the Expression in read only mode.

Publishing Context Expressions

Context Expressions integrates perfectly with Context Engine Cartridge (CEC) providing access to the Expression in the Presentation Server by publishing the Context Expression Target Group using a new feature called Dynamic Vocabulary. So you may be thinking what is a dynamic vocabulary and what is an static vocabulary instead? Well I will try to explain it. I will call a static vocabulary to the set of expressions that are configured in the cwd_engine_vocabulary_conf.xml. So what is a dynamic vocabulary? Well it is the set of expressions that are published from Tridion (Target Groups Publishing).

You might be asking where are those expressions published, well they are pushed to the Ambient Data Framework (ADF) making them available as claims and eventually available for Tags like If or Eval

Targeting Component Presentations using Context Expressions

The process to target Component Presentations is the same to the one used with P&P (Personalization & Profiling), it is done in the Page - Component Presentations tab. Below a sample.




















After publishing the Page you will see content like the following.

<context:If Expression="(os.apple)" runat="server">
    <span>
        <!-- Start Component Presentation: {"ComponentID" : "tcm:5-2051", "ComponentModified" : "2014-02-03T19:39:54", "ComponentTemplateID" : "tcm:5-1058-32", "ComponentTemplateModified" : "2013-12-26T16:18:55", "IsRepositoryPublished" : false } -->
        <div>

            <h2>
                <span>
                    <!-- Start Component Field: {"XPath":"tcm:Content/custom:Content/custom:Heading[1]"} -->
                    <h1><strong>Article For Apple Devices
                    </strong></h1>
                </span>
            </h2>



            <p>
                <span>
                    <!-- Start Component Field: {"XPath":"tcm:Content/custom:Content/custom:Description[1]"} -->
                    This is a description for <strong>Article For Apple Devices.</strong>
                </span>
            </p>

        </div>
    </span>
</context:If>
<context:If Expression="(os.notapple)" runat="server">
    <span>
        <!-- Start Component Presentation: {"ComponentID" : "tcm:5-51", "ComponentModified" : "2014-02-03T17:59:54", "ComponentTemplateID" : "tcm:5-1058-32", "ComponentTemplateModified" : "2013-12-26T16:18:55", "IsRepositoryPublished" : false } -->
        <div>

            <h2>
                <span>
                    <!-- Start Component Field: {"XPath":"tcm:Content/custom:Content/custom:Heading[1]"} -->
                    <h1>This is a Non Apple Devices Article
                    </h1>
                </span>
            </h2>



            <p>
                <span>
                    <!-- Start Component Field: {"XPath":"tcm:Content/custom:Content/custom:Description[1]"} -->
                    This is a description for Non Apple Devices
                </span>
            </p>

        </div>
    </span>
</context:If>

A Context Expression Target Group is transformed in a <context:if> tag by a TCDL Tag Transformed that is configured in the Deployer.

<TCDLEngine>
                <Properties>
                                <Property Name="tcdl.dotnet.style" Value="controls"/>
                                <Property Name="tcdl.jsp.style" Value="tags"/>
                </Properties>
               
                <TagBundle Resource="com/sdl/context/transformer/contextTagTransformerBundle.xml" />
</TCDLEngine>

The final result is shown below (Please note that in order to have your web application working you have to have a Context Engine Cartridge configured and working).

Non Apple Device Result



Apple Device Result