From e19cf74e68ef0ebad22df8a4165a93b897d863b9 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 12 Feb 2010 01:35:33 +0000 Subject: [PATCH] add ticket creation to self-service API, RT#7007 --- FS/FS/ClientAPI/MyAccount.pm | 49 ++++-- FS/FS/Conf.pm | 28 ++++ FS/FS/TicketSystem/RT_External.pm | 4 + FS/FS/TicketSystem/RT_Internal.pm | 177 +++++++++++++++++++-- fs_selfservice/FS-SelfService/SelfService.pm | 1 + .../java/freeside_create_ticket_example.java | 79 +++++++++ fs_selfservice/perl/xmlrpc-create_ticket.pl | 38 +++++ httemplate/view/cust_svc.cgi | 23 +++ rt/lib/RT/URI/freeside.pm | 16 +- 9 files changed, 391 insertions(+), 24 deletions(-) create mode 100755 fs_selfservice/java/freeside_create_ticket_example.java create mode 100755 fs_selfservice/perl/xmlrpc-create_ticket.pl create mode 100644 httemplate/view/cust_svc.cgi diff --git a/FS/FS/ClientAPI/MyAccount.pm b/FS/FS/ClientAPI/MyAccount.pm index a05c6f204..672eec5d8 100644 --- a/FS/FS/ClientAPI/MyAccount.pm +++ b/FS/FS/ClientAPI/MyAccount.pm @@ -1,5 +1,6 @@ package FS::ClientAPI::MyAccount; +use 5.008; #require 5.8+ for Time::Local 1.05+ use strict; use vars qw( $cache $DEBUG $me ); use subs qw( _cache _provision ); @@ -8,6 +9,7 @@ use Digest::MD5 qw(md5_hex); use Date::Format; use Business::CreditCard; use Time::Duration; +use Time::Local qw(timelocal_nocheck); use FS::UI::Web::small_custview qw(small_custview); #less doh use FS::UI::Web; use FS::UI::bytecount qw( display_bytecount ); @@ -29,18 +31,11 @@ use FS::cust_pkg; use FS::payby; use FS::acct_rt_transaction; use HTML::Entities; +use FS::TicketSystem; -$DEBUG = 2; +$DEBUG = 0; $me = '[FS::ClientAPI::MyAccount]'; -#false laziness with FS::cust_main -BEGIN { - eval "use Time::Local;"; - die "Time::Local minimum version 1.05 required with Perl versions before 5.6" - if $] < 5.006 && !defined($Time::Local::VERSION); - eval "use Time::Local qw(timelocal_nocheck);"; -} - use vars qw( @cust_main_editable_fields ); @cust_main_editable_fields = qw( first last company address1 address2 city @@ -1654,6 +1649,42 @@ sub myaccount_passwd { } +sub create_ticket { + my $p = shift; + my($context, $session, $custnum) = _custoragent_session_custnum($p); + return { 'error' => $session } if $context eq 'error'; + + warn "$me create_ticket: initializing ticket system\n" if $DEBUG; + FS::TicketSystem->init(); + + my $conf = new FS::Conf; + my $queue = $conf->config('ticket_system-selfservice_queueid') + || $conf->config('ticket_system-default_queueid'); + + warn "$me create_ticket: creating ticket\n" if $DEBUG; + my $err_or_ticket = FS::TicketSystem->create_ticket( + '', #create RT session based on FS CurrentUser (fs_selfservice) + 'queue' => $queue, + 'custnum' => $custnum, + 'svcnum' => $session->{'svcnum'}, + map { $_ => $p->{$_} } qw( requestor cc subject message ) + ); + + if ( ref($err_or_ticket) ) { + warn "$me create_ticket: sucessful: ". $err_or_ticket->id. "\n" + if $DEBUG; + return { 'error' => '', + 'ticket_id' => $err_or_ticket->id, + }; + } else { + warn "$me create_ticket: unsucessful: $err_or_ticket\n" + if $DEBUG; + return { 'error' => $err_or_ticket }; + } + + +} + #-- sub _custoragent_session_custnum { diff --git a/FS/FS/Conf.pm b/FS/FS/Conf.pm index 615761797..9d7747980 100644 --- a/FS/FS/Conf.pm +++ b/FS/FS/Conf.pm @@ -2071,6 +2071,34 @@ worry that config_items is freeside-specific and icky. }, { + 'key' => 'ticket_system-selfservice_queueid', + 'section' => '', + 'description' => 'Queue used when creating new customer tickets from self-service. Defautls to ticket_system-default_queueid if not specified.', + #false laziness w/above + 'type' => 'select-sub', + 'options_sub' => sub { + my $conf = new FS::Conf; + if ( $conf->config('ticket_system') ) { + eval "use FS::TicketSystem;"; + die $@ if $@; + FS::TicketSystem->queues(); + } else { + (); + } + }, + 'option_sub' => sub { + my $conf = new FS::Conf; + if ( $conf->config('ticket_system') ) { + eval "use FS::TicketSystem;"; + die $@ if $@; + FS::TicketSystem->queue(shift); + } else { + ''; + } + }, + }, + + { 'key' => 'ticket_system-priority_reverse', 'section' => '', 'description' => 'Enable this to consider lower numbered priorities more important. A bad habit we picked up somewhere. You probably want to avoid it and use the default.', diff --git a/FS/FS/TicketSystem/RT_External.pm b/FS/FS/TicketSystem/RT_External.pm index 5c51bdd02..cdb4e90f3 100644 --- a/FS/FS/TicketSystem/RT_External.pm +++ b/FS/FS/TicketSystem/RT_External.pm @@ -382,5 +382,9 @@ sub access_right { 0; } +sub create_ticket { + return 'create_ticket unimplemented w/external RT (write something w/RT::Client::REST?)'; +} + 1; diff --git a/FS/FS/TicketSystem/RT_Internal.pm b/FS/FS/TicketSystem/RT_Internal.pm index 85b2d56dd..d6b9c5298 100644 --- a/FS/FS/TicketSystem/RT_Internal.pm +++ b/FS/FS/TicketSystem/RT_Internal.pm @@ -3,6 +3,7 @@ package FS::TicketSystem::RT_Internal; use strict; use vars qw( @ISA $DEBUG $me ); use Data::Dumper; +use MIME::Entity; use FS::UID qw(dbh); use FS::CGI qw(popurl); use FS::TicketSystem::RT_Libs; @@ -10,7 +11,7 @@ use RT::CurrentUser; @ISA = qw( FS::TicketSystem::RT_Libs ); -$DEBUG = 1; +$DEBUG = 0; $me = '[FS::TicketSystem::RT_Internal]'; sub sql_num_customer_tickets { @@ -38,14 +39,7 @@ sub access_right { #return '' unless $conf->config('ticket_system'); return '' unless FS::Conf->new->config('ticket_system'); - if ( $session && $session->{'Current_User'} ) { - warn "$me access_right: using existing session and CurrentUser: \n". - Dumper($session->{'CurrentUser'}) - if $DEBUG; - } else { - warn "$me access_right: loading session and CurrentUser\n" if $DEBUG > 1; - $self->_web_external_auth($session); - } + $session = $self->session($session); #warn "$me access_right: CurrentUser ". $session->{'CurrentUser'}. ":\n". # ( $DEBUG>1 ? Dumper($session->{'CurrentUser'}) : '' ) @@ -55,6 +49,168 @@ sub access_right { Object => $RT::System ); } +sub session { + my( $self, $session ) = @_; + + if ( $session && $session->{'Current_User'} ) { + warn "$me session: using existing session and CurrentUser: \n". + Dumper($session->{'CurrentUser'}) + if $DEBUG; + } else { + warn "$me session: loading session and CurrentUser\n" if $DEBUG > 1; + $session = $self->_web_external_auth($session); + } + + $session; +} + +sub init { + my $self = shift; + + warn "$me init: loading RT libraries\n" if $DEBUG; + eval ' + use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" ); + use RT; + #it looks like the rest are taken care of these days in RT::InitClasses + #use RT::Ticket; + #use RT::Transactions; + #use RT::Users; + #use RT::CurrentUser; + #use RT::Templates; + #use RT::Queues; + #use RT::ScripActions; + #use RT::ScripConditions; + #use RT::Scrips; + #use RT::Groups; + #use RT::GroupMembers; + #use RT::CustomFields; + #use RT::CustomFieldValues; + #use RT::ObjectCustomFieldValues; + + #for web external auth... + use RT::Interface::Web; + '; + die $@ if $@; + + warn "$me init: loading RT config\n" if $DEBUG; + { + local $SIG{__DIE__}; + eval 'RT::LoadConfig();'; + } + die $@ if $@; + + warn "$me init: initializing RT\n" if $DEBUG; + { + local $SIG{__DIE__}; + eval 'RT::Init("NoSignalHandlers"=>1);'; + } + die $@ if $@; + + warn "$me init: complete" if $DEBUG; +} + +=item create_ticket SESSION_HASHREF, OPTION => VALUE ... + +Class method. Creates a ticket. If there is an error, returns the scalar +error, otherwise returns the newly created RT::Ticket object. + +Accepts the following options: + +=over 4 + +=item queue + +Queue name or Id + +=item subject + +Ticket subject + +=item requestor + +Requestor email address or arrayref of addresses + +=item cc + +Cc: email address or arrayref of addresses + +=item message + +Ticket message + +=item custnum + +Customer number (see L) to associate with ticket. + +=item svcnum + +Service number (see L) to associate with ticket. Will also +associate the customer who has this service (unless the service is unlinked). + +=back + +=cut + +sub create_ticket { + my($self, $session, %param) = @_; + + $session = $self->session($session); + + my $Queue = RT::Queue->new($session->{'CurrentUser'}); + $Queue->Load( $param{'queue'} ); + + my $req = ref($param{'requestor'}) + ? $param{'requestor'} + : ( $param{'requestor'} ? [ $param{'requestor'} ] : [] ); + + my $cc = ref($param{'cc'}) + ? $param{'cc'} + : ( $param{'cc'} ? [ $param{'cc'} ] : [] ); + + my $mimeobj = MIME::Entity->build( + 'Data' => $param{'message'}, + 'Type' => 'text/plain', + ); + + my %ticket = ( + 'Queue' => $Queue->Id, + 'Subject' => $param{'subject'}, + 'Requestor' => $req, + 'Cc' => $cc, + 'MIMEObj' => $mimeobj, + ); + warn Dumper(\%ticket) if $DEBUG > 1; + + my $Ticket = RT::Ticket->new($session->{'CurrentUser'}); + my( $id, $Transaction, $ErrStr ); + { + local $SIG{__DIE__}; + ( $id, $Transaction, $ErrStr ) = $Ticket->Create( %ticket ); + } + return $ErrStr if $id == 0; + + warn "ticket got id $id\n" if $DEBUG; + + #XXX check errors adding custnum/svcnum links (put it in a transaction)... + # but we do already know they're good + + if ( $param{'custnum'} ) { + my( $val, $msg ) = $Ticket->_AddLink( + 'Type' => 'MemberOf', + 'Target' => 'freeside://freeside/cust_main/'. $param{'custnum'}, + ); + } + + if ( $param{'svcnum'} ) { + my( $val, $msg ) = $Ticket->_AddLink( + 'Type' => 'MemberOf', + 'Target' => 'freeside://freeside/cust_svc/'. $param{'svcnum'}, + ); + } + + $Ticket; +} + #shameless false laziness w/RT::Interface::Web::AttemptExternalAuth # to get logged into RT from afar sub _web_external_auth { @@ -62,6 +218,7 @@ sub _web_external_auth { my $user = $FS::CurrentUser::CurrentUser->username; + $session ||= {}; $session->{'CurrentUser'} = RT::CurrentUser->new(); warn "$me _web_external_auth loading RT user for $user\n" @@ -144,6 +301,8 @@ sub _web_external_auth { #} } + $session; + } 1; diff --git a/fs_selfservice/FS-SelfService/SelfService.pm b/fs_selfservice/FS-SelfService/SelfService.pm index 49629d423..7e6821b60 100644 --- a/fs_selfservice/FS-SelfService/SelfService.pm +++ b/fs_selfservice/FS-SelfService/SelfService.pm @@ -59,6 +59,7 @@ $socket .= '.'.$tag if defined $tag && length($tag); 'provision_external' => 'MyAccount/provision_external', 'unprovision_svc' => 'MyAccount/unprovision_svc', 'myaccount_passwd' => 'MyAccount/myaccount_passwd', + 'create_ticket' => 'MyAccount/create_ticket', 'signup_info' => 'Signup/signup_info', 'skin_info' => 'MyAccount/skin_info', 'access_info' => 'MyAccount/access_info', diff --git a/fs_selfservice/java/freeside_create_ticket_example.java b/fs_selfservice/java/freeside_create_ticket_example.java new file mode 100755 index 000000000..8e79ca6f0 --- /dev/null +++ b/fs_selfservice/java/freeside_create_ticket_example.java @@ -0,0 +1,79 @@ + +import biz.freeside.SelfService; +import org.apache.commons.logging.impl.SimpleLog; //included in apache xmlrpc +import java.util.HashMap; +import java.util.Vector; + +public class freeside_create_ticket_example { + private static SimpleLog logger = new SimpleLog("SelfService"); + + public static void main( String args[] ) throws Exception { + SelfService client = + new SelfService( "http://192.168.1.221:8081/xmlrpc.cgi" ); + + Vector params = new Vector(); + params.addElement( "username" ); + params.addElement( "4155551212" ); // svc_phone.phonenum + params.addElement( "password" ); + params.addElement( "5454" ); // svc_phone.pin + params.addElement( "domain" ); + params.addElement( "svc_phone" ); + HashMap result = client.execute( "login", params ); + + String error = (String) result.get("error"); + + if (error.length() < 1) { + + // successful login + + String sessionId = (String) result.get("session_id"); + + logger.trace("[login] logged into freeside with session_id="+sessionId); + + // store session id in your session store to be used for other calls + + // like, say, this one to create a ticket + + Vector ticket_params = new Vector(); + ticket_params.addElement( "session_id" ); + ticket_params.addElement( sessionId ); + ticket_params.addElement( "requestor" ); // these + ticket_params.addElement( "email@example.com" ); // are + ticket_params.addElement( "cc" ); // optional + ticket_params.addElement( "joe@example.com" ); // + ticket_params.addElement( "subject" ); + ticket_params.addElement( "Houston, we have a problem." ); + ticket_params.addElement( "message" ); + ticket_params.addElement( "The Oscillation Overthurster has gone out of alignment!\n\nIt needs to be fixed immediately!" ); + + HashMap ticket_result = client.execute( "create_ticket", ticket_params); + + String error = (String) ticket_result.get("error"); + + if (error.length() < 1) { + + // successful ticket creation + + String ticketId = (String) ticket_result.get("ticket_id"); + + logger.trace("[login] ticket created with id="+ticketId); + + } else { + + // unsuccesful creating ticket + + logger.warn("[login] error creating ticket: "+error); + + } + + }else{ + + // unsuccessful login + + logger.warn("[login] error logging into freeside: "+error); + + // display/say error message to user + + } + } +} diff --git a/fs_selfservice/perl/xmlrpc-create_ticket.pl b/fs_selfservice/perl/xmlrpc-create_ticket.pl new file mode 100755 index 000000000..cd2037e2c --- /dev/null +++ b/fs_selfservice/perl/xmlrpc-create_ticket.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl + +use strict; +use Frontier::Client; +use Data::Dumper;; + +my $server = new Frontier::Client ( + url => 'http://localhost/selfservice/xmlrpc.cgi', +); + +my $result = $server->call('FS.SelfService.XMLRPC.login', + 'username' => '4155551212', + 'password' => '5454', + 'domain' => 'svc_phone', +); + +#print Dumper($result); +die $result->{'error'} if $result->{'error'}; + +my $session_id = $result->{'session_id'}; +warn "logged in with session_id $session_id\n"; + +my $t_result = $server->call('FS.SelfService.XMLRPC.create_ticket', + 'session_id' => $session_id, + 'requestor' => 'harveylala@example.com', + 'cc' => 'chiquitabanana@example.com', + 'subject' => 'Chiquita keeps sitting on me', + 'message' => "Isn't there something you can do about this?\n\nShe keeps waking me up!", +); + +die $t_result->{'error'} if $t_result->{'error'}; + +warn Dumper($t_result); + +my $ticket_id = $t_result->{'ticket_id'}; +warn "ticket $ticket_id created\n"; + +1; diff --git a/httemplate/view/cust_svc.cgi b/httemplate/view/cust_svc.cgi new file mode 100644 index 000000000..8ccfce3ff --- /dev/null +++ b/httemplate/view/cust_svc.cgi @@ -0,0 +1,23 @@ +<% $cgi->redirect(popurl(1)."$svcdb.cgi?". $svcnum ) %> +<%init> + +#needed here? we're just redirecting. i guess it could reveal the svcdb of a +#svcnum... oooooo scary. not. +die "access denied" + unless $FS::CurrentUser::CurrentUser->access_right('View customer services'); + +#some false laziness w/svc_*.cgi + +my($query) = $cgi->keywords; +$query =~ /^(\d+)$/; +my $svcnum = $1; +my $cust_svc = qsearchs( 'cust_svc', { 'svcnum' => $svcnum } ); +die "Unknown svcnum" unless $cust_svc; + +my $part_svc = qsearchs('part_svc',{'svcpart'=> $cust_svc->svcpart } ); +die "Unknown svcpart" unless $part_svc; + +my $svcdb = $part_svc->svcdb; + + + diff --git a/rt/lib/RT/URI/freeside.pm b/rt/lib/RT/URI/freeside.pm index d73dbacad..57c2a2c4e 100644 --- a/rt/lib/RT/URI/freeside.pm +++ b/rt/lib/RT/URI/freeside.pm @@ -132,7 +132,7 @@ sub _FreesideURILabel { my $self = shift; - $RT::Logger->debug("Called _FreesideURILabel()"); + #$RT::Logger->debug("Called _FreesideURILabel()"); return unless (exists($self->{'fstable'}) and exists($self->{'fspkey'})); @@ -140,17 +140,21 @@ sub _FreesideURILabel { my $label; my ($table, $pkey) = ($self->{'fstable'}, $self->{'fspkey'}); - if ($table ne 'cust_main') { - warn "FS::${table} not currently supported"; - return; - } + #if ($table ne 'cust_main') { + # warn "FS::${table} not currently supported"; + # return; + #} my $rec = $self->_FreesideGetRecord(); - if (ref($rec) eq 'HASH' and $table eq 'cust_main') { + if (ref($rec) eq 'HASH' && $table eq 'cust_main') { my $name = $rec->{'last'} . ', ' . $rec->{'first'}; $name = $rec->{'company'} . " ($name)" if $rec->{'company'}; $label = "$pkey: $name"; + } elsif ( $table eq 'cust_svc' && ref($rec) && $rec->{'_object'} ) { + #Internal only + my($l,$v) = $rec->{'_object'}->label; + $label = "$l: $v"; } else { $label = "$pkey: $table"; } -- 2.11.0