This commit was generated by cvs2svn to compensate for changes in r11022,
[freeside.git] / FS / FS / TicketSystem / RT_Internal.pm
1 package FS::TicketSystem::RT_Internal;
2
3 use strict;
4 use vars qw( @ISA $DEBUG $me );
5 use Data::Dumper;
6 use MIME::Entity;
7 use FS::UID qw(dbh);
8 use FS::CGI qw(popurl);
9 use FS::TicketSystem::RT_Libs;
10
11 @ISA = qw( FS::TicketSystem::RT_Libs );
12
13 $DEBUG = 0;
14 $me = '[FS::TicketSystem::RT_Internal]';
15
16 sub sql_num_customer_tickets {
17   "( select count(*) from Tickets
18                      join Links on ( Tickets.id = Links.LocalBase )
19      where ( Status = 'new' or Status = 'open' or Status = 'stalled' )
20        and Target = 'freeside://freeside/cust_main/' || custnum
21    )";
22 }
23
24 sub baseurl {
25   #my $self = shift;
26   if ( $RT::URI::freeside::URL ) {
27     $RT::URI::freeside::URL. '/rt/';
28   } else {
29     'http://you_need_to_set_RT_URI_freeside_URL_in_SiteConfig.pm/';
30   }
31 }
32
33 #mapping/genericize??
34 #ShowConfigTab ModifySelf
35 sub access_right {
36   my( $self, $session, $right ) = @_;
37
38   #return '' unless $conf->config('ticket_system');
39   return '' unless FS::Conf->new->config('ticket_system');
40
41   $session = $self->session($session);
42
43   #warn "$me access_right: CurrentUser ". $session->{'CurrentUser'}. ":\n".
44   #     ( $DEBUG>1 ? Dumper($session->{'CurrentUser'}) : '' )
45   #  if $DEBUG > 1;
46
47   $session->{'CurrentUser'}->HasRight( Right  => $right,
48                                        Object => $RT::System );
49 }
50
51 sub session {
52   my( $self, $session ) = @_;
53
54   if ( $session && $session->{'Current_User'} ) {
55     warn "$me session: using existing session and CurrentUser: \n".
56          Dumper($session->{'CurrentUser'})
57       if $DEBUG;
58  } else {
59     warn "$me session: loading session and CurrentUser\n" if $DEBUG > 1;
60     $session = $self->_web_external_auth($session);
61   }
62
63   $session;
64 }
65
66 sub init {
67   my $self = shift;
68
69   warn "$me init: loading RT libraries\n" if $DEBUG;
70   eval '
71     use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" );
72     use RT;
73     #it looks like the rest are taken care of these days in RT::InitClasses
74     #use RT::Ticket;
75     #use RT::Transactions;
76     #use RT::Users;
77     #use RT::CurrentUser;
78     #use RT::Templates;
79     #use RT::Queues;
80     #use RT::ScripActions;
81     #use RT::ScripConditions;
82     #use RT::Scrips;
83     #use RT::Groups;
84     #use RT::GroupMembers;
85     #use RT::CustomFields;
86     #use RT::CustomFieldValues;
87     #use RT::ObjectCustomFieldValues;
88
89     #for web external auth...
90     use RT::Interface::Web;
91   ';
92   die $@ if $@;
93
94   warn "$me init: loading RT config\n" if $DEBUG;
95   {
96     local $SIG{__DIE__};
97     eval 'RT::LoadConfig();';
98   }
99   die $@ if $@;
100
101   warn "$me init: initializing RT\n" if $DEBUG;
102   {
103     local $SIG{__DIE__};
104     eval 'RT::Init("NoSignalHandlers"=>1);';
105   }
106   die $@ if $@;
107
108   warn "$me init: complete" if $DEBUG;
109 }
110
111 =item create_ticket SESSION_HASHREF, OPTION => VALUE ...
112
113 Class method.  Creates a ticket.  If there is an error, returns the scalar
114 error, otherwise returns the newly created RT::Ticket object.
115
116 Accepts the following options:
117
118 =over 4
119
120 =item queue
121
122 Queue name or Id
123
124 =item subject
125
126 Ticket subject
127
128 =item requestor
129
130 Requestor email address or arrayref of addresses
131
132 =item cc
133
134 Cc: email address or arrayref of addresses
135
136 =item message
137
138 Ticket message
139
140 =item mime_type
141
142 MIME type to use for message.  Defaults to text/plain.  Specifying text/html
143 can be useful to use HTML markup in message.
144
145 =item custnum
146
147 Customer number (see L<FS::cust_main>) to associate with ticket.
148
149 =item svcnum
150
151 Service number (see L<FS::cust_svc>) to associate with ticket.  Will also
152 associate the customer who has this service (unless the service is unlinked).
153
154 =back
155
156 =cut
157
158 sub create_ticket {
159   my($self, $session, %param) = @_;
160
161   $session = $self->session($session);
162
163   my $Queue = RT::Queue->new($session->{'CurrentUser'});
164   $Queue->Load( $param{'queue'} );
165
166   my $req = ref($param{'requestor'})
167               ? $param{'requestor'}
168               : ( $param{'requestor'} ? [ $param{'requestor'} ] : [] );
169
170   my $cc = ref($param{'cc'})
171              ? $param{'cc'}
172              : ( $param{'cc'} ? [ $param{'cc'} ] : [] );
173
174   my $mimeobj = MIME::Entity->build(
175     'Data' => $param{'message'},
176     'Type' => ( $param{'mime_type'} || 'text/plain' ),
177   );
178
179   my %ticket = (
180     'Queue'     => $Queue->Id,
181     'Subject'   => $param{'subject'},
182     'Requestor' => $req,
183     'Cc'        => $cc,
184     'MIMEObj'   => $mimeobj,
185   );
186   warn Dumper(\%ticket) if $DEBUG > 1;
187
188   my $Ticket = RT::Ticket->new($session->{'CurrentUser'});
189   my( $id, $Transaction, $ErrStr );
190   {
191     local $SIG{__DIE__};
192     ( $id, $Transaction, $ErrStr ) = $Ticket->Create( %ticket );
193   }
194   return $ErrStr if $id == 0;
195
196   warn "ticket got id $id\n" if $DEBUG;
197
198   #XXX check errors adding custnum/svcnum links (put it in a transaction)...
199   # but we do already know they're good
200
201   if ( $param{'custnum'} ) {
202     my( $val, $msg ) = $Ticket->_AddLink(
203      'Type'   => 'MemberOf',
204      'Target' => 'freeside://freeside/cust_main/'. $param{'custnum'},
205     );
206   }
207
208   if ( $param{'svcnum'} ) {
209     my( $val, $msg ) = $Ticket->_AddLink(
210      'Type'   => 'MemberOf',
211      'Target' => 'freeside://freeside/cust_svc/'. $param{'svcnum'},
212     );
213   }
214
215   $Ticket;
216 }
217
218 =item get_ticket SESSION_HASHREF, OPTION => VALUE ...
219
220 Class method. Retrieves a ticket. If there is an error, returns the scalar
221 error. Otherwise, currently returns a slightly tricky data structure containing
222 a list of the linked customers and each transaction's content, description, and
223 create time.
224
225 Accepts the following options:
226
227 =over 4
228
229 =item ticket_id 
230
231 The ticket id
232
233 =back
234
235 =cut
236
237 sub get_ticket {
238   my($self, $session, %param) = @_;
239
240   $session = $self->session($session);
241
242   my $Ticket = RT::Ticket->new($session->{'CurrentUser'});
243   my $ticketid = $Ticket->Load( $param{'ticket_id'} );
244   return 'Could not load ticket' unless $ticketid;
245
246   my @custs = ();
247   foreach my $link ( @{ $Ticket->Customers->ItemsArrayRef } ) {
248     my $cust = $link->Target;
249     push @custs, $1 if $cust =~ /\/(\d+)$/;
250   }
251
252   my @txns = ();
253   my $transactions = $Ticket->Transactions;
254   while ( my $transaction = $transactions->Next ) {
255     my $t = { created => $transaction->Created,
256         content => $transaction->Content,
257         description => $transaction->Description,
258         type => $transaction->Type,
259     };
260     push @txns, $t;
261   }
262
263   { txns => [ @txns ],
264     custs => [ @custs ],
265   };
266 }
267
268
269 =item correspond_ticket SESSION_HASHREF, OPTION => VALUE ...
270
271 Class method. Correspond on a ticket. If there is an error, returns the scalar
272 error. Otherwise, returns the transaction id, error message, and
273 RT::Transaction object.
274
275 Accepts the following options:
276
277 =over 4
278
279 =item ticket_id 
280
281 The ticket id
282
283 =item content
284
285 Correspondence content
286
287 =back
288
289 =cut
290
291 sub correspond_ticket {
292   my($self, $session, %param) = @_;
293
294   $session = $self->session($session);
295
296   my $Ticket = RT::Ticket->new($session->{'CurrentUser'});
297   my $ticketid = $Ticket->Load( $param{'ticket_id'} );
298   return 'Could not load ticket' unless $ticketid;
299   return 'No content' unless $param{'content'};
300
301   $Ticket->Correspond( Content => $param{'content'} );
302 }
303
304 =item queues SESSION_HASHREF [, ACL ]
305
306 Retrieve a list of queues.  Pass the name of an RT access control right, 
307 such as 'CreateTicket', to return only queues on which the current user 
308 has that right.  Otherwise this will return all queues with the 'SeeQueue' 
309 right.
310
311 =cut
312
313 sub queues {
314   my( $self, $session, $acl ) = @_;
315   $session = $self->session($session);
316
317   my $showall = $acl ? 0 : 1;
318   my @result = ();
319   my $q = new RT::Queues($session->{'CurrentUser'});
320   $q->UnLimit;
321   while (my $queue = $q->Next) {
322     if ($showall || $queue->CurrentUserHasRight($acl)) {
323       push @result, {
324         Id          => $queue->Id,
325         Name        => $queue->Name,
326         Description => $queue->Description,
327       };
328     }
329   }
330   return map { $_->{Id} => $_->{Name} } @result;
331 }
332
333 #shameless false laziness w/RT::Interface::Web::AttemptExternalAuth
334 # to get logged into RT from afar
335 sub _web_external_auth {
336   my( $self, $session ) = @_;
337
338   my $user = $FS::CurrentUser::CurrentUser->username;
339
340   eval 'use RT::CurrentUser;';
341   die $@ if $@;
342
343   $session ||= {};
344   $session->{'CurrentUser'} = RT::CurrentUser->new();
345
346   warn "$me _web_external_auth loading RT user for $user\n"
347     if $DEBUG > 1;
348
349   $session->{'CurrentUser'}->Load($user);
350
351   if ( ! $session->{'CurrentUser'}->Id() ) {
352
353       # Create users on-the-fly
354
355       warn "can't load RT user for $user; auto-creating\n"
356         if $DEBUG;
357
358       my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') );
359
360       my ( $val, $msg ) = $UserObj->Create(
361           %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} },
362           Name  => $user,
363           Gecos => $user,
364       );
365
366       if ($val) {
367
368           # now get user specific information, to better create our user.
369           my $new_user_info
370               = RT::Interface::Web::WebExternalAutoInfo($user);
371
372           # set the attributes that have been defined.
373           # FIXME: this is a horrible kludge. I'm sure there's something cleaner
374           foreach my $attribute (
375               'Name',                  'Comments',
376               'Signature',             'EmailAddress',
377               'PagerEmailAddress',     'FreeformContactInfo',
378               'Organization',          'Disabled',
379               'Privileged',            'RealName',
380               'NickName',              'Lang',
381               'EmailEncoding',         'WebEncoding',
382               'ExternalContactInfoId', 'ContactInfoSystem',
383               'ExternalAuthId',        'Gecos',
384               'HomePhone',             'WorkPhone',
385               'MobilePhone',           'PagerPhone',
386               'Address1',              'Address2',
387               'City',                  'State',
388               'Zip',                   'Country'
389               )
390           {
391               #uhh, wrong root
392               #$m->comp( '/Elements/Callback', %ARGS,
393               #    _CallbackName => 'NewUser' );
394
395               my $method = "Set$attribute";
396               $UserObj->$method( $new_user_info->{$attribute} )
397                   if ( defined $new_user_info->{$attribute} );
398           }
399           $session->{'CurrentUser'}->Load($user);
400       }
401       else {
402
403          # we failed to successfully create the user. abort abort abort.
404           delete $session->{'CurrentUser'};
405
406           die "can't auto-create RT user"; #an error message would be nice :/
407           #$m->abort() unless $RT::WebFallbackToInternalAuth;
408           #$m->comp( '/Elements/Login', %ARGS,
409           #    Error => loc( 'Cannot create user: [_1]', $msg ) );
410       }
411   }
412
413   unless ( $session->{'CurrentUser'}->Id() ) {
414       delete $session->{'CurrentUser'};
415
416       die "can't auto-create RT user";
417       #$user = $orig_user;
418       # 
419       #if ($RT::WebExternalOnly) {
420       #    $m->comp( '/Elements/Login', %ARGS,
421       #        Error => loc('You are not an authorized user') );
422       #    $m->abort();
423       #}
424   }
425
426   $session;
427
428 }
429
430 1;
431