Scrips, often used together with templates, are the quickest and simplest way to customize RT’s behavior. A scrip consists of a condition, an action, and a template. RT comes with a number of pre-defined conditions, actions, and templates, but you also can write your own. Scrips give you the freedom to implement whatever business logic you need.
Custom conditions and actions can be created via RT’s web interface, or you can create a Perl module for each custom condition and action.
Additionally, a scrip can have custom cleanup code that will run after all other code, but before the scrip exits. Custom scrip code is always written in standard Perl, and templates are created using the Text::Template
module.
Scrips can be applied across all queues or to individual queues. With the current version of RT, if you want to apply the same scrip to a subset of queues, you will have to go into each queue’s configuration and create the scrip for each one.
The scrips system is the last major part of RT that works exactly how it did when RT 2.0 came out in 2001.
In a future version of RT, the scrips system will be overhauled to make it easier to specify which scrips apply to which queues and to build more complex workflow.
A scrip is always run in the context of a transaction. Transactions represent a set of changes to a ticket. For example, when a ticket is resolved, its status changes, and a comment or reply may be added as part of the same transaction.
The transaction object is important when implementing custom scrip conditions and actions, as it lets you see what is being changed in the ticket.
Scrip action and templates often refer to Cc and AdminCc as email recipients, which are simply two generic recipient groups. The AdminCc group usually consists of some or all of the privileged RT users. The Cc group would be anyone else with an interest in a particular ticket. For example, in a tech support department, the support staff and their supervisors could all be in the AdminCc group. The AdminCc group consists of the people who work directly with RT the most. RT is by default configured to send different types of messages based on whether or not the recipient is in the AdminCc or Cc group. For example, by default RT includes the URL for a ticket when emailing a member of the AdminCc group.
RT comes with a set of standard conditions for scrips , as shown in Table 6-1.
Table 6-1. Scrip conditions
Condition |
Triggered |
---|---|
On Create |
When a new ticket is created. |
On Transaction |
When a ticket is modified in any way. |
On Correspond |
When a reply is created for the ticket. This condition is triggered for both replies created via the web interface and for replies received via email. |
On Comment |
When a comment is created for the ticket. Again, this applies to both the web interface and incoming email. |
On Status Change |
When the ticket’s status changes. |
On Resolve |
When the ticket is marked as resolved. |
On Priority Change |
When the ticket’s priority changes. |
On Owner Change |
When the ticket’s owner changes. |
On Queue Change |
When the ticket is moved to a new queue. |
Additionally, you can create a new scrip with a user-defined action. The following example is a very simple user-defined condition:
$self->TicketObj->status eq 'deleted';
This condition is true whenever a ticket’s status is changed to deleted
. This code is suitable for pasting into RT’s web interface.
The equivalent Perl module would look like this:
package RT::Condition::OnDelete; use strict; use base 'RT::Condition::Generic'; sub IsApplicable { my $self = shift; return ($self->TicketObj->status eq 'deleted'); } 1;
If your RT base directory is /opt/rt3, this code could be installed as /opt/rt3/local/lib/RT/Condition/OnDelete.pm. You can automate this process using the Module::Install::RTx
module, which is covered in Chapter 10.
You also need to make RT aware of this module, which means adding some information to the database. Mucking around with the database directly is not a good idea, but there is a simple way to write command-line scrips to manipulate RT’s database.
Use the RT::Interface::CLI
module to talk to RT:
#!/usr/bin/perl use strict; use lib "/opt/rt3/lib"; use RT; use RT::Interface::CLI qw( CleanEnv GetCurrentUser ); use RT::ScripCondition; CleanEnv(); RT::LoadConfig(); RT::Init(); my $user = GetCurrentUser(); unless( $user->Id ) { print "No RT user found. Please consult your RT administrator.\n"; exit 1; }
This first part is the voodoo needed to begin doing anything with RT from a command-line script. When GetCurrentUser()
is called, it will look for a user in the RT database matching the username running the script. It does this by checking the Unix login field for the user, which by default is empty. So you will need to set this for at least one user via the web interface.
Now that you have RT initialized and the current user loaded, you can add the following code to make RT aware of this new condition:
my $sc = RT::ScripCondition->new($user); $sc->Create( Name => 'On Delete', Description => 'When a ticket is deleted', ExecModule => 'OnDelete', ApplicableTransTypes => 'Status', );
The Name
is a short description for the condition and will be seen in the web interface.
After you run this script, when you go to create a new scrip in the web interface, you’ll see a new condition available in the condition select list.
Later in this chapter we’ll explore the possibilities for custom conditions in more detail.
An action is what the scrip does if its condition is true. RT comes with a number of actions built in, as shown in Table 6-2.
Table 6-2. Scrip actions
Action |
What it does |
---|---|
Autoreply to Requestors |
Send email to the ticket’s requestors. This differs from the various “notify” actions in that the generated email’s “From” header will have only the queue name. |
Notify Requestors, Notify Owner, etc. |
Send email to the specific group, such as the ticket’s requestors, its owner, etc. The email is generated using the template associated with the scrip. |
Notify Other Recipients |
This action allows you to specify arbitrary recipients by setting the “To” header in the template. In the template section, we create a custom template that takes advantage of this. |
Notify Owner As Comment, Notify Ccs as Comment, etc. |
When a notification is sent as a comment, the reply to address will be the queue’s comment address, not its correspondence address. |
Create Tickets |
This action can be used to create one or more new tickets based on the template associated with the scrip. This is one of the features that allows you to create workflows using RT. |
Open Tickets |
This action opens a ticket. RT uses this action to open resolved tickets when they receive correspondence. |
Of course, just as with conditions, you can write your own custom actions. And also like conditions, these actions can be defined either through a bit of Perl code pasted into the web interface, or as a separate module.
Let’s assume that you have an LDAP directory that lets you look up the department someone belongs to, based on their email address. You’d like to include that department as a custom field for all tickets.
To do that, you can create a custom action that sets this field for all newly created tickets. That means you would have this action run when the On Create condition was triggered, as part of the action preparation.
Your action code would look something like this:
my $email = ($self->TicketObj->RequestorAddresses)[0]; my $ldap = Net::LDAP->new( 'ldap.example.com' ); $ldap->bind; my $msg = $ldap->search( base => 'dc=ldap,dc=example,dc=com', filter => "(email=$email)", ); my $entry = $msg->entry(0); my $dept = $entry->get_value('ou'); my $cf = RT::CustomField->new( $RT::SystemUser ); $cf->LoadByName( Name => 'Department' ); $self->TicketObj->AddCustomFieldValue( Field => $cf, Value => $dept ); return 1;
This same code as a Perl module looks like this:
package RT::Action::AddDepartment; use strict; use base 'RT::Action::Generic'; sub Prepare { my $self = shift; my $email = ($self->TicketObj->RequestorAddresses)[0]; my $ldap = Net::LDAP->new( 'ldap.example.com' ); $ldap->bind; my $msg = $ldap->search( base => 'dc=ldap,dc=example,dc=com', filter => "(email=$email)", ); my $entry = $msg->entry(0); my $dept = $entry->get_value('ou'); my $cf = RT::CustomField->new( $RT::SystemUser ); $cf->LoadByName( Name => 'Department' ); $self->TicketObj->AddCustomFieldValue( Field => $cf, Value => $dept ); return 1; } 1;
Again, if you add a new module, you need to make RT aware of it by adding it to the database. This can be done with a script just like the one for adding new conditions, except with the following code at the end:
my $sc = RT::ScripAction->new($user); $sc->Create( Name => 'Add Department', Description => 'set department custom field for new tickets', ExecModule => 'AddDepartment', );
After creating this action, you can use the web interface to add a new scrip with the condition set to On Create and the action set to Add Department. You also need to make sure that there is a custom field named Department.
Each scrip can have one associated template. These usually generate email, but they can be used for any purpose you like. They allow you to generate arbitrary text based on the content of a transaction. For example, a scrip with the Create Tickets action will use the output of its template to generate new tickets.
Templates are written using the Text::Template
templating language, which is a very simple templating language allowing you to embed Perl in text. Perl code is placed in curly braces ({
and }
). Everything else is plain text.
RT installs the base templates shown in Table 6-3.
Table 6-3. Standard templates
Template |
Use |
---|---|
Blank |
Because every scrip must have an associated template, you should use this template when you don’t want to generate any text. |
Autoreply |
The default auto-reply template, which can be used when a new ticket is created. |
Transaction |
This is a very simple template that only includes some of the ticket’s information such as status and subject. |
Admin Correspondence |
Shows the contents of the transaction along with a URL to view the ticket. |
Correspondence |
Simply includes the contents of the transaction. |
Admin Comment |
Used to send comments to admins when one is made. |
Status Change |
A template specifically designed for sending email to admins when a ticket’s status is changed. |
Resolved |
This template is designed to be sent to requestors when a ticket is resolved. |
You can make your own custom templates. If you’re using a template to send email, you also can set the email headers from the template.
Let’s assume that you created a custom scrip to fire whenever someone steals a ticket. You want to send email to the former owner letting them know this.
The following example is a template for that email:
To: { my $old_owner = RT::User->new( $self->CurrentUser ); $old_owner->Load( $Transaction->OldValue ); $old_owner->EmailAddress() } Subject: Ticket #{ $Ticket->Id() } taken by { $Ticket->OwnerObj->Name() } A ticket you owned: { $Ticket->Subject() } has been taken by { $Ticket->OwnerObj->Name() }. { $RT::WebURL }Ticket/Display.html?id={ $Ticket->Id() }
If the template is being used to send an email, then you can set headers simply by specifying them as name/value pairs at the beginning of the template. In the code just shown the To and Subject header is set from the template.
To actually send this message only when a ticket is stolen also requires a custom condition, which we will see later.
If a transaction adds attachments to a ticket, and you want to include those attachments with the outgoing email, add this line to the top of your template:
RT-Attach-Message: Yes
Now that you have seen a number of simple examples of custom scrip code, let’s get into details of how these work and what objects are available for your custom code.
The two most important objects for custom scrip code and custom templates are the ticket and transaction objects. The ticket object is the ticket being modified. Any changes applied as part of the current transaction are reflected in the state of the related ticket object.
The transaction object represents the changes being made to the ticket. So for example, if the owner of a ticket is changed, then the transaction contains both the old and new owner IDs.
In a custom action or condition, these two objects are available via $self->TicketObj
and $self->TransactionObj
. You can access additional objects through those objects. For example, if you want the RT::User
object representing a ticket’s owner, you should use $self->TicketObj->OwnerObj
. To get the ticket’s queue object, use $self->TicketObj->QueueObj
.
For more details, read the documentation for the RT::Ticket
, RT::Ticket_Overlay
, RT::Transaction
, and RT::Transaction_Overlay
modules.
Besides the ticket and transaction, several other pieces of the RT API often are useful when creating custom scrips.
$object->CurrentUser()
Most objects in the RT class hierarchy inherit from RT::Base
, which provides a CurrentUser()
method. This represents the user currently operating on the object. In the web interface, this is the logged-in user who is triggering the scrip.
RT::Nobody()
The RT::Nobody() function returns an RT::User object for the Nobody user. You can compare this user’s ID to another user ID to check if that user is a real user. For example, you might have a condition that is true whenever the owner changes, as long as the previous owner wasn’t Nobody. Or you might have an action that is triggered only when a ticket is assigned from a real user to Nobody.
RT::SystemUser()
RT::SystemUser()
returns an RT::User
object for RT’s internal superuser, RT_SystemUser
. RT uses this user internally to do things that need to happen without access control, such as performing internal consistency checks. Generally, you shouldn’t ever do anything as the system user, but it’s ok to use to look at things if you want to avoid ACL checks.
$RT::Logger
This is a Log::Dispatch
object with outputs based on your RT config. You can call methods on this object like debug()
, warn()
, or error()
to log output from your code. This is useful both for debugging as well as for logging serious errors.
Of course, RT is a large system with a complex API. There are many other objects that you may want to make use of, such as queues, users, or custom fields. See Chapter 9 and the documentation for the relevant modules for more details on the API.
When you create or modify a scrip via the web interface, you are given a choice of Stage. The three options are TransactionCreate, TransactionBatch, and Disabled. Disabled simply means that the scrip will never run, and it is a simple way to turn off a scrip that you may want to re-enable later.
The TransactionCreate stage is the default for all scrips, and it is what all the scrips created use when RT is installed.
By default, RT does not enable the TransactionBatch stage. To turn it on you must add this line to your RT_SiteConfig.pm file:
Set($UseTransactionBatch , 1);
The difference between the create and batch stages is only relevant when an action creates multiple transactions, which can happen quite easily with normal web interface usage. For example, if you use the Jumbo form to change the subject of a ticket and assign it to someone else, you will end up creating at least two transactions, one for the subject change and one for the owner change.
When scrips run in the create stage, they run once for each transaction. Generally, this isn’t a problem, as the scrip’s condition will be true only for one of these transactions. But for some types of scrips, this may be problematic.
When a scrip’s stage is set to TranactionBatch, it will run only once, no matter how many transactions are generated. But it will have access to all of the transactions at once. We will show a specific example of why this is useful later.
Writing a custom condition is pretty simple. All your code has to do is return true or false. If it returns true, the scrip continues and executes its action. If it returns false, the scrip is done.
When you’re creating your own condition modules, you should always subclass RT::Condition::Generic
and then override the IsApplicable()
method. This is how the default actions that ship with RT all work.
Actions are actually divided into two phases, prepare and cleanup. The latter is often referred to as the commit phase in the internals.
If the action’s prepare phase code returns a false value, then the scrip is discarded and the action’s commit phase never runs. This allows an action to decide not to run. If your action always will be executed, you can just define code for the commit phase.
Note that stopping an action by returning false from the prepare phase is orthogonal to the scrip’s condition. You can mix and match conditions and actions, so you will still want your action to return false if it cannot execute. For example, you may have an action that creates a new ticket for the ticket’s owner. If the ticket’s owner is “Nobody,” you probably don’t want to run the action.
The commit phase should be where the action does things like send email, create a new ticket, etc.
When you create a custom action, you may want create a custom template to go with it. Or you might just want to change RT’s templates for the standard actions.
As we mentioned earlier, templates use Text::Template
for generating output. Of course, the authoritative source of information on the module is the module’s documentation, but there are a few points worth noting:
Anything enclosed in {
curly braces }
is Perl code. The code may contain multiple statements. The value of the last statement is used as output. If you do not want to generate output simply end the block with an empty string: '';
. Each separate block is in a separate scope, so you cannot use lexical variables created in another block.
Anything you append to the $OUT
variable appears in the template’s output:
One and two: { for ( 1..2 ) { $OUT .= " * $_\n"; } ''; }
This generates this text:
One and two: * 1 * 2
If you want to add a curly brace to the output, you can escape it: \{
.
The best way to understand what custom scrips can do is to look at some complete examples.
The custom template we looked at earlier notified a user when another user stole their ticket. Let’s examine that example in more detail. First, we’ll implement it solely via the web interface, and then we’ll reimplement it as custom modules.
From the web interface, we can create a new scrip. Let’s call it Ticket Stolen. The condition should be set to User Defined. The action will be Notify Other Recipients. We will create a custom template to generate an email to the right recipient. If you created that template earlier, you can set the Template field to use that one now. Otherwise you can leave it empty for now.
The stage for this scrip will be TransactionCreate.
Since there is no OnSteal condition built into RT, we need to create one.
The following code goes in the Custom condition text entry box:
my $trans = $self->TransactionObj; return 0 unless $trans->Field eq 'Owner'; return 1 if $trans->OldValue != RT::Nobody()->id(); return 0;
The first thing we check is that the transaction to which the condition is being applied is changing the Owner field. If it’s not, we return false. Obviously, stealing a ticket involves changing the ticket’s owner.
Next, we check to make sure that the old owner is not the nobody user. If the ticket had no previous owner, then the ticket isn’t being stolen. The RT::Nobody()->id
syntax is a bit of an oddity. We’re calling a function called Nobody
in the RT
namespace. This function returns an RT::User
object, upon which we call the id()
method. If we just wrote RT::Nobody->id()
, the Perl interpreter would try to call the id()
method on a non-existent RT::Nobody
class.
Finally, we return false as a default. Adding this to the end of the condition code is a good practice, since it makes the default clear.
We are going to use the same template we saw earlier. Here it is again:
To: { my $old_owner = RT::User->new( $self->CurrentUser ); $old_owner->Load( $Transaction->OldValue ); $old_owner->EmailAddress() } Subject: Ticket #{ $Ticket->Id() } taken by { $Ticket->OwnerObj->Name() } A ticket you owned: { $Ticket->Subject() } has been taken by {$Ticket->OwnerObj->Name()}. { $RT::WebURL }Ticket/Display.html?id={ $Ticket->Id }
You might wonder what will happen if the old owner doesn’t have an email address. The answer is nothing. If the template’s To
header is empty, then RT will not try to send email. Although it’s not necessary, you could add a bit of action preparation code like this if you wanted:
my $old_owner = RT::User->new( $self->CurrentUser ); $old_owner->Load( $Transaction->OldValue ); return (defined $old_owner->EmailAddress());
Once the template is created, the Ticket Stolen scrip can use it. Once that’s done, you’re all set with your custom business logic.
When someone emails RT for the first time, RT will add them as a user to the database. However, by default this new user will not be able to log in to the system to view their tickets. If you want them to be able to log in, they need to have a password.
You can customize the existing AutoReply template to create a new password for them and include it in the response. For existing users, you can include their current password.
The customized template might look like this:
Subject: AutoReply: { $Ticket->Subject() } Greetings, This message has been automatically generated in response to the creation of a trouble ticket regarding: "{ $Ticket->Subject() }", a summary of which appears below. There is no need to reply to this message right now. Your ticket has been assigned an ID of [{ $rtname } #{ $Ticket->Id() }]. Please include the string: [{ $rtname } #{ $Ticket->Id() }] in the subject line of all future correspondence about this issue. To do so, you may reply to this message. You may log into the Request Tracker system to view your past and current tickets at: { $RT::WebURL } { if ( $Transaction->CreatorObj->id != $RT::Nobody->id && ! $Transaction->CreatorObj->Privileged && ! $Transaction->CreatorObj->HasPassword ) { my $user = RT::User->new( $RT::SystemUser ); $user->Load( $Transaction->CreatorObj->Id ); my ($stat, $password) = $user->SetRandomPassword(); if ($stat) { my $username = $user->Name; $OUT .= " When prompted to log in, please use the following credentials: Username: $username Password: $password "; } else { $RT::Logger->error( 'Error trying to set random password for ' . $user->Name . ": $password" ); $OUT .= " There was an error when trying to assign you a new password. Please contact your local RT administrator at for assistance. "; } } } Thank you, { $Ticket->QueueObj->CorrespondAddress() } ------------------------------------------------------------------------- {$Transaction->Content()}
This template is the same as the one that ships with RT, except for the section in the middle, which automatically assigns a new password to the user if they do not already have one.
Several pieces are worth noting in this template. First, there is the check to see if the user has a password:
unless ($Transaction->CreatorObj->id == $RT::Nobody->id && $Transaction->CreatorObj->Privileged && $Transaction->CreatorObj->HasPassword ) { ...
We want to make sure that we don’t try to give the Nobody user a password, as this user is solely for internal use. We also do not want to auto-generate a password for privileged users, because we assume that the RT administrator manually manages these users. Finally, we need to make sure that the user doesn’t already have a password.
When we call $user->SetRandomPassword()
we check the return value. This method returns a two item list. The first is a return code indicating success (true) or failure (false). If creating the password succeeded, the second item is the new password, which we include in the email. If the password could not be created for some reason, the second item is the error message. We make sure to log this error so that the RT administrator can follow up on it later.
If you are using RT to handle a support or sysadmin center, it might be useful to send a message to a pager for certain types of requests.
Let’s take a look at how to set up RT to send pages for new tickets with a subject matching /^Emergency/i
, but only from 6 p.m. to 8 a.m. These could come from users or from system monitoring software.
This can be done by creating a custom condition and template. The condition checks the subject and time. The template creates the SMS message. To send email, we can use RT’s existing Notify action.
package RT::Condition::OnAfterHoursEmergency; use strict; use base 'RT::Condition::Generic'; sub IsApplicable { my $self = shift; return 0 unless $self->TicketObj->Subject =~ /^Emergency/i; my $hour = (localtime)[2]; return 0 unless $hour >= 18 || $hour <= 8; return 1; } 1;
First, we check the ticket subject to make sure that it indicates an emergency. Then we check the time. This will work properly only if the server RT runs on has the correct time. If all of the checks pass, we return a true value. We also will want to make sure that this condition is checked only when creating a ticket, but that can be done when we register the condition with the system.
Now let’s create our template:
From: Yoyodyne RT <rt@yoyodyne.example.com> To: 6125559912@pager.example.com Subject: Yoyodyne RT 911 { $Ticket->Subject() }
We want to keep the message short, so we use only the ticket’s subject as the email body. The template can be created in the system with RT’s web interface.
To add our custom condition, we need two steps. First, we need to save the code to our RT installation’s local lib directory. This would be /opt/rt3/local/lib/RT/Condition/OnAfterHoursEmergency.pm in this example.
Then we can use a script like the one we saw before to register the condition with RT:
#!/usr/bin/perl use strict; use lib "/opt/rt3/lib"; use RT; use RT::Interface::CLI qw( CleanEnv GetCurrentUser ); use RT::ScripCondition; CleanEnv(); RT::LoadConfig(); RT::Init(); my $user = GetCurrentUser(); unless( $user->Id ) { print "No RT user found. Please consult your RT administrator.\n"; exit 1; } my $sc = RT::ScripCondition->new($user); $sc->Create( Name => 'After Hours Emergency', Description => 'An emergency ticket is created after hours', ExecModule => 'OnAfterHoursEmergency', ApplicableTransTypes => 'Create', );
Note that ApplicableTransTypes
field is set to Create, ensuring that this condition is checked only when a new ticket is created. We could have done this in the condition module’s IsApplicable()
method, but this is more efficient.
To create a new scrip for this condition, we would pick After Hours Emergency from the condition drop down in the new scrip form. The action will be Notify Other Recipients. The actual recipients will be picked up from the template. For the template, we use the one we just created.
If we had different pager numbers for different queues, we could create several templates. Then we would set up scrips for each queue, all using the same condition and action, each with a different template.
Earlier, we talked about the two stages where scrips could run, TransactionCreate and TransactionBatch. The latter stage needs to be enabled in your configuration before it is available.
This might be needed, for example, if you wanted to send an email whenever any of the custom fields for a ticket were updated. Because of the way custom fields work, each change to a custom field is a separate transaction. If a ticket might have five custom fields, we do not want to send five emails every time that ticket is updated!
We can use the TransactionBatch stage to look at all of the transactions at once. Here’s an example of a simple template that would run in the TransactionBatch stage:
{ my @batch = @{ $Ticket->TransactionBatch }; foreach my $txn ( @batch ) { if ( $txn->Type eq 'CustomField' ) { $OUT .= '* ' . $txn->Description . "\n"; } } return $OUT; }
This template could be used with a scrip that had this custom condition:
my @batch = @{ $Ticket->TransactionBatch }; foreach my $txn ( @batch ) { if ( $txn->Type eq 'CustomField' ) { return 1; } } return 0;
This scrip simply checks all the transactions in the batch to see if any of them changed custom fields.
The action for this scrip would be one of the Notify actions, depending on exactly who should receive the email.
This is a simple example, but it illustrates the basic idea behind the TransactionBatch stage, which is to allow you to handle a group of transactions all at once.
As we mentioned earlier, one of the uses for custom scrips is to implement a workflow system in RT. Let’s look at a simple example of this concept, which uses the ability to have a scrip create a new ticket.
For our example, let’s assume that we have two groups of people using RT, each with their own queue. Both of these groups are working in some sort of agency that does creative work, like a graphic design firm.
The first group is the designers. Their queue has tickets like “create mockup of poster for Faye Wong’s spring tour.” The other group is the account representatives. They use RT to track the designer’s work, so they know when things are ready for review with the clients.
It would be possible to simply transfer tickets from one queue to the other, so that when a designer wanted a representative to review their work, they would transfer the ticket to the Review queue and assign ownership to Nobody. But if we wanted to use RT for time tracking, by updating the time worked field for tickets, this could get awkward. In this case, it’s better to create a new ticket for each piece of work. And of course, ideally, every time a ticket in the Design queue is closed, a new ticket in the Review queue is opened. If a given project needs additional design work, the account representative can create a new ticket in the Design queue.
To make the automatic creation of Review tickets happen, we can create a new scrip where the condition is On Resolve, and the action is Create Tickets. We just need to create a custom template that creates the correct ticket in the Review queue. That template would look something like this:
= = =Create-Ticket: design-review Queue: Review Subject: Review of { $Tickets{'TOP'}->Subject() } Owner: { $Tickets{'TOP'}->Requestors->UserMembersObj->Next->Id() } Requestor: { $Tickets{'TOP'}->OwnerObj->EmailAddress() } RefersTo: { $Tickets{'TOP'}->Id() } Content: A ticket in the Design queue has been resolved and requires review.
A template used by the Create Tickets action has a special format. It can create one or more tickets. Each ticket to be created has its own header, which in our example is the first line. The header is = = =Create-Ticket:
followed by a name.
If you were creating multiple tickets, then each ticket could access the tickets already created via the %Tickets
hash. This is handy if you want to create several tickets and link them together. The ticket that triggered the Create Tickets action is always available as $Tickets{'TOP'}
.
In our example, we create only one ticket. The subject of the new ticket is based on the ticket being resolved. The owner of the ticket is the first requestor of the original ticket. The requestor is the ticket’s owner. We want the new ticket to have a link back to the original ticket, so we set the RefersTo field.
Finally, the Content field is the body of the new ticket. This could be combined with a custom script to send an email whenever a new ticket is created in the Review queue with an owner (as opposed to being owned by Nobody). This way the owner of the new ticket would know that it was their responsibility to review the work just completed.