https://rt-wiki.bestpractical.com/api.php?action=feedcontributions&user=Rabin&feedformat=atomRequest Tracker Wiki - User contributions [en]2024-03-28T08:41:35ZUser contributionsMediaWiki 1.37.2https://rt-wiki.bestpractical.com/index.php?title=CustomActionSnippets&diff=27001CustomActionSnippets2021-03-07T19:14:34Z<p>Rabin: /* We want to log all custom and standard fields to syslog, so we can generate reports and audit logs, i.e. with Splunk. This would go in transaction batch */</p>
<hr />
<div>= Introduction =<br />
<br />
This page introduce you with some code that can be used in [[ScripAction]]s and is part of [[CodeSnippets]] series of articles.<br />
<br />
= Custom actions specifics =<br />
<br />
You better read [[WriteCustomAction]] before to understand basics.<br />
<br />
= Code snippets =<br />
<br />
== Change ticket's queue ==<br />
<br />
my $TargetQueueName = 'MyQueue';<br />
my ($status, $msg) = $TicketObj-&gt;SetQueue( $TargetQueueName );<br />
...<br />
<br />
== Set current actor as Ticket owner ==<br />
<br />
my $Actor = $self-&gt;TransactionObj-&gt;CreatorObj-&gt;Id;<br />
if( $Actor != $self-&gt;TicketObj-&gt;OwnerObj-&gt;Id ) {<br />
$RT::Logger-&gt;info("Auto assign ticket #". $self-&gt;TicketObj-&gt;id ." to user #". $Actor );<br />
<br />
my ($status, $msg) = $self-&gt;TicketObj-&gt;SetOwner( $Actor );<br />
unless( $status ) {<br />
die "Error: $msg";<br />
}<br />
}<br />
return 1;<br />
<br />
== Set owner to nobody ==<br />
<br />
We want to be able to assign cases to unprivileged customers, once they have replied the case should be released:<br />
$self-&gt;TicketObj-&gt;SetOwner( $RT::Nobody-&gt;id );<br />
return 1;<br />
<br />
== Get related tickets ==<br />
If you need to grab a given ticket's parent(s), child(ren), or other related tickets.<br />
<br />
my $ParentTickets = $self->TicketObj->MemberOf;<br />
my $FirstParentTicketLink = $ParentTickets->Next;<br />
my $FirstParentTicket = $FirstParentTicketLink->TargetObj;<br />
my $SecondParentTicketLink = $ParentTickets->Next;<br />
my $SecondParentTicket = $SecondParentTicketLink->TargetObj;<br />
<br />
my $DependsOnTickets = $self->TicketObj->DependsOn;<br />
my $FirstDependsOnTicketLink = $DependsOnTickets->Next;<br />
my $FirstDependsOnTicket = $FirstDependsOnTicketLink->TargetObj;<br />
my $SecondDependsOnTicketLink = $DependsOnTickets->Next;<br />
my $SecondDependsOnTicket = $SecondDependsOnTicketLink->TargetObj;<br />
<br />
my $RefersToTickets = $self->TicketObj->RefersTo;<br />
my $FirstRefersToTicketLink = $RefersToTickets->Next;<br />
my $FirstRefersToTicket = $FirstRefersToTicketLink->TargetObj;<br />
my $SecondRefersToTicketLink = $RefersToTickets->Next;<br />
my $SecondRefersToTicket = $SecondRefersToTicketLink->TargetObj;<br />
<br />
my $ChildTickets = $self->TicketObj->Members;<br />
my $FirstChildTicketLink = $ChildTickets->Next;<br />
my $FirstChildTicket = $FirstChildTicketLink->BaseObj;<br />
my $SecondChildTicketLink = $ChildTickets->Next;<br />
my $SecondChildTicket = $SecondChildTicketLink->BaseObj;<br />
<br />
my $DependedOnByTickets = $self->TicketObj->DependedOnBy;<br />
my $FirstDependedOnByTicketLink = $DependedOnByTickets->Next;<br />
my $FirstDependedOnByTicket = $FirstDependedOnByTicketLink->BaseObj;<br />
my $SecondDependedOnByTicketLink = $DependedOnByTickets->Next;<br />
my $SecondDependedOnByTicket = $SecondDependedOnByTicketLink->BaseObj;<br />
<br />
my $ReferredToByTickets = $self->TicketObj->ReferredToBy;<br />
my $FirstReferredToByTicketLink = $ReferredToByTickets->Next;<br />
my $FirstReferredToByTicket = $FirstReferredToByTicketLink->BaseObj;<br />
my $SecondReferredToByTicketLink = $ReferredToByTickets->Next;<br />
my $SecondReferredToByTicket = $SecondReferredToByTicketLink->BaseObj;<br />
<br />
== Log custom fields and standard fields to syslog ==<br />
* We want to log all custom and standard fields to syslog, so we can generate reports and audit logs, i.e. with Splunk. This would go in transaction batch<br />
<br />
<SyntaxHighlight lang="perl"><br />
#On ticket creation, log standard and custom fields<br />
my $ticket = $self->TicketObj;<br />
<br />
#create a hash with all the standard fields we want<br />
my %tkt_props;<br />
$tkt_props{ticket} = $self->TicketObj->id;<br />
$tkt_props{queue} = $self->TicketObj->QueueObj->Name;<br />
$tkt_props{status} = $self->TicketObj->Status;<br />
$tkt_props{subject} = $self->TicketObj->Subject;<br />
$tkt_props{priority} = $self->TicketObj->Priority;<br />
$tkt_props{owner} = $self->TicketObj->OwnerObj->Name;<br />
$tkt_props{requestor_email} = $ticket->RequestorAddresses;<br />
<br />
#add custom fields to the hash as well<br />
my $CustomFields = $ticket->QueueObj->TicketCustomFields();<br />
while (my $CustomField = $CustomFields->Next()) {<br />
my $name = $CustomField->Name;<br />
my $val = $ticket->FirstCustomFieldValue($name);<br />
$tkt_props{$name} = $val;<br />
}<br />
<br />
#create the string to log: RT4 transaction detected: field="value" field_2="value with spaces"<br />
my $log_string = "RT4 transaction detected:";<br />
foreach my $k (sort keys %tkt_props) {<br />
my $val = $tkt_props{$k}; #easier than each if you want them sorted<br />
$k =~ s/\s+/_/g; #remove spaces from name field and replace with _<br />
$k =~ tr/"//; #there really shouldn't be any quotes in the name, but strip them anyway<br />
$val =~ s/\s+/ /; #replace all whitespace with a single space char for Splunk<br />
$val =~ tr/"/'/; #replace double quotes with single for Splunk<br />
$log_string .= qq| $k="$val"|; #wrap values in quotes<br />
}<br />
<br />
$RT::Logger->info( $log_string );<br />
<br />
return 1;<br />
</SyntaxHighlight><br />
<br />
== Log transaction content to syslog ==<br />
* We want to log all content in tickets to syslog, so we can generate reports and audit logs, i.e. with Splunk. This would go in transaction create.<br />
<br />
<SyntaxHighlight lang="perl"><br />
#log content of last transaction, iff it was a create, comment, or correspond<br />
my $ticket_id = $self->TicketObj->id;<br />
my $Transactions = $self->TicketObj->Transactions;<br />
$Transactions->OrderBy(FIELD => 'Created', ORDER => 'DESC');<br />
my $Transaction = $Transactions->Next;<br />
my $transaction_type = $Transaction->Type;<br />
#$RT::Logger->debug( "RT4 ticket=$ticket_id transaction_type=$transaction_type" );<br />
if ( $Transaction->Type =~ /^(Correspond|Comment|EmailRecord|CommentEmailRecord|Create|Resolve)$/ && $Transaction->Content ) {<br />
#might need to strip html tags here?<br />
my $content = $Transaction->Content;<br />
$content =~ s/\s+/ /g; #replace all spaces with a single space char<br />
$content =~ tr/"/'/; #replace all double quotes with a single quote for splunk field extraction<br />
my $log_string = qq|RT4 message detected: ticket="$ticket_id" transaction_type="$transaction_type" content="$content"|;<br />
$RT::Logger->info( $log_string );<br />
}<br />
<br />
return 1;<br />
</SyntaxHighlight><br />
<br />
= More examples =<br />
<br />
[[CodeSnippets]]<br />
<br />
= See also =<br />
<br />
[[WriteCustomAction]], [[CodeSnippets]], [[Scrip]]s</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=CustomActionSnippets&diff=27000CustomActionSnippets2021-03-07T19:02:17Z<p>Rabin: /* We want to log all content in tickets to syslog, so we can generate reports and audit logs, i.e. with Splunk. This would go in transaction create. */</p>
<hr />
<div>= Introduction =<br />
<br />
This page introduce you with some code that can be used in [[ScripAction]]s and is part of [[CodeSnippets]] series of articles.<br />
<br />
= Custom actions specifics =<br />
<br />
You better read [[WriteCustomAction]] before to understand basics.<br />
<br />
= Code snippets =<br />
<br />
== Change ticket's queue ==<br />
<br />
my $TargetQueueName = 'MyQueue';<br />
my ($status, $msg) = $TicketObj-&gt;SetQueue( $TargetQueueName );<br />
...<br />
<br />
== Set current actor as Ticket owner ==<br />
<br />
my $Actor = $self-&gt;TransactionObj-&gt;CreatorObj-&gt;Id;<br />
if( $Actor != $self-&gt;TicketObj-&gt;OwnerObj-&gt;Id ) {<br />
$RT::Logger-&gt;info("Auto assign ticket #". $self-&gt;TicketObj-&gt;id ." to user #". $Actor );<br />
<br />
my ($status, $msg) = $self-&gt;TicketObj-&gt;SetOwner( $Actor );<br />
unless( $status ) {<br />
die "Error: $msg";<br />
}<br />
}<br />
return 1;<br />
<br />
== Set owner to nobody ==<br />
<br />
We want to be able to assign cases to unprivileged customers, once they have replied the case should be released:<br />
$self-&gt;TicketObj-&gt;SetOwner( $RT::Nobody-&gt;id );<br />
return 1;<br />
<br />
== Get related tickets ==<br />
If you need to grab a given ticket's parent(s), child(ren), or other related tickets.<br />
<br />
my $ParentTickets = $self->TicketObj->MemberOf;<br />
my $FirstParentTicketLink = $ParentTickets->Next;<br />
my $FirstParentTicket = $FirstParentTicketLink->TargetObj;<br />
my $SecondParentTicketLink = $ParentTickets->Next;<br />
my $SecondParentTicket = $SecondParentTicketLink->TargetObj;<br />
<br />
my $DependsOnTickets = $self->TicketObj->DependsOn;<br />
my $FirstDependsOnTicketLink = $DependsOnTickets->Next;<br />
my $FirstDependsOnTicket = $FirstDependsOnTicketLink->TargetObj;<br />
my $SecondDependsOnTicketLink = $DependsOnTickets->Next;<br />
my $SecondDependsOnTicket = $SecondDependsOnTicketLink->TargetObj;<br />
<br />
my $RefersToTickets = $self->TicketObj->RefersTo;<br />
my $FirstRefersToTicketLink = $RefersToTickets->Next;<br />
my $FirstRefersToTicket = $FirstRefersToTicketLink->TargetObj;<br />
my $SecondRefersToTicketLink = $RefersToTickets->Next;<br />
my $SecondRefersToTicket = $SecondRefersToTicketLink->TargetObj;<br />
<br />
my $ChildTickets = $self->TicketObj->Members;<br />
my $FirstChildTicketLink = $ChildTickets->Next;<br />
my $FirstChildTicket = $FirstChildTicketLink->BaseObj;<br />
my $SecondChildTicketLink = $ChildTickets->Next;<br />
my $SecondChildTicket = $SecondChildTicketLink->BaseObj;<br />
<br />
my $DependedOnByTickets = $self->TicketObj->DependedOnBy;<br />
my $FirstDependedOnByTicketLink = $DependedOnByTickets->Next;<br />
my $FirstDependedOnByTicket = $FirstDependedOnByTicketLink->BaseObj;<br />
my $SecondDependedOnByTicketLink = $DependedOnByTickets->Next;<br />
my $SecondDependedOnByTicket = $SecondDependedOnByTicketLink->BaseObj;<br />
<br />
my $ReferredToByTickets = $self->TicketObj->ReferredToBy;<br />
my $FirstReferredToByTicketLink = $ReferredToByTickets->Next;<br />
my $FirstReferredToByTicket = $FirstReferredToByTicketLink->BaseObj;<br />
my $SecondReferredToByTicketLink = $ReferredToByTickets->Next;<br />
my $SecondReferredToByTicket = $SecondReferredToByTicketLink->BaseObj;<br />
<br />
== Log custom fields and standard fields to syslog ==<br />
= <span style="font-size:13px;">We want to log all custom and standard fields to syslog, so we can generate reports and audit logs, i.e. with Splunk. This would go in transaction batch</span> =<br />
#On ticket creation, log standard and custom fields<br />
my $ticket = $self->TicketObj;<br />
<br />
#create a hash with all the standard fields we want<br />
my %tkt_props;<br />
$tkt_props{ticket} = $self->TicketObj->id;<br />
$tkt_props{queue} = $self->TicketObj->QueueObj->Name;<br />
$tkt_props{status} = $self->TicketObj->Status;<br />
$tkt_props{subject} = $self->TicketObj->Subject;<br />
$tkt_props{priority} = $self->TicketObj->Priority;<br />
$tkt_props{owner} = $self->TicketObj->OwnerObj->Name;<br />
$tkt_props{requestor_email} = $ticket->RequestorAddresses;<br />
<br />
#add custom fields to the hash as well<br />
my $CustomFields = $ticket->QueueObj->TicketCustomFields();<br />
while (my $CustomField = $CustomFields->Next()) {<br />
my $name = $CustomField->Name;<br />
my $val = $ticket->FirstCustomFieldValue($name);<br />
$tkt_props{$name} = $val;<br />
}<br />
<br />
#create the string to log: RT4 transaction detected: field="value" field_2="value with spaces"<br />
my $log_string = "RT4 transaction detected:";<br />
foreach my $k (sort keys %tkt_props) {<br />
my $val = $tkt_props{$k}; #easier than each if you want them sorted<br />
$k =~ s/\s+/_/g; #remove spaces from name field and replace with _<br />
$k =~ tr/"//; #there really shouldn't be any quotes in the name, but strip them anyway<br />
$val =~ s/\s+/ /; #replace all whitespace with a single space char for Splunk<br />
$val =~ tr/"/'/; #replace double quotes with single for Splunk<br />
$log_string .= qq| $k="$val"|; #wrap values in quotes<br />
}<br />
<br />
$RT::Logger->info( $log_string );<br />
return 1;<span style="font-size:13px;"> </span><br />
<br />
== Log transaction content to syslog ==<br />
* We want to log all content in tickets to syslog, so we can generate reports and audit logs, i.e. with Splunk. This would go in transaction create.<br />
<br />
<SyntaxHighlight lang="perl"><br />
#log content of last transaction, iff it was a create, comment, or correspond<br />
my $ticket_id = $self->TicketObj->id;<br />
my $Transactions = $self->TicketObj->Transactions;<br />
$Transactions->OrderBy(FIELD => 'Created', ORDER => 'DESC');<br />
my $Transaction = $Transactions->Next;<br />
my $transaction_type = $Transaction->Type;<br />
#$RT::Logger->debug( "RT4 ticket=$ticket_id transaction_type=$transaction_type" );<br />
if ( $Transaction->Type =~ /^(Correspond|Comment|EmailRecord|CommentEmailRecord|Create|Resolve)$/ && $Transaction->Content ) {<br />
#might need to strip html tags here?<br />
my $content = $Transaction->Content;<br />
$content =~ s/\s+/ /g; #replace all spaces with a single space char<br />
$content =~ tr/"/'/; #replace all double quotes with a single quote for splunk field extraction<br />
my $log_string = qq|RT4 message detected: ticket="$ticket_id" transaction_type="$transaction_type" content="$content"|;<br />
$RT::Logger->info( $log_string );<br />
}<br />
return 1;<br />
</SyntaxHighlight><br />
<br />
= More examples =<br />
<br />
[[CodeSnippets]]<br />
<br />
= See also =<br />
<br />
[[WriteCustomAction]], [[CodeSnippets]], [[Scrip]]s</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=REST&diff=26999REST2021-02-23T20:40:15Z<p>Rabin: /* Ticket Edit */</p>
<hr />
<div>== Abstract ==<br />
<br />
The REST Interface gives you access to your RT Database. The complete communication is encapsulated in the HTTP protocol. The interface should be accessible in your installation.<br />
<br />
Though you may see references to older 3.x releases of RT below, the REST 1.0 interface has not changed in any significant way in 4.x.<br />
<br />
This page is for REST version 1.0. The next version of the RT REST interface, version 2.0, is available as an [https://metacpan.org/pod/RT::Extension::REST2 extension].<br />
<br />
[{{SERVER}}/index.php?search={{TALKPAGENAME}}&title=Special:Search Legacy Comments] are available from the previous Wikia instance.<br />
<br />
== Interface ==<br />
<br />
Base URL: <code>.../REST/1.0/</code>. The default response should be:<br />
<br />
RT/3.4.5 200 Ok<br />
<br />
# Invalid object specification: 'index.html'<br />
<br />
id: index.html<br />
<br />
<br />
=== Authentication ===<br />
<br />
The REST Interface does not support HTTP-Authentication. So you must get a valid Session-Token and submit the cookie each request. You usually get a Session-Cookie by submitting the default login form. Use variables "<code>user</code>" for login and "<code>pass</code>" for password values. wget doesn't escape any characters in the --post-data option so make sure you properly escape any special characters in the password.<br />
<br />
See the wget invocation line below:<br />
<br />
wget --keep-session-cookies \<br />
--save-cookies cookies.txt \<br />
--post-data 'user=UUUU&pass=PPPP' \<br />
http://my.rt.server<br />
<br />
You need the -keep-session-cookies option to make wget save session cookies.<br />
<br />
=== Ticket ===<br />
<br />
==== Ticket Properties ====<br />
<br />
Gets the data for a single ticket, not including the history and comments.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/show</code><br />
<br />
RT/3.4.5 200 Ok<br />
<br />
id: ticket/&lt;ticket-id&gt;<br />
Queue: &lt;...&gt;<br />
Owner: &lt;...&gt;<br />
Creator: &lt;...&gt;<br />
Subject: &lt;...&gt;<br />
Status: &lt;...&gt;<br />
Priority: &lt;...&gt;<br />
InitialPriority: &lt;...&gt;<br />
FinalPriority: &lt;...&gt;<br />
Requestors: &lt;...&gt;<br />
Cc: &lt;...&gt;<br />
AdminCc: &lt;...&gt;<br />
Created: &lt;...&gt;<br />
Starts: &lt;...&gt;<br />
Started: &lt;...&gt;<br />
Due: &lt;...&gt;<br />
Resolved: &lt;...&gt;<br />
Told: &lt;...&gt;<br />
TimeEstimated: &lt;...&gt;<br />
TimeWorked: &lt;...&gt;<br />
TimeLeft: &lt;...&gt;<br />
<br />
==== Ticket Links ====<br />
<br />
Gets the ticket links for a single ticket.<br />
<br />
Request: <code>REST/1.0/ticket/&lt;ticket-id&gt;/links/show</code><br />
<br />
RT/3.8.2 200 Ok<br />
<br />
id: ticket/&lt;ticket-id&gt;/links<br />
HasMember: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
ReferredToBy: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
DependedOnBy: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
MemberOf: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
RefersTo: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
DependsOn: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
<br />
==== Ticket Attachments ====<br />
<br />
Gets a list of all attachments related to the ticket<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments</code><br />
<br />
==== Ticket Attachment ====<br />
Gets the metadata and content of a specific attachment.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments/&lt;attachment-id&gt;</code><br />
<br />
<pre><br />
RT/3.8.0 200 Ok<br />
<br />
id: <attachment-id><br />
Subject:<br />
Creator: <user-id><br />
Created: <timestamp><br />
Transaction: <transaction-id><br />
Parent: <parent-id><br />
MessageId:<br />
Filename: <filename><br />
ContentType: application/octet-stream<br />
ContentEncoding: none<br />
<br />
Headers: MIME-Version: 1.0<br />
X-Mailer: MIME-tools 5.427 (Entity 5.427)<br />
Content-Type: application/octet-stream;<br />
name="<filename>"<br />
Content-Disposition: inline; filename="<filename>"<br />
Content-Transfer-Encoding: base64<br />
Content-Length: <length in bytes><br />
<br />
Content: ...<br />
...<br />
...<br />
</pre><br />
<br />
''NOTE: RT returns the content indented with 9 spaces on each line, so that it lines up with the "Content:" header. Even if you strip this out with a regexp, the content is still UTF-8, which is probably not what you want. To get the original binary data back, strip out the 9 spaces with a regexp, strip off the 3 carriage returns at the end, and then convert the whole thing from UTF-8 to the native character encoding of the attachment, whatever that is. RT doesn't tell you, so you have know. If the attachments were uploaded by a U.S. Windows system, odds are that Windows-1252 is what you want. If you can't get the binary back intact, see the next method below.''<br />
<br />
==== Ticket Attachment Content ====<br />
<br />
Gets the attachment data content without additional metadata or whitespace characters<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments/&lt;attachment-id&gt;/content</code><br />
<br />
RT/3.8.0 200 Ok<br />
<br />
...<br />
...<br />
...<br />
<br />
So to get the original content you still have to strip the first 2 lines of the response.<br />
<br />
==== Ticket History ====<br />
Gets a list of all the history items for a given ticket.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history</code><br />
<br />
<pre><br />
RT/3.4.5 200 Ok<br />
<br />
# <history-count>/<history-count> (/total)<br />
<br />
<history-id>: <history-name><br />
<history-id>: <history-name><br />
...<br />
</pre><br />
<br />
You will get an additional row, for each history entry found. The first entry is usually: "<code>Ticket created by ...</code>".<br />
<br />
There are two ways to get history item detail: you can do one of these and then recursively perform <code>ticket/history/id/&lt;history-id&gt;</code> for each history-id from this REST call, but that is extremely wasteful and will scale horribly. What you really want to do is one REST call but get the long format:<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history?format=l</code><br />
<br />
<pre><br />
RT/3.8.2 200 Ok<br />
<br />
# <n>/<n> (id/&lt;history-id&gt;/total)<br />
<br />
id: <history-id><br />
Ticket: <ticket-id><br />
TimeTaken: <...><br />
Type: <...><br />
Field: <...><br />
OldValue: <...><br />
NewValue: <...><br />
Data: <...><br />
Description: <...><br />
Content: <...><br />
Creator: <...><br />
<br />
Created: <...><br />
<br />
Attachments:<br />
<attachment-id>: <filename> (<size>)<br />
<attachment-id>: <filename> (<size>)<br />
<br />
--<br />
<br />
# <n>/<n> (id/&lt;history-id&gt;/total)<br />
...<br />
</pre><br />
<br />
''NOTE: the double dash "--" will occur in the long format between each history item. You can split the output on "--" and iterate over it, parsing out the data with an RFC822 parser, such as an email handling library.''<br />
<br />
==== Ticket History Entry ====<br />
Gets the history information for a single history item. Note that the history item must actually correspond to the ticket.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history/id/&lt;history-id&gt;</code><br />
<br />
<pre><br />
RT/3.4.5 200 Ok<br />
<br />
# 70/70 (id/114856/total)<br />
<br />
id: <history-id><br />
Ticket: <ticket-id><br />
TimeTaken: <...><br />
Type: <...><br />
Field: <...><br />
OldValue: <...><br />
NewValue: <...><br />
Data: <...><br />
Description: <...><br />
<br />
Content: <lin1-0><br />
<line-1><br />
...<br />
<line-n><br />
<br />
Creator: <...><br />
Created: <...><br />
Attachments: <...><br />
</pre><br />
<br />
''IMPORTANT NOTE: At least with RT 3.8.0, when you request a history item with this method AND you have attached a file that has Mime type text/plain to the same item (eg. a comment with an attachement), RT will return the complete content of the attachment for the key "Content:" and not your real comment that you can see in the web frontend. This may lead to some problems if the requestor does not expect to get a comment content that is for example 1.8 MB of text. With other Mime type attachments this however seems to work. I don't know if this is a feature or a bug.''<br />
<br />
<br />
==== '''Ticket Search''' ====<br />
Request: <code>/REST/1.0/search/ticket?query=&lt;query&gt;&orderby=&lt;sort-order&gt;&format=&lt;format&gt;</code><br />
<br />
'''Parameters'''<br />
<br />
'''query'''<br />
<br />
You can use any query generated by the query builder - or feel free to write your own. Here an example that will do the following: Find all tickets that have no owner and the status new or open<br />
<br />
<code>query= Owner = 'Nobody' AND ( Status = 'new' OR Status = 'open' )</code><br />
<br />
Example: to get all the tickets in "fooQueue" you'd access:<br />
<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'</code><br />
<br />
Example: to get the tickets for a custom field "Contact Name" you'd access:<br />
<br />
<code>/REST/1.0/search/ticket?query='CF.{Contact Name}'='Shaun Wallace'</code><br />
<br />
<br />
'''orderby'''<br />
<br />
By this parameter you can change the sort field and order of the search result. To sort a list ascending just put a + before the fieldname, otherwise a -. Eg: -Created (will put the newest tickets at the beginning).<br />
<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'&orderby=+Created</code><br />
<br />
<br />
'''format'''<br />
<br />
* i: ticket/&lt;ticket-id&gt;<br />
* s: &lt;ticket-id&gt;: &lt;ticket-subject&gt;<br />
* l: a multi-line format<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'&orderby=+Created&format=i</code><br />
<br />
'''fields'''<br />
<br />
A list of fields you would like included in the result set.<br />
<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=id=42&format=l&fields=Subject,Status,Priority,CF.\{Category\}</code><br />
<br />
Note that you may need to escape characters like the curly braces for CFs.<br />
<br />
==== '''Ticket Create''' ====<br />
<br />
To create a new ticket: post on <code>/REST/1.0/ticket/new</code> with a variable named "<code>content</code>",<br />
<br />
containing "<code>key: value</code>" line by line, example:<br />
<br />
Testing the new ticket section<br />
<br />
<pre><br />
id: ticket/new<br />
Queue: <queue name><br />
Requestor: <requestor email address><br />
Subject: <subject><br />
Cc: <...><br />
AdminCc: <...><br />
Owner: <...><br />
Status: <...><br />
Priority: <...><br />
InitialPriority: <...><br />
FinalPriority: <...><br />
TimeEstimated: <...><br />
Starts: <...><br />
Due: <...><br />
Text: <The ticket content><br />
CF-<CustomFieldName>: <CustomFieldValue><br />
</pre><br />
<br />
If there are any "special" characters (Umlauts, dash, ...?) in a custom field's name, you can still access it via its ID:<br />
CF-$id: <Value><br />
If you want to have a multiline Text, prefix every line with a blank.<br />
<br />
<pre><br />
Due: <...><br />
Text: This is <br />
a <br />
multiline Text<br />
!!!<br />
CF-<CustomFieldName>: <CustomFieldValue><br />
</pre><br />
<br />
The response should look like: <br />
<pre><br />
RT/4.0.6 200 Ok<br />
<br />
# Ticket 775 created.<br />
</pre><br />
<br />
===== curl example =====<br />
<br />
* Create a file containing the ticket form:<br />
<pre><br />
id: ticket/new<br />
Queue: queue1<br />
Requestor: requestor@email<br />
Priority: 4<br />
CF-Type of request: Demande<br />
Subject: Test REST<br />
Text: Multi line<br />
test with<br />
special chars: é<br />
</pre><br />
<br />
* Submit using curl:<br />
<pre><br />
curl --data-urlencode content@file.name 'https://HOSTNAME/REST/1.0/ticket/new?user=USER&pass=PASSWORD'<br />
</pre><br />
==== Ticket Edit ====<br />
To update an existing ticket: post on <code>/REST/1.0/ticket/&lt;ticket-id&gt;/edit</code> with a variable named "content", containing "key: value" line by line (like the one displayed when issuing <code>ticket/&lt;ticket-id&gt;/show</code>). Example:<br />
<br />
<pre><br />
Priority: 5<br />
TimeWorked: 15<br />
</pre><br />
*PHP<br />
<syntaxhighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/<ticket id>/edit?user=$username&pass=$password";<br />
<br />
$request = new HttpRequest($url, HTTP_METH_POST);<br />
$post_data=array("content"=>"AdminCc: userX\nText: This is a REST test edit ticket\n");<br />
<br />
// add the post fields <br />
$request->addPostFields($postData);<br />
<br />
// response from RT<br />
$response = $request->send()->getBody();<br />
print_r($response);<br />
</syntaxhighlight><br />
<br />
==== Tickets History Reply ====<br />
Same as comment: post on <code>/REST/1.0/ticket/&lt;ticket-id&gt;/comment</code> with a variable name <code>content</code>, containing "<code>key: value</code>" line by line:<br />
<br />
<pre><br />
id: <ticket-id><br />
Action: correspond<br />
Text: the text comment<br />
Cc: <...><br />
Bcc: <...><br />
TimeWorked: <...><br />
Attachment: an attachment filename/path<br />
</pre><br />
<br />
<code>Cc</code> and <code>Bcc</code> are for this reply only (''I think'').<br />
<br />
==== Ticket History Comment ====<br />
To add a comment to an existing ticket: POST on "<code>/REST/1.0/ticket/&lt;ticket-id&gt;/comment</code>" with a variable name "<code>content</code>", containing "<code>key: value</code>" line by line:<br />
<br />
<pre><br />
id: <ticket-id><br />
Action: comment<br />
Text: the text comment<br />
Attachment: an attachment filename/path<br />
</pre><br />
<br />
Action can be "<code>comment</code>" or "<code>correspond</code>". For a list of fields you can use in correspondence, try "<code>/opt/rt3/bin/rt correspond ticket/1</code>"<br />
<br />
If your comment contains multiple lines, each new line must be preceded by a space (e.g. "line 1\n line 2").<br />
<br />
If you want to use HTML replies, use <pre>Content-Type: text/html</pre><br />
<br />
If you used "<code>Attachment</code>", you must add to your POST a variable "<code>attachment_1</code>" that contains the raw attachment in multi-part file object.<br />
<br />
You can upload more attachments as well, in this case you have to separate the file names in the "<code style="border-style: initial; border-color: initial; ">Attachment</code>" with "\n "(a newline and space, without quotes) and add a new variable "attachment_$i" to your POST where $i is the index of attachment.<br />
<br />
You need to send header to post comments<br />
<br />
==== Ticket Links Edit ====<br />
To update links on an existing ticket: POST on "<code>/REST/1.0/ticket/&lt;ticket-id&gt;/links</code>" with a variable named "<code>content</code>", containing "<code>key: value</code>" line by line (like the one displayed when issuing "<code>ticket/&lt;ticket-id&gt;/links</code>"). Example:<br />
<br />
<pre><br />
DependsOn: 54354<br />
RefersTo: http://some.external/link<br />
</pre><br />
<br />
==== Ticket Merge ====<br />
To merge tickets: POST on "<code>/REST/1.0/ticket/&lt;origin-ticket-id&gt;/merge/&lt;into-ticket-id&gt;</code>". (I don't think any content is required, but I send "id" and "into" anyway.)<br />
<br />
=== User Properties ===<br />
<br />
Gets the data for a single user.<br />
<br />
Request: <code>/REST/1.0/user/&lt;user-id&gt;</code><br />
<br />
RT/3.8.4 200 Ok<br />
<br />
id: user/&lt;user-id&gt;<br />
Name: &lt;...&gt;<br />
Password: ********<br />
EmailAddress: &lt;...&gt;<br />
RealName: &lt;...&gt;<br />
Organization: &lt;...&gt;<br />
Privileged: &lt;...&gt;<br />
Disabled: &lt;...&gt;<br />
<br />
Also you can use user login instead of user ID.<br />
<br />
<br />
=== User ===<br />
<br />
==== User Create ====<br />
<br />
To create a new user: post on <code>/REST/1.0/user/new</code> with a variable named "<code>content</code>", containing "<code>key: value</code>" line by line, like the response to <code>/user/&lt;ticket-id&gt;</code><br />
<br />
==== User Edit ====<br />
To update an existing user: post on <code>/REST/1.0/user/&lt;user-id&gt;/edit</code> with a variable named "content", containing "key: value" line by line (like the one displayed when issuing <code>user/&lt;user-id&gt;/show</code>).<br />
<br />
=== Queue ===<br />
Search for all queues mail addresses like thist:<br />
/REST/1.0/search/queue?query=\&fields=CorrespondAddress,CommentAddress<br />
<br />
==== Single queue properties ====<br />
<br />
Gets the data for a single queue.<br />
<br />
Request: <code>/REST/1.0/queue/&lt;queue-id&gt;</code><br />
<br />
RT/3.8.4 200 Ok<br />
<br />
id: queue/&lt;queue-id&gt;<br />
Name: &lt;...&gt;<br />
Description: &lt;...&gt;<br />
CorrespondAddress: &lt;...&gt;<br />
CommentAddress: &lt;...&gt;<br />
InitialPriority: &lt;...&gt;<br />
FinalPriority: &lt;...&gt;<br />
DefaultDueIn: &lt;...&gt;<br />
<br />
=== Logout ===<br />
<br />
To logout: post on <code>/REST/1.0/logout</code> with empty content.<br />
<br />
== Types ==<br />
<br />
*new<br />
*open<br />
*stalled<br />
*resolved<br />
*rejected<br />
*deleted<br />
+ other custom values defined in you local RT portal.<br />
<br />
<br />
<br />
=== History entry type ===<br />
* Create<br />
* CustomField<br />
* EmailRecord<br />
* Status<br />
* CommentEmailRecord<br />
* Correspond<br />
* Comment<br />
* Priority<br />
* Give<br />
* Steal<br />
* Take<br />
* Untake<br />
* AddWatcher<br />
* DeleteWatcher<br />
* AddLink<br />
* DeleteLink<br />
* AddReminder<br />
* OpenReminder<br />
* ResolveReminder<br />
* Set<br />
* Force<br />
* Subject<br />
* Told<br />
* PurgeTransaction<br />
<br />
== Miscellaneous ==<br />
=== Data format ===<br />
<br />
* History entries time returns in UTC, boolean returns as <code>1</code> (true) and <code>0</code>(false). <br />
<br />
* Use only <code>"\n"</code>, not <code>"\r\n"</code>in post content. <br />
<br />
* Comments in response body starts with with a hash (<code>#</code>) symbol.<br />
<br />
=== Request status ===<br />
To get real request/post status you need to check status code in first line of server response.<br />
<br />
== Examples ==<br />
<br />
=== Perl ===<br />
To get the results of a single request (without setting the Session-Cookie) - assuming you've set:<br />
<br />
* $uri to your RT REST URL<br />
* $access_user to your username<br />
* $access_password to your password<br />
* $ticketNumber to the ticket you want to see<br />
<br />
<pre><br />
my $ua = LWP::UserAgent-&gt;new;<br />
$ua->timeout(10);<br />
$ua->agent("YOURUSERAGENTHERE");<br />
<br />
my $response = $ua->post($uri."ticket/$ticketNumber",<br />
['user' => $access_user, 'pass' => $access_password],<br />
'Content_Type' => 'form-data');<br />
<br />
if ($response-&gt;is_success) {<br />
print $response->decoded_content;<br />
}<br />
</pre><br />
<br />
=== Java ===<br />
<pre><br />
import java.io.IOException;<br />
import org.apache.commons.httpclient.HttpClient;<br />
import org.apache.commons.httpclient.methods.PostMethod;<br />
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;<br />
import org.apache.commons.httpclient.methods.multipart.Part;<br />
import org.apache.commons.httpclient.methods.multipart.StringPart;<br />
<br />
public class RtTicketCreator {<br />
static final String BASE_URI = "http://rt.xxx.com/REST/1.0";<br />
<br />
public static void main(String[] args) throws IOException {<br />
<br />
PostMethod mPost = new PostMethod(BASE_URI + "/ticket/new?user=username&amp;pass=password");<br />
Part[] parts = { new StringPart("content", "Queue: General\nSubject: 123") };<br />
mPost.setRequestEntity(new MultipartRequestEntity(parts, mPost.getParams()));<br />
HttpClient cl = new HttpClient();<br />
cl.executeMethod(mPost);<br />
System.out.println(mPost.getResponseBodyAsString());<br />
}<br />
}<br />
</pre><br />
=== Java ( Based on new Apache HttpComponents library ) ===<br />
<pre><br />
/*<br />
* RT ticket creator based on the current Apache HttpComponents library 4.1.3<br />
* Created by Koustubha Kale, kmkale at youtility dot in<br />
*/<br />
import org.apache.http.HttpEntity;<br />
import org.apache.http.HttpResponse;<br />
import org.apache.http.auth.AuthScope;<br />
import org.apache.http.auth.UsernamePasswordCredentials;<br />
import org.apache.http.client.HttpClient;<br />
import org.apache.http.client.methods.HttpPost;<br />
import org.apache.http.entity.mime.MultipartEntity;<br />
import org.apache.http.entity.mime.content.StringBody;<br />
import org.apache.http.impl.client.DefaultHttpClient;<br />
import org.apache.http.util.EntityUtils;<br />
<br />
<br />
public class NewApacheHttpcomponentsRtTicketCreator {<br />
<br />
public static void main(String[] args) throws Exception {<br />
<br />
DefaultHttpClient httpclient = new DefaultHttpClient();<br />
try {<br />
<br />
HttpPost httppost = new HttpPost("http://rt.xxx.com/rt/REST/1.0" +<br />
"/ticket/new?user=username&pass=password");<br />
StringBody content = new StringBody("Queue: General\nSubject: 123");<br />
MultipartEntity reqEntity = new MultipartEntity();<br />
reqEntity.addPart("content", content);<br />
httppost.setEntity(reqEntity);<br />
System.out.println("executing request " + httppost.getRequestLine());<br />
HttpResponse response = httpclient.execute(httppost);<br />
HttpEntity resEntity = response.getEntity();<br />
<br />
System.out.println("----------------------------------------");<br />
System.out.println(response.getStatusLine());<br />
if (resEntity != null) {<br />
System.out.println("Response content length: " + resEntity.getContentLength());<br />
}<br />
EntityUtils.consume(resEntity);<br />
} finally {<br />
try { httpclient.getConnectionManager().shutdown(); } catch (Exception ignore) {}<br />
}<br />
}<br />
<br />
}<br />
</pre><br />
<br />
=== Python ===<br />
<SyntaxHighlight lang="python"><br />
import cookielib<br />
import urllib<br />
import urllib2<br />
<br />
# creates a cookie for the rtserver with the credentials given at initialization.<br />
# define your credentials here<br />
access_user = 'your_login'<br />
access_password = 'your_password'<br />
<br />
# here is the RequestTracker URI we try to access<br />
uri = 'http://your-rt-instance.com/REST/1.0/'<br />
<br />
# trying login on rt server<br />
cj = cookielib.LWPCookieJar()<br />
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))<br />
urllib2.install_opener(opener)<br />
data = {'user': access_user, 'pass': access_password}<br />
ldata = urllib.urlencode(data)<br />
login = urllib2.Request(uri, ldata)<br />
try:<br />
response = urllib2.urlopen(login)<br />
print response.read()<br />
print "login successful"<br />
except urllib2.URLError:<br />
# could not connect to server<br />
print "Not able to login"<br />
</SyntaxHighlight><br />
<br />
=== Ruby ===<br />
<pre><br />
#!/usr/bin/env ruby<br />
require 'net/http'<br />
<br />
user = 'username'<br />
pass = 'password'<br />
uri = URI('https://rt.example.com/REST/1.0/ticket/123/show')<br />
<br />
req = Net::HTTP::Post.new(uri.path)<br />
req.set_form_data('user' => user, 'pass' => pass)<br />
<br />
res = Net::HTTP.start(uri.hostname, uri.port, <br />
:use_ssl => uri.scheme == 'https', <br />
:set_debug_output => $stderr) do |http|<br />
http.request(req)<br />
end<br />
<br />
case res<br />
when Net::HTTPSuccess, Net::HTTPRedirection<br />
# OK<br />
puts "HTTP response code: #{res.code}"<br />
puts "HTTP message: #{res.message}"<br />
puts "Response:"<br />
<br />
res.each do |key,val|<br />
puts "#{key} => #{val}"<br />
end<br />
<br />
puts "Data:"<br />
puts res.body<br />
else<br />
res.value<br />
end<br />
</pre><br />
<br />
=== PHP ===<br />
<SyntaxHighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/<ticket id>/show?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_GET);<br />
/* if you want's to pass additional parameters */<br />
$request->addQueryData(array());<br />
$response = $request->send();<br />
print_r($response);<br />
</SyntaxHighlight><br />
<br />
OR<br />
<br />
<SyntaxHighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/new?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_POST);<br />
// Note : to add data in custom fields you need to add element like below example<br />
// in $post_data array<br />
// Example : \nCF-customfield1:testdata<br />
$post_data = array(<br />
"content" => <br />
"Queue: General" . PHP_EOL .<br />
"Requestor: user@domain" . PHP_EOL .<br />
"Subject: REST test 1" . PHP_EOL .<br />
"Owner: userX" . PHP_EOL .<br />
"AdminCc: userX" . PHP_EOL .<br />
"Text: This is a REST test" . PHP_EOL<br />
);<br />
$request->addPostFields($post_data);<br />
<br />
try<br />
{<br />
response = $request->send()->getBody();<br />
print_r($response);<br />
} catch (HttpException $ex) {<br />
echo $ex;<br />
}<br />
</SyntaxHighlight><br />
<br />
=== C# ===<br />
<br />
<pre><br />
// Using "WCF REST Starter Kit" (http://msdn.microsoft.com/en-us/netframework/cc950529.aspx)<br />
using (var client = new HttpClient("http://rt.site.com/REST/1.0/"))<br />
{<br />
client.TransportSettings.Cookies = new CookieContainer();<br />
<br />
var form = new HttpUrlEncodedForm();<br />
form.Add("user", "LOGIN");<br />
form.Add("pass", "PASSWORD");<br />
<br />
client.Post(string.Empty, form.CreateHttpContent());<br />
<br />
// 1. Get ticket data<br />
using (var request = client.Get("ticket/1234/show"))<br />
{<br />
string content = request.Content.ReadAsString();<br />
// Some logic<br />
}<br />
<br />
// 2. Post ticket reply with attachment<br />
var formPost = new HttpMultipartMimeForm();<br />
byte[] attachment = new byte[];<br />
string content = string.Empty;<br />
<br />
// Store data in attachment<br />
// Store data in content string ("Field: Value" line by line)<br />
// ...<br />
<br />
formPost.Add("content", content);<br />
formPost.Add("attachment_1", "attachment_1", HttpContent.Create(attachment, "application/octet-stream"));<br />
<br />
using (var post = client.Post("ticket/1234/comment"), formPost.CreateHttpContent()))<br />
// Some logic<br />
}</pre><br />
<br />
===Powershell (v3)===<br />
* Escape Powershell reserved chars in user/pass<br />
* If connecting via SSL, use fqdn of server, not cname<br />
*Use `n (newline) when composing new ticket structure<br />
Read RT queue based on query: <br />
<pre><br />
$servername="Your Servername here"<br />
$u="user=Username"<br />
$p="pass=Password"<br />
$q="search/ticket?query=(Status='open' OR Status='new') AND (Queue='GENERAL' OR Queue='FOO') AND Owner='Nobody'"<br />
$uri="https://" + $servername + "/rt/REST/1.0/" + $q + "&" + $u + "&" + $p<br />
$RT=Invoke-WebRequest -Uri $uri -SessionVariable sess<br />
$rt.Content<br />
</pre><br />
<br />
<br />
===VB.NET===<br />
<pre><br />
Public Sub AddAttachmentToRT(ByVal url As String, ByVal fileName As String, ByVal filePath As String)<br />
<br />
Dim dataBoundary As String = "--xYzZY"<br />
Dim request As HttpWebRequest<br />
Dim fileType As String = "image/jpeg"<br />
<br />
'Create a POST web request to the REST interface using the passed URL<br />
request = CType(WebRequest.Create(url), HttpWebRequest)<br />
request.ContentType = "multipart/form-data; boundary=xYzZY"<br />
request.Method = "POST"<br />
request.KeepAlive = True<br />
<br />
'Write the request to the requestStream<br />
Using requestStream As IO.Stream = request.GetRequestStream()<br />
<br />
'Create a variable "attachment_1" in the POST, specify the file name and file type<br />
Dim preAttachment As String = dataBoundary + vbCrLf _<br />
+ "Content-Disposition: form-data; name=""attachment_1""; filename=""" + fileName + """" + vbCrLf _<br />
+ "Content-Type: " + fileType + vbCrLf _<br />
+ vbCrLf<br />
<br />
'Convert this preAttachment string to bytes<br />
Dim preAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(preAttachment)<br />
<br />
'Write this preAttachment string to the stream<br />
requestStream.Write(preAttachmentBytes, 0, preAttachmentBytes.Length)<br />
<br />
'Write the file as bytes to the stream by passing its exact location<br />
Using fileStream As New IO.FileStream(Server.MapPath(filePath + fileName), IO.FileMode.Open, IO.FileAccess.Read)<br />
<br />
Dim buffer(4096) As Byte<br />
Dim bytesRead As Int32 = fileStream.Read(buffer, 0, buffer.Length)<br />
<br />
Do While (bytesRead > 0)<br />
<br />
requestStream.Write(buffer, 0, bytesRead)<br />
bytesRead = fileStream.Read(buffer, 0, buffer.Length)<br />
<br />
Loop<br />
<br />
End Using<br />
<br />
'Create a variable named content in the POST, specify the attachment name and comment text<br />
Dim postAttachment As String = vbCrLf _<br />
+ dataBoundary + vbCrLf _<br />
+ "Content-Disposition: form-data; name=""content""" + vbCrLf _<br />
+ vbCrLf _<br />
+ "Action: comment" + vbLf _<br />
+ "Attachment: " + fileName + vbCrLf _<br />
+ "Text: Whatever You Want" + vbCrLf _<br />
+ vbCrLf _<br />
+ "--xYzZY--"<br />
<br />
'Convert postAttachment string to bytes<br />
Dim postAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(postAttachment)<br />
<br />
'Write the postAttachment string to the stream<br />
requestStream.Write(postAttachmentBytes, 0, postAttachmentBytes.Length)<br />
<br />
End Using<br />
<br />
Dim response As Net.WebResponse = Nothing<br />
<br />
'Get the response from our REST request to RT<br />
'Required to capture response, without this Try-Catch attaching will fail<br />
Try<br />
response = request.GetResponse()<br />
<br />
Using responseStream As IO.Stream = response.GetResponseStream()<br />
<br />
Using responseReader As New IO.StreamReader(responseStream)<br />
<br />
Dim responseText = responseReader.ReadToEnd()<br />
<br />
End Using<br />
<br />
End Using<br />
<br />
Catch exception As Net.WebException<br />
<br />
response = exception.Response<br />
<br />
If (response IsNot Nothing) Then<br />
<br />
Using reader As New IO.StreamReader(response.GetResponseStream())<br />
<br />
Dim responseText = reader.ReadToEnd()<br />
<br />
End Using<br />
<br />
response.Close()<br />
<br />
End If<br />
<br />
Finally<br />
<br />
request = Nothing<br />
<br />
End Try<br />
<br />
End Sub<br />
</pre><br />
===JavaScript===<br />
This is an example of updating the RefersTo field for a ticket. Assuming Zepto/jQuery is also being used.<br />
function link_back(url, ticket_num, rt) {<br />
var data, link;<br />
link = "refersto: " + url;<br />
data = {"content": link};<br />
<br />
$.ajax({<br />
type: "POST",<br />
url: rt + ticket_num + "/links",<br />
data: data,<br />
success: function (reply) {<br />
console.log(reply);<br />
}<br />
});<br />
}<br />
<br />
var url = "https://www.wikipedia.org/";<br />
var ticket_num = "123456";<br />
var rt = "https://some.rt.url/REST/1.0/ticket/";<br />
<br />
link_back(url, ticket_num, rt);<br />
<br />
== Convenience libraries ==<br />
<br />
There are libraries which do much of the low-level work shown in the examples below for you, and provide an easier programming interface for dealing with RT:<br />
{| class="wikitable"<br />
!Language<br />
!Package/Module<br />
!Source<br />
!Example<br />
!Note<br />
|-<br />
|'''Perl'''<br />
|[http://metacpan.org/module/RT::Client::REST RT::Client::REST]<br />
|[http://rt-client-rest.googlecode.com/svn/trunk/ Google Code]<br />
|<code>"perl -MCPAN -e install RT::Client::REST"</code> <br />
|<br />
|-<br />
|'''Ruby'''<br />
|[http://rubygems.org/gems/rt-client rt-client]<br />
|[https://github.com/uidzip/rt-client GitHub]<br />
|<code>gem install rt-client</code><br />
|<br />
|-<br />
|'''Ruby'''<br />
|[http://rubygems.org/gems/roart Roart]<br />
|[https://github.com/pjdavis/roart GitHub]<br />
|<code>gem install roart</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/Rickerd0613/rtapi rtapi]<br />
|[https://github.com/Rickerd0613/rtapi GitHub]<br />
|<code>pip install rtapi</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/z4r/python-rtkit rtkit]<br />
|[https://github.com/z4r/python-rtkit GitHub]<br />
|<code>pip install python-rtkit</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/CZ-NIC/python-rt python-rt]<br />
|[https://github.com/CZ-NIC/python-rt Github]<br />
|<code>pip install rt</code><br />
|<br />
|-<br />
|'''Java'''<br />
|[http://projects.boksa.de/RT-REST/ RT-REST]<br />
|[https://github.com/bboksa/RT-REST GitHub]<br />
|<code>N/A</code><br />
|<br />
|-<br />
|'''PHP'''<br />
|[https://github.com/dersam/RTPHPLib RTPHPLib]<br />
|[https://github.com/dersam/RTPHPLib GitHub]<br />
|composer require dersam/rt-php-lib<br />
|<br />
|}</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=REST&diff=26997REST2021-02-16T20:18:56Z<p>Rabin: /* Python */</p>
<hr />
<div>== Abstract ==<br />
<br />
The REST Interface gives you access to your RT Database. The complete communication is encapsulated in the HTTP protocol. The interface should be accessible in your installation.<br />
<br />
Though you may see references to older 3.x releases of RT below, the REST 1.0 interface has not changed in any significant way in 4.x.<br />
<br />
This page is for REST version 1.0. The next version of the RT REST interface, version 2.0, is available as an [https://metacpan.org/pod/RT::Extension::REST2 extension].<br />
<br />
[{{SERVER}}/index.php?search={{TALKPAGENAME}}&title=Special:Search Legacy Comments] are available from the previous Wikia instance.<br />
<br />
== Interface ==<br />
<br />
Base URL: <code>.../REST/1.0/</code>. The default response should be:<br />
<br />
RT/3.4.5 200 Ok<br />
<br />
# Invalid object specification: 'index.html'<br />
<br />
id: index.html<br />
<br />
<br />
=== Authentication ===<br />
<br />
The REST Interface does not support HTTP-Authentication. So you must get a valid Session-Token and submit the cookie each request. You usually get a Session-Cookie by submitting the default login form. Use variables "<code>user</code>" for login and "<code>pass</code>" for password values. wget doesn't escape any characters in the --post-data option so make sure you properly escape any special characters in the password.<br />
<br />
See the wget invocation line below:<br />
<br />
wget --keep-session-cookies \<br />
--save-cookies cookies.txt \<br />
--post-data 'user=UUUU&pass=PPPP' \<br />
http://my.rt.server<br />
<br />
You need the -keep-session-cookies option to make wget save session cookies.<br />
<br />
=== Ticket ===<br />
<br />
==== Ticket Properties ====<br />
<br />
Gets the data for a single ticket, not including the history and comments.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/show</code><br />
<br />
RT/3.4.5 200 Ok<br />
<br />
id: ticket/&lt;ticket-id&gt;<br />
Queue: &lt;...&gt;<br />
Owner: &lt;...&gt;<br />
Creator: &lt;...&gt;<br />
Subject: &lt;...&gt;<br />
Status: &lt;...&gt;<br />
Priority: &lt;...&gt;<br />
InitialPriority: &lt;...&gt;<br />
FinalPriority: &lt;...&gt;<br />
Requestors: &lt;...&gt;<br />
Cc: &lt;...&gt;<br />
AdminCc: &lt;...&gt;<br />
Created: &lt;...&gt;<br />
Starts: &lt;...&gt;<br />
Started: &lt;...&gt;<br />
Due: &lt;...&gt;<br />
Resolved: &lt;...&gt;<br />
Told: &lt;...&gt;<br />
TimeEstimated: &lt;...&gt;<br />
TimeWorked: &lt;...&gt;<br />
TimeLeft: &lt;...&gt;<br />
<br />
==== Ticket Links ====<br />
<br />
Gets the ticket links for a single ticket.<br />
<br />
Request: <code>REST/1.0/ticket/&lt;ticket-id&gt;/links/show</code><br />
<br />
RT/3.8.2 200 Ok<br />
<br />
id: ticket/&lt;ticket-id&gt;/links<br />
HasMember: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
ReferredToBy: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
DependedOnBy: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
MemberOf: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
RefersTo: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
DependsOn: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
<br />
==== Ticket Attachments ====<br />
<br />
Gets a list of all attachments related to the ticket<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments</code><br />
<br />
==== Ticket Attachment ====<br />
Gets the metadata and content of a specific attachment.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments/&lt;attachment-id&gt;</code><br />
<br />
<pre><br />
RT/3.8.0 200 Ok<br />
<br />
id: <attachment-id><br />
Subject:<br />
Creator: <user-id><br />
Created: <timestamp><br />
Transaction: <transaction-id><br />
Parent: <parent-id><br />
MessageId:<br />
Filename: <filename><br />
ContentType: application/octet-stream<br />
ContentEncoding: none<br />
<br />
Headers: MIME-Version: 1.0<br />
X-Mailer: MIME-tools 5.427 (Entity 5.427)<br />
Content-Type: application/octet-stream;<br />
name="<filename>"<br />
Content-Disposition: inline; filename="<filename>"<br />
Content-Transfer-Encoding: base64<br />
Content-Length: <length in bytes><br />
<br />
Content: ...<br />
...<br />
...<br />
</pre><br />
<br />
''NOTE: RT returns the content indented with 9 spaces on each line, so that it lines up with the "Content:" header. Even if you strip this out with a regexp, the content is still UTF-8, which is probably not what you want. To get the original binary data back, strip out the 9 spaces with a regexp, strip off the 3 carriage returns at the end, and then convert the whole thing from UTF-8 to the native character encoding of the attachment, whatever that is. RT doesn't tell you, so you have know. If the attachments were uploaded by a U.S. Windows system, odds are that Windows-1252 is what you want. If you can't get the binary back intact, see the next method below.''<br />
<br />
==== Ticket Attachment Content ====<br />
<br />
Gets the attachment data content without additional metadata or whitespace characters<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments/&lt;attachment-id&gt;/content</code><br />
<br />
RT/3.8.0 200 Ok<br />
<br />
...<br />
...<br />
...<br />
<br />
So to get the original content you still have to strip the first 2 lines of the response.<br />
<br />
==== Ticket History ====<br />
Gets a list of all the history items for a given ticket.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history</code><br />
<br />
<pre><br />
RT/3.4.5 200 Ok<br />
<br />
# <history-count>/<history-count> (/total)<br />
<br />
<history-id>: <history-name><br />
<history-id>: <history-name><br />
...<br />
</pre><br />
<br />
You will get an additional row, for each history entry found. The first entry is usually: "<code>Ticket created by ...</code>".<br />
<br />
There are two ways to get history item detail: you can do one of these and then recursively perform <code>ticket/history/id/&lt;history-id&gt;</code> for each history-id from this REST call, but that is extremely wasteful and will scale horribly. What you really want to do is one REST call but get the long format:<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history?format=l</code><br />
<br />
<pre><br />
RT/3.8.2 200 Ok<br />
<br />
# <n>/<n> (id/&lt;history-id&gt;/total)<br />
<br />
id: <history-id><br />
Ticket: <ticket-id><br />
TimeTaken: <...><br />
Type: <...><br />
Field: <...><br />
OldValue: <...><br />
NewValue: <...><br />
Data: <...><br />
Description: <...><br />
Content: <...><br />
Creator: <...><br />
<br />
Created: <...><br />
<br />
Attachments:<br />
<attachment-id>: <filename> (<size>)<br />
<attachment-id>: <filename> (<size>)<br />
<br />
--<br />
<br />
# <n>/<n> (id/&lt;history-id&gt;/total)<br />
...<br />
</pre><br />
<br />
''NOTE: the double dash "--" will occur in the long format between each history item. You can split the output on "--" and iterate over it, parsing out the data with an RFC822 parser, such as an email handling library.''<br />
<br />
==== Ticket History Entry ====<br />
Gets the history information for a single history item. Note that the history item must actually correspond to the ticket.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history/id/&lt;history-id&gt;</code><br />
<br />
<pre><br />
RT/3.4.5 200 Ok<br />
<br />
# 70/70 (id/114856/total)<br />
<br />
id: <history-id><br />
Ticket: <ticket-id><br />
TimeTaken: <...><br />
Type: <...><br />
Field: <...><br />
OldValue: <...><br />
NewValue: <...><br />
Data: <...><br />
Description: <...><br />
<br />
Content: <lin1-0><br />
<line-1><br />
...<br />
<line-n><br />
<br />
Creator: <...><br />
Created: <...><br />
Attachments: <...><br />
</pre><br />
<br />
''IMPORTANT NOTE: At least with RT 3.8.0, when you request a history item with this method AND you have attached a file that has Mime type text/plain to the same item (eg. a comment with an attachement), RT will return the complete content of the attachment for the key "Content:" and not your real comment that you can see in the web frontend. This may lead to some problems if the requestor does not expect to get a comment content that is for example 1.8 MB of text. With other Mime type attachments this however seems to work. I don't know if this is a feature or a bug.''<br />
<br />
<br />
==== '''Ticket Search''' ====<br />
Request: <code>/REST/1.0/search/ticket?query=&lt;query&gt;&orderby=&lt;sort-order&gt;&format=&lt;format&gt;</code><br />
<br />
'''Parameters'''<br />
<br />
'''query'''<br />
<br />
You can use any query generated by the query builder - or feel free to write your own. Here an example that will do the following: Find all tickets that have no owner and the status new or open<br />
<br />
<code>query= Owner = 'Nobody' AND ( Status = 'new' OR Status = 'open' )</code><br />
<br />
Example: to get all the tickets in "fooQueue" you'd access:<br />
<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'</code><br />
<br />
Example: to get the tickets for a custom field "Contact Name" you'd access:<br />
<br />
<code>/REST/1.0/search/ticket?query='CF.{Contact Name}'='Shaun Wallace'</code><br />
<br />
<br />
'''orderby'''<br />
<br />
By this parameter you can change the sort field and order of the search result. To sort a list ascending just put a + before the fieldname, otherwise a -. Eg: -Created (will put the newest tickets at the beginning).<br />
<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'&orderby=+Created</code><br />
<br />
<br />
'''format'''<br />
<br />
* i: ticket/&lt;ticket-id&gt;<br />
* s: &lt;ticket-id&gt;: &lt;ticket-subject&gt;<br />
* l: a multi-line format<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'&orderby=+Created&format=i</code><br />
<br />
'''fields'''<br />
<br />
A list of fields you would like included in the result set.<br />
<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=id=42&format=l&fields=Subject,Status,Priority,CF.\{Category\}</code><br />
<br />
Note that you may need to escape characters like the curly braces for CFs.<br />
<br />
==== '''Ticket Create''' ====<br />
<br />
To create a new ticket: post on <code>/REST/1.0/ticket/new</code> with a variable named "<code>content</code>",<br />
<br />
containing "<code>key: value</code>" line by line, example:<br />
<br />
Testing the new ticket section<br />
<br />
<pre><br />
id: ticket/new<br />
Queue: <queue name><br />
Requestor: <requestor email address><br />
Subject: <subject><br />
Cc: <...><br />
AdminCc: <...><br />
Owner: <...><br />
Status: <...><br />
Priority: <...><br />
InitialPriority: <...><br />
FinalPriority: <...><br />
TimeEstimated: <...><br />
Starts: <...><br />
Due: <...><br />
Text: <The ticket content><br />
CF-<CustomFieldName>: <CustomFieldValue><br />
</pre><br />
<br />
If there are any "special" characters (Umlauts, dash, ...?) in a custom field's name, you can still access it via its ID:<br />
CF-$id: <Value><br />
If you want to have a multiline Text, prefix every line with a blank.<br />
<br />
<pre><br />
Due: <...><br />
Text: This is <br />
a <br />
multiline Text<br />
!!!<br />
CF-<CustomFieldName>: <CustomFieldValue><br />
</pre><br />
<br />
The response should look like: <br />
<pre><br />
RT/4.0.6 200 Ok<br />
<br />
# Ticket 775 created.<br />
</pre><br />
<br />
===== curl example =====<br />
<br />
* Create a file containing the ticket form:<br />
<pre><br />
id: ticket/new<br />
Queue: queue1<br />
Requestor: requestor@email<br />
Priority: 4<br />
CF-Type of request: Demande<br />
Subject: Test REST<br />
Text: Multi line<br />
test with<br />
special chars: é<br />
</pre><br />
<br />
* Submit using curl:<br />
<pre><br />
curl --data-urlencode content@file.name 'https://HOSTNAME/REST/1.0/ticket/new?user=USER&pass=PASSWORD'<br />
</pre><br />
==== Ticket Edit ====<br />
To update an existing ticket: post on <code>/REST/1.0/ticket/&lt;ticket-id&gt;/edit</code> with a variable named "content", containing "key: value" line by line (like the one displayed when issuing <code>ticket/&lt;ticket-id&gt;/show</code>). Example:<br />
<br />
<pre><br />
Priority: 5<br />
TimeWorked: 15<br />
</pre><br />
*PHP<br />
<pre> <br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/<ticket id>/edit?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_POST);<br />
$post_data=array("content"=>"AdminCc: userX\nText: This is a REST test edit ticket\n");<br />
<br />
// add the post fields <br />
$request->addPostFields($postData);<br />
// response from RT<br />
$response = $request->send()->getBody();<br />
print_r($response);<br />
</pre><br />
<br />
==== Tickets History Reply ====<br />
Same as comment: post on <code>/REST/1.0/ticket/&lt;ticket-id&gt;/comment</code> with a variable name <code>content</code>, containing "<code>key: value</code>" line by line:<br />
<br />
<pre><br />
id: <ticket-id><br />
Action: correspond<br />
Text: the text comment<br />
Cc: <...><br />
Bcc: <...><br />
TimeWorked: <...><br />
Attachment: an attachment filename/path<br />
</pre><br />
<br />
<code>Cc</code> and <code>Bcc</code> are for this reply only (''I think'').<br />
<br />
==== Ticket History Comment ====<br />
To add a comment to an existing ticket: POST on "<code>/REST/1.0/ticket/&lt;ticket-id&gt;/comment</code>" with a variable name "<code>content</code>", containing "<code>key: value</code>" line by line:<br />
<br />
<pre><br />
id: <ticket-id><br />
Action: comment<br />
Text: the text comment<br />
Attachment: an attachment filename/path<br />
</pre><br />
<br />
Action can be "<code>comment</code>" or "<code>correspond</code>". For a list of fields you can use in correspondence, try "<code>/opt/rt3/bin/rt correspond ticket/1</code>"<br />
<br />
If your comment contains multiple lines, each new line must be preceded by a space (e.g. "line 1\n line 2").<br />
<br />
If you want to use HTML replies, use <pre>Content-Type: text/html</pre><br />
<br />
If you used "<code>Attachment</code>", you must add to your POST a variable "<code>attachment_1</code>" that contains the raw attachment in multi-part file object.<br />
<br />
You can upload more attachments as well, in this case you have to separate the file names in the "<code style="border-style: initial; border-color: initial; ">Attachment</code>" with "\n "(a newline and space, without quotes) and add a new variable "attachment_$i" to your POST where $i is the index of attachment.<br />
<br />
You need to send header to post comments<br />
<br />
==== Ticket Links Edit ====<br />
To update links on an existing ticket: POST on "<code>/REST/1.0/ticket/&lt;ticket-id&gt;/links</code>" with a variable named "<code>content</code>", containing "<code>key: value</code>" line by line (like the one displayed when issuing "<code>ticket/&lt;ticket-id&gt;/links</code>"). Example:<br />
<br />
<pre><br />
DependsOn: 54354<br />
RefersTo: http://some.external/link<br />
</pre><br />
<br />
==== Ticket Merge ====<br />
To merge tickets: POST on "<code>/REST/1.0/ticket/&lt;origin-ticket-id&gt;/merge/&lt;into-ticket-id&gt;</code>". (I don't think any content is required, but I send "id" and "into" anyway.)<br />
<br />
=== User Properties ===<br />
<br />
Gets the data for a single user.<br />
<br />
Request: <code>/REST/1.0/user/&lt;user-id&gt;</code><br />
<br />
RT/3.8.4 200 Ok<br />
<br />
id: user/&lt;user-id&gt;<br />
Name: &lt;...&gt;<br />
Password: ********<br />
EmailAddress: &lt;...&gt;<br />
RealName: &lt;...&gt;<br />
Organization: &lt;...&gt;<br />
Privileged: &lt;...&gt;<br />
Disabled: &lt;...&gt;<br />
<br />
Also you can use user login instead of user ID.<br />
<br />
<br />
=== User ===<br />
<br />
==== User Create ====<br />
<br />
To create a new user: post on <code>/REST/1.0/user/new</code> with a variable named "<code>content</code>", containing "<code>key: value</code>" line by line, like the response to <code>/user/&lt;ticket-id&gt;</code><br />
<br />
==== User Edit ====<br />
To update an existing user: post on <code>/REST/1.0/user/&lt;user-id&gt;/edit</code> with a variable named "content", containing "key: value" line by line (like the one displayed when issuing <code>user/&lt;user-id&gt;/show</code>).<br />
<br />
=== Queue ===<br />
Search for all queues mail addresses like thist:<br />
/REST/1.0/search/queue?query=\&fields=CorrespondAddress,CommentAddress<br />
<br />
==== Single queue properties ====<br />
<br />
Gets the data for a single queue.<br />
<br />
Request: <code>/REST/1.0/queue/&lt;queue-id&gt;</code><br />
<br />
RT/3.8.4 200 Ok<br />
<br />
id: queue/&lt;queue-id&gt;<br />
Name: &lt;...&gt;<br />
Description: &lt;...&gt;<br />
CorrespondAddress: &lt;...&gt;<br />
CommentAddress: &lt;...&gt;<br />
InitialPriority: &lt;...&gt;<br />
FinalPriority: &lt;...&gt;<br />
DefaultDueIn: &lt;...&gt;<br />
<br />
=== Logout ===<br />
<br />
To logout: post on <code>/REST/1.0/logout</code> with empty content.<br />
<br />
== Types ==<br />
<br />
*new<br />
*open<br />
*stalled<br />
*resolved<br />
*rejected<br />
*deleted<br />
+ other custom values defined in you local RT portal.<br />
<br />
<br />
<br />
=== History entry type ===<br />
* Create<br />
* CustomField<br />
* EmailRecord<br />
* Status<br />
* CommentEmailRecord<br />
* Correspond<br />
* Comment<br />
* Priority<br />
* Give<br />
* Steal<br />
* Take<br />
* Untake<br />
* AddWatcher<br />
* DeleteWatcher<br />
* AddLink<br />
* DeleteLink<br />
* AddReminder<br />
* OpenReminder<br />
* ResolveReminder<br />
* Set<br />
* Force<br />
* Subject<br />
* Told<br />
* PurgeTransaction<br />
<br />
== Miscellaneous ==<br />
=== Data format ===<br />
<br />
* History entries time returns in UTC, boolean returns as <code>1</code> (true) and <code>0</code>(false). <br />
<br />
* Use only <code>"\n"</code>, not <code>"\r\n"</code>in post content. <br />
<br />
* Comments in response body starts with with a hash (<code>#</code>) symbol.<br />
<br />
=== Request status ===<br />
To get real request/post status you need to check status code in first line of server response.<br />
<br />
== Examples ==<br />
<br />
=== Perl ===<br />
To get the results of a single request (without setting the Session-Cookie) - assuming you've set:<br />
<br />
* $uri to your RT REST URL<br />
* $access_user to your username<br />
* $access_password to your password<br />
* $ticketNumber to the ticket you want to see<br />
<br />
<pre><br />
my $ua = LWP::UserAgent-&gt;new;<br />
$ua->timeout(10);<br />
$ua->agent("YOURUSERAGENTHERE");<br />
<br />
my $response = $ua->post($uri."ticket/$ticketNumber",<br />
['user' => $access_user, 'pass' => $access_password],<br />
'Content_Type' => 'form-data');<br />
<br />
if ($response-&gt;is_success) {<br />
print $response->decoded_content;<br />
}<br />
</pre><br />
<br />
=== Java ===<br />
<pre><br />
import java.io.IOException;<br />
import org.apache.commons.httpclient.HttpClient;<br />
import org.apache.commons.httpclient.methods.PostMethod;<br />
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;<br />
import org.apache.commons.httpclient.methods.multipart.Part;<br />
import org.apache.commons.httpclient.methods.multipart.StringPart;<br />
<br />
public class RtTicketCreator {<br />
static final String BASE_URI = "http://rt.xxx.com/REST/1.0";<br />
<br />
public static void main(String[] args) throws IOException {<br />
<br />
PostMethod mPost = new PostMethod(BASE_URI + "/ticket/new?user=username&amp;pass=password");<br />
Part[] parts = { new StringPart("content", "Queue: General\nSubject: 123") };<br />
mPost.setRequestEntity(new MultipartRequestEntity(parts, mPost.getParams()));<br />
HttpClient cl = new HttpClient();<br />
cl.executeMethod(mPost);<br />
System.out.println(mPost.getResponseBodyAsString());<br />
}<br />
}<br />
</pre><br />
=== Java ( Based on new Apache HttpComponents library ) ===<br />
<pre><br />
/*<br />
* RT ticket creator based on the current Apache HttpComponents library 4.1.3<br />
* Created by Koustubha Kale, kmkale at youtility dot in<br />
*/<br />
import org.apache.http.HttpEntity;<br />
import org.apache.http.HttpResponse;<br />
import org.apache.http.auth.AuthScope;<br />
import org.apache.http.auth.UsernamePasswordCredentials;<br />
import org.apache.http.client.HttpClient;<br />
import org.apache.http.client.methods.HttpPost;<br />
import org.apache.http.entity.mime.MultipartEntity;<br />
import org.apache.http.entity.mime.content.StringBody;<br />
import org.apache.http.impl.client.DefaultHttpClient;<br />
import org.apache.http.util.EntityUtils;<br />
<br />
<br />
public class NewApacheHttpcomponentsRtTicketCreator {<br />
<br />
public static void main(String[] args) throws Exception {<br />
<br />
DefaultHttpClient httpclient = new DefaultHttpClient();<br />
try {<br />
<br />
HttpPost httppost = new HttpPost("http://rt.xxx.com/rt/REST/1.0" +<br />
"/ticket/new?user=username&pass=password");<br />
StringBody content = new StringBody("Queue: General\nSubject: 123");<br />
MultipartEntity reqEntity = new MultipartEntity();<br />
reqEntity.addPart("content", content);<br />
httppost.setEntity(reqEntity);<br />
System.out.println("executing request " + httppost.getRequestLine());<br />
HttpResponse response = httpclient.execute(httppost);<br />
HttpEntity resEntity = response.getEntity();<br />
<br />
System.out.println("----------------------------------------");<br />
System.out.println(response.getStatusLine());<br />
if (resEntity != null) {<br />
System.out.println("Response content length: " + resEntity.getContentLength());<br />
}<br />
EntityUtils.consume(resEntity);<br />
} finally {<br />
try { httpclient.getConnectionManager().shutdown(); } catch (Exception ignore) {}<br />
}<br />
}<br />
<br />
}<br />
</pre><br />
<br />
=== Python ===<br />
<SyntaxHighlight lang="python"><br />
import cookielib<br />
import urllib<br />
import urllib2<br />
<br />
# creates a cookie for the rtserver with the credentials given at initialization.<br />
# define your credentials here<br />
access_user = 'your_login'<br />
access_password = 'your_password'<br />
<br />
# here is the RequestTracker URI we try to access<br />
uri = 'http://your-rt-instance.com/REST/1.0/'<br />
<br />
# trying login on rt server<br />
cj = cookielib.LWPCookieJar()<br />
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))<br />
urllib2.install_opener(opener)<br />
data = {'user': access_user, 'pass': access_password}<br />
ldata = urllib.urlencode(data)<br />
login = urllib2.Request(uri, ldata)<br />
try:<br />
response = urllib2.urlopen(login)<br />
print response.read()<br />
print "login successful"<br />
except urllib2.URLError:<br />
# could not connect to server<br />
print "Not able to login"<br />
</SyntaxHighlight><br />
<br />
=== Ruby ===<br />
<pre><br />
#!/usr/bin/env ruby<br />
require 'net/http'<br />
<br />
user = 'username'<br />
pass = 'password'<br />
uri = URI('https://rt.example.com/REST/1.0/ticket/123/show')<br />
<br />
req = Net::HTTP::Post.new(uri.path)<br />
req.set_form_data('user' => user, 'pass' => pass)<br />
<br />
res = Net::HTTP.start(uri.hostname, uri.port, <br />
:use_ssl => uri.scheme == 'https', <br />
:set_debug_output => $stderr) do |http|<br />
http.request(req)<br />
end<br />
<br />
case res<br />
when Net::HTTPSuccess, Net::HTTPRedirection<br />
# OK<br />
puts "HTTP response code: #{res.code}"<br />
puts "HTTP message: #{res.message}"<br />
puts "Response:"<br />
<br />
res.each do |key,val|<br />
puts "#{key} => #{val}"<br />
end<br />
<br />
puts "Data:"<br />
puts res.body<br />
else<br />
res.value<br />
end<br />
</pre><br />
<br />
=== PHP ===<br />
<SyntaxHighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/<ticket id>/show?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_GET);<br />
/* if you want's to pass additional parameters */<br />
$request->addQueryData(array());<br />
$response = $request->send();<br />
print_r($response);<br />
</SyntaxHighlight><br />
<br />
OR<br />
<br />
<SyntaxHighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/new?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_POST);<br />
// Note : to add data in custom fields you need to add element like below example<br />
// in $post_data array<br />
// Example : \nCF-customfield1:testdata<br />
$post_data = array(<br />
"content" => <br />
"Queue: General" . PHP_EOL .<br />
"Requestor: user@domain" . PHP_EOL .<br />
"Subject: REST test 1" . PHP_EOL .<br />
"Owner: userX" . PHP_EOL .<br />
"AdminCc: userX" . PHP_EOL .<br />
"Text: This is a REST test" . PHP_EOL<br />
);<br />
$request->addPostFields($post_data);<br />
<br />
try<br />
{<br />
response = $request->send()->getBody();<br />
print_r($response);<br />
} catch (HttpException $ex) {<br />
echo $ex;<br />
}<br />
</SyntaxHighlight><br />
<br />
=== C# ===<br />
<br />
<pre><br />
// Using "WCF REST Starter Kit" (http://msdn.microsoft.com/en-us/netframework/cc950529.aspx)<br />
using (var client = new HttpClient("http://rt.site.com/REST/1.0/"))<br />
{<br />
client.TransportSettings.Cookies = new CookieContainer();<br />
<br />
var form = new HttpUrlEncodedForm();<br />
form.Add("user", "LOGIN");<br />
form.Add("pass", "PASSWORD");<br />
<br />
client.Post(string.Empty, form.CreateHttpContent());<br />
<br />
// 1. Get ticket data<br />
using (var request = client.Get("ticket/1234/show"))<br />
{<br />
string content = request.Content.ReadAsString();<br />
// Some logic<br />
}<br />
<br />
// 2. Post ticket reply with attachment<br />
var formPost = new HttpMultipartMimeForm();<br />
byte[] attachment = new byte[];<br />
string content = string.Empty;<br />
<br />
// Store data in attachment<br />
// Store data in content string ("Field: Value" line by line)<br />
// ...<br />
<br />
formPost.Add("content", content);<br />
formPost.Add("attachment_1", "attachment_1", HttpContent.Create(attachment, "application/octet-stream"));<br />
<br />
using (var post = client.Post("ticket/1234/comment"), formPost.CreateHttpContent()))<br />
// Some logic<br />
}</pre><br />
<br />
===Powershell (v3)===<br />
* Escape Powershell reserved chars in user/pass<br />
* If connecting via SSL, use fqdn of server, not cname<br />
*Use `n (newline) when composing new ticket structure<br />
Read RT queue based on query: <br />
<pre><br />
$servername="Your Servername here"<br />
$u="user=Username"<br />
$p="pass=Password"<br />
$q="search/ticket?query=(Status='open' OR Status='new') AND (Queue='GENERAL' OR Queue='FOO') AND Owner='Nobody'"<br />
$uri="https://" + $servername + "/rt/REST/1.0/" + $q + "&" + $u + "&" + $p<br />
$RT=Invoke-WebRequest -Uri $uri -SessionVariable sess<br />
$rt.Content<br />
</pre><br />
<br />
<br />
===VB.NET===<br />
<pre><br />
Public Sub AddAttachmentToRT(ByVal url As String, ByVal fileName As String, ByVal filePath As String)<br />
<br />
Dim dataBoundary As String = "--xYzZY"<br />
Dim request As HttpWebRequest<br />
Dim fileType As String = "image/jpeg"<br />
<br />
'Create a POST web request to the REST interface using the passed URL<br />
request = CType(WebRequest.Create(url), HttpWebRequest)<br />
request.ContentType = "multipart/form-data; boundary=xYzZY"<br />
request.Method = "POST"<br />
request.KeepAlive = True<br />
<br />
'Write the request to the requestStream<br />
Using requestStream As IO.Stream = request.GetRequestStream()<br />
<br />
'Create a variable "attachment_1" in the POST, specify the file name and file type<br />
Dim preAttachment As String = dataBoundary + vbCrLf _<br />
+ "Content-Disposition: form-data; name=""attachment_1""; filename=""" + fileName + """" + vbCrLf _<br />
+ "Content-Type: " + fileType + vbCrLf _<br />
+ vbCrLf<br />
<br />
'Convert this preAttachment string to bytes<br />
Dim preAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(preAttachment)<br />
<br />
'Write this preAttachment string to the stream<br />
requestStream.Write(preAttachmentBytes, 0, preAttachmentBytes.Length)<br />
<br />
'Write the file as bytes to the stream by passing its exact location<br />
Using fileStream As New IO.FileStream(Server.MapPath(filePath + fileName), IO.FileMode.Open, IO.FileAccess.Read)<br />
<br />
Dim buffer(4096) As Byte<br />
Dim bytesRead As Int32 = fileStream.Read(buffer, 0, buffer.Length)<br />
<br />
Do While (bytesRead > 0)<br />
<br />
requestStream.Write(buffer, 0, bytesRead)<br />
bytesRead = fileStream.Read(buffer, 0, buffer.Length)<br />
<br />
Loop<br />
<br />
End Using<br />
<br />
'Create a variable named content in the POST, specify the attachment name and comment text<br />
Dim postAttachment As String = vbCrLf _<br />
+ dataBoundary + vbCrLf _<br />
+ "Content-Disposition: form-data; name=""content""" + vbCrLf _<br />
+ vbCrLf _<br />
+ "Action: comment" + vbLf _<br />
+ "Attachment: " + fileName + vbCrLf _<br />
+ "Text: Whatever You Want" + vbCrLf _<br />
+ vbCrLf _<br />
+ "--xYzZY--"<br />
<br />
'Convert postAttachment string to bytes<br />
Dim postAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(postAttachment)<br />
<br />
'Write the postAttachment string to the stream<br />
requestStream.Write(postAttachmentBytes, 0, postAttachmentBytes.Length)<br />
<br />
End Using<br />
<br />
Dim response As Net.WebResponse = Nothing<br />
<br />
'Get the response from our REST request to RT<br />
'Required to capture response, without this Try-Catch attaching will fail<br />
Try<br />
response = request.GetResponse()<br />
<br />
Using responseStream As IO.Stream = response.GetResponseStream()<br />
<br />
Using responseReader As New IO.StreamReader(responseStream)<br />
<br />
Dim responseText = responseReader.ReadToEnd()<br />
<br />
End Using<br />
<br />
End Using<br />
<br />
Catch exception As Net.WebException<br />
<br />
response = exception.Response<br />
<br />
If (response IsNot Nothing) Then<br />
<br />
Using reader As New IO.StreamReader(response.GetResponseStream())<br />
<br />
Dim responseText = reader.ReadToEnd()<br />
<br />
End Using<br />
<br />
response.Close()<br />
<br />
End If<br />
<br />
Finally<br />
<br />
request = Nothing<br />
<br />
End Try<br />
<br />
End Sub<br />
</pre><br />
===JavaScript===<br />
This is an example of updating the RefersTo field for a ticket. Assuming Zepto/jQuery is also being used.<br />
function link_back(url, ticket_num, rt) {<br />
var data, link;<br />
link = "refersto: " + url;<br />
data = {"content": link};<br />
<br />
$.ajax({<br />
type: "POST",<br />
url: rt + ticket_num + "/links",<br />
data: data,<br />
success: function (reply) {<br />
console.log(reply);<br />
}<br />
});<br />
}<br />
<br />
var url = "https://www.wikipedia.org/";<br />
var ticket_num = "123456";<br />
var rt = "https://some.rt.url/REST/1.0/ticket/";<br />
<br />
link_back(url, ticket_num, rt);<br />
<br />
== Convenience libraries ==<br />
<br />
There are libraries which do much of the low-level work shown in the examples below for you, and provide an easier programming interface for dealing with RT:<br />
{| class="wikitable"<br />
!Language<br />
!Package/Module<br />
!Source<br />
!Example<br />
!Note<br />
|-<br />
|'''Perl'''<br />
|[http://metacpan.org/module/RT::Client::REST RT::Client::REST]<br />
|[http://rt-client-rest.googlecode.com/svn/trunk/ Google Code]<br />
|<code>"perl -MCPAN -e install RT::Client::REST"</code> <br />
|<br />
|-<br />
|'''Ruby'''<br />
|[http://rubygems.org/gems/rt-client rt-client]<br />
|[https://github.com/uidzip/rt-client GitHub]<br />
|<code>gem install rt-client</code><br />
|<br />
|-<br />
|'''Ruby'''<br />
|[http://rubygems.org/gems/roart Roart]<br />
|[https://github.com/pjdavis/roart GitHub]<br />
|<code>gem install roart</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/Rickerd0613/rtapi rtapi]<br />
|[https://github.com/Rickerd0613/rtapi GitHub]<br />
|<code>pip install rtapi</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/z4r/python-rtkit rtkit]<br />
|[https://github.com/z4r/python-rtkit GitHub]<br />
|<code>pip install python-rtkit</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/CZ-NIC/python-rt python-rt]<br />
|[https://github.com/CZ-NIC/python-rt Github]<br />
|<code>pip install rt</code><br />
|<br />
|-<br />
|'''Java'''<br />
|[http://projects.boksa.de/RT-REST/ RT-REST]<br />
|[https://github.com/bboksa/RT-REST GitHub]<br />
|<code>N/A</code><br />
|<br />
|-<br />
|'''PHP'''<br />
|[https://github.com/dersam/RTPHPLib RTPHPLib]<br />
|[https://github.com/dersam/RTPHPLib GitHub]<br />
|composer require dersam/rt-php-lib<br />
|<br />
|}</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=REST&diff=26996REST2021-02-16T20:16:08Z<p>Rabin: /* PHP */</p>
<hr />
<div>== Abstract ==<br />
<br />
The REST Interface gives you access to your RT Database. The complete communication is encapsulated in the HTTP protocol. The interface should be accessible in your installation.<br />
<br />
Though you may see references to older 3.x releases of RT below, the REST 1.0 interface has not changed in any significant way in 4.x.<br />
<br />
This page is for REST version 1.0. The next version of the RT REST interface, version 2.0, is available as an [https://metacpan.org/pod/RT::Extension::REST2 extension].<br />
<br />
[{{SERVER}}/index.php?search={{TALKPAGENAME}}&title=Special:Search Legacy Comments] are available from the previous Wikia instance.<br />
<br />
== Interface ==<br />
<br />
Base URL: <code>.../REST/1.0/</code>. The default response should be:<br />
<br />
RT/3.4.5 200 Ok<br />
<br />
# Invalid object specification: 'index.html'<br />
<br />
id: index.html<br />
<br />
<br />
=== Authentication ===<br />
<br />
The REST Interface does not support HTTP-Authentication. So you must get a valid Session-Token and submit the cookie each request. You usually get a Session-Cookie by submitting the default login form. Use variables "<code>user</code>" for login and "<code>pass</code>" for password values. wget doesn't escape any characters in the --post-data option so make sure you properly escape any special characters in the password.<br />
<br />
See the wget invocation line below:<br />
<br />
wget --keep-session-cookies \<br />
--save-cookies cookies.txt \<br />
--post-data 'user=UUUU&pass=PPPP' \<br />
http://my.rt.server<br />
<br />
You need the -keep-session-cookies option to make wget save session cookies.<br />
<br />
=== Ticket ===<br />
<br />
==== Ticket Properties ====<br />
<br />
Gets the data for a single ticket, not including the history and comments.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/show</code><br />
<br />
RT/3.4.5 200 Ok<br />
<br />
id: ticket/&lt;ticket-id&gt;<br />
Queue: &lt;...&gt;<br />
Owner: &lt;...&gt;<br />
Creator: &lt;...&gt;<br />
Subject: &lt;...&gt;<br />
Status: &lt;...&gt;<br />
Priority: &lt;...&gt;<br />
InitialPriority: &lt;...&gt;<br />
FinalPriority: &lt;...&gt;<br />
Requestors: &lt;...&gt;<br />
Cc: &lt;...&gt;<br />
AdminCc: &lt;...&gt;<br />
Created: &lt;...&gt;<br />
Starts: &lt;...&gt;<br />
Started: &lt;...&gt;<br />
Due: &lt;...&gt;<br />
Resolved: &lt;...&gt;<br />
Told: &lt;...&gt;<br />
TimeEstimated: &lt;...&gt;<br />
TimeWorked: &lt;...&gt;<br />
TimeLeft: &lt;...&gt;<br />
<br />
==== Ticket Links ====<br />
<br />
Gets the ticket links for a single ticket.<br />
<br />
Request: <code>REST/1.0/ticket/&lt;ticket-id&gt;/links/show</code><br />
<br />
RT/3.8.2 200 Ok<br />
<br />
id: ticket/&lt;ticket-id&gt;/links<br />
HasMember: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
ReferredToBy: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
DependedOnBy: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
MemberOf: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
RefersTo: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
DependsOn: fsck.com-rt://your.server.com/ticket/&lt;another-id&gt;<br />
<br />
==== Ticket Attachments ====<br />
<br />
Gets a list of all attachments related to the ticket<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments</code><br />
<br />
==== Ticket Attachment ====<br />
Gets the metadata and content of a specific attachment.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments/&lt;attachment-id&gt;</code><br />
<br />
<pre><br />
RT/3.8.0 200 Ok<br />
<br />
id: <attachment-id><br />
Subject:<br />
Creator: <user-id><br />
Created: <timestamp><br />
Transaction: <transaction-id><br />
Parent: <parent-id><br />
MessageId:<br />
Filename: <filename><br />
ContentType: application/octet-stream<br />
ContentEncoding: none<br />
<br />
Headers: MIME-Version: 1.0<br />
X-Mailer: MIME-tools 5.427 (Entity 5.427)<br />
Content-Type: application/octet-stream;<br />
name="<filename>"<br />
Content-Disposition: inline; filename="<filename>"<br />
Content-Transfer-Encoding: base64<br />
Content-Length: <length in bytes><br />
<br />
Content: ...<br />
...<br />
...<br />
</pre><br />
<br />
''NOTE: RT returns the content indented with 9 spaces on each line, so that it lines up with the "Content:" header. Even if you strip this out with a regexp, the content is still UTF-8, which is probably not what you want. To get the original binary data back, strip out the 9 spaces with a regexp, strip off the 3 carriage returns at the end, and then convert the whole thing from UTF-8 to the native character encoding of the attachment, whatever that is. RT doesn't tell you, so you have know. If the attachments were uploaded by a U.S. Windows system, odds are that Windows-1252 is what you want. If you can't get the binary back intact, see the next method below.''<br />
<br />
==== Ticket Attachment Content ====<br />
<br />
Gets the attachment data content without additional metadata or whitespace characters<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/attachments/&lt;attachment-id&gt;/content</code><br />
<br />
RT/3.8.0 200 Ok<br />
<br />
...<br />
...<br />
...<br />
<br />
So to get the original content you still have to strip the first 2 lines of the response.<br />
<br />
==== Ticket History ====<br />
Gets a list of all the history items for a given ticket.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history</code><br />
<br />
<pre><br />
RT/3.4.5 200 Ok<br />
<br />
# <history-count>/<history-count> (/total)<br />
<br />
<history-id>: <history-name><br />
<history-id>: <history-name><br />
...<br />
</pre><br />
<br />
You will get an additional row, for each history entry found. The first entry is usually: "<code>Ticket created by ...</code>".<br />
<br />
There are two ways to get history item detail: you can do one of these and then recursively perform <code>ticket/history/id/&lt;history-id&gt;</code> for each history-id from this REST call, but that is extremely wasteful and will scale horribly. What you really want to do is one REST call but get the long format:<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history?format=l</code><br />
<br />
<pre><br />
RT/3.8.2 200 Ok<br />
<br />
# <n>/<n> (id/&lt;history-id&gt;/total)<br />
<br />
id: <history-id><br />
Ticket: <ticket-id><br />
TimeTaken: <...><br />
Type: <...><br />
Field: <...><br />
OldValue: <...><br />
NewValue: <...><br />
Data: <...><br />
Description: <...><br />
Content: <...><br />
Creator: <...><br />
<br />
Created: <...><br />
<br />
Attachments:<br />
<attachment-id>: <filename> (<size>)<br />
<attachment-id>: <filename> (<size>)<br />
<br />
--<br />
<br />
# <n>/<n> (id/&lt;history-id&gt;/total)<br />
...<br />
</pre><br />
<br />
''NOTE: the double dash "--" will occur in the long format between each history item. You can split the output on "--" and iterate over it, parsing out the data with an RFC822 parser, such as an email handling library.''<br />
<br />
==== Ticket History Entry ====<br />
Gets the history information for a single history item. Note that the history item must actually correspond to the ticket.<br />
<br />
Request: <code>/REST/1.0/ticket/&lt;ticket-id&gt;/history/id/&lt;history-id&gt;</code><br />
<br />
<pre><br />
RT/3.4.5 200 Ok<br />
<br />
# 70/70 (id/114856/total)<br />
<br />
id: <history-id><br />
Ticket: <ticket-id><br />
TimeTaken: <...><br />
Type: <...><br />
Field: <...><br />
OldValue: <...><br />
NewValue: <...><br />
Data: <...><br />
Description: <...><br />
<br />
Content: <lin1-0><br />
<line-1><br />
...<br />
<line-n><br />
<br />
Creator: <...><br />
Created: <...><br />
Attachments: <...><br />
</pre><br />
<br />
''IMPORTANT NOTE: At least with RT 3.8.0, when you request a history item with this method AND you have attached a file that has Mime type text/plain to the same item (eg. a comment with an attachement), RT will return the complete content of the attachment for the key "Content:" and not your real comment that you can see in the web frontend. This may lead to some problems if the requestor does not expect to get a comment content that is for example 1.8 MB of text. With other Mime type attachments this however seems to work. I don't know if this is a feature or a bug.''<br />
<br />
<br />
==== '''Ticket Search''' ====<br />
Request: <code>/REST/1.0/search/ticket?query=&lt;query&gt;&orderby=&lt;sort-order&gt;&format=&lt;format&gt;</code><br />
<br />
'''Parameters'''<br />
<br />
'''query'''<br />
<br />
You can use any query generated by the query builder - or feel free to write your own. Here an example that will do the following: Find all tickets that have no owner and the status new or open<br />
<br />
<code>query= Owner = 'Nobody' AND ( Status = 'new' OR Status = 'open' )</code><br />
<br />
Example: to get all the tickets in "fooQueue" you'd access:<br />
<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'</code><br />
<br />
Example: to get the tickets for a custom field "Contact Name" you'd access:<br />
<br />
<code>/REST/1.0/search/ticket?query='CF.{Contact Name}'='Shaun Wallace'</code><br />
<br />
<br />
'''orderby'''<br />
<br />
By this parameter you can change the sort field and order of the search result. To sort a list ascending just put a + before the fieldname, otherwise a -. Eg: -Created (will put the newest tickets at the beginning).<br />
<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'&orderby=+Created</code><br />
<br />
<br />
'''format'''<br />
<br />
* i: ticket/&lt;ticket-id&gt;<br />
* s: &lt;ticket-id&gt;: &lt;ticket-subject&gt;<br />
* l: a multi-line format<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=Queue='fooQueue'&orderby=+Created&format=i</code><br />
<br />
'''fields'''<br />
<br />
A list of fields you would like included in the result set.<br />
<br />
Example:<br />
<code>/REST/1.0/search/ticket?query=id=42&format=l&fields=Subject,Status,Priority,CF.\{Category\}</code><br />
<br />
Note that you may need to escape characters like the curly braces for CFs.<br />
<br />
==== '''Ticket Create''' ====<br />
<br />
To create a new ticket: post on <code>/REST/1.0/ticket/new</code> with a variable named "<code>content</code>",<br />
<br />
containing "<code>key: value</code>" line by line, example:<br />
<br />
Testing the new ticket section<br />
<br />
<pre><br />
id: ticket/new<br />
Queue: <queue name><br />
Requestor: <requestor email address><br />
Subject: <subject><br />
Cc: <...><br />
AdminCc: <...><br />
Owner: <...><br />
Status: <...><br />
Priority: <...><br />
InitialPriority: <...><br />
FinalPriority: <...><br />
TimeEstimated: <...><br />
Starts: <...><br />
Due: <...><br />
Text: <The ticket content><br />
CF-<CustomFieldName>: <CustomFieldValue><br />
</pre><br />
<br />
If there are any "special" characters (Umlauts, dash, ...?) in a custom field's name, you can still access it via its ID:<br />
CF-$id: <Value><br />
If you want to have a multiline Text, prefix every line with a blank.<br />
<br />
<pre><br />
Due: <...><br />
Text: This is <br />
a <br />
multiline Text<br />
!!!<br />
CF-<CustomFieldName>: <CustomFieldValue><br />
</pre><br />
<br />
The response should look like: <br />
<pre><br />
RT/4.0.6 200 Ok<br />
<br />
# Ticket 775 created.<br />
</pre><br />
<br />
===== curl example =====<br />
<br />
* Create a file containing the ticket form:<br />
<pre><br />
id: ticket/new<br />
Queue: queue1<br />
Requestor: requestor@email<br />
Priority: 4<br />
CF-Type of request: Demande<br />
Subject: Test REST<br />
Text: Multi line<br />
test with<br />
special chars: é<br />
</pre><br />
<br />
* Submit using curl:<br />
<pre><br />
curl --data-urlencode content@file.name 'https://HOSTNAME/REST/1.0/ticket/new?user=USER&pass=PASSWORD'<br />
</pre><br />
==== Ticket Edit ====<br />
To update an existing ticket: post on <code>/REST/1.0/ticket/&lt;ticket-id&gt;/edit</code> with a variable named "content", containing "key: value" line by line (like the one displayed when issuing <code>ticket/&lt;ticket-id&gt;/show</code>). Example:<br />
<br />
<pre><br />
Priority: 5<br />
TimeWorked: 15<br />
</pre><br />
*PHP<br />
<pre> <br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/<ticket id>/edit?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_POST);<br />
$post_data=array("content"=>"AdminCc: userX\nText: This is a REST test edit ticket\n");<br />
<br />
// add the post fields <br />
$request->addPostFields($postData);<br />
// response from RT<br />
$response = $request->send()->getBody();<br />
print_r($response);<br />
</pre><br />
<br />
==== Tickets History Reply ====<br />
Same as comment: post on <code>/REST/1.0/ticket/&lt;ticket-id&gt;/comment</code> with a variable name <code>content</code>, containing "<code>key: value</code>" line by line:<br />
<br />
<pre><br />
id: <ticket-id><br />
Action: correspond<br />
Text: the text comment<br />
Cc: <...><br />
Bcc: <...><br />
TimeWorked: <...><br />
Attachment: an attachment filename/path<br />
</pre><br />
<br />
<code>Cc</code> and <code>Bcc</code> are for this reply only (''I think'').<br />
<br />
==== Ticket History Comment ====<br />
To add a comment to an existing ticket: POST on "<code>/REST/1.0/ticket/&lt;ticket-id&gt;/comment</code>" with a variable name "<code>content</code>", containing "<code>key: value</code>" line by line:<br />
<br />
<pre><br />
id: <ticket-id><br />
Action: comment<br />
Text: the text comment<br />
Attachment: an attachment filename/path<br />
</pre><br />
<br />
Action can be "<code>comment</code>" or "<code>correspond</code>". For a list of fields you can use in correspondence, try "<code>/opt/rt3/bin/rt correspond ticket/1</code>"<br />
<br />
If your comment contains multiple lines, each new line must be preceded by a space (e.g. "line 1\n line 2").<br />
<br />
If you want to use HTML replies, use <pre>Content-Type: text/html</pre><br />
<br />
If you used "<code>Attachment</code>", you must add to your POST a variable "<code>attachment_1</code>" that contains the raw attachment in multi-part file object.<br />
<br />
You can upload more attachments as well, in this case you have to separate the file names in the "<code style="border-style: initial; border-color: initial; ">Attachment</code>" with "\n "(a newline and space, without quotes) and add a new variable "attachment_$i" to your POST where $i is the index of attachment.<br />
<br />
You need to send header to post comments<br />
<br />
==== Ticket Links Edit ====<br />
To update links on an existing ticket: POST on "<code>/REST/1.0/ticket/&lt;ticket-id&gt;/links</code>" with a variable named "<code>content</code>", containing "<code>key: value</code>" line by line (like the one displayed when issuing "<code>ticket/&lt;ticket-id&gt;/links</code>"). Example:<br />
<br />
<pre><br />
DependsOn: 54354<br />
RefersTo: http://some.external/link<br />
</pre><br />
<br />
==== Ticket Merge ====<br />
To merge tickets: POST on "<code>/REST/1.0/ticket/&lt;origin-ticket-id&gt;/merge/&lt;into-ticket-id&gt;</code>". (I don't think any content is required, but I send "id" and "into" anyway.)<br />
<br />
=== User Properties ===<br />
<br />
Gets the data for a single user.<br />
<br />
Request: <code>/REST/1.0/user/&lt;user-id&gt;</code><br />
<br />
RT/3.8.4 200 Ok<br />
<br />
id: user/&lt;user-id&gt;<br />
Name: &lt;...&gt;<br />
Password: ********<br />
EmailAddress: &lt;...&gt;<br />
RealName: &lt;...&gt;<br />
Organization: &lt;...&gt;<br />
Privileged: &lt;...&gt;<br />
Disabled: &lt;...&gt;<br />
<br />
Also you can use user login instead of user ID.<br />
<br />
<br />
=== User ===<br />
<br />
==== User Create ====<br />
<br />
To create a new user: post on <code>/REST/1.0/user/new</code> with a variable named "<code>content</code>", containing "<code>key: value</code>" line by line, like the response to <code>/user/&lt;ticket-id&gt;</code><br />
<br />
==== User Edit ====<br />
To update an existing user: post on <code>/REST/1.0/user/&lt;user-id&gt;/edit</code> with a variable named "content", containing "key: value" line by line (like the one displayed when issuing <code>user/&lt;user-id&gt;/show</code>).<br />
<br />
=== Queue ===<br />
Search for all queues mail addresses like thist:<br />
/REST/1.0/search/queue?query=\&fields=CorrespondAddress,CommentAddress<br />
<br />
==== Single queue properties ====<br />
<br />
Gets the data for a single queue.<br />
<br />
Request: <code>/REST/1.0/queue/&lt;queue-id&gt;</code><br />
<br />
RT/3.8.4 200 Ok<br />
<br />
id: queue/&lt;queue-id&gt;<br />
Name: &lt;...&gt;<br />
Description: &lt;...&gt;<br />
CorrespondAddress: &lt;...&gt;<br />
CommentAddress: &lt;...&gt;<br />
InitialPriority: &lt;...&gt;<br />
FinalPriority: &lt;...&gt;<br />
DefaultDueIn: &lt;...&gt;<br />
<br />
=== Logout ===<br />
<br />
To logout: post on <code>/REST/1.0/logout</code> with empty content.<br />
<br />
== Types ==<br />
<br />
*new<br />
*open<br />
*stalled<br />
*resolved<br />
*rejected<br />
*deleted<br />
+ other custom values defined in you local RT portal.<br />
<br />
<br />
<br />
=== History entry type ===<br />
* Create<br />
* CustomField<br />
* EmailRecord<br />
* Status<br />
* CommentEmailRecord<br />
* Correspond<br />
* Comment<br />
* Priority<br />
* Give<br />
* Steal<br />
* Take<br />
* Untake<br />
* AddWatcher<br />
* DeleteWatcher<br />
* AddLink<br />
* DeleteLink<br />
* AddReminder<br />
* OpenReminder<br />
* ResolveReminder<br />
* Set<br />
* Force<br />
* Subject<br />
* Told<br />
* PurgeTransaction<br />
<br />
== Miscellaneous ==<br />
=== Data format ===<br />
<br />
* History entries time returns in UTC, boolean returns as <code>1</code> (true) and <code>0</code>(false). <br />
<br />
* Use only <code>"\n"</code>, not <code>"\r\n"</code>in post content. <br />
<br />
* Comments in response body starts with with a hash (<code>#</code>) symbol.<br />
<br />
=== Request status ===<br />
To get real request/post status you need to check status code in first line of server response.<br />
<br />
== Examples ==<br />
<br />
=== Perl ===<br />
To get the results of a single request (without setting the Session-Cookie) - assuming you've set:<br />
<br />
* $uri to your RT REST URL<br />
* $access_user to your username<br />
* $access_password to your password<br />
* $ticketNumber to the ticket you want to see<br />
<br />
<pre><br />
my $ua = LWP::UserAgent-&gt;new;<br />
$ua->timeout(10);<br />
$ua->agent("YOURUSERAGENTHERE");<br />
<br />
my $response = $ua->post($uri."ticket/$ticketNumber",<br />
['user' => $access_user, 'pass' => $access_password],<br />
'Content_Type' => 'form-data');<br />
<br />
if ($response-&gt;is_success) {<br />
print $response->decoded_content;<br />
}<br />
</pre><br />
<br />
=== Java ===<br />
<pre><br />
import java.io.IOException;<br />
import org.apache.commons.httpclient.HttpClient;<br />
import org.apache.commons.httpclient.methods.PostMethod;<br />
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;<br />
import org.apache.commons.httpclient.methods.multipart.Part;<br />
import org.apache.commons.httpclient.methods.multipart.StringPart;<br />
<br />
public class RtTicketCreator {<br />
static final String BASE_URI = "http://rt.xxx.com/REST/1.0";<br />
<br />
public static void main(String[] args) throws IOException {<br />
<br />
PostMethod mPost = new PostMethod(BASE_URI + "/ticket/new?user=username&amp;pass=password");<br />
Part[] parts = { new StringPart("content", "Queue: General\nSubject: 123") };<br />
mPost.setRequestEntity(new MultipartRequestEntity(parts, mPost.getParams()));<br />
HttpClient cl = new HttpClient();<br />
cl.executeMethod(mPost);<br />
System.out.println(mPost.getResponseBodyAsString());<br />
}<br />
}<br />
</pre><br />
=== Java ( Based on new Apache HttpComponents library ) ===<br />
<pre><br />
/*<br />
* RT ticket creator based on the current Apache HttpComponents library 4.1.3<br />
* Created by Koustubha Kale, kmkale at youtility dot in<br />
*/<br />
import org.apache.http.HttpEntity;<br />
import org.apache.http.HttpResponse;<br />
import org.apache.http.auth.AuthScope;<br />
import org.apache.http.auth.UsernamePasswordCredentials;<br />
import org.apache.http.client.HttpClient;<br />
import org.apache.http.client.methods.HttpPost;<br />
import org.apache.http.entity.mime.MultipartEntity;<br />
import org.apache.http.entity.mime.content.StringBody;<br />
import org.apache.http.impl.client.DefaultHttpClient;<br />
import org.apache.http.util.EntityUtils;<br />
<br />
<br />
public class NewApacheHttpcomponentsRtTicketCreator {<br />
<br />
public static void main(String[] args) throws Exception {<br />
<br />
DefaultHttpClient httpclient = new DefaultHttpClient();<br />
try {<br />
<br />
HttpPost httppost = new HttpPost("http://rt.xxx.com/rt/REST/1.0" +<br />
"/ticket/new?user=username&pass=password");<br />
StringBody content = new StringBody("Queue: General\nSubject: 123");<br />
MultipartEntity reqEntity = new MultipartEntity();<br />
reqEntity.addPart("content", content);<br />
httppost.setEntity(reqEntity);<br />
System.out.println("executing request " + httppost.getRequestLine());<br />
HttpResponse response = httpclient.execute(httppost);<br />
HttpEntity resEntity = response.getEntity();<br />
<br />
System.out.println("----------------------------------------");<br />
System.out.println(response.getStatusLine());<br />
if (resEntity != null) {<br />
System.out.println("Response content length: " + resEntity.getContentLength());<br />
}<br />
EntityUtils.consume(resEntity);<br />
} finally {<br />
try { httpclient.getConnectionManager().shutdown(); } catch (Exception ignore) {}<br />
}<br />
}<br />
<br />
}<br />
</pre><br />
<br />
=== Python ===<br />
<pre><br />
import cookielib<br />
import urllib<br />
import urllib2<br />
<br />
# creates a cookie for the rtserver with the credentials given at initialization.<br />
# define your credentials here<br />
access_user = 'your_login'<br />
access_password = 'your_password'<br />
<br />
# here is the RequestTracker URI we try to access<br />
uri = 'http://your-rt-instance.com/REST/1.0/'<br />
<br />
# trying login on rt server<br />
cj = cookielib.LWPCookieJar()<br />
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))<br />
urllib2.install_opener(opener)<br />
data = {'user': access_user, 'pass': access_password}<br />
ldata = urllib.urlencode(data)<br />
login = urllib2.Request(uri, ldata)<br />
try:<br />
response = urllib2.urlopen(login)<br />
print response.read()<br />
print "login successful"<br />
except urllib2.URLError:<br />
# could not connect to server<br />
print "Not able to login"<br />
</pre><br />
<br />
=== Ruby ===<br />
<pre><br />
#!/usr/bin/env ruby<br />
require 'net/http'<br />
<br />
user = 'username'<br />
pass = 'password'<br />
uri = URI('https://rt.example.com/REST/1.0/ticket/123/show')<br />
<br />
req = Net::HTTP::Post.new(uri.path)<br />
req.set_form_data('user' => user, 'pass' => pass)<br />
<br />
res = Net::HTTP.start(uri.hostname, uri.port, <br />
:use_ssl => uri.scheme == 'https', <br />
:set_debug_output => $stderr) do |http|<br />
http.request(req)<br />
end<br />
<br />
case res<br />
when Net::HTTPSuccess, Net::HTTPRedirection<br />
# OK<br />
puts "HTTP response code: #{res.code}"<br />
puts "HTTP message: #{res.message}"<br />
puts "Response:"<br />
<br />
res.each do |key,val|<br />
puts "#{key} => #{val}"<br />
end<br />
<br />
puts "Data:"<br />
puts res.body<br />
else<br />
res.value<br />
end<br />
</pre><br />
<br />
=== PHP ===<br />
<SyntaxHighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/<ticket id>/show?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_GET);<br />
/* if you want's to pass additional parameters */<br />
$request->addQueryData(array());<br />
$response = $request->send();<br />
print_r($response);<br />
</SyntaxHighlight><br />
<br />
OR<br />
<br />
<SyntaxHighlight lang="php"><br />
$username = rt_user;<br />
$password = rt_pass;<br />
$url = "http://server.domain.tld/REST/1.0/ticket/new?user=$username&pass=$password";<br />
$request = new HttpRequest($url, HTTP_METH_POST);<br />
// Note : to add data in custom fields you need to add element like below example<br />
// in $post_data array<br />
// Example : \nCF-customfield1:testdata<br />
$post_data = array(<br />
"content" => <br />
"Queue: General" . PHP_EOL .<br />
"Requestor: user@domain" . PHP_EOL .<br />
"Subject: REST test 1" . PHP_EOL .<br />
"Owner: userX" . PHP_EOL .<br />
"AdminCc: userX" . PHP_EOL .<br />
"Text: This is a REST test" . PHP_EOL<br />
);<br />
$request->addPostFields($post_data);<br />
<br />
try<br />
{<br />
response = $request->send()->getBody();<br />
print_r($response);<br />
} catch (HttpException $ex) {<br />
echo $ex;<br />
}<br />
</SyntaxHighlight><br />
<br />
=== C# ===<br />
<br />
<pre><br />
// Using "WCF REST Starter Kit" (http://msdn.microsoft.com/en-us/netframework/cc950529.aspx)<br />
using (var client = new HttpClient("http://rt.site.com/REST/1.0/"))<br />
{<br />
client.TransportSettings.Cookies = new CookieContainer();<br />
<br />
var form = new HttpUrlEncodedForm();<br />
form.Add("user", "LOGIN");<br />
form.Add("pass", "PASSWORD");<br />
<br />
client.Post(string.Empty, form.CreateHttpContent());<br />
<br />
// 1. Get ticket data<br />
using (var request = client.Get("ticket/1234/show"))<br />
{<br />
string content = request.Content.ReadAsString();<br />
// Some logic<br />
}<br />
<br />
// 2. Post ticket reply with attachment<br />
var formPost = new HttpMultipartMimeForm();<br />
byte[] attachment = new byte[];<br />
string content = string.Empty;<br />
<br />
// Store data in attachment<br />
// Store data in content string ("Field: Value" line by line)<br />
// ...<br />
<br />
formPost.Add("content", content);<br />
formPost.Add("attachment_1", "attachment_1", HttpContent.Create(attachment, "application/octet-stream"));<br />
<br />
using (var post = client.Post("ticket/1234/comment"), formPost.CreateHttpContent()))<br />
// Some logic<br />
}</pre><br />
<br />
===Powershell (v3)===<br />
* Escape Powershell reserved chars in user/pass<br />
* If connecting via SSL, use fqdn of server, not cname<br />
*Use `n (newline) when composing new ticket structure<br />
Read RT queue based on query: <br />
<pre><br />
$servername="Your Servername here"<br />
$u="user=Username"<br />
$p="pass=Password"<br />
$q="search/ticket?query=(Status='open' OR Status='new') AND (Queue='GENERAL' OR Queue='FOO') AND Owner='Nobody'"<br />
$uri="https://" + $servername + "/rt/REST/1.0/" + $q + "&" + $u + "&" + $p<br />
$RT=Invoke-WebRequest -Uri $uri -SessionVariable sess<br />
$rt.Content<br />
</pre><br />
<br />
<br />
===VB.NET===<br />
<pre><br />
Public Sub AddAttachmentToRT(ByVal url As String, ByVal fileName As String, ByVal filePath As String)<br />
<br />
Dim dataBoundary As String = "--xYzZY"<br />
Dim request As HttpWebRequest<br />
Dim fileType As String = "image/jpeg"<br />
<br />
'Create a POST web request to the REST interface using the passed URL<br />
request = CType(WebRequest.Create(url), HttpWebRequest)<br />
request.ContentType = "multipart/form-data; boundary=xYzZY"<br />
request.Method = "POST"<br />
request.KeepAlive = True<br />
<br />
'Write the request to the requestStream<br />
Using requestStream As IO.Stream = request.GetRequestStream()<br />
<br />
'Create a variable "attachment_1" in the POST, specify the file name and file type<br />
Dim preAttachment As String = dataBoundary + vbCrLf _<br />
+ "Content-Disposition: form-data; name=""attachment_1""; filename=""" + fileName + """" + vbCrLf _<br />
+ "Content-Type: " + fileType + vbCrLf _<br />
+ vbCrLf<br />
<br />
'Convert this preAttachment string to bytes<br />
Dim preAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(preAttachment)<br />
<br />
'Write this preAttachment string to the stream<br />
requestStream.Write(preAttachmentBytes, 0, preAttachmentBytes.Length)<br />
<br />
'Write the file as bytes to the stream by passing its exact location<br />
Using fileStream As New IO.FileStream(Server.MapPath(filePath + fileName), IO.FileMode.Open, IO.FileAccess.Read)<br />
<br />
Dim buffer(4096) As Byte<br />
Dim bytesRead As Int32 = fileStream.Read(buffer, 0, buffer.Length)<br />
<br />
Do While (bytesRead > 0)<br />
<br />
requestStream.Write(buffer, 0, bytesRead)<br />
bytesRead = fileStream.Read(buffer, 0, buffer.Length)<br />
<br />
Loop<br />
<br />
End Using<br />
<br />
'Create a variable named content in the POST, specify the attachment name and comment text<br />
Dim postAttachment As String = vbCrLf _<br />
+ dataBoundary + vbCrLf _<br />
+ "Content-Disposition: form-data; name=""content""" + vbCrLf _<br />
+ vbCrLf _<br />
+ "Action: comment" + vbLf _<br />
+ "Attachment: " + fileName + vbCrLf _<br />
+ "Text: Whatever You Want" + vbCrLf _<br />
+ vbCrLf _<br />
+ "--xYzZY--"<br />
<br />
'Convert postAttachment string to bytes<br />
Dim postAttachmentBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(postAttachment)<br />
<br />
'Write the postAttachment string to the stream<br />
requestStream.Write(postAttachmentBytes, 0, postAttachmentBytes.Length)<br />
<br />
End Using<br />
<br />
Dim response As Net.WebResponse = Nothing<br />
<br />
'Get the response from our REST request to RT<br />
'Required to capture response, without this Try-Catch attaching will fail<br />
Try<br />
response = request.GetResponse()<br />
<br />
Using responseStream As IO.Stream = response.GetResponseStream()<br />
<br />
Using responseReader As New IO.StreamReader(responseStream)<br />
<br />
Dim responseText = responseReader.ReadToEnd()<br />
<br />
End Using<br />
<br />
End Using<br />
<br />
Catch exception As Net.WebException<br />
<br />
response = exception.Response<br />
<br />
If (response IsNot Nothing) Then<br />
<br />
Using reader As New IO.StreamReader(response.GetResponseStream())<br />
<br />
Dim responseText = reader.ReadToEnd()<br />
<br />
End Using<br />
<br />
response.Close()<br />
<br />
End If<br />
<br />
Finally<br />
<br />
request = Nothing<br />
<br />
End Try<br />
<br />
End Sub<br />
</pre><br />
===JavaScript===<br />
This is an example of updating the RefersTo field for a ticket. Assuming Zepto/jQuery is also being used.<br />
function link_back(url, ticket_num, rt) {<br />
var data, link;<br />
link = "refersto: " + url;<br />
data = {"content": link};<br />
<br />
$.ajax({<br />
type: "POST",<br />
url: rt + ticket_num + "/links",<br />
data: data,<br />
success: function (reply) {<br />
console.log(reply);<br />
}<br />
});<br />
}<br />
<br />
var url = "https://www.wikipedia.org/";<br />
var ticket_num = "123456";<br />
var rt = "https://some.rt.url/REST/1.0/ticket/";<br />
<br />
link_back(url, ticket_num, rt);<br />
<br />
== Convenience libraries ==<br />
<br />
There are libraries which do much of the low-level work shown in the examples below for you, and provide an easier programming interface for dealing with RT:<br />
{| class="wikitable"<br />
!Language<br />
!Package/Module<br />
!Source<br />
!Example<br />
!Note<br />
|-<br />
|'''Perl'''<br />
|[http://metacpan.org/module/RT::Client::REST RT::Client::REST]<br />
|[http://rt-client-rest.googlecode.com/svn/trunk/ Google Code]<br />
|<code>"perl -MCPAN -e install RT::Client::REST"</code> <br />
|<br />
|-<br />
|'''Ruby'''<br />
|[http://rubygems.org/gems/rt-client rt-client]<br />
|[https://github.com/uidzip/rt-client GitHub]<br />
|<code>gem install rt-client</code><br />
|<br />
|-<br />
|'''Ruby'''<br />
|[http://rubygems.org/gems/roart Roart]<br />
|[https://github.com/pjdavis/roart GitHub]<br />
|<code>gem install roart</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/Rickerd0613/rtapi rtapi]<br />
|[https://github.com/Rickerd0613/rtapi GitHub]<br />
|<code>pip install rtapi</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/z4r/python-rtkit rtkit]<br />
|[https://github.com/z4r/python-rtkit GitHub]<br />
|<code>pip install python-rtkit</code><br />
|<br />
|-<br />
|'''Python'''<br />
|[https://github.com/CZ-NIC/python-rt python-rt]<br />
|[https://github.com/CZ-NIC/python-rt Github]<br />
|<code>pip install rt</code><br />
|<br />
|-<br />
|'''Java'''<br />
|[http://projects.boksa.de/RT-REST/ RT-REST]<br />
|[https://github.com/bboksa/RT-REST GitHub]<br />
|<code>N/A</code><br />
|<br />
|-<br />
|'''PHP'''<br />
|[https://github.com/dersam/RTPHPLib RTPHPLib]<br />
|[https://github.com/dersam/RTPHPLib GitHub]<br />
|composer require dersam/rt-php-lib<br />
|<br />
|}</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=FastCGIConfiguration&diff=26415FastCGIConfiguration2016-10-27T09:20:38Z<p>Rabin: /* Configuring RT to run as a static/external FastCGI application */</p>
<hr />
<div>{{OutdatedInstallGuide}}<br />
<br />
As of the 12/19/2012, a fastcgi_server is in the libexec folder of your request_tracker path in debian. It is flawed in such a way (line 202; ''unless'' clause) that you cannot specify the socket file you want to use, it will always use the default one (specifying the pid file works though). For this very reason, the configuration values below correspond to the default ones of the fastcgi_server script.<br />
<br />
Following are the initscript and the nginx configuration necessary to use it:<br />
<br />
#!/bin/sh<br />
<br />
RTPATH="/usr/share/request-tracker3.8/"<br />
RTUSER=www-data<br />
SOCKET_FILE=/var/cache/request-tracker3.8/fastcgi.sock<br />
# if you want to use a IP connection instead, please modify the su line to start it with -p or --port option instead of the --socket one.<br />
PID_FILE=/var/cache/request-tracker3.8/fastcgi.pid<br />
<br />
case $1 in<br />
start)<br />
echo -n "Starting RT: "<br />
cd $RTPATH<br />
export FCGI_SOCKET_PATH<br />
RTUSER_GROUP="`id -gn $RTUSER 2>/dev/null`"<br />
if [ -n "$RTUSER_GROUP" ]<br />
then<br />
touch $PID_FILE $SOCKET_FILE;<br />
chown $RTUSER:$RTUSER_GROUP $PID_FILE $SOCKET_FILE<br />
fi<br />
su $RTUSER -c "libexec/fastcgi_server --pidfile=$PID_FILE --socket=$SOCKET_FILE 2> /dev/null" &<br />
echo "Started."<br />
;;<br />
<br />
stop)<br />
echo -n "Stopping RT: "<br />
PID="`cat $PID_FILE 2>/dev/null`"<br />
if [ -n "$PID" ]<br />
then<br />
kill $PID<br />
echo "Stopped."<br />
rm -f $SOCKET_FILE $PID_FILE<br />
else<br />
echo "not running."<br />
fi<br />
;;<br />
<br />
restart|force-reload)<br />
$0 stop<br />
$0 start<br />
;;<br />
<br />
*)<br />
echo "Usage: /etc/init.d/rt { stop | start | restart }"<br />
exit 1<br />
;;<br />
esac<br />
<br />
server {<br />
root /usr/share/request-tracker3.8/html;<br />
index index.html index.htm;<br />
server_name localhost debian;<br />
location / {<br />
rewrite ^/rt/(.*)$ /$1 last; # this line can be removed if the 50-debconf file is modified. (''Set($WebPath , "");'')<br />
try_files $uri $uri/ index.html;<br />
fastcgi_pass unix:/var/cache/request-tracker3.8/fastcgi.sock;<br />
include /etc/nginx/fastcgi_params;<br />
fastcgi_param SCRIPT_NAME "";<br />
fastcgi_param PATH_INFO $uri;<br />
}<br />
}<br />
<br />
<hr />'''NOTE: The information below is outdated. It is superseded by the documentation in docs/web_deployment.pod in the base RT tarball.'''<br />
<br />
<hr />[https://www.soljerome.com/blog/2011/04/22/installing-request-tracker-on-ubuntu-with-nginx/ http://soljerome.com/blog/category/linux/nginx/], 4/22/2011 blog post about setting up<br />
<br />
<ul><li>RT 3.8</li><li>nginx</li><li>MySQL</li><li>Ubuntu 10.04</li></ul><br />
-- MarkB<br />
<br />
<hr />FastCGI is a variant to run RT server. You may want to read [[ManualApacheConfig]] to decide what you want to use.<br />
<br />
Even if you decided to use [[FastCGI]] then anyway you must read [[ManualApacheConfig]]. It's sort of mandatory reading :)<br />
<br />
Configuring RT to run in [[FastCGI]] mode is dead simple. However, configuring Apache is hard. If you are considering a single web server to use with RT, you might like to consider a fast and simple solution such as lighttpd, nginx or any of the commercial and free web servers available that support [[FastCGI]].<br />
<br />
= Configuring RT 3.2 and newer to run in FastCGI mode =<br />
<br />
When installing RT, run:<br />
<br />
./configure \<br />
--with-web-user="webuser" \<br />
--with-web-group="webgroup" \<br />
--with-rt-user="webuser" \<br />
--with-rt-group="webgroup"<br />
<br />
Don't forget to include other configuration options that matter to you, like --prefix. You will still need to customize [=etc/[[RT SiteConfig|RT_SiteConfig]].pm] using [=etc/[[RT Config|RT_Config]].pm] as a base, before you run `<code>make install</code>'. If you do not have `root' access to the server you are installing to, you can use `<code>fakeroot</code>' or a similar utility to use `<code>make install</code>'.<br />
<br />
== Configuring RT to run as a dynamic FastCGI application ==<br />
<br />
A dynamic [[FastCGI]] application is one in which the application process is dynamically started and stopped by the webserver process, on demand.<br />
<br />
RT's [[FastCGI]] handler defaults to assuming it is being run as as a dynamic application. So, configure your web server to start <code>bin/mason_handler.fcgi</code> inside the RT installation directory for requests that it is to handle. The first time RT is accessed the web server will attempt to start RT.<br />
<br />
Note that this is a less-than-ideal approach, security wise, since the RT application runs under the same user-id as the web server. A better approach would be to have the web server somehow switch users when starting RT. This is commonly achieved via set-uid wrappers, which are generally hard to debug when things go wrong. Also, you will be relying on the process manager included with your web server.<br />
<br />
== Configuring RT to run as a static/external FastCGI application ==<br />
<br />
A static/external [[FastCGI]] application is one in which the application is started manually or independently of the webserver, and the webserver connects to it when needed. This makes it easy to setup user separation between the RT process and the webserver process, but you lose out on the ability for the webserver to dynamically determine how many RT processes need to be running.<br />
<br />
This is achieved by running the <code>bin/mason_handler.fcgi</code> script as the configured RT user, setting <code>FCGI_SOCKET_PATH</code> in the process environment to the location of the application socket, or a /hostname:port/ combination. This is a feature of the <code>CGI::Fast</code> perl module, so you may need to upgrade your version of that module if your version does not support this feature.<br />
<br />
Note that the [[FastCGI]] handler for RT does not currently include a process manager, instead single threading requests. This is likely to be addressed in a later version of either <code>CGI::Fast</code> or RT, but for small installations this is usually acceptable. A simple workaround is simply to start more than one RT daemon.<br />
<br />
Note that running RT in single threaded [[FastCGI]] mode is the '''only''' time that it is actually /safe/ to turn off the [=$[[WebFlushDbCacheEveryRequest]]] configuration option.<br />
<br />
You will need to set up a script that starts RT after your web server. For best results, use a [[SysV]]-style <code>/etc/init.d/</code> script. Here is an example of such a script:<br />
<br />
<pre>#!/bin/sh<br />
<br />
RTPATH=/path/to/rt<br />
RTUSER=rt<br />
FCGI_SOCKET_PATH=$RTPATH/var/appSocket<br />
<br />
case $1 in<br />
start)<br />
echo -n "Starting RT: mason_handler.fcgi"<br />
cd $RTPATH<br />
export FCGI_SOCKET_PATH<br />
su $RTUSER -c perl bin/mason_handler.fcgi &amp;<br />
echo<br />
;;<br />
<br />
stop)<br />
echo -n "Stopping RT: "<br />
PIDS=`ps axww | awk '/[m]ason_handler.fcgi/ { print $1}'`<br />
if [ -n "$PIDS" ]<br />
then<br />
echo -n kill -TERM $PIDS<br />
kill $PIDS<br />
echo<br />
else<br />
echo RT not running<br />
fi<br />
;;<br />
<br />
restart|force-reload)<br />
$0 stop<br />
$0 start<br />
;;<br />
<br />
*)<br />
echo "Usage: /etc/init.d/rt { stop | start | restart }"<br />
exit 1<br />
;;<br />
esac<br />
<br />
</pre><br />
<br />
(this example corresponds to the lighttpd setup example)<br />
<br />
= Web-server specific FastCGI notes =<br />
<br />
== Configuring RT to run with lighttpd ==<br />
<br />
A small patch needs to be applied to lighttpd 1.3.x, as its [[FastCGI]] support is slightly broken. See [[LighttpdPatch]] for the patch. Lighttpd 1.4.x does not have the problem.<br />
<br />
First, make sure that <code>"mod_fastcgi"</code> is uncommented in the <code>server.modules</code> option. Then you can use a <code>fastcgi.server</code> configuration setting similar to:<br />
<br />
fastcgi.server = ( "/rt" =&gt;<br />
( "rt" =&gt;<br />
(<br />
"socket" =&gt; "/path/to/rt/var/appSocket",<br />
"check-local" =&gt; "disable"<br />
)<br />
)<br />
)<br />
<br />
Information for lighttpd is included as it is the easiest to setup open source web server that supports [[FastCGI]] that I am aware of. However, the process is just as simple using commercial web servers, like Zeus, [[LiteSpeed]] or thttpd professional, and no doubt other web servers with [[FastCGI]] support like Roxen or Pi3web.<br />
<br />
== Configuring RT to run with Apache ==<br />
<br />
RT 3.2 includes a significant change to the [[FastCGI]] handler. It is no longer "setgid" to the RT group. Perl's setid support has been deprecated for the last several releases and a number of platforms don't bundle the "sperl" or "suidperl" executable by default. Additionally, when perl is run [[SetUID]] or [[SetGID]], the interpreter is automatically switched into /taint mode/, in which all incoming data, no matter the source is considered suspect. At first, this seems like a great idea. But perl's taint mode is a big sledgehammer used to hit small nails. Many perl libraries aren't tested in taint mode and will fail when least expected. Moving away from a [[SetGID]] [[FastCGI]] handler will enable more users to have a smoother RT experience. It does require some changes in how you set up and configure RT.<br />
<br />
Beginning with RT 3.2, you have several choices about how to configure RT to run as a [[FastCGI]]:<br />
<br />
=== 1. Install RT as the user your webserver runs as ===<br />
<br />
Pros: Very easy to configure<br />
<br />
Cons: Your webserver has access to RT's private database password<br />
<br />
==== How To ====<br />
<br />
First, determine whether you are using mod_fastcgi or mod_fcgid. I believe mod_fastcgi is older and supports older versions of Apache (i.e. Apache 1.x). The mod_fcgid is newer and is included with newer versions of Fedora Linux, etc. RT does not care which module is being used, but the configuration syntax is different for each module.<br />
<br />
If you are using mod_fastcgi (apache 1.x), you'll want to add something like the following to your httpd.conf:<br />
<br />
'''NOTE:''' Most [[FastCGI]] options are available only in apache's server config context, not in virtual host sections.<br />
<br />
<nowiki># Tell FastCGI to put its temporary files somewhere sane.<br />
FastCgiIpcDir /tmp<br />
<br />
# Number of processes is tunable, but you need at least 3 or 4<br />
FastCgiServer /opt/rt3/bin/mason_handler.fcgi -idle-timeout 120 -processes 4<br />
<br />
&lt;VirtualHost rt.example.com&gt;<br />
<br />
AddHandler fastcgi-script fcgi<br />
ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/<br />
<br />
&lt;/VirtualHost&gt;<br />
<br />
</nowiki><br />
<br />
If you are using mod_fcgid, you'll want to add something like the following to your httpd.conf:<br />
<br />
&lt;VirtualHost rt.example.com&gt;<br />
<br />
ServerName rt.example.com<br />
DocumentRoot /opt/rt3/share/html<br />
<br />
AddHandler fcgid-script fcgi<br />
ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/<br />
<br />
&lt;/VirtualHost&gt;<br />
<br />
=== 2. Make your webserver user a member of the "rt" group ===<br />
<br />
Pros: Easy to configure<br />
<br />
Cons: Your webserver has access to RT's private database password<br />
<br />
==== How To ====<br />
<br />
Install RT normally. Add whichever user your webserver runs as (whatever you set --with-web-user to) to the "rt" group (whatever you set --with-rt-group to) in /etc/groups.<br />
<br />
To find out what user your webserver runs as, look for the line<br />
<br />
User some-user-name<br />
<br />
in your apache httpd.conf. Common values are www, www-data, web and nobody.<br />
<br />
=== 3. Run RT using _suexec_ or a similar mechanism ===<br />
<br />
Pros: More secure<br />
<br />
Cons: Sometimes very difficult to configure<br />
<br />
==== How To ====<br />
<br />
Apache's <u>suexec</u> utility allows you run CGI programs as specific users. Because that's a relatively heavy responsibility, it's very, very conservative about what it's willing to do for you. On top of that, Apache's mod_fastcgi plugin doesn't respect all of suexec's features. While suexec is designed to execute CGI scripts in a given virtual host's DocumentRoot, It can only execute [[FastCGI]] scripts in the system's '''main''' DocumentRoot.<br />
<br />
This means you have to copy the RT [[FastCGI]] handler into your main DocumentRoot<br />
<br />
The following example VirtualHost will run RT as a [[FastCGI]] on Apache 1.3 on a Debian Linux server.<br />
<br />
<nowiki># Tell FastCGI to put its temporary files somewhere sane.<br />
FastCgiIpcDir /tmp<br />
<br />
# Tell FastCGI that it should use apache's "suexec" binary to call any FastCGI script.<br />
# This is a GLOBAL setting<br />
FastCgiWrapper /usr/lib/apache/suexec<br />
<br />
# You need to copy the rt mason_handler.fcgi into a directory inside the main server DocumentRoot<br />
# That directory must be owned by the user and group that will execute the FastCGI script<br />
# In this case, that directory is /var/www/rt<br />
<br />
# To find the local DocumentRoot, run "suexec -V" as root and look for the<br />
# -D DOC_ROOT parameter.<br />
<br />
# Apache 1.3 discards the user and group parameters on the FastCgiServer line.<br />
# Apache 2.0 requires them.<br />
FastCgiServer /var/www/rt/mason_handler.fcgi -idle-timeout 120 -user rt -group rt -processes 4<br />
<br />
&lt;VirtualHost rt.example.com&gt;<br />
<br />
DocumentRoot /opt/rt3/share/html<br />
<br />
# Set the rt user and group as the executing user for this virtual host.<br />
# For apache 2 you need<br />
# SuexecUserGroup rt rt<br />
# instead<br />
User rt<br />
Group rt<br />
<br />
AddHandler fastcgi-script fcgi<br />
ScriptAlias / /var/www/rt/mason_handler.fcgi/<br />
<br />
&lt;/VirtualHost&gt;<br />
</nowiki><br />
<br />
== Configuring RT3 to run with nginx ==<br />
<br />
The patch listed below is no longer required with the latest version of nginx ([[ManualApacheConfig|http://www.nginx.net]]),<br />
<br />
Here is a quick excerpt that shows how to run rt3 with a [[FastCGI]] socket,<br />
<br />
server {<br />
listen 80;<br />
server_name localhost;<br />
<br />
location / {<br />
root /usr/local/rt3/share/html;<br />
fastcgi_pass unix:/var/run/rt3/rt3.socket;<br />
<br />
fastcgi_param QUERY_STRING $query_string;<br />
fastcgi_param REQUEST_METHOD $request_method;<br />
fastcgi_param CONTENT_TYPE $content_type;<br />
fastcgi_param CONTENT_LENGTH $content_length;<br />
fastcgi_param PATH_INFO $fastcgi_script_name;<br />
}<br />
<br />
location /NoAuth/ {<br />
alias /usr/local/rt3/share/html/NoAuth/;<br />
}<br />
<br />
It seems that declaring SCRIPT_NAME /and/ PATH_INFO clobbers some functions so be sure to use only PATH_INFO.<br />
<br />
A small patch needs to be applied to mason_handler.fcgi to run properly under [[FastCGI]] witn nginx. Problem - nginx not send PATH_INFO to fastcgi server.<br />
<br />
First apply next patch to mason_handler.fcgi:<br />
<br />
--- ./bin/mason_handler.fcgi 2005-12-02 08:23:20.000000000 -0500<br />
+++ /usr/local/rt3/bin/mason_handler.fcgi 2005-12-02 11:54:56.000000000 -0500<br />
@@ -68,11 +68,17 @@<br />
Module::Refresh-&gt;refresh if $RT::DevelMode;<br />
RT::ConnectToDatabase();<br />
<br />
+ my $path_info = $cgi-&gt;script_name;<br />
+ $path_info =~ s/^\///;<br />
+ $cgi-&gt;path_info($path_info);<br />
+ $RT::Logger-&gt;crit($path_info);<br />
+<br />
if ( ( !$Handler-&gt;interp-&gt;comp_exists( $cgi-&gt;path_info ) )<br />
&amp;&amp; ( $Handler-&gt;interp-&gt;comp_exists( $cgi-&gt;path_info . "/index.html" ) ) ) {<br />
$cgi-&gt;path_info( $cgi-&gt;path_info . "/index.html" );<br />
}<br />
-<br />
+<br />
+ $RT::Logger-&gt;crit($cgi-&gt;path_info);<br />
eval { $Handler-&gt;handle_cgi_object($cgi); };<br />
if ($@) {<br />
$RT::Logger-&gt;crit($@);<br />
<br />
Then create config for vhost that you want run rt3 on:<br />
<br />
(example of config for fastcgi socket path - /tmp/request_tracker and rt3 installation path - /usr/local/rt3/<br />
<br />
server {<br />
listen IP_ADDRESS:PORT;<br />
server_name domain.com;<br />
<br />
location /images/ { alias /usr/local/rt3/share/html/NoAuth/images/; }<br />
<br />
location / {<br />
fastcgi_pass unix:/tmp/request_tracker;<br />
fastcgi_x_powered_by off; # default on<br />
fastcgi_param DOCUMENT_ROOT /usr/local/rt3/share/html;<br />
fastcgi_param SCRIPT_FILENAME /usr/local/rt3/share/html$fastcgi_script_name;<br />
fastcgi_param PATH_TRANSLATED /usr/local/rt3/share/html$fastcgi_script_name;<br />
fastcgi_param SCRIPT_NAME $fastcgi_script_name;<br />
fastcgi_param QUERY_STRING $query_string;<br />
fastcgi_param CONTENT_TYPE $content_type;<br />
fastcgi_param CONTENT_LENGTH $content_length;<br />
fastcgi_param REDIRECT_STATUS 200;<br />
fastcgi_param SERVER_ADDR $server_addr;<br />
fastcgi_param SERVER_PORT $server_port;<br />
fastcgi_param SERVER_PROTOCOL $server_protocol;<br />
fastcgi_param SERVER_SOFTWARE "nginx/0.3.15";<br />
fastcgi_param GATEWAY_INTERFACE "CGI/1.1";<br />
fastcgi_param SERVER_NAME $server_name;<br />
fastcgi_param SERVER_NAME $server_name;<br />
fastcgi_param REQUEST_URI $request_uri;<br />
fastcgi_param REQUEST_METHOD $request_method;<br />
fastcgi_param REMOTE_USER $remote_user;<br />
fastcgi_param REMOTE_ADDR $remote_addr;<br />
fastcgi_param REMOTE_PORT $remote_port;<br />
}<br />
<br />
location ~* .+\.(html|js|css)$ { ....same as above location.... }<br />
<br />
location /NoAuth/ { alias /usr/local/rt3/share/html/NoAuth/; }<br />
}<br />
<br />
All tested and working with rt 3.5.5<br />
<br />
Other information about nginx and FastCGI can be found on our site - [[ManualApacheConfig|http://www.nginx.info]] (now russian only, will be translated soon)<br />
<br />
PS. If you want run rt3 with prefix - domain.com/prefix/, so need to remove that prefix for path_info (in perl code of patch above)<br />
<br />
== RT 3.0 FastCGI configuration ==<br />
<br />
=== <nowiki>!FastCGI configuration for Apache 2.0</nowiki> ===<br />
<br />
(assumes FastCGI is already installed)<br />
<br />
This is roughly what I had to do to get [[RequestTracker]] working using [[FastCGI]] on [[RedHat9]] system.<br />
<br />
1. Install suidperl package, if not already installed.<br />
<br />
2. Edit first line of /opt/rt3/bin/mason_handler.fcgi to be<br />
<br />
<nowiki>#!/usr/bin/suidperl<br />
<br />
</nowiki><br />
<br />
3. Add the following to /etc/httpd/conf/httpd.conf<br />
<br />
LoadModule fastcgi_module modules/mod_fastcgi.so<br />
<br />
FastCgiIpcDir /tmp<br />
FastCgiServer /opt/rt3/bin/mason_handler.fcgi -idle-timeout 300 -processes 5<br />
&lt;VirtualHost *&gt;<br />
ServerName rt-host-name.example.org<br />
DocumentRoot /opt/rt3/share/html<br />
<br />
AddHandler fastcgi-script fcgi<br />
ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/<br />
<br />
&lt;Location /&gt;<br />
AddDefaultCharset UTF-8<br />
SetHandler fastcgi-script<br />
&lt;/Location&gt;<br />
&lt;/VirtualHost&gt;<br />
<br />
=== <nowiki>!FastCGI configuration for Apache 1.3</nowiki> ===<br />
<br />
LoadModule fastcgi_module libexec/apache/mod_fastcgi.so<br />
AddModule mod_fastcgi.c<br />
<br />
Outside the vhost:<br />
<br />
FastCgiServer /opt/rt3/bin/mason_handler.fcgi -idle-timeout 300 -processes 5<br />
<br />
In your vhost:<br />
<br />
AddHandler fastcgi-script fcgi<br />
ScriptAlias / /opt/rt3/bin/mason_handler.fcgi/<br />
&lt;Location /&gt;<br />
AddDefaultCharset UTF-8<br />
SetHandler fastcgi-script<br />
&lt;/Location&gt;<br />
<br />
----<br />
<br />
Since the default Apache install includes a Directory directive blocking access to / and another to allow for the [[DocumentRoot]], you might need to add something like:<br />
<br />
&lt;Directory "/opt/rt3"&gt;<br />
Order allow,deny<br />
Allow from all<br />
&lt;/Directory&gt;<br />
<br />
To allow access to that directory, too. The symptom is getting '403 Forbidden' and a line like 'client denied by server configuration' in your Apache error log, even for index.html. -- [[HowardJones]]<br />
<br />
=== See also ===<br />
<br />
* [http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#fastcgiserver mod_fastcgi for apache]<br />
<br />
== Running rt 4.2.6 / nginx / DocumentRoot-prefix "/rt" ==<br />
I updated to rt 4.2.6 - now, the combination of rt / nginx / fastcgi / document-root '/rt' does not work again. My goal is to map a RT with nginx to a URL like "http://www.domain.de:8080/rt" (instead of "http://www.domain.de:8080"). I played around and found the following working setup - Danny70437.<br />
/etc/nginx/conf.d/rt4.conf<br />
<br />
server {<br />
<br />
listen 8080;<br />
server_name host.domain.de;<br />
root /tmp;<br />
<br />
<nowiki>#</nowiki> /opt/rt4/etc/RT_SiteConfig.pm<br />
<nowiki>#</nowiki> Set($WebDomain, "host.domain.de");<br />
<nowiki>#</nowiki> Set($WebPort , 8080);<br />
<nowiki>#</nowiki> Set($WebPath , "/rt");<br />
<nowiki>#</nowiki> Set(@ReferrerWhitelist, qw(extranet.domain.de:443 host.domain.de:8080 ));<br />
<br />
<nowiki>#<br />
#<br />
#<br />
location / {<br />
<br />
###</nowiki> <original /etc/nginx/fastcgi_params - debian 7.x><br />
<nowiki>#</nowiki> include /etc/nginx/fastcgi_params;<br />
fastcgi_param QUERY_STRING $query_string;<br />
fastcgi_param REQUEST_METHOD $request_method;<br />
fastcgi_param CONTENT_TYPE $content_type;<br />
fastcgi_param CONTENT_LENGTH $content_length;<br />
<br />
fastcgi_param SCRIPT_FILENAME $request_filename;<br />
fastcgi_param SCRIPT_NAME $fastcgi_script_name;<br />
fastcgi_param REQUEST_URI $request_uri;<br />
fastcgi_param DOCUMENT_URI $document_uri;<br />
fastcgi_param DOCUMENT_ROOT $document_root;<br />
fastcgi_param SERVER_PROTOCOL $server_protocol;<br />
<br />
fastcgi_param GATEWAY_INTERFACE CGI/1.1;<br />
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;<br />
<br />
fastcgi_param REMOTE_ADDR $remote_addr;<br />
fastcgi_param REMOTE_PORT $remote_port;<br />
fastcgi_param SERVER_ADDR $server_addr;<br />
fastcgi_param SERVER_PORT $server_port;<br />
fastcgi_param SERVER_NAME $server_name;<br />
<br />
fastcgi_param HTTPS $https;<br />
<br />
<nowiki>#</nowiki> PHP only, required if PHP was built with --enable-force-cgi-redirect<br />
fastcgi_param REDIRECT_STATUS 200;<br />
<nowiki>###</nowiki> </original /etc/nginx/fastcgi_params><br />
<br />
fastcgi_pass unix:/opt/rt4/var/fastcgi.sock;<br />
<br />
fastcgi_param SCRIPT_NAME "/rt";<br />
fastcgi_param PATH_INFO $uri;<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
rt-server.fcgi was called like this:<br />
/opt/rt4/sbin/rt-server.fcgi --pidfile=/opt/rt4/var/fastcgi.pid --socket=/opt/rt4/var/fastcgi.sock 2> /dev/null</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=OnMaxPriority&diff=5355OnMaxPriority2016-06-18T19:46:46Z<p>Rabin: </p>
<hr />
<div>I use this very simple Custom action to check if ticket has maximum priority and thus is overdue. When it's true, I send email to someone, that ticket needs to be done.<br />
<br />
my $status = $self-&gt;TicketObj-&gt;Status;<br />
my $final = $self-&gt;TicketObj-&gt;FinalPriority;<br />
my $priority = $self-&gt;TicketObj-&gt;Priority;<br />
return 0 unless $self-&gt;TransactionObj-&gt;Field eq 'Priority';<br />
return 0 unless $status eq 'open' || $status eq 'new' || $status eq 'stalled';<br />
return 0 unless $final &gt; 0;<br />
return 0 unless $final == $priority;<br />
return 1;<br />
<br />
<br />
For this solution to work, the us of rt-crontool is needed, like this one:<br />
<br />
<pre><br />
$ crontab -l<br />
<br />
# m h dom mon dow command<br />
0 6,12,18,0 * * * rt-crontool --search RT::Search::FromSQL --search-arg "(Status='new' OR Status='open') AND FinalPriority &gt; 0" --action RT::Action::LinearEscalate<br />
<br />
</pre><br />
<br />
This line escalate ticket's priority in every queue (if the [[FinalPriority]] is set) using rt-crontool every 6 hours. I use [[LinearEscalate]], whis makes much more sense than [[EscalatePriority]].</div>Rabinhttps://rt-wiki.bestpractical.com/index.php?title=OnCreateCheckCF&diff=5354OnCreateCheckCF2016-06-18T19:45:29Z<p>Rabin: /* 4. Creating the Scrip */</p>
<hr />
<div>Contributed by [http://wiki.bestpractical.com/view/Cris Cris]<br />
<br />
=== Overview ===<br />
<br />
----<br />
<br />
You may want to create a mandatory custom field, so that people will have to fill the value before being able to submit a new ticket. However, if you define a cf as mandatory, only people creating tickets from the web interface will be forced to fill the value. Those creating a new ticket using the mail gateway won't notice the presence of the mandatory cf, and the ticket will be created anyway.<br />
<br />
My solution consists of three pieces:<br />
<br />
* the [[ExtractCustomFieldValues]] extension, needed to be able assign a value to the cf through an email message<br />
* an OnCreate scrip which checks if the cf is present, and eventually marks the ticket as rejected and notifies the requestor<br />
* a custom template to use when notifying the requestor about a rejected ticket<br />
<br />
This is implemented on RT 3.8.4.<br />
<br />
=== How to setup OnCreateCheckCF in RT3 ===<br />
<br />
----<br />
<br />
===== 1. Custom field creation =====<br />
<br />
First create a new custom field: Configuration -&gt; Custom Fields -&gt; Create<br />
<br />
'''Name:''' <code>cfTest</code> ''(you can use the name you want)''<br />
<br />
'''Description:''' <code>test mandatory custom field</code><br />
<br />
'''Type:''' <code>Fill in one text area</code> ''(you can use the type you want)''<br />
<br />
'''Validation:''' (?#Mandatory).<br />
<br />
Now "Save Changes".<br />
<br />
On the "Applies To" page select the queues where you want to enable the custom field, then on the "Group Rights" give the "ModifyCustomField" right to the "Everyone" system group (if you want everyone to be able to create tickets on those queues).<br />
<br />
'''Note''' that this is just a sample mandatory custom field. The scrip below is generic and will check for the presence of any and all <u>mandatory</u> ticket custom fields.<br />
<br />
----<br />
<br />
===== 2. Configure the ExtractCustomFieldValues =====<br />
<br />
Follow the instructions in the [[ExtractCustomFieldValues]] section<br />
<br />
----<br />
<br />
===== 3. Creating the template =====<br />
<br />
Go to Configuration -&gt; Global -&gt; Templates -&gt; New<br />
<br />
'''Name:''' <code>Error: no mandatory CF</code><br />
<br />
'''Description:''' <code>Inform the user that the ticket has been rejected due to a missing mandatory custom field</code><br />
<br />
'''Content:'''<br />
<br />
Subject: Your ticket has been rejected due to a missing mandatory field<br />
<br />
You sent a ticket with a missing piece of information. Since the missing information<br />
is mandatory, the ticket has been rejected.<br />
<br />
Please verify the informations you sent and then retry submitting the ticket.<br />
If you feel your ticket has been rejected due to an error, please contact the administrator.<br />
<br />
Obviously this is only an example, you can write what you want. Just be sure to have the "Subject:" line as the very first line, and to leave a blank line after that.<br />
<br />
----<br />
<br />
===== 4. Creating the Scrip =====<br />
<br />
Go to Configuration -&gt; Queues (or Global if you want this for all queues) -&gt; (QueueName) -&gt; Scrips -&gt; New scrip<br />
<br />
'''Description:''' OnCreateCheckCF<br />
<br />
'''Condition:''' User Defined<br />
<br />
'''Action:''' Autoreply To Requestors<br />
<br />
'''Template:''' Global template: Error: no mandatory CF<br />
<br />
'''Stage:''' TransactionBatch<br />
<br />
In the "Custom condition" box enter this code:<br />
<br />
<pre><br />
my $trans = $self-&gt;TransactionObj;<br />
return 0 unless $trans-&gt;Type eq "Create";<br />
<br />
my $ticket = $self-&gt;TicketObj;<br />
<br />
my $Email_Rq = $ticket-&gt;RequestorAddresses;<br />
<br />
my $CustomFields = $ticket-&gt;QueueObj-&gt;TicketCustomFields();<br />
while (my $CustomField = $CustomFields-&gt;Next()) {<br />
my $nam = $CustomField-&gt;Name;<br />
my $typ = $CustomField-&gt;Type;<br />
my $vad = $CustomField-&gt;Pattern;<br />
<br />
if ($vad == '(?#Mandatory)') {<br />
my $val = $ticket-&gt;FirstCustomFieldValue($nam);<br />
if ($val ne '') {<br />
next;<br />
} else {<br />
$ticket-&gt;SetStatus("rejected");<br />
}<br />
}<br />
}<br />
<br />
return 1;<br />
</pre><br />
<br />
----<br />
<br />
That's all folks!<br />
<br />
<br />
<br />
'''P.S.''': Thanks to the user who spotted a typo in my scrip!</div>Rabin