Merge branch 'master' of git.freeside.biz:/home/git/freeside
[freeside.git] / rt / share / html / REST / 1.0 / Forms / ticket / default
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
6 %#                                          <sales@bestpractical.com>
7 %#
8 %# (Except where explicitly superseded by other copyright notices)
9 %#
10 %#
11 %# LICENSE:
12 %#
13 %# This work is made available to you under the terms of Version 2 of
14 %# the GNU General Public License. A copy of that license should have
15 %# been provided with this software, but in any event can be snarfed
16 %# from www.gnu.org.
17 %#
18 %# This work is distributed in the hope that it will be useful, but
19 %# WITHOUT ANY WARRANTY; without even the implied warranty of
20 %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 %# General Public License for more details.
22 %#
23 %# You should have received a copy of the GNU General Public License
24 %# along with this program; if not, write to the Free Software
25 %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 %# 02110-1301 or visit their web page on the internet at
27 %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 %#
29 %#
30 %# CONTRIBUTION SUBMISSION POLICY:
31 %#
32 %# (The following paragraph is not intended to limit the rights granted
33 %# to you to modify and distribute this software under the terms of
34 %# the GNU General Public License and is only of importance to you if
35 %# you choose to contribute your changes and enhancements to the
36 %# community by submitting them to Best Practical Solutions, LLC.)
37 %#
38 %# By intentionally submitting any modifications, corrections or
39 %# derivatives to this work, or any other work intended for use with
40 %# Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 %# you are the copyright holder for those contributions and you grant
42 %# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 %# royalty-free, perpetual, license to use, copy, create derivative
44 %# works based on those contributions, and sublicense and distribute
45 %# those contributions and any derivatives thereof.
46 %#
47 %# END BPS TAGGED BLOCK }}}
48 %# REST/1.0/Forms/ticket/default
49 %#
50 <%ARGS>
51 $id
52 $changes => {}
53 $fields => undef
54 $args => undef
55 </%ARGS>
56 <%INIT>
57 use MIME::Entity;
58 use RT::Interface::REST;
59
60 my $cf_spec = RT::Interface::REST->custom_field_spec(1);
61
62 my @comments;
63 my ($c, $o, $k, $e) = ("", [], {}, 0);
64 my %data   = %$changes;
65 my $ticket = RT::Ticket->new($session{CurrentUser});
66 my @dates  = qw(Created Starts Started Due Resolved Told LastUpdated);
67 my @people = qw(Requestors Cc AdminCc);
68 my @create = qw(Queue Requestor Subject Cc AdminCc Owner Status Priority
69                 InitialPriority FinalPriority TimeEstimated TimeWorked
70                 TimeLeft Starts Started Due Resolved);
71 my @simple = qw(Subject Status Priority Disabled TimeEstimated TimeWorked
72                 TimeLeft InitialPriority FinalPriority);
73 my %dates  = map {lc $_ => $_} @dates;
74 my %people = map {lc $_ => $_} @people;
75 my %create = map {lc $_ => $_} @create;
76 my %simple = map {lc $_ => $_} @simple;
77
78 # Are we dealing with an existing ticket?
79 if ($id ne 'new') {
80     $ticket->Load($id);
81     if (!$ticket->Id) {
82         return [ "# Ticket $id does not exist.", [], {}, 1 ];
83     }
84     elsif ( %data ) {
85         if ( $data{status} && $data{status} eq 'deleted' && ! grep { $_ ne 'id' && $_ ne 'status' } keys %data ) {
86             if ( !$ticket->CurrentUserHasRight('DeleteTicket') ) {
87                 return [ "# You are not allowed to delete ticket $id.", [], {}, 1 ];
88             }
89         }
90         elsif ( !$ticket->CurrentUserHasRight('ModifyTicket') ) {
91                 return [ "# You are not allowed to modify ticket $id.", [], {}, 1 ];
92         }
93     }
94     elsif (!$ticket->CurrentUserHasRight('ShowTicket')) {
95         return [ "# You are not allowed to display ticket $id.", [], {}, 1 ];
96     }
97 }
98 else {
99     if (!keys(%data)) {
100         # GET ticket/new: Return a suitable default form.
101         # We get defaults from queue/1 (XXX: What if it isn't there?).
102         my $due = RT::Date->new($session{CurrentUser});
103         my $queue = RT::Queue->new($session{CurrentUser});
104         my $starts = RT::Date->new($session{CurrentUser});
105         $queue->Load(1);
106         $due->SetToNow;
107         $due->AddDays($queue->DefaultDueIn) if $queue->DefaultDueIn;
108         $starts->SetToNow;
109
110         return [
111             "# Required: id, Queue",
112             [ qw(id Queue Requestor Subject Cc AdminCc Owner Status Priority
113                  InitialPriority FinalPriority TimeEstimated Starts Due Text) ],
114             {
115                 id               => "ticket/new",
116                 Queue            => $queue->Name,
117                 Requestor        => $session{CurrentUser}->Name,
118                 Subject          => "",
119                 Cc               => [],
120                 AdminCc          => [],
121                 Owner            => "",
122                 Status           => "new",
123                 Priority         => $queue->InitialPriority,
124                 InitialPriority  => $queue->InitialPriority,
125                 FinalPriority    => $queue->FinalPriority,
126                 TimeEstimated    => 0,
127                 Starts           => $starts->ISO,
128                 Due              => $due->ISO,
129                 Text             => "",
130             },
131             0
132         ];
133     }
134     else {
135         # We'll create a new ticket, and fall through to set fields that
136         # can't be set in the call to Create().
137         my (%v, $text);
138
139         foreach my $k (keys %data) {
140             # flexibly parse any dates
141             if ($dates{lc $k}) {
142                 my $time = RT::Date->new($session{CurrentUser});
143                 $time->Set(Format => 'unknown', Value => $data{$k});
144                 $data{$k} = $time->ISO;
145             }
146
147             if (exists $create{lc $k}) {
148                 $v{$create{lc $k}} = delete $data{$k};
149             }
150             # Set custom field
151             elsif ($k =~ /^$cf_spec/) {
152                 my $key = $1 || $2;
153
154                 my $cf = RT::CustomField->new( $session{CurrentUser} );
155                 $cf->LoadByName( Name => $key, Queue => $data{Queue} || $v{Queue} );
156                 unless ( $cf->id ) {
157                     $cf->LoadByName( Name => $key, Queue => 0 );
158                 }
159
160                 if (not $cf->id) {
161                     push @comments, "# Invalid custom field name ($key)";
162                     delete $data{$k};
163                     next;
164                 }
165                 $v{"CustomField-".$cf->Id()} = delete $data{$k};
166             }
167             elsif (lc $k eq 'text') {
168                 $text = delete $data{$k};
169             }
170             elsif ( $k !~ /^(?:id|requestors)$/i ) {
171                 $e = 1;
172                 push @$o, $k;
173                 push(@comments, "# $k: Unknown field");
174             }
175         }
176
177         if ( $e ) {
178             unshift @comments, "# Could not create ticket.";
179             $k = \%data;
180             goto DONE;
181         }
182
183         # people fields allow multiple values
184         $v{$_} = vsplit($v{$_}) foreach ( grep $create{lc $_}, @people );
185
186         if ($text) {
187             $v{MIMEObj} =
188                 MIME::Entity->build(
189                     From => $session{CurrentUser}->EmailAddress,
190                     Subject => $v{Subject},
191                     Data => $text,
192                     'X-RT-Interface' => 'REST',
193                 );
194         }
195
196         my($tid,$trid,$terr) = $ticket->Create(%v);    
197         unless ($tid) {
198             push(@comments, "# Could not create ticket.");
199             push(@comments, "# " . $terr);
200             goto DONE;
201         }
202
203         delete $data{id};
204         $id = $ticket->Id;
205         push(@comments, "# Ticket $id created.");
206         # see if the hash is empty
207         goto DONE if ! keys(%data);
208     }
209 }
210
211 # Now we know we're dealing with an existing ticket.
212 if (!keys(%data)) {
213     my ($time, $key, $val, @data);
214
215     push @data, [ id    => "ticket/".$ticket->Id   ];
216     push @data, [ Queue => $ticket->QueueObj->Name ] 
217         if (!%$fields || exists $fields->{lc 'Queue'});
218     push @data, [ Owner => $ticket->OwnerObj->Name ]
219         if (!%$fields || exists $fields->{lc 'Owner'});
220     push @data, [ Creator => $ticket->CreatorObj->Name ]
221         if (!%$fields || exists $fields->{lc 'Creator'});
222
223     foreach (qw(Subject Status Priority InitialPriority FinalPriority)) {
224         next unless (!%$fields || (exists $fields->{lc $_}));
225         push @data, [$_ => $ticket->$_ ];
226     }
227
228     foreach $key (@people) {
229         next unless (!%$fields || (exists $fields->{lc $key}));
230         push @data, [ $key => [ $ticket->$key->MemberEmailAddresses ] ];
231     }
232
233     $time = RT::Date->new ($session{CurrentUser});
234     foreach $key (@dates) {
235         next unless (!%$fields || (exists $fields->{lc $key}));
236         $time->Set(Format => 'sql', Value => $ticket->$key);
237         push @data, [ $key => $time->AsString ];
238     }
239
240     $time = RT::Date->new ($session{CurrentUser});
241     foreach $key (qw(TimeEstimated TimeWorked TimeLeft)) {
242         next unless (!%$fields || (exists $fields->{lc $key}));
243         $val = $ticket->$key || 0;
244         $val = "$val minutes" if $val;
245         push @data, [ $key => $val ];
246     }
247
248     # Display custom fields
249     my $CustomFields = $ticket->CustomFields;
250     while (my $cf = $CustomFields->Next()) {
251         next unless !%$fields
252                  || exists $fields->{"cf.{".lc($cf->Name)."}"}
253                  || exists $fields->{"cf-".lc $cf->Name};
254
255         my $vals = $ticket->CustomFieldValues($cf->Id());
256         my @out = ();
257         if ( $cf->SingleValue ) {
258             my $v = $vals->Next;
259             push @out, $v->Content if $v;
260         }
261         else {
262             while (my $v = $vals->Next()) {
263                 my $content = $v->Content;
264                 $content =~ s/'/\\'/g;
265                 if ( $v->Content =~ /,/ ) {
266                     push @out, q{'} . $content . q{'};
267                 }
268                 else {
269                     push @out, $content;
270                 }
271             }
272         }
273         push @data, [ ('CF.{' . $cf->Name . '}') => join ',', @out ];
274     }
275
276     my %k = map {@$_} @data;
277     $o = [ map {$_->[0]} @data ];
278     $k = \%k;
279 }
280 else {
281     my ($get, $set, $key, $val, $n, $s);
282
283     foreach $key (keys %data) {
284         $val = $data{$key};
285         $key = lc $key;
286         $n = 1;
287
288         if (ref $val eq 'ARRAY') {
289             unless ($key =~ /^(?:Requestors|Cc|AdminCc)$/i) {
290                 $n = 0;
291                 $s = "$key may have only one value.";
292                 goto SET;
293             }
294         }
295
296         if ($key =~ /^queue$/i) {
297             next if $val eq $ticket->QueueObj->Name;
298             ($n, $s) = $ticket->SetQueue($val);
299         }
300         elsif ($key =~ /^owner$/i) {
301             next if $val eq $ticket->OwnerObj->Name;
302             ($n, $s) = $ticket->SetOwner($val);
303         }
304         elsif (exists $simple{$key}) {
305             $key = $simple{$key};
306             $set = "Set$key";
307             my $current = $ticket->$key;
308             $current = '' unless defined $current;
309
310             next if ($val eq $current) or ($current =~ /^\d+$/ && $val =~ /^\d+$/ && $val == $current);
311             ($n, $s) = $ticket->$set("$val");
312         }
313         elsif (exists $dates{$key}) {
314             $key = $dates{$key};
315
316             # We try to detect whether it should update a field by checking
317             # whether its current value equals the entered value. Since the
318             # LastUpdated field is automatically updated as other columns are
319             # changed, it is not properly skipped. Users cannot update this
320             # field anyway.
321             next if $key eq 'LastUpdated';
322
323             $set = "Set$key";
324
325             my $time = RT::Date->new($session{CurrentUser});
326             $time->Set(Format => 'sql', Value => $ticket->$key);
327             next if ($val =~ /^not set$/i || $val eq $time->AsString);
328
329             $time->Set(Format => 'unknown', Value => $val);
330             ($n, $s) = $ticket->$set($time->ISO);
331         }
332         elsif (exists $people{$key}) {
333             $key = $people{$key};
334             my ($p, @msgs);
335
336             my %new  = map {$_=>1} @{ vsplit($val) };
337             my %old  = map {$_=>1} $ticket->$key->MemberEmailAddresses;
338             my $type = $key eq 'Requestors' ? 'Requestor' : $key;
339
340             foreach $p (keys %old) {
341                 unless (exists $new{$p}) {
342                     ($s, $n) = $ticket->DeleteWatcher(Type => $type,
343                                                       Email => $p);
344                     push @msgs, [ $s, $n ];
345                 }
346             }
347             foreach $p (keys %new) {
348                 unless ($ticket->IsWatcher(Type => $type, Email => $p)) {
349                     ($s, $n) = $ticket->AddWatcher(Type => $type,
350                                                    Email => $p);
351                     push @msgs, [ $s, $n ];
352                 }
353             }
354
355             $n = 1;
356             if (@msgs = grep {$_->[0] == 0} @msgs) {
357                 $n = 0;
358                 $s = join "\n", map {"# ".$_->[1]} @msgs;
359                 $s =~ s/^# //;
360             }
361         }
362         # Set custom field
363         elsif ($key =~ /^$cf_spec/) {
364             $key = $1 || $2;
365
366             my $cf = RT::CustomField->new( $session{CurrentUser} );
367             $cf->LoadByName( Name => $key, Queue => $ticket->Queue );
368             unless ( $cf->id ) {
369                 $cf->LoadByName( Name => $key, Queue => 0 );
370             }
371
372             if (not $cf->id) {
373                 $n = 0;
374                 $s = "Unknown custom field.";
375             }
376             else {
377                 my $vals = $ticket->CustomFieldValues($cf->id);
378
379                 if ( !defined $val || !length $val ) {
380                     while ( my $val = $vals->Next ) {
381                         ($n, $s) = $ticket->DeleteCustomFieldValue(
382                             Field => $cf, ValueId => $val->id,
383                         );
384                         $s =~ s/^# // if defined $s;
385                     }
386                 }
387                 elsif ( $cf->SingleValue ) {
388                     my $old = $vals->Next;
389                     if ( $old ) {
390                         if ( $val ne $old->Content ) {
391                             ($n, $s) = $ticket->AddCustomFieldValue(
392                                  Field => $cf, Value => $val );
393                             $s =~ s/^# // if defined $s;
394                         }
395                     }
396                     else {
397                         ($n, $s) = $ticket->AddCustomFieldValue(
398                              Field => $cf, Value => $val );
399                         $s =~ s/^# // if defined $s;
400                     }
401                 }
402                 else {
403                     my @new;
404                     my ( $a, $b ) = split /\s*,\s*/, $val, 2;
405                     while ($a) {
406                         no warnings 'uninitialized';
407                         if ( $a =~ /^'/ ) {
408                             my $s = $a;
409                             while ( $a !~ /'$/ || ( $a !~ /(\\\\)+'$/
410                                             && $a =~ /(\\)+'$/ ) ) {
411                                 ( $a, $b ) = split /\s*,\s*/, $b, 2;
412                                 $s .= ',' . $a;
413                             }
414                             $s =~ s/^'//;
415                             $s =~ s/'$//;
416                             $s =~ s/\\'/'/g;
417                             push @new, $s;
418                         }
419                         elsif ( $a =~ /^q{/ ) {
420                             my $s = $a;
421                             while ( $a !~ /}$/ ) {
422                                 ( $a, $b ) = split /\s*,\s*/, $b, 2;
423                                 $s .= ',' . $a;
424                             }
425                             $s =~ s/^q{//;
426                             $s =~ s/}//;
427                             push @new, $s;
428                         }
429                         else {
430                             push @new, $a;
431                         }
432                         ( $a, $b ) = split /\s*,\s*/, $b, 2;
433                     }
434
435                     my %new;
436                     $new{$_}++ for @new;
437
438                     while (my $v = $vals->Next()) {
439                         my $c = $v->Content;
440                         if ( $new{$c} ) {
441                             $new{$c}--;
442                         }
443                         else {
444                             $ticket->DeleteCustomFieldValue( Field => $cf, ValueId => $v->id );
445                         }
446                     }
447                     for ( @new ) {
448                         while ( $new{$_} && $new{$_}-- ) {
449                             ($n, $s) = $ticket->AddCustomFieldValue(
450                                 Field => $cf, Value => $_ );
451                             $s =~ s/^# // if defined $s;
452                         }
453                     }
454                 }
455             }
456         }
457         elsif ($key ne 'id' && $key ne 'type' && $key ne 'creator') {
458             $n = 0;
459             $s = "Unknown field.";
460         }
461
462     SET:
463         if ($n == 0) {
464             $e = 1;
465             push @comments, "# $key: $s";
466             unless (@$o) {
467                 # move id forward
468                 @$o = ("id", grep { $_ ne 'id' } keys %$changes);
469                 $k = $changes;
470             }
471         }
472     }
473     push(@comments, "# Ticket ".$ticket->id." updated.") unless $n == 0;
474 }
475
476 DONE:
477 $c ||= join("\n", @comments) if @comments;
478 return [$c, $o, $k, $e];
479
480 </%INIT>