Working With Tridion Workflow Automatic Activities and VBScript

My preference for working with Tridion Workflows is to use the Event System and the Tridion API (TOM API with 5.3 and 2009, and TOM.NET API with Tridion 2011).  This is probably due to my favoritism to .NET over using VBScript (who wouldn’t favor that?).  But knowing how to use the TOM API in the VBScript sections of Automatic Activities does come in handy, and just in case anyone is working with these Automatic Activities, I thought I’d put together some quick samples of how to do some basic things. When working with the VBScrit from “Edit Script…” button, remember to reference the TOM API documentation (not the TOM.NET API docs!).

Getting the Component or Page of the workflow work item.

Dim obtItem
Set objItem = CurrentWorkItem.GetItem()

Remember that VBScript works dynamically here, and the variable Item above will be a Page or a Component based on which WorkItem it is.

Dim strMetaInfo, strPageUrl
If Not objItem.MetadataSchema Is Nothing Then
    strMetaInfo = "Metadata Schema: " + objItem.MetadataSchema.Title
    If Not objItem.MetadataFields.Item("Keywords") Is Nothing Then
        strMetaInfo = strMetaInfo + "Keywords: " + objItem.MetadataFields.Item("Keywords").Value(1)
    End If
End If
strPageUrl =  objItem.Info.PublishLocationUrl

When working with Components, you can access the component fields in a similar way to metadata fields.

strSomeField = objItem.Fields.Item("SomeField").Value(1)

When working with Workflows, you’ll probably want to deal with the various workflow objects like ProcessInstance, ProcessDefinition, ActivityInstance and ActivityDefinition.

' Get the current Process Instance object
Dim objProcessInstance
Set objProcessInstance = CurrentWorkItem.ActivityInstance.ProcessInstance

' Get all Activity Instances that has happened thus far in this Process Instance
Dim objActivityInstances
Set objActivityInstances = objProcessInstance.ActivityInstances

' Get the previous Activity Instance (usually the manual activity that led to this automatic activity)
Dim objLastActivityInstance
Set objLastActivityInstance = objActivityInstances(objActivityInstances.Count - 1)

' Get the Finish Message that was input from the previous Activity Instance
Dim strFinishMessage
strFinishMessage = objLastActivityInstance.FinishMessage

' Get the performer who finished the last activity instance
Dim objLastPerformer, strLastPerformerName
Set objLastPerformer = objLastActivityInstance.Performer
' User object's use "Name" instead of "Title" (and same with groups)
strLastPerformerName = objLastPerformer.Name

' Get the first Activity Instance
Dim objOrigActivityInstance
Set objOrigActivityInstance = objActivityInstances(1)

' Get a specific ActivityDefinition in the Process matching a specific title
Dim objAct, objActivityDefinition
For Each objAct In objProcessInstance.ProcessDefinition.ActivityDefinitions
    If objAct.Title = "Some Specific Title" Then
        Set objActivityDefinition = objAct
    End If
Next

' Get a comma separated list of user IDs from an assigned group in an Activity Definition.
Dim objTrustee, objMembersXml, strMembers
strMembers = ""
Set objMembersXml = CreateObject("MSXML2.DOMDocument.4.0")
Call objMembersXml.LoadXml(objActivityDefinition.Assignee.GetMembersList)
For Each objTrustee In objMembersXml.documentElement.childNodes
    If Len(strMembers) > 0 Then
        strMembers = strMembers + ","
    End if
    strMembers = strMembers & objTrustee.getAttribute("xlink:title")
Next

Finally another important topic of working with Workflows… automatic publishing!

Call objItem.Publish("tcm:0-1-65538", True, True, True)

You’ll want to pay attention to that third argument, especially when you want to publish a work item that hasn’t completed a workflow process yet. You’ll notice that typically, only the last COMPLETED version gets published when you put something in the queue. Setting this third argument to True ensures that the version in the work list gets published. The full method definition for Publish is as follows:

Public Function Publish( ByVal targets As Variant, ByVal activateBlueprinting As Boolean, ByVal activateWorkflow As Boolean, ByVal rollbackOnFailure As Boolean, Optional ByVal publishTime As Date = 0, Optional ByVal unpublishTime As Date = 0, Optional ByVal deployTime As Date = 0, Optional ByVal resolveComponentLinks As Boolean = True, Optional ByVal priority As TDSDefines.EnumPublishPriority = Normal, Optional ByVal ignoreRenderFailures As Boolean = False, Optional ByVal maximumRenderFailures As Long = 0 ) As String

The arguments are as follows:

targets – Specifies to/from which target(s) to (un-/re-)publish. Can be one of the following:
A TargetType object or URI
An array of TargetType URIs
A TargetTypes collection object
A PublicationTarget object or URI
An array of PublicationTarget URIs
A PublicationTarget collection object
activateBlueprinting – Indicates whether the item should also be (un-/re-)published in child publications.
activateWorkflow – Indicates whether the item is being (un-/re-)published from the user’s work list.
rollbackOnFailure – Indicates if the entire publish session should be rolled back if a failure occurs while deploying
publishTime – If specified, the item is published (rendered) at the given date/time.
unpublishTime – If specified, the item is un-published at the given date/time. Should be later than publishTime (if specified)
deployTime – If specified, the item is deployed at the given date/time. Should be later than publishTime and earlier than unpublishTime (if specified). If not specified, the item will be deployed on publishTime (i.e. immediately after rendering).
resolveComponentLinks If specified, it resolves the component links. Default is set to true.
priority If specified, it gives a priority on the publish action. Default is set to normal.
ignoreRenderFailures – If specified, it gives the possibility to continue a publish action when there are render failures. Default is set to false.
maximumRenderFailures – If specified, it sets the limit on the number of render failures and ignoreRenderFailures must be set to true.

To further extend your functionality that you can do in Automatic Workflows, you can also create your own classes and functions that are COM visible, and call those methods and objects from your VBScript.  From your .NET code, you can use the Core Services API or even the old TOM API using the interop DLLs.  Remember, although the TOM.NET API has workflow objects, using it in this manner is not supported, and you should stick with one of the other two APIs.

Tridion PublishEngine – Of Transactions and Publish Information

Today I thought I would give some examples of querying publish transactions (publishing queue) as well as getting publish information from specific items, like seeing which Publication Targets a page has been published to and when, or just seeing if an item has been published in general. This post is inspired by a recent question asked in the forums, but I have noticed it come up from time to time. Luckily this task is easy using the TOM.NET API and the static PublishEngine class.

IsPublished

One task you might need to attempt in your Tridion development day to day activities is to check to see if a given item has been published, or published to a particular Publication Target.  PublishEngine contains the following overloaded methods.

bool IsPublished(IdentifiableObject item)
bool IsPublished(IdentifiableObject item, PublicationTarget publicationTarget)
bool IsPublished(IdentifiableObject item, PublicationTarget publicationTarget, bool isPublishedInContext)

The first IsPublished method is useful if you just want to see if an item is published, regardless of which Publication Target it has been published to.

if (PublishEngine.IsPublished(page))
{
    // This page has been published. Do cool stuff here.
}

The second IsPublished method will return true only if the item has been published to the Publication Target passed in the 2nd argument. If the 2nd argument is null, then it’ll act just like the first method and return true if the item has been published to any Publication Target.

if (PublishEngine.IsPublished(page, pubTarget))
{
    // This page has been published to a specific publication target. Do cool stuff here.
}

The third method allows for even finer control. I haven’t personally played with this one as of yet, but the documentation for the isPublishedInContext argument states “Indicates if state should be returned regardless of the context Publication. true only check if item is published in the context Publication; otherwise, false.” I’m assuming this means that, if this argument is set to true, the method will only pass if the item is published to a particular Publication Target, and only if the item is published in it’s own context Publication.

GetPublishInfo

What if you need to get more information about an item? Like, what if you not only wanted to see what Publication Targets it was published to, but at what time too? That’s where the following method comes in hand.

foreach (PublishInfo info in PublishEngine.GetPublishInfo(page))
{
    Console.WriteLine("Published To: " + info.PublicationTarget.Title);
    Console.WriteLine("Published At: " + info.PublishedAt); // The time the page was published
    Console.WriteLine("Published By: " + info.PublishedBy.Title); // The user who published
    Console.WriteLine("Rendered With: " + info.RenderedWith.Title); // The title of the template used
}

The above will loop through each Publication Target that the item has been published to and give you some useful information about the publishing.

GetPublishTransactions

And what if you actually need to check the publishing queue to see if an item has recently been published in the past hour? The PublishEngine allows you to also query the publish transactions.

XmlElement GetListPublishTransactions(PublishTransactionsFilter filter)
IEnumerable<PublishTransaction> GetPublishTransactions(PublishTransactionsFilter filter)

Note that the above methods will throw an AccessDeniedException if the user is not a System Administrator or have PublishManagement rights in any publication. That means if your code relies on always needing to be able to check the transactions, regardless of rights, you’ll need to impersonate.

Session session = new Session("DOMAIN\username"); // Impersonate System Admin or at minimum user with PublishManagement rights.
 
PublishTransactionsFilter filter = new PublishTransactionsFilter(session);
filter.StartDate = DateTime.Now.AddHours(-1); // Add some criteria for when the publishing was started.
filter.PublishTransactionState = PublishTransactionState.Success;

IEnumerable<PublishTransaction> transactions = PublishEngine.GetPublishTransactions(filter);

foreach (PublishTransaction transaction in transactions)
{
    // Check cool stuff with the transaction, like the transaction.Items property.
}

The above will loop through all the successful transactions that has happened in the past hour. Notice the PublishTransactionState property… it allows you to only filter based on one state. But what if you need to grab anything that’s not Success or Failed perhaps, or any other combination of states? You’ll have to grab the items and filter programatically.

PublishTransactionsFilter filter = new PublishTransactionsFilter(session);
filter.StateDate = DateTime.Now.AddHours(-1);

IEnumerable<PublishTransaction> transactions = PublishEngine.GetPublishTransactions(filter)
    .Where(t => t.State != PublishTransactionState.Success && t.State != PublishTransactionState.Failed);

foreach (PublishTransaction transaction in transactions)
{
    // Do cool stuff with transactions that are not Success or Failed
}

And with that I leave. Happy developing everyone!