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