I have an alpha version working... if you are interested in it, please let me know
0

Add a comment

  1. Last month I took part in the Hackathon organized by SDL in San Francisco. It was a great event and had the chance to meet with the most brilliant minds when it comes to SDL products...

    As part of the event we (Warner Soditus and I, here is our team page The Tappers!) came up with an idea that I am glad to announce has materialized into DTAP!
    Our vision (or at least the starting point) on how to enahnce the DTAP capabilities of SDL Tridion.

    If you want to jump directly to the code here is the repository:

    The  content that follows is available as a word document in such repository too, but you can continue reading here to get a better understanding on what this solutions is about. Enjoy!

    What is DTAP-It?


    What do you do when you want to export content from one Tridion environment to another?
    What do you do when you want to copy Groups from one Tridion environment to another?What do you do when you need to copy a bunch of Folders or Structure Groups with complex ACL from one Tridion environment to another?
    You DTAP-It!

    DTAP-It is a powerful Tridion GUI extension which will allow you to select items and move them to another Tridion environment using Content Porter directly from the UI. On top of that it will allow you to select Groups, Folders and Structure Groups to move permissions from one environment to another.

    Setup

    Since you have already unzipped DTAP.zip, you should see several directories. There are several elements to configure.

    PowerShell Modules

    Note: This step is only necessary if you are planning to call the scripts from the command line using the PowerShell console.* The UI eXtension will add those itself when running from the UI.
    Place the modules directory your desired location. For example, assume this location is: C:\tridion\modules
    Now, you must edit your PowerShell module path to include the above directory and load the included modules. You can choose one of two ways to do this. You can edit the profile for your specific user or the profile for all users. The easiest way is to execute one of the following:
    All users (if first file is not found, use the second):
    notepad $PsHome\Microsoft.PowerShell_profile.ps1
    notepad $PsHome\Profile.ps1
    Current User:
    notepad $Profile


    Somewhere in the file, add the below lines:
    $curPSModulePath = Get-Content Env:\PSModulePath
    $env:PSModulePath = $curPSModulePath + ";C:\tridion\modules"
    Import-Module Tridion-API -force
    Import-Module Tridion-DTAP -force



    Core Service Connections

    There is a directory included named "settings." Note where you have placed this directory. For example, assume this location is: C:\tridion\settings.
    Edit the file:
    notepad C:\tridion\modules\Tridion-API\CoreServiceSettings.psm1
    Look for this line:
    $coreserviceSettingPath = "D:\opt\apps\scripts\powershell\settings\coreservice"
    Modify to match the settings directory location you noted earlier.
    $coreserviceSettingPath = "C:\tridion\settings\coreservice"


    Save and close the file.
    Server Details
    Note: This step is only necessary if you are planning to call the scripst from the command line using PowerShell console.* The UI eXtension will add those itself when running from the UI.


    Duplicate the example.xml in the settings\coreservice directory to an appropriate name (e.g. "dev.xml," "qa.xml," "prod.xml") and edit. Below is an example with highlights to show what should likely be modified. If you are not using Tridion 2013 SP1 you will want to change the DLL specification to match your version.
    <Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
     <Obj RefId="0">
       <TN RefId="0">
         <T>Deserialized.System.Object</T>
       </TN>
       <ToString>System.Object</ToString>
       <MS>
         <Version N="ModuleVersion">1.0.0</Version>
         <S N="Version">2013SP1</S>
         <S N="AssemblyPath">[your module path]\modules\Tridion-API\Clients\Tridion.ContentManager.CoreService.Client.2013sp1.dll</S>
         <S N="ClassName">Tridion.ContentManager.CoreService.Client.SessionAwareCoreServiceClient</S>
         <S N="ConnectionType">netTcp</S>
         <S N="HostName">myhost</S>
         <S N="CME">http://myhost</S>
         <S N="EndpointUrl">net.tcp://myhost:2660/CoreService/2013/netTcp</S>
         <S N="SendTimeout">00:10:00</S>
         <S N="ReceiveTimeout">00:10:00</S>
         <S N="CloseTimeout">00:10:00</S>
         <S N="SendTimeout">00:10:00</S>
         <S N="MaxReceivedMessageSize">2147483647</S>
         <S N="MaxStringContentLength">2147483647</S>
         <S N="MaxArrayLength">2147483647</S>
         <S N="MaxBytesPerRead">2147483647</S>
         <S N="MaxItemsInObjectGraph">2147483647</S>
       </MS>
     </Obj>
    </Objs>


    Test Your Connections

    Open a new PowerShell window. Execute the following with each of the settings files you specified:
    Import-TridionCoreServiceSettings -ServerName "[setting name]"
    For example:
    Import-TridionCoreServiceSettings -ServerName "dev"
    Then execute:
    Get-TridionUser
    You should see information regarding your Tridion User. If you experience any error, then things are not configured correctly ☺
    Repeat with each settings file you created and ensure you can connect to each Core Service host.
    Import-TridionCoreServiceSettings -ServerName "qa"
    Get-TridionUser
    Import-TridionCoreServiceSettings -ServerName "prod"
    Get-TridionUser


    GUI Extension

    Note: in this step we assume you have unzipped the DTA.zip file into a folder within the server where SDL Tridion is running.
    Ensure you are logged onto the server as an administrator. Locate the gui_install.ps1 file. Right-click and select "Run with PowerShell"
    Follow all instructions. The script will prompt you for:
    1. The Tridion Home directory
    2. A directory to hold the GUI Extension files
    3. CME URL
    4. Tridion IIS Site Name
    The script will copy the following DLL files to the [Tridion Home]\web\WebUI\WebRoot\bin directory.
    • Tridion.Extensions.UI.DTAP.Base.dll
    • Tridion.Extensions.UI.DTAP.Editor.dll
    • Tridion.Extensions.UI.DTAP.Model.dll
    Note that it will alert you if you are missing any of the dependent DLL files in your [Tridion Home]\web\WebUI\WebRoot\bin directory.
    • ChilkatDotNet4.dll
    • Tridion.ContentManager.ImportExport.Client.dll
    • Tridion.ContentManager.CoreService.Client.dll
    • Tridion.ContentManager.ImportExport.Common.dll
    • System.Management.Automation.dll
    The script will create two IIS virtual directories, each named DTAP, one under Editors and one under Models. The System.Config file will be updated with the editor/model specification. Also the [Tridion_Home]\web\WebUI\WebRoot\Web.config file will be updated with the CME host you specified.
    The script will also restart IIS for you.
    You should clear your browser cache before proceeding.

    Getting Started

    This UI eXtension allows you to move items across environments, so in order to do that you need to configure such environments. This is a very simple process
    Click on the DTAP Page in the Ribbon Toolbar and then Click on Configuration
    This will show the Configuration dialog:


    Configuration is publication specific, so in order to be able to save different environments, you need to select a publication from the top dropdown and then click on add:
    Note: this dialog behaves similarly to the Session Preview sites URL in the Publication Target Editor.
    Enter a value in the following format and hit save when done (you can add multiple servers at once):

    To remove an entry, select the entry to delete and click on "Remove"

    To edit an entry, select the entry to modify and hit "Edit", Then you can modify the value.
    To add another entry, repeat the first step.
    Note: The eXtension uses the 'hyphen' character (-) to determine the server url, so you can potentially enter any name before the hyphen, but you must ensure that the part after the hyphen is a valid url.
    HERE YOU CAN TYPE ANYTHING - http://127.0.0.1


    Once the servers are configured, the system will use the browser url as the source server to move items from. So you need to be in the "source" server to move items to "another" environment
    In this example I am going to use two environments QA (http://localhost) and PROD (http://sdltridion.pe.local). I will be moving things from QA to DEV
    As explained before, I need to start on the "Source" server (localhost):
    There are two tasks that you can perform using the eXtension:

    Content Porting

    In order to enable this task, you need to start from a selection in the CMS, you can select a component, multiple components, a folder, a bundle, etc… You can start the tool from a virtual folder as well as from a search result too. Simply select several items and click the "DTAP-It" option:


    Select the target environment (Defined in the configuration section). Test the connection.
    If the connection is successful you can now run Content Porter. The feedback from the service will be displayed in the progress window area:

    Permissions Porting

    From this same dialog you can port permissions too. Simply click on the "Organizational Items" tab or the "Groups" tab to select which items you want to move (including their ACLs)


    Once the items are selected simply click the "Port Permissions" button. The process will start.
    Be patient, wait for the confirmation in the progress area.
    Disclaimer: If the process fails stating that access is denied for user 'DOMAIN\USER' as follows:
    Make sure you create that user in both environments, source and target, and make it  and administrator:
    Once that is done, the error should go away:
    If you want to simply "Port Permissions" there is no need to select anything in the CMS. You just need to click the DTAP-It command button and you'll get the DTAP dialog without the content porter option.


    Command Line Execution for Folders, Structure Groups, and Groups

    If you do not want/need to use the GUI extension to help you migrate Folders, Structure Groups, and Groups from one environment to another, you can do so via the PowerShell command line. This section shows you how.


    Build Export List

    First, connect to your source machine in a PowerShell window.
    Import-TridionCoreServiceSettings -ServerName "dev"
    Next, build up an array of assets. You can specify either TCM IDs or WebDavUrls.
    $assets = @()
    $assets += (Get-TridionGroup -GroupName "Group 1").Id
    $assets += (Get-TridionGroup -GroupName "Group 2").Id
    $assets += "\TS070 Public Site\Home\pages"
    $assets += "\C020 Shared Global Content US English\Building Blocks\Content\HTML Block"
    $assets += "\TC040 Local Public Content US English\Building Blocks\Content\test"


    Export Assets

    To perform the export, execute the following cmdlet. Make sure to specify an appropriate directory for the output files. You can add a Verbose flag if desired.
    Export-TridionDTAPAssets `
     -TcmIdOrWebDavUrls $assets `
     -Recursive `
     -Directory "C:\tridion\modules\output\dtap"


    Import Assets

    First, connect to your destination server.
    Import-TridionCoreServiceSettings -ServerName "qa"
    To perform the import, execute the following cmdlet. Make sure to specify an appropriate directory for the files previously output. You can add a Verbose flag if desired.
    Import-TridionDTAPAssets `
     -Directory "C:\tridion\modules\output\dtap"


    Maintenance
    The tool creates several temporary files in these locations:
    • INSTALL PATH\DTAP.Model\PowerShell\output\dtap
    • INSTALL PATH\DTAP.Model\PowerShell\settings\coreservice
    You can delete the files on those folders EXCEPT sample.xml under DTAP.Model\PowerShell\settings\coreservice
    Supported SDL Tridion Version
    SDL TRIDION 2013 SP1

    0

    Add a comment

  2. Recently I read about the "Tridion Bookmarlet Challenge" and I though, I need to do one of those myself....

    Well, here it is, I got asked if there was a way to go to the "Owning" publication of a given item while browsing Tridion using the Content Manager Explorer (CME). So I implemented my first bookmarklet to do so, you just need to copy the following link to your bookmarks:

    GoTo Owning Publication

    It will check the current selection in the list in the CME and will look for the parent item (in its owning level) and will execute the GoTo command to take you there!

    The full code is here:

    javascript:(function(){
    for (var i=0; i<window.frames.length; i++){

    var frame = window.frames[i];
    if(frame.document.URL.indexOf("Dashboard.aspx")>=0){
    var d = frame.window.$display;
    var m = frame.window.$models;
    var e = frame.window.$evt;
    var x = frame.window.$xml;
    var u = frame.window.$tcmutils;
    var cme = frame.window.$cme;
    var Tridion = frame.window.Tridion;
    var items = d.getView().getListSelection().getItems();
    if(items.length==1){
    var item = items[0];
    var it = m.getItem(item);

    function goToParent(input){
    var doc = input.getXmlDocument();
    var pubId = x.selectNodes(doc, "//tcm:OwningPublication/@xlink:href")[0].value;
    var contextId = u.getItemIdInSpecifiedPublicationContext(it.getId(), pubId);
    var sel = new Tridion.Cme.Selection();
    sel.addItem(contextId);
    cme.executeCommand("Goto", sel);
    }

    if(it.isLoaded(true)){

    goToParent(it);
    }else{
    it.load(false);
    e.addEventHandler(it, "load", function(){
    goToParent(it)
    });
    }

    }else if(items.length==0){
    alert("No items are selected");
    }else if(items.length>0){
    alert("Multiple are selected, select only one");


    }
    }

    }
    }
    )();



    Hope you like it!


    0

    Add a comment

  3. Recently we started a project after the MVP retreat event called the FBI, as in Field Builder Injection. That project is meant to allow the developer to add extended functionality, i.e. a a "field behaviour".

    The FBI Project hasn't got a wiki yet (at least one with enough documentation) but there you can get the latest code with all the goodies developed so far. The project is still under development and we haven't yet released a "production" version. As soon as an stable version is ready, we'll release it together with the proper documentation.

    Said that, I believe that it is good to blog (that's what this post is all about) about the ability to add extended information to our schema fields, in such a way, that the information architects can manipulate it in a convenient manner, for example, by clicking a checkbox in the Schema Designer view when creating a new field, or selecting a value from a dropdown with predifined values also in the Schema Desginer Views.

    One use case that last night came to my mind (I got an email of a colleage asking for some examples on how to achieve what I mention in the previous paragraph), was having the option in the Schema Designer to define whether a field is DD4T-able or not (If you don't know what I am talking about, please check this link).

    So, the goal of this blog post is to let the reader know how to add your custom extended area in the Schema Design View within SDL Tridion's Content Manager Explorer (remember there's no such view on Experience Manager, otherwise we should implement it there too, as "my best practices" say to implement any UI eXtension in both (or as many as there area available) UI-s.

    Preparing your Visual Studio Project

    You are only going to need 1 project, which is the UI Editor. Such project should consist of:
    Note: I always choose the "Empty Web Application" as the project type. It simplifies deployment and makes more sense, since at the end of the day what we are building is a "Web Application" in the manner of a Virtual Directory.



    - Once the project is created, you can safely delete the 'Web.config' automatically created by Visual Studio, since we are not gonna need it.

    0. Add Tridion.Web.UI.Core.dll dependency. It will allow you to use existing Tridion controls and WebUI-related SDL Tridion's features.
    1. One Configuration folder containing the editor.config file
    - In here we will define the extended area as well as the required javascript/css files for the extension to work
    2. On .ascx file which will be the "html" for your extended area, in our case, will consist of a checkbox and a label
    - Normally this file goes within an "Extension" folder.
    3. One C# Class to be used as the Code Behind for your Extended Area, mainly used to fetch together the .ascx and its dependent files (javascript and css)
    - A good practice is to group these together with their .ascx



    Registering the eXtension

    1. Create a Virtual Directory under Edtiors within the SDL Tridion Website on IIS
    2. Add an "editor" entry on the System.config file with your new editor (TRIDION_HOME\Web\WebUI\Webroot\Configuration\System.config)
    - For more information on how to register a UI eXtension check this documentation link (requires password)

    Understanding the eXtension

    The editor.config defines two things:

    1. Where the extended areas go
    <!-- Extended Area DD4T -->
    <ext:extension assignid="SchemaFieldDD4T" name="SchemaFieldDD4T">
    <ext:control>~/Extensions/SchemaFieldDD4T/SchemaFieldDD4T.ascx</ext:control>
    <ext:pagetype />
    <ext:apply>
    <ext:view name="SchemaView">
    <ext:container name="SchemaDesignFieldDesigner">
    <ext:control id="AfterMandatory" />
    </ext:container>
    </ext:view>
    </ext:apply>
    </ext:extension>
    <ext:extension assignid="MDSchemaFieldDD4T" name="MDSchemaFieldDD4T">
    <ext:control>~/Extensions/SchemaFieldDD4T/SchemaFieldDD4T.ascx</ext:control>
    <ext:pagetype />
    <ext:apply>
    <ext:view name="SchemaView">
    <ext:container name="MetadataDesignFieldDesigner">
    <ext:control id="AfterMandatory" />
    </ext:container>
    </ext:view>
    </ext:apply>
    </ext:extension>

    The extended areas are going to be shown, rigth after the "enable for inline editing":



    2. What to load when the extended area is loaded



    The SchemaFIeldDD4T.ascx.js does a few things:

    1. Initializes the Extended area, i.e. registers the controls and their events
    JSA.Extensions.UI.DD4TField.prototype.initialize = function DD4TField$initialize(deckPage)

    2. Writes the configuration value back to the tcm:ExtensionXml node within the Schema source XML
    JSA.Extensions.UI.DD4TField.prototype.onDD4TChecked = function DD4TField$onDD4TChecked()

    3. Updates the view when changing fields
    JSA.Extensions.UI.DD4TField.prototype.onUpdateView = function DD4TField$onUpdateView()

    4. Updates the view when changing the Schema type
    JSA.Extensions.UI.DD4TField.prototype.onSchemaChanged = function DD4TField$onSchemaChanged()


    So basically with this extension you now have the ability to add your custom configuration to the Schema XML within the tcm:Extension nodes, which is a supported location (it is where Experience Manager (aka SiteEdit) stores its information too when enabling/disabling inline editing in a given field. Having implemented this in this way allows you to leverage such custom information in other SDL Tridion modules/eXtension points, such as Templating, Event System, Workflow, etc...

    You can give it a try by downloading the Visual Studio Project and registering the eXtension as described in the "Registering the eXtension" section.


    0

    Add a comment

  4. A few days ago there was a very nice discussion on Tridion Stack Exchange regarding the usability within the Content Manager Explorer (CME) interface in Tridion. So this post will focus in one of the use cases: 
    When they insert a component onto a page, they need to resize the columns (because the titles of the components are very long here). This setting is not remembered, they need to resize it everytime.
    What I am going to be sharing here is an approach to modify the "default" column width for such column "Name" within the List view in the CME:
    The approach taken here is to implement a “Model” (UI eXtension) that loads a javascript piece of code which listens to certain events in order to hook the resizing code into the appropriate moment when the List is loaded, i.e. when the columns become available for resizing.

    Installation

    • Unzip it somewhere in the Tridion Server

    Create a Virtual Directory in the Tridion WebUI\Models virtual Directory

    • Register the Model in Tridion (TRIDION_HOME\web\WebUI\WebRoot\Configuration\System.config):

    <model name="CWE" xmlns="http://www.sdltridion.com/2009/GUI/Configuration">
          <installpath xmlns="http://www.sdltridion.com/2009/GUI/Configuration">PATH TO UNZIPPED LOCATION\CW.Model\</installpath>
          <configuration xmlns="http://www.sdltridion.com/2009/GUI/Configuration">Configuration\model.config</configuration>
          <vdir xmlns="http://www.sdltridion.com/2009/GUI/Configuration">CWE</vdir>
    </model>


    • Clear the Cache and refresh the CME. Now all your lists will have a “wider” Name column (100px wider exactly):


    Here is a brief explanation (javascript comments) of the code

    //Increase the default width by # of pixels, in this case 50
    var cw_increaseWidth = 100;
    var cw_ListPage;
    var cw_ItemsList;


    //TO DISABLE THIS EXTENSION, JUST COMMENT OUT THIS LINE
    $evt.addEventHandler($display, "start", onDisplayStarted$ColumnWidth);

    // This callback is called when any view has finished loading
    function onDisplayStarted$ColumnWidth() {
        $evt.removeEventHandler($display, "start", onDisplayStarted$ColumnWidth);
        var toolbar = $display.getView().properties.controls.toolbar;
        cw_ListPage = toolbar.getPage("DashboardPage");
        //Initialize the DragAndDropUploader after the frame has loaded
        $evt.addEventHandler(cw_ListPage, "frameloaded", cw$onPageReady);
        $list = cw_ItemsList;
       
    };

    // This callback is called when the frame containing the list is finished loading
    function cw$onPageReady() {   
        $evt.removeEventHandler(cw_ListPage, "frameloaded", cw$onPageReady);
        cw_ItemsList = $controls.getControl($("#FilteredItemsList"), "Tridion.Controls.List");
        $evt.addEventHandler(cw_ItemsList, "draw", cw$setWidth);
    };


    //This callback is called when the list is drawn, i.e. the columns become available
    //The code below is a replica of what anguilla does when you resize a column, only difference is that
    //diffX = Set to the 'increase' value on the top of this document (cw_increaseWidth)
    function cw$setWidth(e) {   
        $evt.removeEventHandler(cw_ItemsList, "draw", cw$setWidth);
        var p = cw_ItemsList.properties;
        var cols = p.definition.getColumns();   
        var resizeIndex = 1;   
        
        if (resizeIndex < cols.length - 1) {
           
            //This is how many pixels wider you want it
            var diffX = cw_increaseWidth;       
            var leftCol = cols[resizeIndex];
            var rightCol = cols[resizeIndex + 1];

            if (diffX > 0) {
                var maxDiff = Math.max(0, rightCol.getOuterWidth() - rightCol.getMinWidth());
                if (maxDiff > 0 && diffX > maxDiff) {
                    diffX = maxDiff - 1;
                }
            }
            else if (diffX < 0) {
                var maxDiff = Math.min(0, -leftCol.getOuterWidth() + leftCol.getMinWidth());
                if (maxDiff < 0 && diffX < maxDiff) {
                    diffX = maxDiff + 1;
                }
            }

            if (diffX) {
                leftCol.setOuterWidth(leftCol.getOuterWidth() + diffX);
                rightCol.setOuterWidth(rightCol.getOuterWidth() - diffX);
                // Invalidate original width
                leftCol.setDefinedWidth(null);
                rightCol.setDefinedWidth(null);
            }
        }

        cw_ItemsList._initializeDimensions(true);
        p.resizeEngaged = false;
        p.resized = true;
       
    };


    If you guys are interested on how to store this values per user (and even per list type), or make it more "clever" like only increase the value to the actual size of the larger Name, please leave a comment and I will see what I can do.

    Also you can follow the discussion on this topic here

    0

    Add a comment

  5. Pre-Introduction
    Before the deployer storage layer (JPA) was introduced in the 2011 release, the most common way to extend the deployer was, well, extending the deployer J.
    Basically you’d extend the Modules and Processors available within the cd_deployer_conf.xml configuration file. Nowadays it is more common to find Storage Extensions for CDN integrations, search index creation etc, but that doesn’t mean that you cannot still extend the deployer… in fact in some cases you still need to extend it, like for example to extend the metadata of a component when it is published. Anyway this post will contain all the information you need to implement your Deployer extensions together with a Sample Code Download at the end. Please read through it and you’ll find not only an example on how to make your deployer extensions, extendable, but also an idea on how your Storage extensions (or any other delivery extensions) can be implemented.
    This is something I have had for a while, but never really thought of sharing. However after attending the MVP retreat 2013, I learnt that I have to SHARE MORE!, so here it is. I hope you find it useful and even inspirational for your future deployer/storage extensions
    Note: The project references the 2011 libraries (jar files). It should also work (maybe with minor changes) with 2013.
    Introduction
    This is always a tough task to extend the default Tridion deployer functionality. To ease this task the Custom Deployer Extension comes to scene. This extension can be considered a framework that allows the developers to easily add functionality to the default deployment mechanism. Some scenarios where the deployer could be extended are:
    1. Updating Front-end Search Services Information
    2. Adding Custom Metadata to the components/pages at deploy time
    3. Reallocating Dynamic Component presentations at deploy time
    4. Reallocating Binaries at deploy time
    5. Email Notification after specific publishing tasks
    6. Other External Systems integration (update external databases, as an example)
    7. ??
    Installation, Configuration & Scalability
     
    The Custom Deployer Framework (extension) is built in a way that most of the actions/logic to be executed during the deployment process can be configured by using xml files. Once the framework is installed, adding new functionality is a pretty straight-forward task. Basically the framework executes what is called “Custom Actions” that can be easily configured within the main configuration xml file.
    These “Custom Actions” can be configured in such a way that can be executed either before the actual deployment process takes place or after, allowing the logic to be executed when and only when it’s needed.
    The framework consists in one single jar file : sdl.custom.deployer.jar. Within this file we can find the following packages:
    1. com.tridion.deployer.extensions: contains the main Processor classes both, for deployment and undeployment.
    2. com.tridion.deployer.extensions.base: contains the base classes with the common functionality needed to execute the actions.
    3. com.tridion.deployer.extensions.behaviour: contains the Interface that defines the behavior of the actions and a Comparator that allows to execute those actions in an specific order
    4. com.tridion.deployer.extensions.examples: example classes implementing the framework
    5. com.tridion.deployer.extensions.utils: some common utilities to be used in the “Action” classes
    Installation

    To install the extension, the jar file (sdl.custom.deployer.jar) needs to be copied into the lib folder within the Content Delivery installation path, by default: Tridion_Installation_Path/lib.
    Once the file is located in the lib folder within the Content Delivery installation location the cd_deployer_conf.xml needs to be updated to enable the new extension in the following way:
    Within the ‘Processors’ node, both the Action=”Deploy” and the Action=”Undeploy” processors need to be updated:
    <Processors>
    <!-- <Processor Action="Deploy" Class="com.tridion.deployer.Processor">  -->
    <Processor Action="Deploy" Class="com.tridion.deployer.extensions.CustomDeployProcessor">
           <Module Type="SchemaDeploy" Class="com.tridion.deployer.modules.SchemaDeploy"/>
           <Module Type="PageDeploy" Class="com.tridion.deployer.modules.PageDeploy">
           <Transformer Class="com.tridion.deployer.TCDLTransformer"/>
           </Module>
           <Module Type="BinaryDeploy" Class="com.tridion.deployer.modules.BinaryDeploy"/>
           <Module Type="ComponentDeploy" Class="com.tridion.deployer.modules.ComponentDeploy"/>
           <Module Type="TemplateDeploy" Class="com.tridion.deployer.modules.TemplateDeploy"/>
           <!-- This module enables deployment of taxonomies -->
           <Module Type="TaxonomyDeploy" Class="com.tridion.deployer.modules.TaxonomyDeploy"/>
           <Module Type="ComponentPresentationDeploy" Class="com.tridion.deployer.modules.ComponentPresentationDeploy">
           <Transformer Class="com.tridion.deployer.TCDLTransformer"/>
           </Module>
           <Param Name="CustomDeployerConfigFilePath" Value="PATH\cd_deployer_custom_deploy.xml" />
    </Processor>
    <!-- <Processor Action="Undeploy" Class="com.tridion.deployer.Processor"> -->
    <Processor Action="Undeploy" Class="com.tridion.deployer.extensions.CustomUndeployProcessor">
    <Module Type="PageUndeploy" Class="com.tridion.deployer.modules.PageUndeploy"/>
           <Module Type="ComponentPresentationUndeploy" Class="com.tridion.deployer.modules.ComponentPresentationUndeploy"/>
           <!-- This module enables the undeploy of taxonomies -->
           <Module Type="TaxonomyUndeploy" Class="com.tridion.deployer.modules.TaxonomyUndeploy"/>
                  <Param Name="CustomDeployerConfigFilePath" Value="PATH\cd_deployer_custom_undeploy.xml" />
    </Processor>
    </Processors>

    The changes to be considered are that a new class is now used as the “Deploy” and “Undeploy” processors: com.tridion.deployer.extensions.CustomUndeployProcessor and com.tridion.deployer.extensions.CustomUndeployProcessor respectively and a configuration file location file is added per each of those: cd_deployer_custom_deploy.xml and cd_deployer_custom_undeploy.xml

    Once the configuration file (cd_deployer_conf.xml) is updated we are ready to use the new framework. To explain how the framework works we’ll be using the Example Log extension available in the package. As mentioned before the framework executes what is called “Custom Actions” which are Java Classes that extend a certain java class and implements a certain Interface, both available in the framework.

    The new “Custom” processors will dynamically load those “Custom Actions” based on the information provided on each of the configuration files, one for deployment and another one for undeployment.
    Both configuration files follow the same structure and we’ll be using the “deploy” configuration file as the example to understand how to add new functionality to our default deployment process.

    cd_deployer_custom_deploy.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <!--  CustomDeployerConfigFilePath -->
    <actions>
           <action order="1" active="true" type="pre" errorHandling="true" class="com.tridion.deployer.extensions.examples.SampleLogAction">
                  <description>Logs</description>
                  <config-location>F:\PROJECTS\QUEST\DEV\Tridion Custom Deployer\config\samplelogaction-config.xml</config-location>     
           </action>    
           <action order="2" active="true" type="post" class="com.tridion.deployer.extensions.examples.SampleLogAction">
                  <description>Logs</description>         
           </action>
           <action order="1" active="true" type="pre" class="com.tridion.deployer.extensions.examples.SampleLogAction">
                  <description>Logs</description>
                  <config-location>F:\PROJECTS\QUEST\DEV\Tridion Custom Deployer\config\samplelogaction-config2.xml</config-location>
           </action>
    </actions>

    As shown in the xml file, it contains several action nodes, which will be executed before (“pre”) or after (“post”) the deployment process in the given order (“order”).

    For each action we can specify:

    1. Order: determines in which position the action is executed.
    2. Active: determines whether the action is active or not. (it it’s not, it won’t be executed)
    3. Type: “pre” or “post” determining when to execute the action. i.e. before or after the default deployment process.
    4. Class: the class containing the actual java logic. This class must extend com.tridion.deployer.extensions.base.CustomAction and must implement com.tridion.deployer.extensions.behaviour.ICustomAction
    5. ErrorHandling: true or false, depending on this value the method “executeOnError” will be exectuted when an error occurs during the default deployment process. This feature is meant to be used only for “pre” actions and allows to execute specific logic in case the default deployment process  fails.
    6. Description: a brief description  for logging purposes.
    7. Config-location: full path to the configuration xml file for the current action, allowing the possibility of having separate/different configuration files per action. We can have also different configuration files for the same action.

    Every Class needs to implement at least the two following methods:
    public void executeAction() throws ProcessingException
    The actual logic for the action
    public void executeOnError() throws ProcessingException
    The logic to be executed if something fails

    Based on this configuration file the whole deployment process would be:






    The example “Action” com.tridion.deployer.extensions.examples.SampleLogAction uses a configuration file for Deployment located at: com.tridion.deployer.extensions.examples  named as : samplelogaction-config.xml . This action simply writes a log per each “message” node within the configuration file:
    <?xml version="1.0" encoding="utf-8"?>
    <config>
          <message>SampleLogAction: Hello World!</message>     
          <message>SampleLogAction: Hello World 2!</message>
          <message>SampleLogAction: Hello Tridion</message>
          <message>SampleLogAction: Hello Content</message>    
    </config>

    The example “Action” com.tridion.deployer.extensions.examples.SampleLogAction code is:

    public class SampleLogAction extends CustomAction implements ICustomAction {     
          
           /* (non-Javadoc)
           * @see com.tridion.deployer.extensions.base.CustomAction#executeAction()
           */
           public void executeAction() throws ProcessingException {
                  try {        
                        
                         log.info("Starts Custom Action: "+this.getClass().getCanonicalName());
                         NodeList sampleMessages=null;
                         if(getConfig()!=null){
                               sampleMessages = XPathAPI.selectNodeList(getConfig(), "//message");
                         }
                         if(sampleMessages!=null){
                               int length = sampleMessages.getLength();
                               for(int i=0; i<length;i++){                           
                                      String currentDummyMessage = sampleMessages.item(i).getTextContent();
                                      log.info(currentDummyMessage);                               
                               }
                         }else{
                               log.info("No messages configured!");
                         }
                        
                  } catch (TransformerException e) {
                         throw new ProcessingException(e);
                  }finally{
                         log.info("Ends Custom Action: "+this.getClass().getCanonicalName());
                  }
                 
           }
          
           /* (non-Javadoc)
           * @see com.tridion.deployer.extensions.base.CustomAction#executeOnError()
           */
           public void executeOnError() throws ProcessingException{            
                  log.info("Custom Action ["+ this.getClass().getCanonicalName() +"] executeOnError method. Error Handling: "+this.isErrorHandlingEnabled());
           }
    }

    The code iterates over the “message” nodes on the configuration file for the specific Action (samplelogaction-config.xml) and will write out a log line per node containing the text within each of those.

    Util methods available on the Action classes:
    1. getConfig() : Returns the XML document Object (org.w3c.Document) with the config file information related to the current action.
    2. getUtils(): Set of utilities available on the com.tridion.deployer.extensions.CustomUtils class
      • getSchemasDocument():Returns the XML document Object (org.w3c.Document)  containing the schemas.xml data in the current package
      • getComponentsDocument():Returns the XML document Object (org.w3c.Document)  containing the components.xml data in the current package
      • getPagesDocument():Returns the XML document Object (org.w3c.Document)  containing the pages.xml data in the current package
      • getComponentPresentationsDocument():Returns the XML document Object (org.w3c.Document)  containing the component_presentations.xml data in the current package
      • getBinariesDocument():Returns the XML document Object (org.w3c.Document)  containing the binaries.xml data in the current package
      • actionIsDeploy(): Determines if the current action is deploy. If false, the action is undeploy.
      • getTransportPackageLocation(): Returns the full path to the transport package when unpacked.
      • getTransportPackageFolderName(): Returns the folder name of the Transport Package.
      • cloneTransportFolder(): creates a copy of the Trasnport package folder in the given location.
      • deleteDir(): deletes the given directory
      • copyDirectory(): copies the given directory to the given location.
      • getSchemaURIByTitle() : returns the Schema URI, based on its Title
      • getComponentsBySchemaURI() returns a list of Documents (org.w3c.Document) with the xml content per each Component based on the given Schema URI.
      • nodeToString() converts a Node to a String.
      • stringToNode() converts a String to Node.

    Real-World Scenario Setup
    One of our customers wanted to extend the deployer in such a way they could create xml files for Endeca (A Search Engine) to be able to index those based on the information coming from the pages in Tridion. So, every time a page is published, an xml file is created containing specific information to be available for searching in the published website. The requirements here are:
    1. For every page published, an xml file needs to be created
      • The file will contain certain information available in the page metadata
    2. For every page unpublished, an xml file needs to be created
      • The file will contain enough information to delete the specific entry from Endeca
    This requirements, based on the Deployer mechanism where translated into:
    1. Custom Deployer Actions (On Deploy)
    2. Generate Endeca XML Files based on Page metadata in a temporary location (we cannot deploy those files until the default process action is successfully finished)
    3. If the default process fails, the temporary files, need to be deleted
    4. Once the default process has successfully finished, Move the xml files from the temporary location into the Endeca Repository
    5. Custom Deployer Actions (On Undeploy)
    6. Generate Endeca XML File to remove the reference from Endeca. This file needs to be created in a temporary location and then deleted when the “UnDeploy” action has been successfully  finished.
    The framework implementation will consist of the following xml files setup:
    cd_deployer_custom_deploy.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!--  CustomDeployerConfigFilePath -->
    <actions>
           <action order="1" active="true" type="pre" errorHandling="true" class="com.customer.cms.deployer.extensions.actions.GenerateEndecaXMLAction">
                  <description>Generates Endeca XML Files</description>
                  <config-location> PATH_TO_CONFIG\generate-endeca-xml-action-config.xml</config-location>
           </action>    
           <action order="1" active="true" type="post" errorHandling="false" class="com.customer.cms.deployer.extensions.actions.DeployEndecaXMLFilesAction">
                  <description>Deploys Endeca XML Files</description>   
                  <config-location> PATH_TO_CONFIG \deploy-endeca-xml-action-config.xml</config-location> 
           </action>    
    </actions>

    cd_deployer_custom_undeploy.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!--  CustomDeployerConfigFilePath -->
    <actions>
           <action order="1" active="true" type="pre" errorHandling="true" class="com.customer.cms.deployer.extensions.actions.GenerateEndecaXMLAction">
                  <description>Generates Endeca XML Files</description>
                  <config-location>PATH_TO_CONFIG\generate-undeploy-endeca-xml-action-config.xml</config-location>    
           </action>           
    </actions>
    The following actions would be implemented:
    com.customer.cms.deployer.extensions.actions.GenerateEndecaXMLAction
    com.customer.cms.deployer.extensions.actions.DeployEndecaXMLFilesAction
    com.customer.cms.deployer.extensions.actions.GenerateEndecaXMLAction

    DEPLOY
    The first action will create the xml files for Endeca based on both, the tridion xml files available in the package containing the metadata info for each page. This action will create the files in a temporary location define on its configuration file. If the default deployment process fails, this action will delete that temporary location.
    The second action will be executed once the default deployment action has successfully finished and will move the files from the temporary location to the Endeca repository location. If something fails, this class will delete the temporary location also.
    UNDEPLOY
    The third action will create an xml to notify Endeca in order to remove the entry from the search index once the page is unpublished. If something goes wrong during the default undeployment action, such xml file won’t be generated.
    CONSIDERATIONS
    The current extension is meant to be the starting point of a deployer extension.
    Error handling might be implemented in a different way based on customer’s need
    This extension only extends the functionality at a higher level (Processors) but the same approach can be followed at a lower level (Modules)
    The CustomUtils comes with some useful methods, but of course, some others could be implemented.
    Any Method Implmented on the CustomAction class would be available on each action extending such a class.
    Any Method Implemented on the CustomActions would be available on each action by calling
    getUtils().newMethod()
    Every Action has access to the configuration file defined in the config-location node per each action by calling the getConfig() method. It will return a org.w3c.Document with the configuration.

    1

    View comments

  6. With the release of SDL Tridion 2013, a new feature was added to the Ambient Data Framework, claims are scope aware. Actually it wasn't really added, but rather exposed through configuration. If you are not familiar with Ambient Data Framework, I suggest you read my friend Eric Huiza's post about it before moving forward with this post (Ambient Data Framework in a Nutshell).

    As I said claims are now scope aware, being the two possible options for such scope either REQUEST or SESSION. That means that the claim will be available throughout either the Request or the Session objects respectively once it is set within our application container using the ADF API.

    In this post I am going to explain how to create an empty cartridge that will allow you to define a custom set of claims and their scope. Once the cartridge is registered you will be able to modify such claims from your application’s code.


    In order to add your custom claims you will have to:
    1.      Create your own cartridge (Implement your Claim Processor)
    2.      Define your claims (configuration file where you define scope , name and description of the claims)
    3.      Register your cartridge
    4.      Test

    Here is an example on how to accomplish it:

    Create your own cartridge

    To create your own cartridge you will have to create a Java Project (using your preferred IDE). I will be using Eclipse (as usual) for the sample project available for download in the downloads section as well as at the bottom of the post.

    Your cartridge is nothing but a Java Class that extends from com.tridion.ambientdata.processing.AbstractClaimProcessor;
    You could also implement the com.tridion.ambientdata.processing.ClaimProcessor; instead.

    In that class you won’t be writing any code (unless you want to manipulate the current claims in the chain) , just simply leave the three methods empty. Remember this example tries to show how to add your claims and manipulate their values afterwards in your application. There’s a lot more you can do in your cartridges like intercept what claims are in the chain, manipulate them, set your claims depending on them, etc… but that’s not the goal of this post.

    Your java class should look like:

    package com.jsa.tridion.adf.extensions;

    import com.tridion.ambientdata.AmbientDataException;
    import com.tridion.ambientdata.claimstore.ClaimStore;
    import com.tridion.ambientdata.processing.AbstractClaimProcessor;
    import com.tridion.ambientdata.processing.ClaimProcessor;

    public class JSAWebClaimProcessor extends AbstractClaimProcessor {     
         
          @Override
          public void onRequestStart(ClaimStore claimStore) throws AmbientDataException { }

          @Override
          public void onRequestEnd(ClaimStore claimStore) throws AmbientDataException { }

          @Override
          public void onSessionStart(ClaimStore claimStore) throws AmbientDataException { }

    }

    Define your claims
    The next step is to define the claims you want to be available in your application (within ADF). You will need an xml like the following (you can name it whatever you want, in my case: jsa_cartridge.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <CartridgeDefinition Version="7.0" Uri="tcd:cartridge:example"
          Description="Example cartridge." xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="schemas/cd_ambient_cartridge_conf.xsd">
          <ClaimDefinitions>
                <ClaimDefinition Uri="taf:claim:jaimesideas:blogurl" Scope="SESSION"
                      Description="Jaime's Ideas' Blog URL." />
          </ClaimDefinitions>
          <ClaimProcessorDefinitions>
                <ClaimProcessorDefinition Uri="tcd:claim:jsa:processor"
                      ImplementationClass="com.jsa.tridion.adf.extensions.JSAWebClaimProcessor"
                      Description="Custom claim processor that allows setting claims through the api in the right scope.">

                </ClaimProcessorDefinition>
          </ClaimProcessorDefinitions>
    </CartridgeDefinition>


    As you can see I am only declaring one Claim: taf:claim:jaimesideas:blogurl and its scope is Scope="SESSION". Another thing to note is that in the ClaimProcessorDefinition I am specifying that the processor used for this claims is the class I just implemented: com.jsa.tridion.adf.extensions.JSAWebClaimProcessor

    Register your cartridge
    In order to register your cartridge you’ll need to:

    1. Generate a jar file out of your Java Project
    a.       If you are using Eclipse for this, remember to mark “Include directory entries” when exporting your jar file:
    2. Install the jar file (the name of the file is irrelevant) within your application. In this case within the /bin/lib folder since I am in a .Net environment.
    3.Install the config file jsa_cartridge.xml. This file should go into the /bin/config
    4. Register the cartridge:
    a.       Find your cd_ambient_conf.xml in your content deliver configuration folder (/bin/config by default) and add your cartridge:
    <Cartridges>
        <!-- Example cartridge definition -->             
        <Cartridge File="jsa_cartridge.xml"/> 
    </Cartridges>
    5.     Restart your Application.

    Test
    In order to test the cartridge we need an aspx page to set the claim and another aspx to show the claims. Both are also within the downloadable file (ShowClaims.aspx, SetClaim.aspx)

    SetClaim.aspx: Will set the claim
    AmbientDataContext.CurrentClaimStore.Put(new Uri("taf:claim:jaimesideas:blogurl"), "http://jaimesantosalcon.blogspot.com", Tridion.ContentDelivery.AmbientData.Runtime.ClaimType.Normal);

    ShowClaims.aspxWill show all the claims



    3

    View comments

  7. After attending the MVP 2012 Retreat I felt a bit embarrassed when I saw what my fellow MVPs had been doing throughout the year and I thought it was fair to go back to my blog and share a little bit…
      
    So here I am, as Alvin would say: sharing, sharing, sharing …

    I am currently flying to Seattle and I have spent half of the flight reading blog entries on the Tridion community and I realized that a lot of people is STILL very interested in GUI extensions (I know, I know, you might be thinking, “Is this guy going to talk about anything else other than GUI Extensions??”, and the answer is yes, but not today).

    Since I was one of the pioneers writing GUI extensions (together with Yoav, of course, and some others I may be missing)  I think it makes sense I share my experience in the last –almost- two years writing GUI extensions and,  why not?, what I consider the My Best Practices when developing GUI extensions. Before I dig any deeper, please remember these rules before writing an extension:

    Rule #1: A GUI extension is not always the solution, please be careful when recommending a GUI extension to your customer, although it might be the coolest, it might not be the best way to solve the problem .
    Rule #2. Do not reinvent the wheel. Check within the existing editors how things are done. There might be functionality that you can reuse in your extension. Spend some time reading/reviewing existing objects.
    Rule #3: Make sure that what you are trying to extend is extendable.

    If after applying these rules, you still think you need an extension, consider the following too:

    Location & Availability
    Think where you are going to place your extension. There might be extensions that should only make sense in certain contexts. Review the existing GUI “locations” such as Ribbon Tool Tabs and Groups, context Menu options and Groupings, Item Editors, Favorites section, etc…) to get a better understanding on where things are and why.

    Ribbon toolbar buttons cannot be hidden (only disabled) but context menu extensions can. So for a better user experience (which at the end of the day is what GUI extensions should be all about) make sure that the extensions are available in a context that makes sense, for example, if you are creating an extension that shows the “Related Schemas” of a Component Template, make sure that it is only available for Component Templates.

    Security: Access & Roles
    Consider security also when implementing your extensions, not all the users will have access to all the extensions. Try to implement your extensions permissions following a similar pattern than SDL Tridion does itself. For example, rely on the existing user groups to enable/disable extensions, grant administrators access to full functionality, etc…

    File Groups Extension
    Existing group files in the CMS can be extended, meaning that your files will be added together with the files added by the group you are extending. This is perfect to make sure that your files are only loaded when required. For example, when we define the icons to be used for the ribbon toolbar button that we added with our extension, we normally do it within a CSS. Well this CSS should only be loaded once the Ribbon Tool bar is loaded. That can be done by extending the file group: Tridion.Web.UI.Controls.RibbonToolbar. I will cover this topic in detail my next post.

    External Libraries
    SDL Tridion provides with enough built-in javascript functionality, i.e.the Anguila framework, but sometimes you want to leverage some advance functionality by using external libraries like jQuery, jQuery UI, etc. Well, you need to know that Anguilla already uses some reserved variable names, such as $, $$. It might be a conflict if you need to use some jQuery functionality for example. So you need to make sure that you add the files in a way that doesn’t conflict with the existing ones. And make sure that you use “safe” variable names.

    CME vs UI 2012
    Recently I had an assignment where I was asked to check why an extension that works in the CME, doesn’t work in UI 2012 (aka Experience Manager, or XM or SiteEdit). Well the answer is simple: UI 2012 and CME are different editors (GUI) and use different JavaScript classes. So, if you want your extension to work in both, you need to design and implement it accordingly. There are some other reasons why an extension might not work in UI 2012, for example because in certain situations it wouldn’t make any sense, for example, extensions that have to do with System Items such as Users, Publication Targets, etc… those, you don’t manage from UI 2012, so an extension there would be pointless.

    One more thing to keep in mind, is that UI handles content edition in a different way, think about Rich Text Fields, we have the Format tab in both cases, but in UI 2012 we also have the contextual/popup RTF editor. An extension that extends such RTF functionality is going to be quite different on each editor.

    UI 2012 itself implements the same command differently for the CME and SiteEdit editors, so you might need to do the same with your commands.

    These were some considerations to bear in mind when writing extensions for UI 2012 but the one that is the most important in my opinion is the Cross Domain Calls that take place in UI 2012. This editor is executed on top of our staging website normally (domain B), but it calls the editor which is installed in the Tridion Server (domain A). Well, for those with some JavaScript experience, we know that the browsers don’t like cross-domain calls, which means nothing but calling functionality in a javascript in domain A from domain B or vice-versa. There are a few solutions out there to accomplish this like dojo and jsonp (among others) but since we are extending an existing solution, Anguilla already solves that problem for as giving us a way to implement cross domain handlers. I am not going to get into detail in this post, since I will be posting a UI 2012 “Hellow world” extension shortly, but basically for those willing to investigate, here is a hint… look for the $xdm variable within the UI 2012/XM/SiteEdit javascript files. By using this approach, we can interact with the dom in the Staging page from, for example, a button in the Ribbon Tool bar.

    Cross-Browser compatibility
    Your extensions should work on all SDL-Tridion Supported Browsers (Safari, Chrome, Internet Explorer, Firefox etc…). Be careful with the browser version and make sure you are using a “really” supported version. You can check the supported versions in the documentation.

    Other Extensions
    Try to implement your extensions in a way that they are completely independent. Always try to check what happens if there are other SDL Tridion GUI extensions already installed like, UGC, AM, etc…) There might be conflict if those are installed. You might not be the only one writing extensions, so make sure that you have all the information you need. For example you might need to extend the “Delete” command, and after you write your code, you see it doesn’t behave as you expected. Well, maybe someone is also extending the “Delete” command and you didn’t know…

    Summary
    So to summarize what I talked about before, this is a check list of the minimum number of things to consider even before writing a SDL Tridion GUI Extension:
    1. Apply the 3 rules (Use your common sense, Reuse: ,Is this extendable?)
    2. Check before you implement: Is SDL Tridion CME already doing something similar. You might be able to reuse some code.
    3. Location: Where my extension is going to show (dashboard, context menu, editor tab, ribbon toolbar)
    4. Availability: When is my extension to be available (for components, for pages, CME, UI 2012)
    5. Access & Roles: Does everyone have access to use the extension? can everyone use the same functionalities within the extension?
    6. File Groups Extensions: Only add JS files when really needed. Do not use a domain file that is only used within a command. Instead add the file only when the command is loaded.
    7. External Libraries: Be careful when adding third-party libraries, they might not be suitable to be used together with Anguilla. Use “safe” variable names
    8. CME vs. XM (UI 2012 | SiteEdit): Be aware that both editors are different and that sometimes the same command is implemented differently for each editor. You might need a cross domain handler…
    9. Cross-Browser compatibility: Your extensions should work in all the browsers supported by SDL Tridion.
    10. Other/Existing extensions: check what’s already in the system. Other extensions (SDL Tridion proprietary or not) might conflict with yours 


    TODO LIST
    I owe you guys a UI 2012 GUI “Hello World” extension with an example extending file groups. I promise it will be coming shortly…

    1

    View comments

  8. Introduction

    With the launch of 2011 we were all very excited with all the extension points the new GUI provides out of the box, but we were also a bit lost on how to use those extension points.
    In this post I will explain how to implement a Data Extender and how to fully implement its behavior according to the Anguilla framework capabilities.

    So far I have mention a few terms: Data Extender, Anguilla, GUI extension points, etc… which means that the reader of this post should be familiar with all this concepts and should have some hands-on experience with the Anguila GUI extensions.

    In the following sections I will provide with a step-by-step “Tutorial” on how to implement a Data Extender to display additional information on the items in the lists displayed in the SDL Tridion 2011 GUI. This post also tries to answer this question in stack overflow, which the reader should check before continuing reading this post.

    Requirements
    We want to add an additional column to the lists in the main content area of the Tridion GUI to display additional information:
    We are going to implement this functionality only for pages and components.
    That information will be relative to the item in the list, for example, for a Components we will display the Schema it is based on and for Pages we will display the page Template It is based on.
    The lists need to be refreshed in the same way Anguilla does, meaning we always display the appropriate information.

    Tutorial Steps
    1. Create the Data Extender Class
    2. Create a GUI Editor
    3. Editor Configuration
    4. Deploy the solution



    Create a Data Extender Class
    To deal with data extenders we will need to create a Visual Studio Solution. My recommendation is to create one with the following structure:



    As you can see there are also 3 other dependencies in the Data Extender Project, plus the Core Service Reference.

    1. Tridion.Common.dll, that can be found under TRIDION_INSTALL_PATH\bin\client
    2. Tridion.Web.UI.Core, that can be found under TRIDION_INSTALL_PATH\web\WebUI\WebRoot\bin
    3. Tridion.Web.UI.DataExtenders, that can be found under TRIDION_INSTALL_PATH\web\WebUI\WebRoot\bin
    4. ServiceReference (CoreService), you need to add the Service Reference, normally under: TRIDION_DOMAIN/webservices/CoreService.svc

    Once we have the solution ready we can implement the code within the Extender Class.
    The class in question is IntelligentDataExtender that extends the DataExtender
    This  class intercepts the list generation and appends an attribute to each tcm:Item node within the regular list with the Additional Information, only for Components and Pages:


    The reader should be able to follow the code J, it is quite straight forward.

    Create a GUI Editor
    The GUI Editor is already available within the solution, it is under: IDataExt folder


    In order to configure the editor, please refer to the following link:
    And you should end up with something like:


    Editor Configuration
    The areas to configure in the editor are:
    Adding the javascript files:
        
        <cfg:groups>
          <cfg:group name="Com.Tridion.PS.Extensions.UI.Model" merger="Tridion.Web.UI.Core.Configuration.Resources.DomainModelProcessor" merge="always">
            <cfg:domainmodel name="Com.Tridion.PS.Extensions.UI.Model">
              <cfg:fileset>
                <cfg:file type="script">/Scripts/ExtendedPage.js</cfg:file>
                <cfg:file type="script">/Scripts/ExtendedComponent.js</cfg:file>
              </cfg:fileset>
              <cfg:services />
            </cfg:domainmodel>
          </cfg:group>
        </cfg:groups>

    Defining the data extender:
        
       <ext:dataextenders>
          <ext:dataextender name="IntelligentDataExtender" type="Com.Tridion.PS.Extensions.IntelligentDataExtender, PS.GUI.Extensions">
            <ext:description>
              Shows extra info
            </ext:description>
          </ext:dataextender>
        </ext:dataextenders>

        <ext:editorextensions>
          <ext:editorextension target="CME">
            <ext:editurls/>
            <ext:listdefinitions/>
            <ext:taskbars/>
            <ext:commands/>
            <ext:commandextensions/>
            <ext:contextmenus/>
            <ext:lists>
              <ext:add>
                <ext:extension name="IntelligentColumnExtender" assignid="IntelligentDataColumnExtender">
                  <ext:listDefinition>
                    <ext:selectornamespaces/>
                    <ext:columns>
                      <column xmlns="http://www.sdltridion.com/2009/GUI/extensions/List" id="IntelligentData" type="data"  title="Additional Info" selector="@ExtendedInfo" translate="String"/>
                    </ext:columns>
                  </ext:listDefinition>
                  <ext:apply>
                    <ext:view name="DashboardView" />
                  </ext:apply>
                </ext:extension>
              </ext:add>
            </ext:lists>
            <ext:tabpages/>
            <ext:toolbars/>
            <ext:ribbontoolbars/>
          </ext:editorextension>
        </ext:editorextensions>

    Extending the Model:
        <ext:modelextensions>
          <cfg:itemtypes>
            <cfg:itemtype id="tcm:16" implementation="Com.Tridion.PS.Extensions.UI.ExtendedComponent" />
            <cfg:itemtype id="tcm:64" implementation="Com.Tridion.PS.Extensions.UI.ExtendedPage" />       
          </cfg:itemtypes>
        </ext:modelextensions>

    Deploy the solution
    Once the Editor is configured, we just need to move our DLL’s and configuration files to the Tridion Web Application. When we compile our solution we see that it generates 2 dlls:

    We need to move those DLLs to TRIDION_INSTALL_PATH\web\WebUI\WebRoot\bin
    We also need to move the configuration file IntelligentDataExtender.config, to the same path. This file contains the following information:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <add key="IMPERSONATE_USER" value="Administrator"/>
        <add key="CORE_SVC_ENDPOINT_URL" value="net.tcp://localhost:2660/CoreService/netTcp_2010"/>
      </appSettings>
    </configuration>

    The IMPERSONATE_USER setting is used to “Impersonate” the Core Service call.
    The CORE_SVC_ENDPOINT_URL is used to connect to the Core Service.

    DEBRIEF
    It might look like there’s a lot of code and things to do to enable this Data Extender, but these are actually the steps we took:

    1. Create a Data Extender (Everything could have been done in the same class, but the idea was to show how you can separate your classes in a more maintainable way)
    2. The Data extender, does nothing but adding another attribute to the tcm:Item node within the tcm:ListItems list, depending on the item type, it adds different information. The attribute is “ExtendedInfo
    3. Create a GUI Editor that uses the Data Extender: we told the editor, what column to add and what is the xPath to retrieve the information: @ExtendedInfo
    4. We implemented two JavaScript classes to handle list refreshing (Com.Tridion.PS.Extensions.UI.ExtendedComponent and Com.Tridion.PS.Extensions.UI.ExtendedPage), these two classes contain some logic to keep track of the @ExtendedInfo attribute: Com.Tridion.PS.Extensions.UI.ExtendedComponent/ExtendedPage.prototype.setDataFromList: Sets a property in the Object with the value from the data extender; Com.Tridion.PS.Extensions.UI.ExtendedComponent/ExtendedPage.prototype. getListItemXmlAttributes: returns the value in the property we set before, note that in a component we cannot change the schema, so we always return the value without doing any check, However in the page we can change the page template, so we check the value before returning it.
    5. In this classes we check if OME (Online Marketing Explorer) is installed, since it already extends the classes, so we need to extend ours accordingly:
    Com.Tridion.PS.Extensions.UI.ExtendedPage = function ExtendedPage(id) {
        Tridion.OO.enableInterface(this, "Com.Tridion.PS.Extensions.UI.ExtendedPage");
        // test if initiative (from OME) exists
        var isInitiative = false;
        if (Tridion.OO.implementsInterface(this, "Tridion.OnlineMarketing.UI.InitiativePage")) {
            this.addInterface("Tridion.OnlineMarketing.UI.InitiativePage", [id]);
            isInitiative = true;
        } else {
            this.addInterface("Tridion.ContentManager.Page", [id]);
        }
        var p = this.properties;
        p.isInitiative = isInitiative;
        p.extendedInfo = undefined;
       
    };

    Note: The code is compiled and developed using SDL Tridion 2011 HF#1 DLLs, to deploy this code in a different version, update the DLL references accordingly.

    This is basically it, I hope you manage to install it and make it work. Otherwise, please let me know.

    Have fun!

    4

    View comments

  9. Introduction

    This post is meant to help you guys when dealing with a CWA Views implementation. I will use Eclipse (Helios) as the IDE for this "Tutorial" and I will try to cover the following topics:
    Configuring Eclipse to edit your DWT Views
    Configuring Eclipse to edit the view as "JSP" code.

    Requirements to follow the tutorial
    To follow this tutorial the reader must be familiar with the CWA Framework and JAVA.
    You can get some details in the SDL Live Documentation: SDL CWA 2011 section.
    Also Java knowledge is required.


    CWA Definition

    This is the CWA definition available in the SDL Live Documentation portal:

    "SDL Content Web Application (CWA) is an add-on to SDL Tridion that allows you create and manage a dynamic Web site. CWA supports the dynamic assembly of content on a special Web page called a View, invocation of a Java application from published content, welcome-file functionality, and serving of static Web pages and binaries. The CWA documentation describes the system architecture and how to install and implement CWA."

    CWA MVC Model & Views

    When dealing with CWA we are actually dealing with a model-view-controller framework (kind-of) that allow us to separate these three features of our website in an easy and convenient manner.
    I am not going to get into detail on how CWA works, and I am expecting that the audience reading this post has already certain degree of knowledge regarding this framework (SDL CWA 2011 Documentation).
    In a nutshell the three elements in a MVC scenario are the Model, the View and the Controller. Within the CWA scope those items are clearly identify:
    1. Model: the Storage Layer and the Content Data Store database. When I said “kind-of” MVC framework is because the content can be published in different ways, since we can publish just the content (as XML for example) and its metadata, or we can also published the content together with its layout. In a real MVC architecture, the Model should just be content, published as is, without any “extra” layout-related information, so it is actually the Model. Anyways, Let’s say the Model would be our content available in the front-end ready for its consumption.
    2. View: The view, for those familiar with CWA, know that is handled by the CWA views, that are nothing but JSP pages.
    3. Controller: a Servlet. Going back to the “kind-of” MVC statement, there are some controller operations that happen in the JSP pages normally, but it can be extracted from JSP pages and delegate the controller responsibility to either more servlets, taglibs, etc..

    Said this, we need to assemble all these guys together.
    The views can be handled externally from the CMS but normally (in most of the cases) they are published from the CMS too, so you can leverage the CMS capabilities to enrich the View information as well as leverage all the other CMS functionality, like Content Access, Security, Personalization, etc…

    So in the following section I am trying to give you some tips on how to enhance your overall experience as an implementer when dealing with CWA Views.
    As I mentioned before this views are nothing but JSP pages, so to create pages in the CMS we normally need a page template to define, first the page extension and the content layout of your page, in this case we’ll be mostly adding just code to retrieve the content from the actual pages, since the view is just that a view, reused on different pages associated with it.
    In SDL Tridion (since 5.3) the (typical) way to create a Page Template is by using a bunch of TBBs (Template Building Blocks) together. One of those is normally a DWT (Dreamweaver) TBB that takes care of the final output of the page (actual characters/code written to the file when publishing).

    Since the views are JSP pages, we are mainly writing Java code in them and we normally used our preferred IDE to do so, in this case Eclipse.
    Now the problems  this post is trying to make less painful are:
    1.  Editing JSP Code directly from Eclipse
    2. Leverage Eclipse capabilities to write that code (intellisense)



    Configuring Eclipse to edit your DWT Views

    To enable this, you will need Webdav configured properly on your Tridion server. We are going to leverage the functionality available on the O.S. that allows to create a folder pointing to a webdav url and browse it as if it was a regular folder in the file system. This functionality is available in Windows Server 2008 and Windows Vista/ Windows 7.

    First of all we need a J2EE Web project in Eclipse. Follow this post to get your J2EE web project up and running on Eclipse:
    After following this post you should end up with something like this in your Eclipse:
    Once you have this configured, let’s prepare Eclipse to browse Tridion DWT templates. The first thing I recommend is to create a NTFS Symbolic Link that points to the network location you just created but actually to the Folder where you have the TBBs you want to edit from eclipse, so you have a “filtered” view of the webdav network location (and you can potentially add file system permissions to allow other developers to access this location). First of all we need to define a location for our DWT views. In this example I will create a folder within WebContent/SDL Tridion/CWA Views/
    Now we are going to create the Symbolic Link and add it to our project:

    Configure windows to browse the CMS through Webdav



    You should end up with something like:
    Browse the location where you want to create the symbolic link using the command line

    Create the Symbolic Link  ( mklink [[/D] | [/H] | [/J]] linkName target)

    Refresh your Eclipse folder structure
    This is the view as shown in the CMS:


    Now we need to tell eclipse what a DWT file extension is:

    Within the Eclipse menu we browse Windowà Preferences and filter the results to find the File Association option, and we are going to add the .dwt extension:
    Then we should see that the “default” editors are associated with the extension:
    We are done!
    Now we can use the JSP Editor to edit our DWT (JSP Views) from eclipse and save them directly on the CMS!!!
    As You can see I have a Default View.dwt template, that’s the actual content of your view.

    Well, now let’s give it a though, what did we exactly do?
    Basically we told eclipse where to grab the dwt templates from and then tell it that the content within that template (file) should be treated as JSP content.
    Knowing and understanding this will allow us to apply the same principles for other template types, for example, if we are using old-school templates (vbScript or jScript) to produce JSP pages, we can follow the same steps and tell Eclipse that files with tbbs extension should be treated as JSP pages too:
    Even if you have your own mediator and your own template type that also generates JSP content with a different file extension, you can configure Eclipse in the same way, too.
    Another feature here is that now, we can browse pretty much any item from eclipse using webdav, so we can potentially use Eclipse to edit all well know extensions, like xml, xsl, etc..

    I hope you enjoy these tips and I actually achieveD to make you more productive J

    1

    View comments

  10. I just learnt that it is possible to post to your blog (blogspot) using your email!
    Check this link out:


    Note: The title of the post will be the same as the subject... You can also save as draft or publish directly from your email too...
    I think it is cool!
    
    


    <IN HERE YOU WOULD SEE YOUR EMAIL SIGNATURE>
    2

    View comments

Stack Exchange Q&A site proposal: Tridion
Stack Exchange Q&A site proposal: Tridion
Stack Exchange Q&A site proposal: Tridion
Stack Exchange Q&A: Tridion
Labels & Keywords
Blog Archive
Downloads
About Me
Loading
By Jaime Santos Alcón. Dynamic Views theme. Powered by Blogger. Report Abuse.