Tridion 2011 provides some obvious benefits and many hidden benefits for upgrading from 2009 to 2011.  Overall it feels faster and for a very good reason-  Tridion R&D has upgraded every architecture piece in the puzzle – taking us from 2008 to 2011 in 1 upgrade. For example, we now have .NET 4.0, WCF, OData, NHibernate, Logback, Java 6, and Razor.

1.  World Server connector – If your organization uses SDL World Server for translations then the new SDL Tridion connector is for you.  Simply send the item for translation and it gets translated and automatically updated back in Tridion, triggering a possible workflow, and removing 90% of the effort of translations.  It works similar to previous SDL connectors such as SVN or Perforce.

2.  Faster and Cross-platform GUI – Better tree controls, faster GUI, and a thousand other small GUI improvements that make using Tridion 2011 a much better experience.  Use Chrome, Firefox, Safari, or IE to access the GUI.  The faster the js engine, the faster Tridion will respond.  Also supports Mac!

3.  GUI Extensions – Easier to extend and customize the GUI to improve the Editors  use of Tridion.  Create new tabs on a Component Edit screen, add a new column to the main list view, add a button to the toolbar or even a whole new tab in the toolbar.  Anything is possible in the new Anguilla GUI framework – and it is all supported by SDL Tridion.  Many blog posts are written about creating a GUI extension.

4.  .NET 4.0 – TOM.NET is Read/Write from native .NET.  This means our code is faster, but more importantly, we can use language features from .NET 4.0 to simplify our code and make it more maintainable

5.  Razor Template Mediator – Not available in 2009 because it uses Microsoft’s Razor engine, only available with .NET 4.0. Download the Razor Mediator and experience the power of .NET with the simplicity of VBScript.  A major advantage when upgrading VBScript templates.

6.  Re-invented Event System – The Event System is rewritten from ground up.  Now it is possible to fire events asynchronously, during 5 phases (instead of 2), and be written in .NET 4.0.  Overall a huge improvement.  See my article about creating a first 2011 Event System.

7.  Solr Search Engine – Verity is out and Solr is in. Solr is an open-source, http-based, faceted search engine from the Apache project that is much faster and handles large indexes much better. XML.com has an intro as well as IBM having a tutorial.

8.  New Business Connector (aka Core Service) – A WCF 3.5 Web Service called the Core Service can be used to access Tridion objects from any SOAP client.  An excellent article by Bart Koopman provides some insight into the new architecture.

9.  OData Web Service – Content delivery gets a new OData web service that can be used by external data consumers for querying the Broker database.  Ryan Durkin has a nice post walking through creating an OData client.

10.  Better multimedia publishing – Choose to store multimedia files in different storage places.  Julian has a great article explaining how to do this.

Do you have another benefit for upgrading?

Background

Tridion 2011 provides us with a WCF 3.5 Web Service named the “Core Service”. Previous versions shipped with the Business Connector, a SOAP-enabled webservice that accepts XML to perform actions in the GUI.  However, if we want a simple RESTful web service to call from jQuery or .NET in any version of Tridion we need to roll it ourselves.  In this article I want to demonstrate how to create a web service using the ServiceStack framework, jQuery, and Tridion.

Of course, I have to admit that this is the first step in my ultimate goal of having a GUI-based content porter – a ‘smart porter’ if you will, that will send a POST request to another CMS with Component XML and create the new item, but if the linked items are not available, it will call back to the original CMS, get the linked items, and continue. The example here proves it is not that difficult and with jsonp we can make cross-domain calls to various CMS servers – something that was not available at the time when Content Porter was created.

A better web service

In my current project we have 1 Dev server, multiple test servers, 1 Pre-Production and 1 Production server. All needing good content – specifically web application content in the form of string key/value pairs.  For new strings we would like our Production Content editors to create the content once, and only create it from 1 custom page in Production for all systems. I want to create a jQuery friendly web service that could give me the ability to talk to the Tridion API on multiple CMS servers from 1 custom page on the Production CMS.

Benefits of this approach:

  • Separate UI from server side Tridion API calls
  • Easy to upgrade custom page in future by only changing Tridion API code, no GUI changes needed
  • Follows similar approach as the Tridion 2011 Powertools
  • Could be easily integrated in Tridion 2011 as a GUI extension
  • Web Service can also be called from the Event System instead of a custom page

Behind the scenes we use jQuery jsonp to do a post of the form contents to each CMS. Also, we don’t want to have to content port each new string resource to all environments, since this incurs an extra manual task.

Enter ServiceStack

After searching for a simple and clean way to setup a web service in .Net, reading numerous WCF tutorials and not feeling convinced it was what I needed, I was relieved to find ServiceStack.  It is a great alternative to a WCF Web Service.

Advantages of ServiceStack:

  • Excellent examples
  • No config
  • Active project, great Google groups
  • Native json support
  • Host the web service in MVC, Windows Service, or Console app
  • Easy to get started with NuGet MVC package
  • Lean and clean

Creating a web service for Tridion using ServiceStack

I am working on Tridion version 5.3 and using Visual Studio 2010 SP1 ASP.NET MVC 3, but this code works on all Tridion versions.  In a future article I will explain how we can solve the same scenario using the CoreService of 2011.

1.  Getting ServiceStack installed

Create a new MVC 3 Web app.  Use Web Platform Installer (WebPI) if you do not have it.  It is also possible to host a ServiceStack web service with a Console app and also a Windows Service.

NuGet

NuGet is a package manager for Visual Studio and is awesome.  It saves about 10-15 minutes per external assembly you might want to use.  It is also the quickest way to get started with ServiceStack.

Get ServiceStack MVC Example

Right-click the solution, choose Add Library Reference, Select ServiceStack.Host.Mvc.  If NuGet complains that your version of NuGet is incompatible, follow this StackOverflow post (http://stackoverflow.com/questions/6496640/nuget-upgrade-issue).  For me on Windows 7 with a fresh install of VS 2010 SP1 I needed to open Visual Studio with ‘Run as Admin’, go to Tools, Extensions, Uninstall NuGet, then install it from the download.  Good news is it installs in 2 seconds…

Re-open Visual Studio and right-click on the solution and select Manage NuGet Packages, select ‘Online’ in the left, then search for ServiceStack, and select ServiceStack.Host.Mvc.  It will add references and a lot of items in the project.

Running the example code

1. Update global.asax with the ignore for /api route and the favicon.ico file.

routes.IgnoreRoute("api/{*pathInfo}");
routes.IgnoreRoute("favicon.ico");

2. Hit F5 and run the app. Run the project and see the Demo ToDo app. You can put a breakpoint in the Post method of the ToDoService and when you submit a ToDo you will step into the Post method.  The ToDo App GUI is built using 100% Javascript using the BackboneJS framework and I will not cover it here. BackboneJS is an event-based JS framework with a steep learning curve.  If you’d like to know more then head over to Tekpub.com and view their backbone.js screencast.

3. App_Start/ServiceStackFramework.cs – All the magic is here.  In fact, EVERYTHING in the example is here, the model, services, etc.  I like to split these into separate .cs files to make it more clear, and we’ll do that later in the code. For now, notice the DTO class contains the properties we are serializing back to the web form.

4.  Structure – I prefer to keep the classes in separate folders based on their intent.  I will use the following structure for the rest of this example:

    • Models
    • Repositories
    • Services

The ServiceStackFramework.cs now only contains the very important URL Web Service routes, IoC injection, and service settings.  Let’s leave it like that, add the using statements to our new .cs files for ServiceStack examples, and re-compile.  Note, so far we have not changed and config files – and the good news is that we will not have to!  ServiceStack is a no-config happy solution.  :)

Creating the first Tridion Web Service:

Step 1:  Creating the POCO (Plain Old CLR Object)

The first step is our model – the object that we will pass back and forth from the webpage to the web service.  In this example we will create a KeyValuePair class.  Right-click on the Models folder, select Add Class, and name the item ‘KeyValuePairComponent.cs’.  Add the following properties to the class:

public class KeyValuePairComponent
{
public string Uri { get; set; }
public string Key { get; set; }
public string Value { get; set; }
public string ParentFolderUri { get; set; }
}

Step 2.  Create the service class

Right-click the Services folder, select add class, and add KeyValuePairService.cs.  The class needs to implement the RestServiceBase to be able to respond to service requests and the Type is our POCO created earlier.  This is very important as it is the ‘glue’ to connect our service to our model.  No config needed!

It is a good idea to follow the repository pattern and put our code in the Repository class we will create next to perform the actual work we are going to do instead of putting the code here in the service class. Note that the code will not compile yet since we do not have our Repository object. We will not implement the Delete action and for now I return null.

public class KeyValuePairService : RestServiceBase<KeyValuePairComponent>
{
    public KeyValuePairRepository Repository { get; set; }  //Injected by IOC

    public override object OnGet(KeyValuePairComponent keyValuePair)
    {
        return Repository.GetByUri(keyValuePair.Uri);
    }

    public override object OnPost(KeyValuePairComponent keyValuePair)
    {
        return Repository.Store(keyValuePair);
    }

    public override object OnPut(KeyValuePairComponent keyValuePair)
    {
        return Repository.Store(keyValuePair);
    }

    public override object OnDelete(KeyValuePairComponent keyValuePair)
    {
        //Repository.DeleteById(request.keyValuePair);
        return null;
    }
}

Step 3. Create the Repository

Add a new class to the Repositories folder and call it ‘KeyValuePairRepository.cs’.

We need to add References to the Tridion DLLs, Tridion.ContentManager.Interop.cm_defines.dll and Tridion.ContentManager.Interop.cm_tom.dll, located in Tridion CMS server at /Tridion/Bin/Client/PIA folder. Logging is needed – so I will use NuGet to grab the log4net package and then updating my web.config with the log4net config. We also need to update the Global.asax with the log4net configuration call.

// add to Application_Start()
log4net.Config.XmlConfigurator.Configure()

Our new Repository class calling the Tridion API looks like this:

 public class KeyValuePairRepository
    {
        string impersonationUser = "tst-vega28-cms\\curlettr";
        private static readonly ILog log = LogManager.GetLogger("KeyValuePairRepository");

        public KeyValuePairComponent GetByUri(string uri)
        {
            TDSE tdse = null;
            Component component = null;
            KeyValuePairComponent keyValuePair = new KeyValuePairComponent() { Uri = uri };

            try
            {
                tdse = new TDSE();
                tdse.Impersonate(impersonationUser);
                tdse.Initialize();
                component = tdse.GetObject(keyValuePair.Uri, EnumOpenMode.OpenModeView);
                keyValuePair.Key = component.Fields["key"].value[1];
                keyValuePair.Value = component.Fields["value"].value[1];
                return keyValuePair;
            }
            catch (Exception ex)
            {
                log.ErrorFormat("Error getting Component.  " + keyValuePair.ToString() + " Error:" + ex.Message + ", " + ex.ToString());
                throw;
            }
            finally
            {
                if (component != null)
                    Marshal.ReleaseComObject(component);
                if (tdse != null)
                    Marshal.ReleaseComObject(tdse);
            }

	        return keyValuePair;
        }

        public KeyValuePairComponent Store(KeyValuePairComponent keyValuePair)
        {
            log.Debug("Start KeyValuePairComponent Store");
            if (keyValuePair.ParentFolderUri == null)
                throw new Exception("Must specify a parent folder");
            if (keyValuePair.Key == null)
                throw new Exception("Must specify a Key");

            TDSE tdse = null;
            Schema schema = null;
            Component component = null;
            string schemaWebDavUrl = "/webdav/30%20Content%20-%20Global/Building%20Blocks/System/Schemas/Generic%20Schemas/S_String.xsd";
            try
            {
                tdse = new TDSE();
                tdse.Impersonate(impersonationUser);
                tdse.Initialize();
                schema = tdse.GetObject(schemaWebDavUrl, EnumOpenMode.OpenModeView);
                component = tdse.GetNewObject(ItemType.ItemTypeComponent, keyValuePair.ParentFolderUri);
                component.Schema = schema;
                component.Title = keyValuePair.Key;
                component.Fields["key"].value[1] = keyValuePair.Key;
                component.Fields["value"].value[1] = keyValuePair.Value;
                component.Save(true);
                keyValuePair.Uri = component.ID;
            }
            catch (Exception ex)
            {
                log.ErrorFormat("Error creating Component.  " + keyValuePair.ToString() + " Error:" + ex.Message + ", " + ex.ToString());
                throw;
            }
            finally
            {
                if (component != null)
                    Marshal.ReleaseComObject(component);
                if (schema != null)
                    Marshal.ReleaseComObject(schema);
                if (tdse != null)
                    Marshal.ReleaseComObject(tdse);
            }
            return keyValuePair;
        }

Step 4.  Setting the WebService Endpoint URI

We need to specify the URL to access our new web service.  The magic happens in the ServiceStackFramework.cs class.  Look for the comment ‘//Configure User Defined REST Paths’ and add our new Service to the list of Routes, and in the end my Routes looks like this:

Routes
.Add<Hello>("/hello")
.Add<Hello>("/hello/{Name*}")
.Add<Todo>("/todos")
.Add<Todo>("/todos/{Id}")
.Add<KeyValuePair>("/tridion/component/keyvaluepair");

Now, our Post and Get methods in our new Service class are wired up.  Notice the /api/ is not in the beginning of our route, but we will see later we must use it when calling our service.

Step 5.  Wiring up the Repository to the IoC Container

The last step we need to do is to tell our Funq IoC container about our new Repository class.  We have to do this every time we have a new Model object. In the same file as above, ServiceStackFramework.cs, add our new Repository to the container:

//Register all your dependencies
container.Register(new TodoRepository());
container.Register(new KeyValuePairRepository());

Add code to the ServiceStackFramework.cs class to allow cross-domain posts

// Allow cross-domain posts
SetConfig(new EndpointHostConfig
{
    GlobalResponseHeaders =
    {
        { "Access-Control-Allow-Origin", "*" }, // You probably want to restrict this to a specific origin
        { "Access-Control-Allow-Methods", "PUT, GET, POST, DELETE, OPTIONS" },
        { "Access-Control-Allow-Headers", "Content-Type" }
    },
});

Step 6.  Test the Add method

Add a new HTML page to the Solution.  Right-click the Project, select New Item, Web type, HTML Page.  Name it AddKeyValuePair.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Add KeyValuePair</title>
</head>
<body>
    <form method="post" action="http://TridionDev2011:8001/WebService4Tridion/api/tridion/component/keyvaluepair">
        <label>Key:</label><input type="text" name="Key" id="Key" />
        <label>Value:</label><input type="text" name="Value" id="Value" />
        <label>Create in folder:</label><input type="text" name="ParentFolderUri" id="ParentFolderUri" />
        <input type="submit" value="Save" />
    </form>
</body>
</html>

The important part is the form tag – notice the method is ‘post’ and the action starts with ‘api’.  This is very important as it tells MVC not to process the request with normal MVC routing.  Remember the first step of adding the ignores to the api path in Global.asax?  This is a very easy to miss and important to get right, otherwise you will get errors.  Each text field uses the name attribute the specify the Class property it maps the form to in the .NET class, and if not the same, the web service will have a value of null for the property.

Open the KeyValuePairService.cs file and place a breakpoint on the Post method:

return Repository.Store(keyValuePair);

Set the AddKeyValuePair.html page as the Start page by right-clicking on it and select ‘Set as Start Page’.  Run the solution by hitting F5.  Fill out the form, hit the save button, and you should hit the breakpoint.  Continue and you will get the default ServiceStack return page.  If you inspect the keyValuePair object you should see the properties populated.  Pretty cool, right?

Before we get into the Tridion API things, let’s test the Get method.  Create an HTML page like in the previous step, name it GetKeyValuePair.html, and paste the following HTML.

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Get KeyValuePair</title>
</head>
<body>
  <form method="get" action="/api/tridion/component/keyvaluepair">
    <label>URI:</label><input type="text" name="Uri" id="Uri" />
    <input type="submit" value="Get KeyValuePair" />
  </form>
</body>
</html>

Remember to set a breakpoint in the OnGet method of the KeyValuePairService.  Set it as the start page, hit F5, and the following breakpoint should be hit:

return Repository.GetByUri(keyValuePair.Uri);

Yeah!  We have successfully created our web service and tested that our new URI endpoints are working.

Step 7.  Test the Get Method – Let’s call our web service with jQuery instead of an HTML post / get.

Add the following code to our Get page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript" src="Scripts/jquery-1.5.1.js"></script>
    <script type="text/javascript">
        $(function () {
            function GetKeyValuePair(dataString) {
                $.ajax({
                    type: "GET",
                    url: "http://TridionDev2011:8001/WebService4Tridion/api/tridion/component/keyvaluepair",
                    dataType: "jsonp",
                    jsonp: true,
                    crossDomain: true,
                    data: dataString,
                    success: function (data) {
                        $('#KvpUri').text(data.uri);
                        $('#KvpKey').text(data.key);
                        $('#KvpValue').text(data.value);
                    },
                    error: function (xhr, ajaxOptions, thrownError) {
                        alert('status=' + xhr.status + ', err=' + thrownError);
                    }
                });
            }

            $('#btnGet').click(function (event) {
                var dataString = $('#frmKeyValuePair').serialize();
                GetKeyValuePair(dataString);
            });
        });
    </script>
    <title>Get KeyValuePair</title>

</head>
<body>
    <form method="get" id="frmKeyValuePair">
        <label>URI:</label><input type="text" name="Uri" id="Uri" />
        <input id="btnGet" type="button" value="Get KeyValuePair" />
    </form>
    URI:<span id="KvpUri"></span><br />
    Key:<span id="KvpKey"></span><br />
    Value:<span id="KvpValue"></span><br />
</body>
</html>

Notice that the url is the same as from our form tag before.  Also, the ‘Uri’ property is all lowercase because ServiceStack removes CamelCase from the properites, specified in the ServiceStackFramweork.cs class.  ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;

Step 8. Create new Component with AJAX Post

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Add KeyValuePair</title>
     <script type="text/javascript" src="Scripts/jquery-1.5.1.js"></script>
     <script type="text/javascript">
        $(function () {
            function GetKeyValuePair(dataString) {

                $.ajax({
                    type: "POST",
                    url: "http://TridionDev2011:8001/WebService4Tridion/api/tridion/component/keyvaluepair",
                    dataType: "jsonp",
                    jsonp: true,
                    crossDomain: true,
                    data: dataString,
                    success: function (data) {
                        $('#KvpUri').text(data.uri);
                        $('#KvpKey').text(data.key);
                        $('#KvpValue').text(data.value);
                        $('#saveResults').show();
                    },
                    error: function (xhr, ajaxOptions, thrownError) {
                        alert('status=' + xhr.status + ', err=' + thrownError);
                    }
                });
            }

            $('#btnSave').click(function (event) {
                document.body.style.cursor = 'wait';
                var dataString = $('#frmKeyValuePair').serialize();
                GetKeyValuePair(dataString);
                document.body.style.cursor = 'default';
            });
        });
    </script>
</head>
<body>
    <form method="post" id="frmKeyValuePair" >
        <label>Key:</label><input type="text" name="Key" id="Key" />
        <label>Value:</label><input type="text" name="Value" id="Value" />
        <label>Create in folder:</label><input type="text" name="ParentFolderUri" id="ParentFolderUri" />
        <input id="btnSave" type="button" value="Save" />
    </form>

    <div id="saveResults" style="display:none">
        <b>Save results</b><br />
        URI:<span id="KvpUri"></span><br />
        Key:<span id="KvpKey"></span><br />
        Value:<span id="KvpValue"></span><br />
    </div>

</body>
</html>

Summary

It is a real pleasure working with ServiceStack and the Tridion API together. It feels right – little config, separation of concerns, future-proof, and full of possibilities. I hope this article gave you some ideas and you can go further by creating your own web services and calling them from a web form, .NET Event System class, or even a PowerShell script. Enjoy!

Download the code (WebService4Tridion-Example.zip, ~2MB)

Connect to Tridion with WebDAV

January 12th, 2012 | Posted by Robert Curlette in Tridion - (1 Comments)

Tridion provides great WebDAV functionality to browse and add items to Tridion. I use this to bulk upload images to Tridion. The unfortunate thing is the WebDAV protocol is very quirky and the client is implemented differently per OS.

One trick I use is to open the WebDAV url with Microsoft Office Word.

Steps:
1. Open Word
2. File, open
3. Insert webdav url. Mine is: http://tridcms.test.tomtom.com/webdav
4. Type your credentials and smile. You’re connected to the CMS via WebDAV.
5. I usually go back to Windows Explorer, copy a bunch of images, and then in Word paste them.

Enjoy!

Automate your tasks

January 12th, 2012 | Posted by Robert Curlette in Lifehacking - (1 Comments)

Automate it

My timesheet is one of the most important things to do every day but the last thing I think of. Last year I discovered the Windows Scheduler program and my timesheet has never been so up to date. I only noticed how effective it was when my password changed and the programs stopped opening automagically every day at the same time.

Scheduler opens the Excel sheet and puts it in front of my face at 4:55 pm every day. It takes me 5 seconds to put the hours in and save. Done.

How to do schedule Excel to open automatically on Windows XP:

1. Open Control Panel, Scheduled Tasks
2. Add Scheduled Task
3. In the Application list select Microsoft Excel. If it is not listed there, you can copy the shortcut from the Start/Programs menu
4. Select when to perform the task
5. Enter password. Note: if your password changes the tasks stop running and do not show you any errors or reminders. It usually takes me a few days before I realize it is not opening. Forgetting about it is specific to my timesheet, for some strange reason.
6. Checkbox for advanced properties. This is where we put the file we want to open.
7. In the run textbox, go to the end of the line, put a space, then add the path to your Excel file like this “C:\RC\Docs\Timesheet_Curlette_2012.xls”
8. Finish, put your password in again, and forget about it. Your computer is now going to do the most difficult part – starting the task!

Scheduling the browser:

I live in Amsterdam and there is a last minute ticket website that sells tickets at 50% off starting at 12:00 noon every day. The ticket office is around the corner from where I work, so if I see something, I can walk over and buy them right away.

1. Follow the steps above, in step 3 choose Firefox, Chrome, Safari, etc.
2. In step 7, after the link to the app in the run box, type the website address in quotes. My link to firefox looks like this: “C:\Documents and Settings\All Users\Application Data\Mozilla Firefox\firefox.exe” http://www.lastminuteticketshop.nl

Good luck with improving your daily tasks!

Tridion Event System 2011

November 29th, 2011 | Posted by Robert Curlette in Tridion - (10 Comments)

The Tridion 2011 upgrade brings a whole new set of possibilities for the Tridion Event System. Exciting improvements include Asynchronous events, multiple Event System DLL support, configuration via XML file, and a much more flexible architecture. Overall a great effort from Tridion and a much welcomed one. :)

Advantages to 2011 Event System

  • Native .Net architecture
  • Multiple Event systems per server
  • Asynchronous events
  • Uses delegates to subscribe to Events
  • Additional Event Phases (Abort, InDoubt, Processed)
  • Configured via XML file. Can easily see which .dlls will be called by the Event System

Event Phases

The Pre (Initiated) and Post (Processed) are still there, and are joined by 3 new Event Phases.

  • Initiated = Pre
  • Processed = Post
  • TransactionCommitted = Post
  • TransactionAborted
  • TransactionInDoubt

Sample Event System Project

Getting started with the new Tridion Event System in 2011 is easier and a complete different experience than with previous Event Systems. I will walk through setting up a new Event System for 2011 and implementing 1 sample event.

Let’s create a sample event system project in Tridion 2011 for the famous OnComponentSavePre event. For this demo I will use Visual Studio 2010, but you can also use VS 2008 or VS 2011. Following these examples it is easy to map an older Event System to then new Tridion 2011 model. We also gain a lot more flexibility, control, and predictability over the Events in the implementation.

Create a new Windows Class project from Visual Studio

– Target Framework can be .Net 4.0 or .Net 3.5

Add references and using statements

DLLs are located at \{install folder}\Tridion\bin\client
– Tridion.Common.dll
– Tridion.ContentManager
– Tridion.Logging
– Tridion.ContentManager.Publishing (if using Publish Events)

using System;
using Tridion.ContentManager.ContentManagement;
// Add Reference to 'Tridion.ContentManager', located @ Tridion\bin\client on CMS Server
using Tridion.ContentManager.Extensibility;
using Tridion.ContentManager.Extensibility.Events;
using Tridion.ContentManager.Publishing;
using Tridion.Logging;

Create the Class

Tridion calls all of our Event Systems configured in the Tridion.ContentManager.Config file. For this reason, we need to make sure the class attribute [TcmExtension("SomethingUnique")] is placed on our Event System class.

The class extends the TcmExtension class and this is needed on our class for Tridion to implement the methods.

namespace Tridion.EventSystem.v2011
{
    // The class attribute is unique per Event System dll on the server.
    // In 2011 we can have multiple Event System dlls on 1 server.
    [TcmExtension("Tridion2011EventSystemRC")]  // This needs to be unique per Event System
    public class EventSystem : TcmExtension
    {

Subscribe to Events

Place all of your subscribe calls in the class constructor or create a method as I do here called Subscribe and place the subscription methods there. Tridion only calls the methods in the Constructor when firing Events.

The .Subscribe method signatures are very special and needs all of the params to work and in this order. The online documentation describes the method signatures in more detail. See the documentation link at http://www.sdltridionworld.com.

public EventSystem()
{
	Subscribe();
}

public void Subscribe()
{
	// OnComponentSavePre
	EventSystem.Subscribe<Component, SaveEventArgs>(OnComponentSavePre, EventPhases.Initiated);
}

Component is the object we are subscribing to and SaveEventArgs is the Event we listen for. There are lots of EventArgs and it is best to use the Visual Studio object browser to see them all.

OnComponentSavePre is MY method and I could call it OnComponentDoThisBefore if I wanted to, but I use the original R5 names to keep it simple.

EventPhases.Initiated is the new -Pre and TransactionCommitted is the new -Post.

Implement Events

Create the methods to handle the event with the above method signature. The Params must match the signature of the Subscribe event. The order should be the same, (Object, Event, Phase).

private static void OnComponentSavePre(Component comp, SaveEventArgs args, EventPhases phase)
{
	comp.Title = "a " + comp.Title;
	args.ContextVariables.Add("StartTime", DateTime.Now);
}

Here I use Component for the Object and update the Title when saving. This is one way to test if the Event system is working. If I used a Post method I would need to throw an error to the Event Log or use a Logger to test if it works.

If I wanted to capture the Event for all items, I could subscribe to the IdentifyableObject and if I only wanted to catch the Events on VersionedITems (think Component, Page, Template, etc) then I could subscribe to a VersionedItem – both new in 2011.

Handling Errors

Any Pre- Errors show up in the new Tridion Message Center. This is really nice and much improved from previous error dialogs. The messages are saved and we can view them later. This implementation of errors is very handy when debugging the -Pre Events, now called ‘Initiated’.

The Tridion.Common assembly is needed for the TridionException event.

Installation / configuration

  • Copy the compiled DLL to the CMS Server
  • Open the Tridion.ContentManager.config file
  • Add the dll to the extensions:
    <add assemblyFileName=”C:\Program Files (x86)\Tridion\bin\Tridion.EventSystem.v2011.dll”/>
  • Shut down COM+
  • Restart the ‘Tridion Content Manager Service Host’ Service

Summary

I really enjoy working with the new Event System and think the Async support, multiple event systems, and .NET 4.0 integration will provide a stable foundation for the future. A big thanks to the Tridion R&D team for doing a great job with the new and improved Event System for Tridion 2011.

Full Code:

using System;
using Tridion.ContentManager.ContentManagement;
// Add Reference to 'Tridion.ContentManager', located @ Tridion\bin\client on CMS Server
using Tridion.ContentManager.Extensibility;
using Tridion.ContentManager.Extensibility.Events;

namespace Tridion.EventSystem.v2011
{
    [TcmExtension("Tridion2011EventSystemRC")]  // This needs to be unique per Event System
    public class EventSystem : TcmExtension
    {
        public EventSystem()
        {
            Subscribe();
        }

        public void Subscribe()
        {
            // OnComponentSavePre
            EventSystem.Subscribe<Component, SaveEventArgs>(OnComponentSavePre, EventPhases.Initiated);
        }

        private static void OnComponentSavePre(Component comp, SaveEventArgs args, EventPhases phase)
        {
            comp.Title = "a " + comp.Title;
            args.ContextVariables.Add("StartTime", DateTime.Now);
        }
    }
}

Tridion WebDav URL

November 9th, 2011 | Posted by Robert Curlette in Tridion - (0 Comments)

Tridion makes all items available using a WebDav URL. This is an environment independent variable that never changes. It is preferred to use this instead of an item URI when programming templates or scripts.

I recently needed a WebDavURL for a Folder and wrote the following snippet in a Component Template and then previewed with a Component in the desired folder:

[%
writeout component.organizationalitem.info.webdavurl
%]

This WebDavURL can be used anyplace you would normally use a URI.

The WebDavURL is also available on the info tab – however, on that screen you cannot copy it.

Getting ready for the upgrade

The Tridion 2009 upgrade is a low-risk upgrade that provides significant benefits for templating development, tagging content with keywords, and more efficient Broker queries. Anyone using Tridion 5.3 today can gain many benefits with little effort.

Step 1: Confirm server hardware requirements

Read the manual. Login to www.sdltridionworld.com and download the Tridion 2009 Upgrade guide.  Pay attention to the supported platforms information and confirm your servers meet the minimum requirements.

Most likely your support agreement includes upgrading to the latest version, including Tridion 2009

Step 2: Prepare workstations for developing using Visual Studio

Software: Visual Studio 2008 / 2010 – I prefer VS 2010 with features such as intellisense (matching a word anywhere the function contains it, not just at the beginning), jQuery debugging, and great Asp.net MVC support.  It also supports compiling to asp.net 2.0, 3.5, and 4.0.

Version control – I prefer Subversion but have heard great things about GIT.  SVN has a free plugin to Visual Studio called Ankh, allowing commits and updates from VS.  TortoiseSVN provides tight Subversion integration with Windows Explorer

Hardware – If running Visual Studio 2010 then 2GB Ram is essential.  For Event System development and workflow I suggest to run a local instance of Tridion inside a VMWare image.  This works really well on Macs too!  Otherwise, it might be possible to remote into a development server, but sharing connections is less than ideal in a development team, and installing workstation tools on a server is not ideal.

Questions and Answers

Templates- Will my existing .NET, VBScript, and XSLT templates work?  Yes,   Tridion is notoriously good about supporting legacy methods (there are still some version 4.4 methods in the API!).  No worries here.

Compound Templates- Do I need to re-write my templates?  No, all 5.3 templates will work, and you can use the new Compound Templating features side-by-side with the older VBScript templates.

Template Functions:  What are template functions?  Tridion 2009 provides the ability to write custom functions in .NET and call them from your HTML DWT templates.  This brings a lot of possibilities to DWT templates.

Broker- Will my Broker queries still run?  The answer is yes, and they may even be improved due to refactoring of the SQL generated to the Broker.  

SiteEdit- Which version of SiteEdit does Tridion 2009 work with? Do I need to upgrade to SiteEdit 2009?

Tridion 2009 works with SiteEdit 1.3 as well as SiteEdit 2009.  Please make sure to get all the hotfixes for SiteEdit 1.3.  However, SiteEdit 2009 SP2 provides great customization features, including adding items to the toolbar.  It also works with both VBScript templates and Compound Templates.  New browser support is added and editing pages in Chrome and Firefox never felt better.  SiteEdit 2009 SP2 is definitely worth the upgrade.

GUI- Will my Tridion users be OK with the new GUI?  Yes, the GUI now offers more options for keywords, but otherwise the other features work as they do in 5.3.  

Outbound Email- Is Outbound Email compatible?  Yes, and there is a new version of Outbound Email, Outbound Email 2009, that provides better email campaign and contacts management and continues to promote content re-use for all mailings.

Part 2 will cover upgrading the CMS, Database, and Broker. Part 3 will cover the first steps in working with Compound Templating.

Book: Design of Design

June 2nd, 2010 | Posted by Robert Curlette in Book review - (0 Comments)

Fred Brooks of Mythical Man Month fame is back and brings all of his insights together in one book.  The short chapters in the book are refreshing and bring pointed insights gained from years of experience as both a software architect and a traditional building architect.  Going into challenges such as remote teams and communicating the book approaches current issues facing many organizations.

I really enjoyed his discussion of using prototypes and iterations to achieve software success.  He is a ‘dyed-wool empiricist’ meaning that in his view all people will make mistakes and it is impossible to write a perfect software program that addresses all user requirements and without bugs the first time.  This is a refreshing view and relieves the pressure of the waterfall model where there are no iterations or going back to previous steps for refactoring the solution based on new information gathered while building the software.  He mentions that in other fields the designer or architect often reworks the solution to take advantage of a constraint that has disappeared during the project.  Often in the waterfall model there is no time to do this because the project team is living inside a time box where everyone is rushing to finish the deliverable to hand the ‘final’ version to the customer.

I am looking forward to incorporating more prototypes and iterative approaches in my next projects.  Hopefully the customer will be happy and have the right expectations set before seeing the final product.