rt 4.2.15
[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-2018 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 Content-Type);
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} && lc $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 $queue = RT::Queue->new($session{CurrentUser});
103         $queue->Load(1);
104
105         my $due;
106         if ($queue->DefaultDueIn) {
107             $due = RT::Date->new($session{CurrentUser});
108             $due->SetToNow;
109             $due->AddDays($queue->DefaultDueIn);
110         }
111         my $starts = RT::Date->new($session{CurrentUser});
112         $starts->SetToNow;
113
114         return [
115             "# Required: id, Queue",
116             [ qw(id Queue Requestor Subject Cc AdminCc Owner Status Priority
117                  InitialPriority FinalPriority TimeEstimated Starts Due Attachment Text) ],
118             {
119                 id               => "ticket/new",
120                 Queue            => $queue->Name,
121                 Requestor        => $session{CurrentUser}->Name,
122                 Subject          => "",
123                 Cc               => [],
124                 AdminCc          => [],
125                 Owner            => "",
126                 Status           => "new",
127                 Priority         => $queue->InitialPriority,
128                 InitialPriority  => $queue->InitialPriority,
129                 FinalPriority    => $queue->FinalPriority,
130                 TimeEstimated    => 0,
131                 Starts           => $starts->ISO(Timezone => 'user'),
132                 Due              => $due ? $due->ISO(Timezone => 'user') : undef,
133                 Attachment       => '',
134                 Text             => "",
135             },
136             0
137         ];
138     }
139     else {
140         # We'll create a new ticket, and fall through to set fields that
141         # can't be set in the call to Create().
142         my (%v, $text, @atts);
143
144         foreach my $k (keys %data) {
145             # flexibly parse any dates
146             if ($dates{lc $k}) {
147                 my $time = RT::Date->new($session{CurrentUser});
148                 $time->Set(Format => 'unknown', Value => $data{$k});
149                 $data{$k} = $time->ISO;
150             }
151
152             if (exists $create{lc $k}) {
153                 $v{$create{lc $k}} = delete $data{$k};
154             }
155             # Set custom field
156             elsif ($k =~ /^$cf_spec/) {
157                 my $key = $1 || $2;
158
159                 my $cf = RT::CustomField->new( $session{CurrentUser} );
160                 $cf->LoadByName(
161                     Name          => $key,
162                     LookupType    => RT::Ticket->CustomFieldLookupType,
163                     ObjectId      => $data{Queue} || $v{Queue},
164                     IncludeGlobal => 1,
165                 );
166
167                 if (not $cf->id) {
168                     push @comments, "# Invalid custom field name ($key)";
169                     delete $data{$k};
170                     next;
171                 }
172                 my $val = delete $data{$k};
173                 next unless defined $val && length $val;
174                 $v{"CustomField-".$cf->Id()} = $cf->SingleValue ? $val : vsplit($val,1);
175             }
176             elsif (lc $k eq 'text') {
177                 $text = delete $data{$k};
178             }
179             elsif (lc $k eq 'attachment') {
180                 push @atts, @{ vsplit(delete $data{$k}) };
181             }
182             elsif ( $k !~ /^(?:id|requestors)$/i ) {
183                 $e = 1;
184                 push @$o, $k;
185                 push(@comments, "# $k: Unknown field");
186             }
187         }
188
189         if ( $e ) {
190             unshift @comments, "# Could not create ticket.";
191             $k = \%data;
192             goto DONE;
193         }
194
195         # people fields allow multiple values
196         $v{$_} = vsplit($v{$_}) foreach ( grep $create{lc $_}, @people );
197
198         if ($text || @atts) {
199             $v{MIMEObj} =
200                 MIME::Entity->build(
201                     Type => "multipart/mixed",
202                     From => Encode::encode( "UTF-8", $session{CurrentUser}->EmailAddress ),
203                     Subject => Encode::encode( "UTF-8", $v{Subject}),
204                     'X-RT-Interface' => 'REST',
205                 );
206             $v{MIMEObj}->attach(
207                 Type    => $v{'Content-Type'} || 'text/plain',
208                 Charset => "UTF-8",
209                 Data    => Encode::encode( "UTF-8", $text ),
210             ) if $text;
211             my ($status, $msg) = process_attachments($v{'MIMEObj'}, @atts);
212             unless ($status) {
213                 push(@comments, "# $msg");
214                 goto DONE;
215             }
216             $v{MIMEObj}->make_singlepart;
217         }
218
219         my($tid,$trid,$terr) = $ticket->Create(%v);    
220         unless ($tid) {
221             push(@comments, "# Could not create ticket.");
222             push(@comments, "# " . $terr);
223             goto DONE;
224         }
225
226         delete $data{id};
227         $id = $ticket->Id;
228         push(@comments, "# Ticket $id created.");
229         # see if the hash is empty
230         goto DONE if ! keys(%data);
231     }
232 }
233
234 # Now we know we're dealing with an existing ticket.
235 if (!keys(%data)) {
236     my ($time, $key, $val, @data);
237
238     push @data, [ id    => "ticket/".$ticket->Id   ];
239     push @data, [ Queue => $ticket->QueueObj->Name ]
240         if (!%$fields || exists $fields->{lc 'Queue'});
241     push @data, [ Owner => $ticket->OwnerObj->Name ]
242         if (!%$fields || exists $fields->{lc 'Owner'});
243     push @data, [ Creator => $ticket->CreatorObj->Name ]
244         if (!%$fields || exists $fields->{lc 'Creator'});
245
246     foreach (qw(Subject Status Priority InitialPriority FinalPriority)) {
247         next unless (!%$fields || (exists $fields->{lc $_}));
248         push @data, [$_ => $ticket->$_ ];
249     }
250
251     foreach $key (@people) {
252         next unless (!%$fields || (exists $fields->{lc $key}));
253         push @data, [ $key => [ $ticket->$key->MemberEmailAddresses ] ];
254     }
255
256     $time = RT::Date->new ($session{CurrentUser});
257     foreach $key (@dates) {
258         next unless (!%$fields || (exists $fields->{lc $key}));
259         $time->Set(Format => 'sql', Value => $ticket->$key);
260         push @data, [ $key => $time->AsString ];
261     }
262
263     $time = RT::Date->new ($session{CurrentUser});
264     foreach $key (qw(TimeEstimated TimeWorked TimeLeft)) {
265         next unless (!%$fields || (exists $fields->{lc $key}));
266         $val = $ticket->$key || 0;
267         $val = "$val minutes" if $val;
268         push @data, [ $key => $val ];
269     }
270
271     # Display custom fields
272     my $CustomFields = $ticket->CustomFields;
273     while (my $cf = $CustomFields->Next()) {
274         next unless !%$fields
275                  || exists $fields->{"cf.{".lc($cf->Name)."}"}
276                  || exists $fields->{"cf-".lc $cf->Name};
277
278         my $vals = $ticket->CustomFieldValues($cf->Id());
279         my @out = ();
280         if ( $cf->SingleValue ) {
281             my $v = $vals->Next;
282             push @out, $v->Content if $v;
283         }
284         else {
285             while (my $v = $vals->Next()) {
286                 my $content = $v->Content;
287                 if ( $v->Content =~ /,/ ) {
288                     $content =~ s/([\\'])/\\$1/g;
289                     push @out, q{'} . $content . q{'};
290                 }
291                 else {
292                     push @out, $content;
293                 }
294             }
295         }
296         push @data, [ ('CF.{' . $cf->Name . '}') => join ',', @out ];
297     }
298
299     my %k = map {@$_} @data;
300     $o = [ map {$_->[0]} @data ];
301     $k = \%k;
302 }
303 else {
304     my ($get, $set, $key, $val, $n, $s);
305     my $updated;
306
307     foreach $key (keys %data) {
308         $val = $data{$key};
309         $key = lc $key;
310         $n = 1;
311
312         if (ref $val eq 'ARRAY') {
313             unless ($key =~ /^(?:Requestors|Cc|AdminCc)$/i) {
314                 $n = 0;
315                 $s = "$key may have only one value.";
316                 goto SET;
317             }
318         }
319
320         if ($key =~ /^queue$/i) {
321             next if $val eq $ticket->QueueObj->Name;
322             ($n, $s) = $ticket->SetQueue($val);
323         }
324         elsif ($key =~ /^owner$/i) {
325             next if $val eq $ticket->OwnerObj->Name;
326             ($n, $s) = $ticket->SetOwner($val);
327         }
328         elsif (exists $simple{$key}) {
329             $key = $simple{$key};
330             $set = "Set$key";
331             my $current = $ticket->$key;
332             $current = '' unless defined $current;
333
334             next if ($val eq $current) or ($current =~ /^\d+$/ && $val =~ /^\d+$/ && $val == $current);
335             ($n, $s) = $ticket->$set("$val");
336         }
337         elsif (exists $dates{$key}) {
338             $key = $dates{$key};
339
340             # We try to detect whether it should update a field by checking
341             # whether its current value equals the entered value. Since the
342             # LastUpdated field is automatically updated as other columns are
343             # changed, it is not properly skipped. Users cannot update this
344             # field anyway.
345             next if $key eq 'LastUpdated';
346
347             $set = "Set$key";
348
349             my $time = RT::Date->new($session{CurrentUser});
350             $time->Set(Format => 'sql', Value => $ticket->$key);
351             next if ($val =~ /^not set$/i || $val eq $time->AsString);
352
353             $time->Set(Format => 'unknown', Value => $val);
354             ($n, $s) = $ticket->$set($time->ISO);
355         }
356         elsif (exists $people{$key}) {
357             $key = $people{$key};
358             my ($p, @msgs);
359
360             my %new  = map {$_=>1} @{ vsplit($val) };
361             my %old  = map {$_=>1} $ticket->$key->MemberEmailAddresses;
362             my $type = $key eq 'Requestors' ? 'Requestor' : $key;
363
364             foreach $p (keys %old) {
365                 unless (exists $new{$p}) {
366                     ($s, $n) = $ticket->DeleteWatcher(Type => $type,
367                                                       Email => $p);
368                     push @msgs, [ $s, $n ];
369                 }
370             }
371             foreach $p (keys %new) {
372                 unless ($ticket->IsWatcher(Type => $type, Email => $p)) {
373                     ($s, $n) = $ticket->AddWatcher(Type => $type,
374                                                    Email => $p);
375                     push @msgs, [ $s, $n ];
376                 }
377             }
378
379             $n = 1;
380             if (@msgs = grep {$_->[0] == 0} @msgs) {
381                 $n = 0;
382                 $s = join "\n", map {"# ".$_->[1]} @msgs;
383                 $s =~ s/^# //;
384             }
385         }
386         # Set custom field
387         elsif ($key =~ /^$cf_spec/) {
388             $key = $1 || $2;
389
390             my $cf = RT::CustomField->new( $session{CurrentUser} );
391             $cf->ContextObject( $ticket );
392             $cf->LoadByName(
393                 Name          => $key,
394                 LookupType    => RT::Ticket->CustomFieldLookupType,
395                 ObjectId      => $ticket->Queue,
396                 IncludeGlobal => 1,
397             );
398
399             if (not $cf->id) {
400                 $n = 0;
401                 $s = "Unknown custom field.";
402             }
403             else {
404                 my $vals = $ticket->CustomFieldValues($cf->id);
405
406                 if ( !defined $val || !length $val ) {
407                     while ( my $val = $vals->Next ) {
408                         ($n, $s) = $ticket->DeleteCustomFieldValue(
409                             Field => $cf, ValueId => $val->id,
410                         );
411                         $s =~ s/^# // if defined $s;
412                     }
413                 }
414                 elsif ( $cf->SingleValue ) {
415                     ($n, $s) = $ticket->AddCustomFieldValue(
416                          Field => $cf, Value => $val );
417                     $s =~ s/^# // if defined $s;
418                 }
419                 else {
420                     my @new = @{vsplit($val, 1)};
421
422                     my %new;
423                     $new{$_}++ for @new;
424
425                     while (my $v = $vals->Next()) {
426                         my $c = $v->Content;
427                         if ( $new{$c} ) {
428                             $new{$c}--;
429                         }
430                         else {
431                             $ticket->DeleteCustomFieldValue( Field => $cf, ValueId => $v->id );
432                         }
433                     }
434                     for ( @new ) {
435                         while ( $new{$_} && $new{$_}-- ) {
436                             ($n, $s) = $ticket->AddCustomFieldValue(
437                                 Field => $cf, Value => $_ );
438                             $s =~ s/^# // if defined $s;
439                         }
440                     }
441                 }
442             }
443         }
444         elsif ($key ne 'id' && $key ne 'type' && $key ne 'creator' && $key ne 'content-type' ) {
445             $n = 0;
446             $s = "Unknown field.";
447         }
448
449     SET:
450         if ($n == 0) {
451             $e = 1;
452             push @comments, "# $key: $s";
453             unless (@$o) {
454                 # move id forward
455                 @$o = ("id", grep { $_ ne 'id' } keys %$changes);
456                 $k = $changes;
457             }
458         }
459         else {
460             $updated ||= 1;
461         }
462     }
463     push(@comments, "# Ticket ".$ticket->id." updated.") if $updated;
464 }
465
466 DONE:
467 $c ||= join("\n", @comments) if @comments;
468 return [$c, $o, $k, $e];
469
470 </%INIT>