add ticket creation to self-service API, RT#7007
[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 use RT::CurrentUser;
11
12 @ISA = qw( FS::TicketSystem::RT_Libs );
13
14 $DEBUG = 0;
15 $me = '[FS::TicketSystem::RT_Internal]';
16
17 sub sql_num_customer_tickets {
18   "( select count(*) from tickets
19                      join links on ( tickets.id = links.localbase )
20      where ( status = 'new' or status = 'open' or status = 'stalled' )
21        and target = 'freeside://freeside/cust_main/' || custnum
22    )";
23 }
24
25 sub baseurl {
26   #my $self = shift;
27   if ( $RT::URI::freeside::URL ) {
28     $RT::URI::freeside::URL. '/rt/';
29   } else {
30     'http://you_need_to_set_RT_URI_freeside_URL_in_SiteConfig.pm/';
31   }
32 }
33
34 #mapping/genericize??
35 #ShowConfigTab ModifySelf
36 sub access_right {
37   my( $self, $session, $right ) = @_;
38
39   #return '' unless $conf->config('ticket_system');
40   return '' unless FS::Conf->new->config('ticket_system');
41
42   $session = $self->session($session);
43
44   #warn "$me access_right: CurrentUser ". $session->{'CurrentUser'}. ":\n".
45   #     ( $DEBUG>1 ? Dumper($session->{'CurrentUser'}) : '' )
46   #  if $DEBUG > 1;
47
48   $session->{'CurrentUser'}->HasRight( Right  => $right,
49                                        Object => $RT::System );
50 }
51
52 sub session {
53   my( $self, $session ) = @_;
54
55   if ( $session && $session->{'Current_User'} ) {
56     warn "$me session: using existing session and CurrentUser: \n".
57          Dumper($session->{'CurrentUser'})
58       if $DEBUG;
59  } else {
60     warn "$me session: loading session and CurrentUser\n" if $DEBUG > 1;
61     $session = $self->_web_external_auth($session);
62   }
63
64   $session;
65 }
66
67 sub init {
68   my $self = shift;
69
70   warn "$me init: loading RT libraries\n" if $DEBUG;
71   eval '
72     use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" );
73     use RT;
74     #it looks like the rest are taken care of these days in RT::InitClasses
75     #use RT::Ticket;
76     #use RT::Transactions;
77     #use RT::Users;
78     #use RT::CurrentUser;
79     #use RT::Templates;
80     #use RT::Queues;
81     #use RT::ScripActions;
82     #use RT::ScripConditions;
83     #use RT::Scrips;
84     #use RT::Groups;
85     #use RT::GroupMembers;
86     #use RT::CustomFields;
87     #use RT::CustomFieldValues;
88     #use RT::ObjectCustomFieldValues;
89
90     #for web external auth...
91     use RT::Interface::Web;
92   ';
93   die $@ if $@;
94
95   warn "$me init: loading RT config\n" if $DEBUG;
96   {
97     local $SIG{__DIE__};
98     eval 'RT::LoadConfig();';
99   }
100   die $@ if $@;
101
102   warn "$me init: initializing RT\n" if $DEBUG;
103   {
104     local $SIG{__DIE__};
105     eval 'RT::Init("NoSignalHandlers"=>1);';
106   }
107   die $@ if $@;
108
109   warn "$me init: complete" if $DEBUG;
110 }
111
112 =item create_ticket SESSION_HASHREF, OPTION => VALUE ...
113
114 Class method.  Creates a ticket.  If there is an error, returns the scalar
115 error, otherwise returns the newly created RT::Ticket object.
116
117 Accepts the following options:
118
119 =over 4
120
121 =item queue
122
123 Queue name or Id
124
125 =item subject
126
127 Ticket subject
128
129 =item requestor
130
131 Requestor email address or arrayref of addresses
132
133 =item cc
134
135 Cc: email address or arrayref of addresses
136
137 =item message
138
139 Ticket message
140
141 =item custnum
142
143 Customer number (see L<FS::cust_main>) to associate with ticket.
144
145 =item svcnum
146
147 Service number (see L<FS::cust_svc>) to associate with ticket.  Will also
148 associate the customer who has this service (unless the service is unlinked).
149
150 =back
151
152 =cut
153
154 sub create_ticket {
155   my($self, $session, %param) = @_;
156
157   $session = $self->session($session);
158
159   my $Queue = RT::Queue->new($session->{'CurrentUser'});
160   $Queue->Load( $param{'queue'} );
161
162   my $req = ref($param{'requestor'})
163               ? $param{'requestor'}
164               : ( $param{'requestor'} ? [ $param{'requestor'} ] : [] );
165
166   my $cc = ref($param{'cc'})
167              ? $param{'cc'}
168              : ( $param{'cc'} ? [ $param{'cc'} ] : [] );
169
170   my $mimeobj = MIME::Entity->build(
171     'Data' => $param{'message'},
172     'Type' => 'text/plain',
173   );
174
175   my %ticket = (
176     'Queue'     => $Queue->Id,
177     'Subject'   => $param{'subject'},
178     'Requestor' => $req,
179     'Cc'        => $cc,
180     'MIMEObj'   => $mimeobj,
181   );
182   warn Dumper(\%ticket) if $DEBUG > 1;
183
184   my $Ticket = RT::Ticket->new($session->{'CurrentUser'});
185   my( $id, $Transaction, $ErrStr );
186   {
187     local $SIG{__DIE__};
188     ( $id, $Transaction, $ErrStr ) = $Ticket->Create( %ticket );
189   }
190   return $ErrStr if $id == 0;
191
192   warn "ticket got id $id\n" if $DEBUG;
193
194   #XXX check errors adding custnum/svcnum links (put it in a transaction)...
195   # but we do already know they're good
196
197   if ( $param{'custnum'} ) {
198     my( $val, $msg ) = $Ticket->_AddLink(
199      'Type'   => 'MemberOf',
200      'Target' => 'freeside://freeside/cust_main/'. $param{'custnum'},
201     );
202   }
203
204   if ( $param{'svcnum'} ) {
205     my( $val, $msg ) = $Ticket->_AddLink(
206      'Type'   => 'MemberOf',
207      'Target' => 'freeside://freeside/cust_svc/'. $param{'svcnum'},
208     );
209   }
210
211   $Ticket;
212 }
213
214 #shameless false laziness w/RT::Interface::Web::AttemptExternalAuth
215 # to get logged into RT from afar
216 sub _web_external_auth {
217   my( $self, $session ) = @_;
218
219   my $user = $FS::CurrentUser::CurrentUser->username;
220
221   $session ||= {};
222   $session->{'CurrentUser'} = RT::CurrentUser->new();
223
224   warn "$me _web_external_auth loading RT user for $user\n"
225     if $DEBUG > 1;
226
227   $session->{'CurrentUser'}->Load($user);
228
229   if ( ! $session->{'CurrentUser'}->Id() ) {
230
231       # Create users on-the-fly
232
233       warn "can't load RT user for $user; auto-creating\n"
234         if $DEBUG;
235
236       my $UserObj = RT::User->new( RT::CurrentUser->new('RT_System') );
237
238       my ( $val, $msg ) = $UserObj->Create(
239           %{ ref($RT::AutoCreate) ? $RT::AutoCreate : {} },
240           Name  => $user,
241           Gecos => $user,
242       );
243
244       if ($val) {
245
246           # now get user specific information, to better create our user.
247           my $new_user_info
248               = RT::Interface::Web::WebExternalAutoInfo($user);
249
250           # set the attributes that have been defined.
251           # FIXME: this is a horrible kludge. I'm sure there's something cleaner
252           foreach my $attribute (
253               'Name',                  'Comments',
254               'Signature',             'EmailAddress',
255               'PagerEmailAddress',     'FreeformContactInfo',
256               'Organization',          'Disabled',
257               'Privileged',            'RealName',
258               'NickName',              'Lang',
259               'EmailEncoding',         'WebEncoding',
260               'ExternalContactInfoId', 'ContactInfoSystem',
261               'ExternalAuthId',        'Gecos',
262               'HomePhone',             'WorkPhone',
263               'MobilePhone',           'PagerPhone',
264               'Address1',              'Address2',
265               'City',                  'State',
266               'Zip',                   'Country'
267               )
268           {
269               #uhh, wrong root
270               #$m->comp( '/Elements/Callback', %ARGS,
271               #    _CallbackName => 'NewUser' );
272
273               my $method = "Set$attribute";
274               $UserObj->$method( $new_user_info->{$attribute} )
275                   if ( defined $new_user_info->{$attribute} );
276           }
277           $session->{'CurrentUser'}->Load($user);
278       }
279       else {
280
281          # we failed to successfully create the user. abort abort abort.
282           delete $session->{'CurrentUser'};
283
284           die "can't auto-create RT user"; #an error message would be nice :/
285           #$m->abort() unless $RT::WebFallbackToInternalAuth;
286           #$m->comp( '/Elements/Login', %ARGS,
287           #    Error => loc( 'Cannot create user: [_1]', $msg ) );
288       }
289   }
290
291   unless ( $session->{'CurrentUser'}->Id() ) {
292       delete $session->{'CurrentUser'};
293
294       die "can't auto-create RT user";
295       #$user = $orig_user;
296       # 
297       #if ($RT::WebExternalOnly) {
298       #    $m->comp( '/Elements/Login', %ARGS,
299       #        Error => loc('You are not an authorized user') );
300       #    $m->abort();
301       #}
302   }
303
304   $session;
305
306 }
307
308 1;
309