While entering into the world of GUI Extension programming I have found it is another world where we need new tools and approaches to supplement our classic web development view.  For me it was an entirely different way of working, something I am still getting used to.  Today I discovered 2 things that I hope will help you get more comfortable in the new Tridion 2011 GUI Extension world.

Debugging JavaScript in Tridion 2011 GUI
Debugging has always been an art and the art is elevated with Tridion GUI Extensions, where everything starts with JavaScript.  I prefer to use Chrome for Tridion 2011 – it has the fastest JavaScript engine at the moment and this gives the fastest Tridion performance.  The developer tools included in Chrome are very good and using them is the key to debugging your new GUI Extension JavaScript.  I am using Tridion 2011 SP1.

1.  Open the Tridion 2011 GUI in Google Chrome browser
2.  Open Developer Tools (F12)
3.  Scripts tab
4.  In the file listbox, choose ‘Dashboard_v6.1.0.55920.10_aspx?mode=js’ located in WebUI/Editors/CME/Views/Dashboard/
5.  Use the search box (top-right of Dev Tools) to find some text from your _execute method in the js GUI Extension code.  For me it starts at around line 85702.
6.  Set a breakpoint by right-clicking the line # and add breakpoint
7.  Refresh the GUI, hit the button for your extension, and the breakpoint will be highlighted in the bottom debug window.
8.  On the right side of the debug window are the Watch Expressions, Call Stack, Scope Variables, etc.  I prefer to minimize Call Stack and expand Scope Variables.
9.  Step through the code by using the down-arrow on the right side.

Debugging Breaking Javascript Errors
Let’s say you forget to end a line with a ;.  That is bad js and will not compile.  If your JavaScript has an error then the Tridion GUI will be blank.  See below.  Tridion tries to load the js in your GUI Extension when it loads the browser, and as we can see from above it ends up in the main JavaScript of the GUI itself.  If you have breaking code (or even just write ‘something’ text in your js) it will break the GUI.  Good JavaScript programming practices is a must here!

Tridion GUI Extension js error

Solution
Have no fear, the Chrome debugger is here! Go to Console, see the red X with the error, click on the link on the right side with the Dashboard filename.  Your breaking error is shown.

Getting a popup URL
OK – now for a programming tip.  Once you get into the _execute statement then you might want to show a popup to get input from the user before performing actions.  This example uses the excellent HellowWorldCM tutorial from Jaime.  Here is 1 way to show a popup:

Extensions.HW.prototype._execute = function HW$_execute(selection, pipeline) {
var host = window.location.protocol + "//" + window.location.host;
var url = host + '/WebUI/Editors/HelloWorldCM/client/html/popup.htm#popup=UID_355';
var popup = $popup.create(url, "toolbar=no,width=600px,height=300px,resizable=false,scrollbars=false", null);
popup.open();

Also, if you are using Jaime’s example, comment out the message center code because in Tridion 2011 SP1 there are changes in the message center that are not compatible with this code.

//        var msg = $messages.createMessage("Tridion.Cme.Model.InfoMessage", "HELLO WORLD EXTENSIONS", ...
...
//        $messages.registerMessage(msg);

Setting up the IIS Virtual Directory
If you have any issues with the IIS Virtual Directory you will see the Tridion Minimalist view of the GUI.  This is the new cool thing – not very functional, but that is not the point.  😉

Update:  You will also see this view if any file paths in the  <cfg:fileset> of your GUI Extension cannot be found!

Tridion GUI Extension IIS VDIR Error

Likely the IIS Virtual Directory is not there or the name of the Virtual Directory does not match the name of the VDIR paramatere in the Extension config inside the system.config file.  It looks like:

<vdir>HelloWorldCM</vdir>

.  Get this in sync and you can again enjoy the full splendor of Tridion 2011.

Happy debugging!

Tridion provides excellent versioning on many items, including Components, Pages, Keywords and Templates. This weeks bUtil is bRollback, giving the ability to rollback an item and its’ Blueprint children to the previous version. This is very handy when you update Components via an external script and then realize there was a mistake and want to rollback that Component. The script operates on a Component, but can easily be modified to work on a Folder. Also, sometimes the editor updates 1 item and all of its’ localized copies but then needs to rollback 1 version because the business has changed their minds or the deadline is postponed. This might also happen with translations if there was a mistake in the original source content going out and only notice it later and need to rollback 1 version.

The Tridion GUI allows us to rollback versioned items using the context menu, select Versioning, History and all the previous versions of the item appear. When we rollback to a previous version and have 2 options – first to select to make a copy of the previous version and save it as the new version (safest) or to go back to that version and remove all versions after it (losing all changes after the rollback-to version). We can also compare our current version to a previous version from this screen.  This takes about 2 minutes per item – with 30 localized items it would take around 1 hour to rollback all of them to the previous version.

The Tridion API provides an easy way for us to access the versions of an items and to rollback to a previous version. I created an ASP Custom Page to do just this – rollback an item, and all of its’ localized items, to the previsou version, or in other words, current version – 1. There is also a ModifiedDate checkbox that will only show the last modified date and not rollback anytrhing. My assumption is that most of the time you use this tool the items are updated via a script or an author AND around the same time. I would not want to run the rollback tool if some items were updated much more recently than what I expected.

The script itself is a take on my 2 previous scripts, BCopy and BDelete. These are Blueprint-aware Custom Pages where the action starts on the Blueprint parent and also takes effect on the Blueprint child. Currently the only action in the GUI that provides this type of functionality is the Publish action, where we can choose to publish child items.

The Code to rollback an item in Tridion 2009 and before, with TOM API is:

Item.Rollback(toVersion, deleteVersions)

toVersion – Version# to rollback to
deleteVersion – Create copy of old version (safe) or Go back to old version and delete newer versions (unsafe))

The script uses the safe option creates a new version.

The Tridion 2011+ Core Service code is:

client.Rollback(uri, false, “rollback from script”, new ReadOptions());

 

The last modified date is found with:
Item.Info.RevisionDate

View the source on GitHub at https://github.com/rcurlette/TridionBRollback

The Blueprinting feature of Tridion is really great and keeps all of our items ‘connected’ to the parent – and won’t let us delete an item if it has localized children. This is a problem when we want to clean up our system and remove old items – or if you’ve been using bCopy and accidentally copied an item you didn’t want to.   

BDelete is the inverse of BCopy – UnLocalizing all children and then after this deleting the parent.  This script does this for Components, Pages, Structure Groups, or Folders.

Scenarios – When you need to do pre-cleanup work

Delete Page:  Pages need to be unpublished first.  The tool does not unpublish pages.
Delete Component:  Components need to be removed from Pages, removed from Component Link fields in other Components, and unpublished.  The tool does not do this and it will fail.
Delete Folder:  Will work every time, no dependencies.
Delete Structure Group:  Will work every time, no dependencies.

Code

Code is on GitHub at https://github.com/rcurlette/TridionBDelete/blob/master/bDelete.asp

Tridion provides great Blueprint functionality allowing us to share and localize content across many locales and Publications.  Sometimes we want to make a copy of an item – the whole item with all of its’ Blueprint-children, and then maybe change some metadata with the new item and nothing else. However, the copy button in Tridion only copies the current item –  and not its’ associated Blueprint-children.

Tridion BCopy – When A copy is not enough

Tridion BCopy copies the item (Component, Page, Structure Group, or Folder) and all of its’ Blueprint-children, including associated metadata, etc.  When copying a Folder or Structure Group it does not copy the contents of it – only the item itself.

Solution

Copy item with Blueprint children
The code is written in classic ASP – yes, I hear you moaning or cheering, but rest assured this approach works on all versions of Tridion up to Tridion 2011 and is quick to deploy and start using right away.  I tested the code on Tridion version 5.3 and it works great.

Getting Localized Items

The most interesting part of the code is getting the localized items.  This approach uses the GetListUsingItems and a special RowFilter condition.  You might want to use the same code to process localized items for updates, etc, and I have used it many times.

Function GetLocalizedItemNodes(itemUri)
    Dim tridionItem : set tridionItem = tdse.GetObject(itemUri,1)
    Dim rowFilter : set rowFilter = tdse.CreateListRowFilter()
    call rowFilter.SetCondition("ItemType", GetItemType(itemUri))
    call rowFilter.SetCondition("InclLocalCopies", true)
    Dim usingItemsXml : usingItemsXml = tridionItem.Info.GetListUsingItems(1919, rowFilter)
    Dim domDoc : set domDoc = GetNewDOMDocument()
    domDoc.LoadXml(usingItemsXml)
    Dim nodeList : set nodeList = domDoc.SelectNodes("/tcm:ListUsingItems/tcm:Item[@CommentToken='LocalCopy']")
    set tridionItem = nothing
    set domDoc = nothing
    set GetLocalizedItemNodes = nodeList
End Function

Updating content

We use the GetXML method to get the contents of the item.

GetLocalizedXml = localizedItem.GetXml(1919)

When updating, we use the UpdateXml method of an item to save the original contents to the copy. This is the best and fastest way to update Tridion content.

newItem.UpdateXml(xml)

Layout

I use Twitter Bootstrap version 1.0 for all custom pages.  If you have not checked this out – you need to.  No need to write your own CSS for custom pages anymore!  Use the Bootstrap HTML / CSS and have nice looking custom pages in no time.

Installing

Get the code
View the full code on GitHub at TridionBCopy or download the zip.

Upload to server
Copy to the Tridion/Web/CustomPages folder on your server or wherever you put your asp custom pages.

Use it
Create a link to it in the Custom Pages tree menu and save time with it!

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!

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.