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!