The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright (c) 2013 Experieco Ltd. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

package RT::Action::UpdateIcestack;

use 5.006;
use strict;
use warnings;

our $VERSION = '0.01';

use base qw(RT::Action);

use LWP::UserAgent;
use HTTP::Request::Common;
use XML::LibXML;
use Date::Manip;
use MIME::Base64;

my $DEBUG_FILE = "/tmp/ui_debug.log";

sub Commit {
	my $self = shift;
	my $ti = $self->TicketObj;

	### Fetch these variables from Creator's custom fields (specific to each customer)
	my $host = $ti->CreatorObj->CustomFieldValues("IcestackHostURL")->First->Content;
	my $key = $ti->CreatorObj->CustomFieldValues("IcestackCode")->First->Content;

	my $targetURL = $host . "/STACK/stack/requestUpdate.action?PluginID=3";
	my $valueId = $ti->Type."/".$ti->id;
	my $valueType = $ti->QueueObj->Name;
	my $valueStatus = $ti->Status;
	my $valueEmail = ($ti->Requestors->MemberEmailAddresses)[0];
	my $valueStart = UnixDate(ParseDate($ti->StartsObj->AsString), "%Y-%m-%dT%T%z");
	my $valuePriority = $ti->Priority;
	my $valueSummary = $ti->Subject;
	my $valueTitle = $ti->Type."/".$ti->id;
	
	# Program starts
	my $doc = XML::LibXML::Document->new('1.0', 'utf-8');
	my $root = $doc->createElement("ServiceRequest");
	
	my $elementId = $doc->createElement("ID");
	$elementId->appendTextNode($valueId);
	$root->appendChild($elementId);
	
	my $elementType = $doc->createElement("Type");
	$elementType->appendTextNode($valueType);
	$root->appendChild($elementType);
	
	my $elementStatus = $doc->createElement("Status");
	$elementStatus->appendTextNode($valueStatus);
	$root->appendChild($elementStatus);
	
	my $elementEmail = $doc->createElement("Email");
	$elementEmail->appendTextNode($valueEmail);
	$root->appendChild($elementEmail);
	
	my $elementStart = $doc->createElement("CommitmentStart");
	$elementStart->appendTextNode($valueStart);
	$root->appendChild($elementStart);
	
	my $elementCommitmentType = $doc->createElement("CommitmentType");
	$elementCommitmentType->appendTextNode("BY");
	$root->appendChild($elementCommitmentType);
	
	my $elementMonitor = $doc->createElement("MonitorData");
	
	my $elementPriorityData = $doc->createElement("Data");
	my $elementPriorityType = $doc->createElement("Type");
	$elementPriorityType->appendTextNode("String");
	$elementPriorityData->appendChild($elementPriorityType);
	my $elementPriorityName = $doc->createElement("Name");
	$elementPriorityName->appendTextNode("Priority");
	$elementPriorityData->appendChild($elementPriorityName);
	my $elementPriorityValue = $doc->createElement("Value");
	$elementPriorityValue->appendTextNode($valuePriority);
	$elementPriorityData->appendChild($elementPriorityValue);
	$elementMonitor->appendChild($elementPriorityData);
	
	my $elementSummaryData = $doc->createElement("Data");
	my $elementSummaryType = $doc->createElement("Type");
	$elementSummaryType->appendTextNode("String");
	$elementSummaryData->appendChild($elementSummaryType);
	my $elementSummaryName = $doc->createElement("Name");
	$elementSummaryName->appendTextNode("Summary");
	$elementSummaryData->appendChild($elementSummaryName);
	my $elementSummaryValue = $doc->createElement("Value");
	$elementSummaryValue->appendTextNode($valueSummary);
	$elementSummaryData->appendChild($elementSummaryValue);
	$elementMonitor->appendChild($elementSummaryData);
	
	my $elementTitleData = $doc->createElement("Data");
	my $elementTitleType = $doc->createElement("Type");
	$elementTitleType->appendTextNode("String");
	$elementTitleData->appendChild($elementTitleType);
	my $elementTitleName = $doc->createElement("Name");
	$elementTitleName->appendTextNode("Title");
	$elementTitleData->appendChild($elementTitleName);
	my $elementTitleValue = $doc->createElement("Value");
	$elementTitleValue->appendTextNode($valueTitle);
	$elementTitleData->appendChild($elementTitleValue);
	$elementMonitor->appendChild($elementTitleData);
	
	$root->appendChild($elementMonitor);
	
	$doc->setDocumentElement($root);
	my $message=$doc->toString();

	# log debug for xml input (optional)
	open OUT, ">$DEBUG_FILE";
	print OUT "\n\nInput";
	print OUT "-----------------\n";
	print OUT $message;
	print OUT "\n";
	
	my $client = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0, SSL_verify_mode => 0x00 });
	my $request = HTTP::Request->new;
	$request->method("POST");
	$request->uri($targetURL);
	$request->header('Authorization' => 'BASIC ' . encode_base64($key));
	$request->content_type('application/xml');
	$request->content($message);
	my $response = $client->request($request);

	# log debug for xml output (optional)
	print OUT "\n\nOutput";
	print OUT "-----------------\n";
	print OUT $response->content;
	print OUT "\n";
	close OUT;

	return 1;
}

1;

__END__

=head1 NAME

RT::Action::UpdateIcestack - Send ticket updates to Icestack

=head1 DESCRIPTION

This RT ScripAction is invoked when a ticket is updated and sends the update through to Icestack.

=head1 INSTALLATION

Can be installed using the following commands:

    perl Makefile.PL
    make
    make install
    make initdb

=head1 CONFIGURATION

=head2 Enable Plugin

Update RT_SiteConfig.pm with a line similar to the below:

    Set(@Plugins,(qw(RT::Action::UpdateIcestack)));

=head2 Create Scrip

This Scrip will invoke the ScripAction installed above whenever a ticket is updated.

    Log onto RT web interface as root
    Click Tools->Configuration->Global->Scrips->Create
    Enter in Scrip Fields as follows:
        Description: Send update to Icestack
        Condition: On Transaction
        Action: Update Icestack
        Template: Global template: Transaction
        Stage: TransactionCreate
    Click Create

=head2 Add custom fields to Users table

These fields are used to associate an RT User with a particular Icestack instance.

    Log onto RT web interface as root
    Click Tools->Configuration->Custom Fields->Create
        Name: IcestackCode
        Description: Key code issued from Icestack
        Type: Enter one value
        Applies to: Users
    Click Tools->Configuration->Custom Fields->Create
        Name: IcestackHostURL
        Description: Host URL used to push updates back into Icestack
        Type: Enter one value
        Applies to: Users
    Click Create

=head2 Configure Icestack Integration

Icestack communicates with RT via an RT User. It is necessary to populate the IcestackCode and IcestackHostURL fields for this User so that the update mechanism will function.

This User should have access to one or more queues. These will be visible to the Icestack system.

The instructions given below are for creation of new User and Queue(s). It is also possible to modify an existing User.

=head3 Create User

As alluded to above this User will be associated with a particular Icestack instance via the custom fields.

    Log onto RT web interface as root
    Click Tools->Configuration->Users->Create
        Username: <user name>
        Let this user access RT: CHECK
        Let this user be granted rights (Privileged): CHECK
        IcestackHostURL: <url>
        IcestackCode: <code>
    Click Create

=head3 Create Queue and assign rights for User

Create one or more Queues to be associated with the User and Icestack instance.

    Log onto RT web interface as root
    Click Tools->Configuration->Queues->Create
        Queue Name: <queue name>
        Description: Queue for <user name>
        Lifecycle: default
    Click Create
    Click Tools->Configuration->Queues->Select
    Click <queue name>
    Click User Rights
    Add rights for this user: <user name>
        Create tickets: CHECK
        View queue: CHECK
        View ticket summaries: CHECK
        Modify tickets: CHECK
        (others may be necessary, depending...)
    Click Save Changes

=head1 NOTES

To assist with testing the integration the XML exchange with Icestack is logged to /tmp/ui_debug.log.

=head1 AUTHOR

Mark Ibell E<lt>marki@econz.co.nzE<gt>

=head1 COPYRIGHT

Copyright (c) 2013 Experieco Ltd. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.

=cut