add ticket creation to self-service API, RT#7007
authorivan <ivan>
Fri, 12 Feb 2010 01:35:33 +0000 (01:35 +0000)
committerivan <ivan>
Fri, 12 Feb 2010 01:35:33 +0000 (01:35 +0000)
FS/FS/ClientAPI/MyAccount.pm
FS/FS/Conf.pm
FS/FS/TicketSystem/RT_External.pm
FS/FS/TicketSystem/RT_Internal.pm
fs_selfservice/FS-SelfService/SelfService.pm
fs_selfservice/java/freeside_create_ticket_example.java [new file with mode: 0755]
fs_selfservice/perl/xmlrpc-create_ticket.pl [new file with mode: 0755]
httemplate/view/cust_svc.cgi [new file with mode: 0644]
rt/lib/RT/URI/freeside.pm

index a05c6f2..672eec5 100644 (file)
@@ -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 {
index 6157617..9d77479 100644 (file)
@@ -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.',
index 5c51bdd..cdb4e90 100644 (file)
@@ -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;
 
index 85b2d56..d6b9c52 100644 (file)
@@ -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<FS::cust_main>) to associate with ticket.
+
+=item svcnum
+
+Service number (see L<FS::cust_svc>) 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;
index 49629d4..7e6821b 100644 (file)
@@ -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 (executable)
index 0000000..8e79ca6
--- /dev/null
@@ -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 (executable)
index 0000000..cd2037e
--- /dev/null
@@ -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 (file)
index 0000000..8ccfce3
--- /dev/null
@@ -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;
+
+</%init>
+
index d73dbac..57c2a2c 100644 (file)
@@ -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";
   }