rt 4.2.16
[freeside.git] / rt / share / html / Ticket / Create.html
1 %# BEGIN BPS TAGGED BLOCK {{{
2 %#
3 %# COPYRIGHT:
4 %#
5 %# This software is Copyright (c) 1996-2019 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 <& /Elements/Header,
49     Title => $title,
50     onload => "function () { hide('Ticket-Create-details') }" &>
51 <& /Elements/Tabs &>
52     
53 <& /Elements/ListActions, actions => \@results &>
54
55 <form action="<% RT->Config->Get('WebPath') %>/Ticket/Create.html" method="post" enctype="multipart/form-data" name="TicketCreate">
56   <input type="submit" name="SubmitTicket" value="Create" style="display:none">
57   <input type="hidden" class="hidden" name="id" value="new" />
58   <input type="hidden" class="hidden" name="Token" value="<% $ARGS{'Token'} %>" />
59   
60 % $m->callback( CallbackName => 'FormStart', QueueObj => $QueueObj, ARGSRef => \%ARGS );
61
62 % if ($gnupg_widget) {
63   <& /Elements/Crypt/SignEncryptWidget:ShowIssues, self => $gnupg_widget &>
64 % }
65
66 <div id="Ticket-Create-basics">
67 <a name="basics"></a>
68
69 <div id="ticket-create-metadata">
70     <&| /Widgets/TitleBox, title => loc("Basics"), class=>'ticket-info-basics' &>
71     <table width="100%" border="0">
72     <& /Ticket/Elements/EditBasics,
73         InTable => 1,
74         fields  => [
75             {   name => 'Queue',
76                 comp => '/Elements/SelectQueue',
77                 args => {
78                     Name => 'Queue',
79                     Default => $QueueObj->Name,
80                     QueueObj => $QueueObj,
81                     ShowNullOption => 0,
82                     ShowAllQueues => 0,
83                     OnChange => "document.getElementsByName('id')[0].value = 'refresh'; form.submit()",
84                 },
85             },
86             {   name => 'Status',
87                 comp => '/Ticket/Elements/SelectStatus',
88                 args => {
89                     Name            => "Status",
90                     QueueObj        => $QueueObj,
91                 },
92             },
93             {   name => 'Owner',
94                 comp => '/Elements/SelectOwner',
95                 args => {
96                     Name            => "Owner",
97                     Default         => $ARGS{Owner} || RT->Nobody->Id,
98                     DefaultValue    => 0,
99                     QueueObj        => $QueueObj,
100                 },
101             }
102         ]
103         &>
104
105 % $m->callback( CallbackName => 'AfterOwner', ARGSRef => \%ARGS );
106
107       <& /Elements/EditCustomFields,
108           %ARGS,
109           Object => $ticket,
110           CustomFields => $QueueObj->TicketCustomFields,
111           Grouping => 'Basics',
112           InTable => 1,
113           KeepValue => 1,
114       &>
115       <& /Ticket/Elements/EditTransactionCustomFields, %ARGS, QueueObj => $QueueObj, InTable => 1, KeepValue => 1, &>
116     </table>
117   </&>
118 % $m->callback( CallbackName => 'AfterBasics', QueueObj => $QueueObj, ARGSRef => \%ARGS );
119
120 <& /Elements/EditCustomFieldCustomGroupings,
121     %ARGS,
122     Object => $ticket,
123     CustomFieldGenerator => sub { $QueueObj->TicketCustomFields },
124     KeepValue => 1,
125 &>
126
127 </div>
128
129 <div id="ticket-create-message">
130   <&| /Widgets/TitleBox, title => $title, class => 'messagedetails' &>
131 <table border="0" cellpadding="0" cellspacing="0">
132 <tr>
133 <td class="label">
134 <&|/l&>Requestors</&>:
135 </td>
136 <td class="value" colspan="5">
137 <& /Elements/EmailInput, Name => 'Requestors', Size => undef, Default => $ARGS{Requestors} // $session{CurrentUser}->EmailAddress, AutocompleteMultiple => 1 &>
138 % $m->callback( CallbackName => 'AfterRequestors', QueueObj => $QueueObj, ARGSRef => \%ARGS );
139 </td>
140 </tr>
141 <tr>
142 <td class="label">
143 <&|/l&>Cc</&>:
144 </td>
145 <td class="value" colspan="5"><& /Elements/EmailInput, Name => 'Cc', Size => undef, Default => $ARGS{Cc}, AutocompleteMultiple => 1 &></td>
146 </tr>
147
148 <tr>
149   <td class="label">&nbsp;</td>
150   <td class="comment" colspan="5">
151     <i><font size="-2">
152       <&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of email addresses. These people <strong>will</strong> receive future updates.)</&>
153     </font></i>
154   </td>
155 </tr>
156
157 <tr>
158 <td class="label">
159 <&|/l&>Admin Cc</&>:
160 </td>
161 <td class="value" colspan="5"><& /Elements/EmailInput, Name => 'AdminCc', Size => undef, Default => $ARGS{AdminCc}, AutocompleteMultiple => 1 &></td>
162 </tr>
163
164 <tr>
165   <td class="label">&nbsp;</td>
166   <td class="comment" colspan="5">
167     <i><font size="-2">
168       <&|/l&>(Sends a carbon-copy of this update to a comma-delimited list of administrative email addresses. These people <strong>will</strong> receive future updates.)</&>
169     </font></i>
170   </td>
171 </tr>
172
173 <& /Elements/EditCustomFields,
174     %ARGS,
175     Object => $ticket,
176     CustomFields => $QueueObj->TicketCustomFields,
177     Grouping => 'People',
178     InTable => 1,
179     KeepValue => 1,
180 &>
181
182 <tr>
183 <td class="label">
184 <&|/l&>Subject</&>:
185 </td>
186 <td class="value" colspan="5">
187 <input type="text" name="Subject" maxsize="200" value="<%$ARGS{Subject} || ''%>" />
188 % $m->callback( %ARGS, CallbackName => 'AfterSubject' );
189 </td>
190 </tr>
191
192 % if ( $gnupg_widget ) {
193 <tr><td>&nbsp;</td><td colspan="5">
194 <& /Elements/Crypt/SignEncryptWidget, self => $gnupg_widget, QueueObj => $QueueObj &>
195 </td></tr>
196 % }
197
198 <tr>
199 <td colspan="6">
200 <&|/l&>Describe the issue below</&>:<br />
201 % if ( RT->Config->Get('ArticleOnTicketCreate')) {
202 <& /Articles/Elements/BeforeMessageBox, %ARGS, QueueObj => $QueueObj &>
203 % }
204 % $m->callback( %ARGS, QueueObj => $QueueObj, CallbackName => 'BeforeMessageBox' );
205 % if (exists $ARGS{Content}) {
206 <& /Elements/MessageBox, Default => $ARGS{Content}, IncludeSignature => 0 &>
207 % } else {
208 <& /Elements/MessageBox, QuoteTransaction => $QuoteTransaction &>
209 %}
210 % $m->callback( %ARGS, QueueObj => $QueueObj, CallbackName => 'AfterMessageBox' );
211
212 <br />
213 </td>
214 </tr>
215
216         <& /Ticket/Elements/AddAttachments, %ARGS, QueueObj => $QueueObj &>
217       </table>
218     </&>
219     <& /Elements/Submit, Label => loc("Create"), id => 'SubmitTicket' &>
220   </div>
221 </div>
222
223 <div id="Ticket-Create-details">
224 <a name="details"></a>
225 <table width="100%" border="0">
226 <tr>
227 <td width="50%" valign="top" class="boxcontainer">
228     <div class="ticket-info-basics">
229           <&| /Widgets/TitleBox, title => loc('The Basics'), 
230                 title_class=> 'inverse',  
231                 color => "#993333" &>
232 <table border="0">
233 <tr><td class="label"><&|/l&>Priority</&>:</td>
234 <td><& /Elements/SelectPriority,
235     Name => "InitialPriority",
236     Default => $ARGS{InitialPriority} ? $ARGS{InitialPriority} : $QueueObj->InitialPriority,
237 &></td></tr>
238 <tr><td class="label"><&|/l&>Final Priority</&>:</td>
239 <td><& /Elements/SelectPriority,
240     Name => "FinalPriority",
241     Default => $ARGS{FinalPriority} ? $ARGS{FinalPriority} : $QueueObj->FinalPriority,
242 &></td></tr>
243 <tr><td class="label"><&|/l&>Time Estimated</&>:</td>
244 <td>
245 <& /Elements/EditTimeValue, Name => 'TimeEstimated', Default => $ARGS{TimeEstimated} || '' &>
246
247 </td></tr>
248 <tr><td class="label"><&|/l&>Time Worked</&>:</td>
249 <td>
250 <& /Elements/EditTimeValue, Name => 'TimeWorked', Default => $ARGS{TimeWorked} || '' &>
251 </td></tr>
252 <tr>
253 <td class="label"><&|/l&>Time Left</&>:</td>
254 <td>
255 <& /Elements/EditTimeValue, Name => 'TimeLeft', Default => $ARGS{TimeLeft} || '' &>
256 </td></tr>
257 </table>
258 </&>
259 <br />
260 <div class="ticket-info-dates">
261 <&|/Widgets/TitleBox, title => loc("Dates"),
262   title_class=> 'inverse',  
263   color => "#663366" &>
264
265 <table>
266 <tr><td class="label"><&|/l&>Starts</&>:</td><td><& /Elements/SelectDate, Name => "Starts", Default => $ARGS{Starts} || '' &></td></tr>
267 <tr><td class="label"><&|/l&>Due</&>:</td><td><& /Elements/SelectDate, Name => "Due", Default => $ARGS{Due} || '' &></td></tr>
268 <& /Elements/EditCustomFields,
269     %ARGS,
270     Object => $ticket,
271     CustomFields => $QueueObj->TicketCustomFields,
272     Grouping => 'Dates',
273     InTable => 1,
274     KeepValue => 1,
275 &>
276 </table>
277 </&>
278 </div>
279 </div>
280 <br />
281 </td>
282
283 <td valign="top" class="boxcontainer">
284 <div class="ticket-info-links">
285 <&| /Widgets/TitleBox, title => loc('Links'), title_class=> 'inverse' &>
286
287 <& /Elements/AddLinks,
288     Object          => $ticket,
289     CustomFields    => $QueueObj->TicketCustomFields,
290     ARGSRef         => \%ARGS,
291     &>
292 </&>
293 </div>
294 <br />
295
296 </td>
297 </tr>
298 </table>
299 <& /Elements/Submit, Label => loc("Create") &>
300 </div>
301 </form>
302
303 <%INIT>
304 $m->callback( CallbackName => "Init", ARGSRef => \%ARGS );
305 my $Queue = $ARGS{Queue};
306 $session{DefaultQueue} = $Queue;
307
308 my $current_user = $session{'CurrentUser'};
309
310 if ($CloneTicket) {
311     my $CloneTicketObj = RT::Ticket->new( $session{CurrentUser} );
312     $CloneTicketObj->Load($CloneTicket)
313         or Abort( loc("Ticket could not be loaded") );
314
315     my $clone = {
316         Requestors => join( ',', $CloneTicketObj->RequestorAddresses ),
317         Cc         => join( ',', $CloneTicketObj->CcAddresses ),
318         AdminCc    => join( ',', $CloneTicketObj->AdminCcAddresses ),
319         InitialPriority => $CloneTicketObj->Priority,
320     };
321
322     $clone->{$_} = $CloneTicketObj->$_()
323         for qw/Owner Subject FinalPriority Status/;
324         # not TimeWorked, TimeEstimated, or TimeLeft
325
326     $clone->{$_} = $CloneTicketObj->$_->AsString
327         for grep { $CloneTicketObj->$_->IsSet }
328         map      { $_ . "Obj" } qw/Starts Started Due Resolved/;
329
330     my $get_link_value = sub {
331         my ($link, $type) = @_;
332         my $uri_method = $type . 'URI';
333         my $local_method = 'Local' . $type;
334         my $uri = $link->$uri_method;
335         return if $uri->IsLocal and
336                 $uri->Object and
337                 $uri->Object->isa('RT::Ticket') and
338                 $uri->Object->__Value('Type') eq 'reminder';
339
340         return $link->$local_method || $uri->URI;
341     };
342     my (@refers, @refers_by);
343     my $refers = $CloneTicketObj->RefersTo;
344     while ( my $refer = $refers->Next ) {
345         my $refer_value = $get_link_value->($refer, 'Target');
346         push @refers, $refer_value if defined $refer_value;
347     }
348     $clone->{'new-RefersTo'} = join ' ', @refers;
349
350     my $refers_by = $CloneTicketObj->ReferredToBy;
351     while ( my $refer_by = $refers_by->Next ) {
352         my $refer_by_value = $get_link_value->($refer_by, 'Base');
353         push @refers_by, $refer_by_value if defined $refer_by_value;
354     }
355     $clone->{'RefersTo-new'} = join ' ', @refers_by;
356
357     my $cfs = $CloneTicketObj->QueueObj->TicketCustomFields();
358     while ( my $cf = $cfs->Next ) {
359         next if $cf->FirstAttribute('NoClone');
360         my $cf_id     = $cf->id;
361         my $cf_values = $CloneTicketObj->CustomFieldValues( $cf->id );
362         my @cf_values;
363         while ( my $cf_value = $cf_values->Next ) {
364             push @cf_values, $cf_value->Content;
365         }
366
367         if ( @cf_values > 1 && $cf->Type eq 'Select' ) {
368             $clone->{GetCustomFieldInputName( CustomField => $cf )} = \@cf_values;
369         }
370         else {
371             $clone->{GetCustomFieldInputName( CustomField => $cf )} = join "\n",
372               @cf_values;
373         }
374     }
375
376     # Pass customer links along (even though cloning of parent links
377     # in general is disabled).
378     my $customers = $CloneTicketObj->Customers;
379     my @customers;
380     while ( my $customer = $customers->Next ) {
381       my ($custnum) = $customer->Target =~ /cust_main\/(\d+)$/;
382       push @customers, $custnum if $custnum;
383     }
384     $clone->{'new-Customer'} = join(' ', @customers);
385
386     $m->callback( CallbackName => 'MassageCloneArgs', ARGSRef => $clone, Queue => $Queue );
387
388     for ( keys %$clone ) {
389         $ARGS{$_} = $clone->{$_} if not defined $ARGS{$_};
390     }
391
392 }
393
394 my @results;
395
396 my $title = loc("Create a new ticket");
397
398 my $QueueObj = RT::Queue->new($current_user);
399 $QueueObj->Load($Queue) || Abort(loc("Queue [_1] could not be loaded.", $Queue||''));
400
401 $m->callback( QueueObj => $QueueObj, title => \$title, results => \@results, ARGSRef => \%ARGS );
402
403 $m->scomp( '/Articles/Elements/SubjectOverride', ARGSRef => \%ARGS, QueueObj => $QueueObj, results => \@results );
404
405 $QueueObj->Disabled && Abort(loc("Cannot create tickets in a disabled queue."));
406
407 my $ticket = RT::Ticket->new($current_user); # empty ticket object
408
409 ProcessAttachments(ARGSRef => \%ARGS);
410
411 my $checks_failure = 0;
412
413 {
414     my ($status, @msg) = $m->comp(
415         '/Elements/ValidateCustomFields',
416         CustomFields    => $QueueObj->TicketCustomFields,
417         ARGSRef         => \%ARGS
418     );
419     unless ($status) {
420         $checks_failure = 1;
421         push @results, @msg;
422     }
423 }
424
425 my $gnupg_widget = $m->comp('/Elements/Crypt/SignEncryptWidget:new', Arguments => \%ARGS );
426 $m->comp( '/Elements/Crypt/SignEncryptWidget:Process',
427     self      => $gnupg_widget,
428     QueueObj  => $QueueObj,
429 );
430
431
432 if ( !exists $ARGS{'AddMoreAttach'} && ($ARGS{'id'}||'') eq 'new' ) {
433     my $status = $m->comp('/Elements/Crypt/SignEncryptWidget:Check',
434         self      => $gnupg_widget,
435         Operation => 'Create',
436         QueueObj  => $QueueObj,
437     );
438     $checks_failure = 1 unless $status;
439 }
440
441 # check email addresses for RT's
442 {
443     foreach my $field ( qw(Requestors Cc AdminCc) ) {
444         my $value = $ARGS{ $field };
445         next unless defined $value && length $value;
446
447         my @emails = Email::Address->parse( $value );
448         foreach my $email ( grep RT::EmailParser->IsRTAddress($_->address), @emails ) {
449             push @results, loc("[_1] is an address RT receives mail at. Adding it as a '[_2]' would create a mail loop", $email->format, loc($field =~ /^(.*?)s?$/) );
450             $checks_failure = 1;
451             $email = undef;
452         }
453         $ARGS{ $field } = join ', ', map $_->format, grep defined, @emails;
454     }
455 }
456
457 my $skip_create = 0;
458 $m->callback( CallbackName => 'BeforeCreate', ARGSRef => \%ARGS, skip_create => \$skip_create, 
459               checks_failure => $checks_failure, results => \@results );
460
461 $m->comp( '/Articles/Elements/CheckSkipCreate', ARGSRef => \%ARGS, skip_create => \$skip_create,
462               checks_failure => $checks_failure, results => \@results );
463
464 if ((!exists $ARGS{'AddMoreAttach'}) and (defined($ARGS{'id'}) and $ARGS{'id'} eq 'new')) { # new ticket?
465     if ( !$checks_failure && !$skip_create ) {
466 # CREATE THE TICKET.
467 # For some reason it's done by a Mason component named "Display.html"
468 # and the call is buried in obscure error-handling stuff.
469 # This comment exists to make it more visually obvious.
470 # ************************************************************
471
472         $m->comp('Display.html', %ARGS);
473
474 # ************************************************************
475 # Execution should not continue here.  Display.html calls 
476 # Redirect() which does an $m->abort.  We only get here if the 
477 # code dies before then, hence "$@".
478         $RT::Logger->crit("After display call; error is $@");
479         $m->abort();
480     }
481 }
482 PageMenu->child( basics => raw_html =>  q[<a href="#basics" onclick="return switchVisibility('Ticket-Create-basics','Ticket-Create-details');">] . loc('Basics') . q[</a>]);
483 PageMenu->child( details => raw_html =>  q[<a href="#details" onclick="return switchVisibility('Ticket-Create-details','Ticket-Create-basics');">] . loc('Details') . q[</a>]);
484 </%INIT>
485
486 <%ARGS>
487 $DependsOn => undef
488 $DependedOnBy => undef
489 $MemberOf => undef
490 $QuoteTransaction => undef
491 $CloneTicket => undef
492 </%ARGS>