Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / weblog

Dominic Cronin's weblog

Showing blog entries tagged as: SDL Tridion

Republish from publish queue for Tridion 2011

Posted by Dominic Cronin at Mar 21, 2011 06:20 PM |
Filed under: ,

Many of you will remember that absolutely the most popular Tridion extension ever :-) was my Republish from publish queue extension. It has just come to my attention that Bart Koopman has implemented pretty much the same thing for Tridion 2011.

First of all - it's great to see this old chestnut get a new lease of life. Thanks Bart.

Just in case anyone thinks Bart nicked my idea - well he didn't. We were both inspired by Hendrik Tredoux's idea which he posted on the ideas site way back. (Actually the "most voted" idea on the site to this day.) The irony of it is that although Hendrik was the one who posted the idea - he could probably have implemented it in his sleep, and the people of whom that is true would make a very short list indeed. Hopefully, with the great extensibility API in 2011, the list will be much longer.

But back to the main theme. That's the great thing with a software community; we're all throwing ideas around and bouncing off each other. I love it. The really good part is that right now I'm just a tad too busy to re-implement this extension for 2011 (it was on my to-do list - honest!) and now I don't have to. I suppose I should at least find the time to download Bart's extension and kick the tyres. :-)

Using powershell to do useful things with XML lists from Tridion

Posted by Dominic Cronin at Dec 30, 2010 08:55 PM |
Filed under: , ,

For a while now I've been trying to persuade anyone that would listen that Windows Powershell is great for interacting with the Tridion object model (TOM). What I mean by this is that you can easily use the powershell to instantiate and use COM objects, and more specifically, TOM objects. In this post, I'm going to take it a bit further, and show how you can use the powershell's XML processing facilities to easily process the lists that are available from the TOM as XML Documents. The example I'm going to use is a script to forcibly finish all the workflow process instances in a Tridion CMS. (This is quite useful if you are doing workflow development, as you can't upload a new version of a workflow while there are process instances that still need to be finished.)

Although useful, the example itself isn't so important. I'm simply using it to demonstrate how to process lists. Tridion offers several API calls that will return a list, and in general, the XML looks very similar. I'm going to aim to finish all my process instances as a "one-liner", although I'm immediately going to cheat by setting up the necessary utility objects as shell variables:

> $tdse = new-object -com TDS.TDSE
> $wfe = $tdse.GetWFE()

As you can see, I'm using the new-object cmdlet to get a TDSE object, specifying that it is a COM object (by default new-object assumes you want a .NET object). Then I'm using $tdse to get the WFE object which offers methods that list workflow items. With these two variables in place, I can attempt my one liner. Here goes:

> ([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances.Item | % {$tdse.GetObject($_.ID,2)} | % {$_.FinishProcess()}

Well, suffice it to say that this works, and once you've run it (assuming you are an admin), you won't have any process instances, but perhaps we need to break it down a bit....

If you start off with just $wfe.GetListProcessInstances(), the powershell will invoke the method for you, and return the XML as a string, which is what GetListProcessInstances returns. Just like this:

> $wfe.GetListProcessInstances()
<?xml version="1.0"?>
<tcm:ListWFProcessInstances xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/x
link"><tcm:Item ID="tcm:24-269-131076" PublicationTitle="300 Global Content (NL)" TCMItem="tcm:24-363" Title="Test 1" T
CMItemType="16" ProcessDefinitionTitle="Application Content Approval" ApprovalStatus="Unapproved" ActivityDefinitionTyp
e="1" WorkItem="tcm:24-537-131200" CreationDate="2010-12-30T19:35:33" State="Started" Icon="T16L1P0" Allow="41955328" D
eny="16777216"/><tcm:Item ID="tcm:24-270-131076" PublicationTitle="300 Global Content (NL)" TCMItem="tcm:24-570" Title=
"Test 2" TCMItemType="16" ProcessDefinitionTitle="Application Content Approval" ApprovalStatus="Unapproved" ActivityDef
initionType="1" WorkItem="tcm:24-538-131200" CreationDate="2010-12-30T19:36:04" State="Started" Icon="T16L1P0" Allow="4
1955328" Deny="16777216"/></tcm:ListWFProcessInstances>

OK - that's great - if you dig into it, you'll see that there is a containing element called ListWFProcessInstances, and that what it contains are some Item elements. All of this is in the tcm namespace, and each Item has various attributes. Unfortunately, the XML in this form is ugly and not particularly useful. Fortunately, the powershell has some built-in features that help quite a lot with this. The first is that if you use the [xml] cast operator, the string is transformed into a System.Xml.XmlDocument. To test this, just assign the result of the cast to a variable and use the get-member cmdlet to display it's type and methods:

> $xml = [xml]$wfe.GetListProcessInstances()
> $xml | gm

(Of course, you don't type "get-member". "gm" is sufficient - most standard powershell cmdlets have consistent and memorable short aliases.)

I won't show the output here, as it fills the screen, but at the top, the type is listed, and then you see the API of System.Xml.XmlDocument. (Actually you don't need a variable here, but it's nice to have a go and use some of the API methods.)

All this would be pretty useful even if it stopped there, but it gets better. Because the powershell is intended as a scripting environment, the creators have wrapped an extra layer of goodness around XmlDocument. The assumption is that you probably want to extract some values without having to write XPaths, instantiate Node objects and all that other nonsense, so they let you access Elements and Attributes by magicking up collections of properties. Using the example above, I can simply type the names of the Elements and Attributes I want in a "dot-chain". For example:

> ([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances.Item[0].ID
tcm:24-269-131076

Here you can also see that I'm referencing the first Item element in the collection and getting its ID attribute. The tcm ID is returned. All this is great for exploring the data interactively, but be warned, there is a fly in the ointment. Behind the scenes, the powershell uses its own variable called Item to represent the members of the collections it creates. This means that whereas you ought to be able to type

([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances

and get some meaningful output, instead, you'll get an error saying:

format-default : The member "Item" is already present.
    + CategoryInfo          : NotSpecified: (:) [format-default], ExtendedTypeSystemException
    + FullyQualifiedErrorId : AlreadyPresentPSMemberInfoInternalCollectionAdd,Microsoft.PowerShell.Commands.FormatDefaultCommand

This is because Tridion's list XML uses "Item" for the element name, and it conflicts with powershell's own use of the name. It's an ugly bug in powershell, but fortunately it doesn't affect us much. Instead of saying "ListWFProcessInstances", just keep on typing and say "ListWFProcessInstances.Item" and you are back in the land of sanity.

Apart from this small annoyance, the powershell offers superb discoverability, so for example, it will give you tab completion so that you don't even have to know the name of ListWFProcessInstances. If at any point you are in doubt as to what to type next, just stop where you are and pipe the result into get-member - all will be revealed.

OK - back to the main plot. If you're with me this far, you have probably realised that

([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances.Item

will get you a collection of objects representing the Item elements in the XML. As you probably know, an important feature of powershell is that you can pipeline collections of objects, and that there is syntax built in for processing them. The % character is used as shorthand for foreach, and within the foreach block (delimited by braces), the symbol $_ represents the current item in the iteration. For example, we could write:

> ([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances.Item | % {$_.ID}

and get the output:

tcm:24-269-131076
tcm:24-270-131076

I'm sure you can see where this is going. We need to transform the collection of XML attributes: the IDs of the process instances, into a collection of TOM objects, so with a small alteration in the body of the foreach block, we have

% {$tdse.GetObject($_.ID,2)}

and then we can pipe the resulting collection of TOM objects into a foreach block which invokes the FinishProcess() method:

 

% {$_.FinishProcess()}

Of course, if you like really terse one-liners, you could amalgamate the last two pipeline elements so that instead of:

> ([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances.Item | % {$tdse.GetObject($_.ID,2)} | % {$_.FinishProcess()}

we get:

> ([xml]$wfe.GetListProcessInstances()).ListWFProcessInstances.Item | % {$tdse.GetObject($_.ID,2).FinishProcess()} 

but in practice, you develop these one-liners by exploration, and if you want something really terse, you are more likely to write a more long-hand version, put it in your $profile, and give it an alias.

As I said at the top - this is just an example. All the TOM functions that return XML lists can be treated in a similar manner. Generally all that changes is the name of the root element of the XML document, and as I have pointed out, this is easily discoverable.

I hope this approach proves useful to you. If you have any examples of good applications, please let me know in the comments.

A Happy New Year to you all.

Dominic

A new version of the Tridion developers' Powershell profile for SDL Tridion 2011

Posted by Dominic Cronin at Nov 30, 2010 11:30 AM |
Filed under: ,

As I'm up at Tridion HQ for a few days for the 2011 boot camp, here's an updated version of the powershell profile for Tridion developers. Nothing really new, just that a couple of things got their names changed. (With the added bonus that the service names are now consistently named again!!).

 

# http://www.leastprivilege.com/MyMonadCommandPrompt.aspx
function prompt { "PS " + (get-location).Path + "`n> " }
# http://www.interact-sw.co.uk/iangblog/2007/02/09/pshdetectelevation
& {
  $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent()
  $prp=new-object System.Security.Principal.WindowsPrincipal($wid)
  $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator
  $IsAdmin=$prp.IsInRole($adm)
  if ($IsAdmin)
  {
    (get-host).UI.RawUI.Backgroundcolor="DarkRed"
    clear-host
  }
}
# http://www.leastprivilege.com/AdminTitleBarForPowerShell.aspx
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$p = New-Object System.Security.Principal.WindowsPrincipal($id)
if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator))
{
 $Host.UI.RawUI.WindowTitle = "Administrator: " + $Host.UI.RawUI.WindowTitle
}
"rat"
set-alias -name rat -value RestartAllTridion
function RestartAllTridion
{
"### Restart All Tridion ###"
"Stopping All Tridion CM services"
$runningServices = service     TCMBCCOM, `
                TCMIMPEXP, `
                TcmPublisher, `
                TCMSearchHost, `
                TcmSearchIndexer, `
                TcmServiceHost, `
                TCMWorkflow `
        | where {$_.Status -eq "Running"}
$runningServices | % {stop-service -force -InputObject $_}
kt
"Doing an IISRESET"
iisreset
"Starting Tridion services"
# This script basically does best-effort, so we need a sick-bag in case a service is disabled or whatever 
# (feel free to wire up the WMI stuff if you need to scratch this)
&{
  trap [Exception] {}
  $runningServices | where { "Stopped", "StopPending" -contains $_.Status } | start-service
  }
}
"kt"
set-alias -name kt -value killTridion
function KillTridion {
"Shutting down Tridion COM+ Application"
$COMAdminCatalog = new-object -com COMAdmin.COMAdminCatalog
$COMAdminCatalog.ShutdownApplication("SDL Tridion Content Manager")
}

Javascript for Tridion scripting

Posted by Dominic Cronin at Oct 09, 2010 05:30 PM |
Filed under: , ,

During the recent Tridion MVP summit, the subject of Javascript came up. That's not surprising, as we were busy with building some GUI extensions to run on the SDL Tridion 2011 CTP, which is a pretty Javascript intensive activity. Most Tridion people reading this won't be too surprised if I say that Javascript isn't really in the comfort zone for many of us. We've done templating in VBScript, and although Tridion has always supported Javascript (or more strictly, JScript) for templating, we've always avoided it. Tridion has never shipped a JavaScript version of the default templates, and I suspect that's why, very early on, VBScript became the VHS to JavaScript's BetaMax. Most people accept that Betamax lost to VHS despite being technologically better, and for the sake of this discussion I'm going to ignore the dissenting opinions that have surfaced recently. In my view JavaScript is a superior technology to VBScript in many ways, but VBScript achieved critical mass in our world, and that's that. Or is it?

Well to start with, I'm not about to suggest that anyone should start to write Tridion component templates in JavaScript. These days, if you're defecting from VBScript templating, you'll be going to a .NET language; probably C#, and I'm all in favour of that. On the other hand, for many of us, JavaScript clearly has a place in our future. If you're writing web applications these days, a solid grasp of js is essential, and as noted already, if you want to customise the Tridion GUI, you'll be elbow-deep in the stuff before you get anything useful done.

For myself, I started using Javascript for simple update scripts and the like quite some time ago, and this mostly means running the scripts from the command line on the server using the cscript processor built-in to Windows. (These days, I also use PowerShell scripting for some of the more ad hoc work, but that's another story.)

The first obvious point about the language is that it has several styles or idioms in common use. For those of you that are familiar with libraries such as JQuery, you'll know that the style can be very similar to what you might encounter in languages with a functional flavour. To gain expertise in this style, I'd strongly recommend jumping in the deep end with John Resig's "Learning Advanced Javascript". (It's a pretty deep deep end. Note to self: have another go soon, and try to understand it this time!)

The other two idioms I'd mention are the object-oriented style, and what I can only describe as Microsoft style. For the object-oriented style, and how to achieve it, you can do no better than to view Doug Crockford's lecture series hosted at Yahoo. I suppose the best place to learn the Microsoft style is MSDN. In the days of the Atlas project, Microsoft tried to use JavaScript as just another layer in the ASP.NET stack, and with some success. For Tridion guys, this style is most notable because some of this flavour is to be seen in the Tridion GUI - or at least that's how it appears to me based on a fairly peremptory poke around. (I'd love someone to tell me if if I'm wrong. Really! Although comments won't show up, as I have to moderate to prevent spam. That's my only moderation criterion, though. If you take the trouble to write a real comment, I'll publish it.)

I hope to return to to the subject of Tridion and Javascript in future posts, but for now, I'd just like to start with a simple example of why it works for me. But please be kind; I'm not a Resig or a Crockford.

It's fairly often useful to be able to recurse through a folder structure and process each item within a given hierarchy. Usually, the processing involves two parts. Firstly, filtering: am I interested in this item or not? Secondly, for the items I'm interested in, I want to run some code, either to report on the item or to alter it in some way. This pattern comes up a lot, and for a lot of the small- to medium-scale jobs, the recurse-filter-process logic is a significant part of the work. Maybe the actual payload only amounts to a couple of lines of code. If you had a simple way to re-use the recurse-filter-process part, you could do a lot of quick-and-dirty jobs, erm... pretty quickly.

In Javascript, I can paste the following function in to a template or a script file, and the recurse-filter-process part is done:

function recurseItems( orgItem, filter, process){
  var items = orgItem.GetItems();
  var eItems = new Enumerator(items);
  for (;!eItems.atEnd();eItems.moveNext()){
    var itemType = eItems.item().ItemType;
    if (itemType === TDSDefines.ItemType.Folder || itemType === TDSDefines.ItemType.StructureGroup){
        recurseItems(eItems.item(), filter, process)
    } else {
      if (filter(eItems.item())) {
          process(eItems.item());
      }
    }
  }
}

I'm quite sure you could tidy this up a bit, but whatever... What makes this so straightforward to re-use is the fact that in JavaScript, functions are first-class objects, and it's very easy to pass functions as arguments to another function, and invoke them from within that function. The recurseItems() function expects to be passed an organizational Item as its first argument. (OK - as written, this won't work for categories, or for the sake of argument, publications, but bear with me...)

The "filter", and "process" arguments are functions.

Let's say I want to list all the components in a given hierarchy. I could write something like this (by which I mean this code isn't tested, it's for illustration purposes, right?):

function isComponent(item){return item.ItemType === TDSDefines.ItemType.Component;}
function outputTitle(item){WScript.Echo(item.Title + "\n";))
var topFolder = tdse.GetObject("tcm:1-1234-2", TDSDefines.OpenMode.View);
recurseItems(topFolder, isComponent, outputTitle);

So with 4 lines of code, I've listed the items I'm interested in. Not bad, eh? OK - I cheated. But how? Take a look at the attached file TDSDefines.js. It's a port of the standard TDSDefines constants to JavaScript. The cheating part is that I instantiate the "tdse" object in there, which somewhat breaks the purity of having a TDSDefines file, but you're always going to want tdse, so wtf not? Anyway - this file is what allows me to type things like "TDSDefines.ItemType.Component", or TDSDefines.OpenMode.View, instead of 16, or 1. JavaScript lends itself very well to this kind of nested data structure, in ways that VBScript would struggle with.

Assuming you are using the cscript host on your Tridion server, and that TDSDefines.js is in the same directory as your script, you'll need to use a couple of lines of code to import your "defines".

    var fso = new ActiveXObject("Scripting.FileSystemObject");
    eval(fso.OpenTextFile(fso.BuildPath(fso.GetParentFolderName(WScript.ScriptFullName), "TDSDefines.js"), 1).ReadAll());

Inside Tridion, of course, TDSDefines.js just becomes a template building block, and gets included in the normal way.

Funnily enough, I never got round to porting the default template code to JavaScript, and now, presumably, I never will. All the same, using JavaScript for this kind of work has allowed me to practice and get more familiar with the language, and to have a toolkit that allows for very, very fast creation of quick-and-dirty recursion scripts among others. It doesn't end there. JavaScript, or perhaps JSON, allows you to take a script-as-data approach which will get you to your desired result much quicker than, for example, having to write scripts that crunch through XML data files. Perhaps that would make a good subject for a future post.

In the meantime, I hope this gives you yet another excuse to hone your JavaScript skills. Those skills will definitely come in useful.

XML Namespaces aren't mandatory, and tools shouldn't assume that they are.

Posted by Dominic Cronin at Jan 02, 2010 10:40 PM |
Filed under: , ,

In his recent blog posting on XML Namespaces, James Clark questions the universal goodness of namespaces. Of course, there is plenty of goodness there, but he's right to question it. He says the following:

For XML, what is done is done.  As far as I can tell, there is zero interest amongst major vendors in cleaning up or simplifying XML. I have only two small suggestions, one for XML language designers and one for XML tool vendors:

  • For XML language designers, think whether it is really necessary to use XML Namespaces. Don’t just mindlessly stick everything in a namespace because everybody else does.  Using namespaces is not without cost. There is no inherent virtue in forcing users to stick xmlns=”…” on the document element.

  • For XML vendors, make sure your tool has good support for documents that don’t use namespaces.  For example, don’t make the namespace URI be the only way to automatically find a schema for a document

It's the second point that interests me. During a recent Tridion project, there was a requirement to accept data from an external source as an XML document. I wanted to use a Tridion component to store this data, as this would give me the benefits of XML Schema validation, and controlled publishing. The document didn't have a namespace, although it had a schema. In order to get this to work with Tridion, I had to go to the provider of the document, and get them to add a namespace. Tridion wouldn't allow me to create a schema whose target namespace was empty. It seemed a shame that even when hand-editing the schema (so presumably asserting that I knew what I was about) the system wouldn't let me make this choice.

At the time, I just got the other party to make the change, and went back to more important things. Maybe there's some internal constraint in the way Tridion works that prevents them from supporting this, or maybe it's such an edge case that no-one was ever bothered by it. If the former, then I can't think what the problem would be; there's no reason to abuse the namespace to locate the schema. Tridion's quite happy enough to allow several schemas targetting the same namespace, so what's so special about the "no" namespace? In Tridion components, XML attributes (quite correctly) are in no namespace, but as long as the correct schema gets used for validation, so what?

I suspect it's more likely that this just comes under the "edge case" heading, in which case, perhaps they can improve it in a future release.

XML Schema validation from Powershell - and how to keep your Tridion content delivery system neat and tidy

Posted by Dominic Cronin at Dec 12, 2009 10:55 PM |
Filed under: , , ,

I don't know exactly when it was that Tridion started shipping the XML Schema files for the content delivery configuration files. For what it's worth, I only really became aware of it within the last few months. In that short time, schema validation has saved my ass at least twice when configuring a Tridion Content Delivery system. What's not to like? Never mind "What's not to like?" - I'll go further. Now that the guys over at Tridion have gone to the trouble of including these files as release assets - it is positively rude of you not to validate your config files.

Being a well-mannered kind of guy, I figured that I'd like to validate my configuration files not just once, but repeatedly. All the time, in fact. Whenever I make a change. The trouble is that the typical server where you find these things isn't loaded down with tools like XML Spy. The last time I validated a config file, it involved copying the offending article over to a file share, and then emailing it to myself on another machine. Not good. Not easy. Not very repeatable.

But enter our new hero, Windows 2008 Server - these days the deployment platform of choice if you want to run Tridion Content Delivery on a Windows box. And fully loaded for bear. At least the kind of bears you can hunt using powershell. Now that I can just reach out with powershell and grab useful bits of the .NET framework, I don't have any excuse any more, or anywhere to hide, so this afternoon, I set to work hacking up something to validate my configuration files. Well - of course, it could be any XML file. Maybe other people will find it useful too.

So to start with - I thought - just do the simplest thing. I needed to associate the xml files with their relevant schemas, and of course, I could have simply done that in the script, but then what if people move things around etc., so I decided that I would put the schemas in a directory on the server, and use XMLSchema-instance attributes to identify which schema belongs with each file.

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="schema.xsd"

OK - so I'd have to edit each of the half-dozen or so configuration files, but that's a one-off job, so not much trouble. The .NET framework's XmlReader can detect this, and use it to locate the correct schema. (although if it isn't correctly specified, you won't see any validation errors even if the file is incorrect. I'll hope to fix that in a later version of my script.)

I created a function in powershell, like this:

# So far this silently fails to catch any problems if the schema locations aren't set up properly
# needs more work I suppose. Until then it can still deliver value if set up correctly
function ValidateXmlFile {
    param ([string]$xmlFile       = $(read-host "Please specify the path to the Xml file"))
    "==============================================================="
    "Validating $xmlFile using the schemas locations specified in it"
    "==============================================================="
    $settings = new-object System.Xml.XmlReaderSettings
    $settings.ValidationType = [System.Xml.ValidationType]::Schema
    $settings.ValidationFlags = $settings.ValidationFlags `
            -bor [System.Xml.Schema.XmlSchemaValidationFlags]::ProcessSchemaLocation
    $handler = [System.Xml.Schema.ValidationEventHandler] {
        $args = $_ # entering new block so copy $_
        switch ($args.Severity) {
            Error {
                # Exception is an XmlSchemaException
                Write-Host "ERROR: line $($args.Exception.LineNumber)" -nonewline
                Write-Host " position $($args.Exception.LinePosition)"
                Write-Host $args.Message
                break
            }
            Warning {
                # So far, everything that has caused the handler to fire, has caused an Error...
                Write-Host "Warning:: Check that the schema location references are joined up properly."
                break
            }
        }
    }
    $settings.add_ValidationEventHandler($handler)
    $reader = [System.Xml.XmlReader]::Create($xmlfile, $settings)
    while($reader.Read()){}
    $reader.Close()
}

With this function in place, all I have to do is have a list of lines like the following:

ValidateXmlFile "C:\Program Files\Tridion\config\cd_instances_conf.xml"
ValidateXmlFile "C:\Program Files\Tridion\config\live\cd_broker_conf.xml"

If I've made typos or whatever, I'll pretty soon find them, and this can easily save hours. My favourite mistake is typing the attributes in lower case. Typically in these config files, attributes begin with a capital letter. Once you've made a mistake like that, trust me, no amount of staring at the code will make it obvious. You can stare straight at it and not see it.

So there you have it - as always - comments or improvements are always welcome, particularly if anyone knows how to get the warnings to show up!

SDL Tridion's YouTube marketing

Posted by Dominic Cronin at Dec 10, 2009 10:10 PM |
Filed under: ,

In recent times, SDL Tridion have been putting out marketing videos on their very own YouTube channel. Each one takes a fictitious (or perhaps anonymised) use-case for their products and presents a fairly non-technical view of it by means of animated graphics, with voice-overs in disconcertingly generic varieties of British English. Never mind British, it's English English, of a sort of carefully not-too-posh-but-not-too-common-either, home-counties kind.

It's a bit strange for me. Obviously, I'm not the target audience. The videos are aimed at non-technical people who are not familiar with Tridion. Over the years I've got to know Tridion pretty well, both as a company and as a product suite. From so close up, the videos have an almost surreal aura. The multitudinous mismatches between the marketeers' in-world reality, and my own, leap out at me.

Why British English, when it's a Dutch company, whose biggest growth market is the US? Why this particularly strange variant of British English? (OK - full disclosure here; I'm a Geordie!) Is this the SDL influence, or does this come from US marketeers?

And are the examples real? I'm curious.

And so on....

As for the content, at least the most recent one (SDL Tridion WCM Platform for Syncronizing Online Content) had me jumping up and down in a couple of places. Like this: "... I can always revert back to previous versions of any content. It is important for our compliance department that we have proof of how a web site looked at any date in the past". Talk about a confusing message. Firstly, normal versioning in Tridion is not intended for that, and won't meet that need. They have a separate product called Archive Manager, which could be used to implement a solution for this problem.

Of course, if their marketing department were to put out a message aimed at someone like me, they would almost certainly deserve to be fired on the spot. It just hits my weirdness button. Know what I mean?

From the horse's mouth... a hot content portering tip (and a bit of a rant)

Posted by Dominic Cronin at Mar 23, 2008 09:25 AM |
Filed under: ,

I have to admit, I nearly choked when I saw this. Bear with me a moment, and I'll tell you all about it.

Not so very long back, I was trying to set up the SDL Tridion Content Porter to work as part of an automated "build" system. One of the requirements was that I'd be able to save content and then re-import it into various different publications. After all, you need a go-to-production option as well as being able to support development and test work. After beating my brains out trying to figure out the publication mapping features in Content Porter, I asked around a bit, and found that lots of people have trouble with this, and one well-favoured option is just to go round the problem and run regexes against the entire intermediate file set to swap publication names. Oh-kaay - so a couple of hours later I'd hacked out some javascript that would do this, and solving the mapping problem the official way promptly went on the back burner, perhaps for ever (or at least until the pan boils dry). Moral of the story: doing it the official way is just too stupidly hard.

So on with the tale: This morning I was installing SDL Tridion Webforms on my research image. This was the first time I've installed WebForms, so I was stepping gently through the documentation when I realised that the WebForms installation relies on Content Porter. OK then - a quick excursion to install Content Porter, configure the Business Connector etc., and back to the main plot. That's when I nearly choked. The documentation for the WebForms installation contains this little gem:

When you perform this import, Content Porter creates a Publication called WebForms. Once created, this Publication contains the items that you need in order to use WebForms Designer and WebForms Field Type Editor. Alternatively, you can also import WebForms items into an existing Publication. Note To import WebForms into an existing Publication, rename the Publication to which you want to import the items to WebForms before you run Content Porter. Then, after you have used Content Porter, rename the Publication back to its original name.

So there you have it: according to Tridion, the correct way to solve this problem is to rename the publication, and then rename it back again afterwards. Let's hope that's an OK thing to do in your environment.

To tell the truth, I quite like this as a "hack". It's robust and solid, and very definitely gets the job done. In fact, it's about as nice a job of working around Content Porter's limitations as you'll find. I wish I'd thought of it myself. In fact, part of the reason for this post is as a public service announcement for anyone who doesn't happen to spend their Sunday mornings reading the WebForms installation manual.

But please, Tridion. Isn't this a wake-up call? When your own product installation guides have to give out workarounds like this. I know there's a new version of Content Porter on the roadmap, and I very sincerely hope that it's going to come with batteries included. While I'm on the subject - this is what the WebForms installation guide says a couple of lines further down:

Important:
Due to dependencies between items that you are importing, you will have to run the Content Porter twice in order to import all items used by SDL Tridion WebForms Designer. The first time that you run the Content Porter, you will receive error messages during the import process. These messages are not critical. You can click "Skip All" to continue.

The Content Porter should manage this. If the import of an item fails because its dependencies aren't there yet, and the dependencies are there in the package, then just wait until the end and redo it. Automatically! Rinse and repeat. Why inflict this misery on end users? There's a very real use case for Content Porter where you want to produce a package to give to someone else to import, and you don't want them worried by this kind of nonsense.  If there's any reason for the existence of Content Porter, it's the managing of depencies between the items being imported.

End-user misery aside - I should be able to use the Content Porter as part of an automated solution, and that just won't fly while I have to know in advance that a particular package requires two or more attempts to succeed.

 

/rant

Why is Tridion's configuration library called the TDSXGit?

Posted by Dominic Cronin at Mar 09, 2008 07:05 PM |
Filed under: ,

Those of you who read my previous post will remember that I accessed Tridion's configuration by instantiating a TDSXGit.Configuration object. People who've worked with Tridion for a while may remember that it used to be quite common to edit the configuration in a file called cm_cnfg_git.xml. This file is still there, but without the xml extension, and these days it's encrypted so it doesn't make much sense to try to edit it directly.

To an Englishman like myself, this name TDSXGit is vaguely funny, because "git" in British English is a mild term of abuse. It's not uncommon for me to come out with phrases like: "Which stupid git broke the build"? It's definitely abuse, but fairly mild; you can say it to someone that you like. 

But to the point: Back in the R4 days, Tridion's configuration data was kept in the registry, which was all well and good, but had it's own problems. When R5 was designed, there was so much XML around the place that it seemed much more sensible to keep the configuration in an XML file. The problem with this was that all that disk IO would have been a total performance killer. We needed a memory cache. Good idea, you might think, but in a COM-based web application, how do you do that? The design we ended up with makes use of a couple of fairly obscure features of COM. (By the way - I'm not claiming any credit for this, just describing what was designed by other members of the team.)

The idea is to get an object to remain in memory, and to provide a mechanism whereby any code within the application can grab a reference to the object. In COM, a reference to an object is always a pointer to an interface. Memory access in COM is controlled by "apartments" - objects running in one apartment can't directly access objects running in another apartment.  In particular, if you have an interface pointer for an object in one apartment, you can't just use that pointer from a different apartment. The interface pointer needs to be "marshalled" across the apartment boundary; in other words, if you should be talking to a proxy that's local to your apartment, you'll get a pointer to that instead.  The mechanism for doing this is called the global interface table, hence the acronym GIT.

The GIT is visible from anywhere in the process, and if you register an interface with the GIT, that immediately takes place of the first problem, that of keeping the object in memory. In COM, memory management is done by reference counting. An object keeps track of how many other objects currently have a reference to it, and if that number drops to zero, the object will self-destruct, thereby freeing any memory it was using. As soon as you register an object with the GIT, well the GIT has a reference to it, and therefore it isn't going to self destruct, so you have your memory cache.

When you register an object with the GIT, the API hands you back a "cookie". A cookie in this context is just a number. If you know this number, you can ask the GIT for an interface pointer that references the object. You can keep doing this as many times as you like, unless there's been an explicit call to release the object from the GIT. The interface pointer you get back will work in the apartment you are in.

There's one more thing that you need to make this all work, and that's a way of making sure the cookie is always available when you need to get hold of your memory cache. For this, you can use another obscure COM feature: the shared properties manager (SPM). This just allows you to save a value by name and retrieve it. (The SPM also takes care of a couple of other things, like grouping the properties to prevent name collisions, and locking to control access contentions.)

So when a Tridion process first accesses the configuration, the configuration file will be decrypted and loaded into a DOM object, and the DOM will be registered with the GIT. The cookie is then stored in the SPM. Any subsequent accesses for the life of the process will be simply a matter of grabbing the cookie from the SPM and using it to get the interface pointer from the GIT.

There are other techniques that could be used, but this has the advantage not only of eliminating disk IO, but also repeated parsing of the XML to create the DOM.

This should explain why when you update some configuration value, you have to shut down each of the processes that make use of the configuration. The GIT and SPM are each specific to a process. It is technically possible to get TDSXGit.Configuration to release the DOM from the GIT, but none of Tridion's application code actually does this. That's a reasonable design for a server application that isn't re-configured very often.

In theory (at least according to the theory I just described), it should only be necessary to restart a process if it is affected by the configuration value that just changed, but my own experience flies in the face of this, and I always restart all the processes. It's a bit of a cargo cult thing I suppose, but I'll keep doing it. Actually, I'd love someone to point out where my reasoning is flawed. I hate that cargo cult thing.

"Which stupid git forgot to restart the processes after that configuration change?"

Strange dreams of nightmare code

Posted by Dominic Cronin at Mar 04, 2007 05:00 PM |
Filed under: ,

One morning this week I woke up to a very strange combination of life and programming. Kind of a "things that make ya go hmm..." moment. Here's how it went:

Some considerable time ago, I began work on a re-write of a Tridion events system. The existing system was so gunged up with layer upon layer of cruft that we decided it was unmaintainable and that we wouldn't touch it unless absolutely necessary. The fickle winds of project management have now blown us back onto the jagged rocks; we ended up dispatching from the new system back to the old code, which turns out to be fairly broken, and I have to mend it. Ouch!

No big deal, you may think, until you realise that the broken piece is a thirty page long stretch of Visual Basic 6. That's thirty pages all in one function. This code handles a Tridion OnComponentSavePost event, and in the process, saves the component four separate times. Each of these saves causes a recursion into the handler, each time from a different place and with a different data state. Depending on the state, various if-then-else blocks either fire or don't, leading to the code being executed in an order very different from what you see laid out as you hit page-down thirty-odd times. As you can imagine, before making even minor changes to this stuff, you have to marinate in the code for quite some time, tracing the various paths of illogic through the smoke-trails of your predecessor's brain-damaged nightmare. One thing's for sure - you know if you touch anything, there's a huge risk that you'll break something else.

If you've ever done this kind of work, you'll know that once you've lived in the code for a few days, there's every likelihood that you'll spend your nights dreaming of it too.

During this time, our seven-month old son, Finlay has had a virus, resulting in some fairly disrupted sleep for us all. Perhaps once every hour or two through the night, the poor lad would cry out, and you'd find yourself awake in response. After a night of this, it becomes increasingly hard to come up out of deep sleep, and one morning at five or six o'clock, and  I found myself in the middle of a dream of scrolling code, hearing a cry, but hearing it as a bug. Huh? How does that work? You hear of people with psychological disorders hearing colours and so forth, and I suppose in the dream world there are no rules. I heard a cry, and experienced it as a sort of "Oh shit, there's another bug." moment.

Finlay's back to health now, and (mostly) sleeping through the night. Maybe some time soon, I will too.