%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2018 Best Practical Solutions, LLC
%#                                          <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%#
%# LICENSE:
%#
%# This work is made available to you under the terms of Version 2 of
%# the GNU General Public License. A copy of that license should have
%# been provided with this software, but in any event can be snarfed
%# from www.gnu.org.
%#
%# This work is distributed in the hope that it will be useful, but
%# WITHOUT ANY WARRANTY; without even the implied warranty of
%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%# General Public License for more details.
%#
%# You should have received a copy of the GNU General Public License
%# along with this program; if not, write to the Free Software
%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
%# 02110-1301 or visit their web page on the internet at
%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
%#
%#
%# CONTRIBUTION SUBMISSION POLICY:
%#
%# (The following paragraph is not intended to limit the rights granted
%# to you to modify and distribute this software under the terms of
%# the GNU General Public License and is only of importance to you if
%# you choose to contribute your changes and enhancements to the
%# community by submitting them to Best Practical Solutions, LLC.)
%#
%# By intentionally submitting any modifications, corrections or
%# derivatives to this work, or any other work intended for use with
%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
%# you are the copyright holder for those contributions and you grant
%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
%# royalty-free, perpetual, license to use, copy, create derivative
%# works based on those contributions, and sublicense and distribute
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
<& /Elements/Header, Title => $title &>
<& /Elements/Tabs &>

<& /Elements/ListActions, actions => \@results &>
<form method="post" action="<% RT->Config->Get('WebPath') %>/Search/Bulk.html" enctype="multipart/form-data" name="BulkUpdate" id="BulkUpdate">
% foreach my $var (qw(Query Format OrderBy Order Rows Page SavedSearchId SavedChartSearchId Token)) {
<input type="hidden" class="hidden" name="<%$var%>" value="<%$ARGS{$var} || ''%>" />
%}
<& /Elements/CollectionList, 
    Query => $Query,
    DisplayFormat => $DisplayFormat,
    Format => $Format,
    Verbatim => 1,
    AllowSorting => 1,
    OrderBy => $OrderBy,
    Order => $Order,
    Rows => $Rows,
    Page => $Page,
    BaseURL => RT->Config->Get('WebPath')."/Search/Bulk.html?",
    Class => 'RT::Tickets'
   &>

% $m->callback(CallbackName => 'AfterTicketList', ARGSRef => \%ARGS);

<hr />

<& /Elements/Submit, Label => loc('Update'), CheckboxNameRegex => '/^UpdateTicket(All)?$/', CheckAll => 1, ClearAll => 1 &>
<br />
<&|/Widgets/TitleBox, title => $title &>
<table>
<tr>
<td valign="top">
<table>
<tr><td class="label"> <&|/l&>Make Owner</&>: </td>
<td class="value"> <& /Elements/SelectOwner, Name => "Owner", Default => $ARGS{Owner} || '' &>
<label>(<input type="checkbox" class="checkbox" name="ForceOwnerChange"
<% $ARGS{ForceOwnerChange} ? 'checked="checked"' : '' %> /> <&|/l&>Force change</&>)</label></td></tr>
<tr><td class="label"> <&|/l&>Add Requestor</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "AddRequestor", Size=> 20, Default => $ARGS{AddRequestor} &> </td></tr>
<tr><td class="label"> <&|/l&>Remove Requestor</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "DeleteRequestor", Size=> 20, Default => $ARGS{DeleteRequestor} &> </td></tr>
<tr><td class="label"> <&|/l&>Add Cc</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "AddCc", Size=> 20, Default => $ARGS{AddCc} &> </td></tr>
<tr><td class="label"> <&|/l&>Remove Cc</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "DeleteCc", Size=> 20, Default => $ARGS{DeleteCc} &> </td></tr>
<tr><td class="label"> <&|/l&>Add AdminCc</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "AddAdminCc", Size=> 20, Default => $ARGS{AddAdminCc} &> </td></tr>
<tr><td class="label"> <&|/l&>Remove AdminCc</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "DeleteAdminCc", Size=> 20, Default => $ARGS{DeleteAdminCc} &> </td></tr>

% my $single_roles = RT::CustomRoles->new($session{CurrentUser});
% $single_roles->LimitToSingleValue;
% $single_roles->LimitToObjectId($_) for keys %$seen_queues;
% while (my $role = $single_roles->Next) {
<tr>
<td class="label"> <&|/l, $role->Name &>Make [_1]</&>: </td>
<td class="value"><& /Elements/SingleUserRoleInput, role => $role, ShowPlaceholder => 0, ShowEntryHint => 0, Size => 20, Default => $ARGS{"RT::CustomRole-" . $role->Id} &></td>
</tr>
% }

% my $multi_roles = RT::CustomRoles->new($session{CurrentUser});
% $multi_roles->LimitToMultipleValue;
% $multi_roles->LimitToObjectId($_) for keys %$seen_queues;
% while (my $role = $multi_roles->Next) {
<tr>
<td class="label"> <&|/l, $role->Name &>Add [_1]</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "AddRT::CustomRole-" . $role->Id, Size=> 20, Default => $ARGS{"AddRT::CustomRole-" . $role->Id} &> </td>
</tr>
<tr>
<td class="label"> <&|/l, $role->Name &>Remove [_1]</&>: </td>
<td class="value"> <& /Elements/EmailInput, Name => "DeleteRT::CustomRole-" . $role->Id, Size=> 20, Default => $ARGS{"DeleteRT::CustomRole-" . $role->Id} &> </td>
</tr>
% }

</table>
</td>
<td valign="top">
<table>
<tr><td class="label"> <&|/l&>Make subject</&>: </td>
<td class="value"> <input name="Subject" size="20" value="<% $ARGS{Subject} || '' %>"/> </td></tr>
<tr><td class="label"> <&|/l&>Make priority</&>: </td>
<td class="value"> <& /Elements/SelectPriority, Name => "Priority", Default => $ARGS{Priority} &> </td></tr>
<tr><td class="label"> <&|/l&>Make queue</&>: </td>
<td class="value"> <& /Elements/SelectQueue, Name => "Queue", Default => $ARGS{Queue} &> </td></tr>
<tr><td class="label"> <&|/l&>Make Status</&>: </td>
<td class="value"> <& /Ticket/Elements/SelectStatus, Name => "Status", Default => $ARGS{Status}, Queues => $seen_queues &> </td></tr>
% if ($hasSLA) {
<tr><td class="label"> <&|/l&>Make SLA</&>: </td>
<td class="value"> <& /Elements/SelectSLA, Name => "SLA", Default => $ARGS{SLA} &> </td></tr>
% }
<tr><td class="label"> <&|/l&>Make date Starts</&>: </td>
<td class="value"> <& /Elements/SelectDate, Name => "Starts_Date", Default => $ARGS{Starts_Date} || '' &> </td></tr>
<tr><td class="label"> <&|/l&>Make date Started</&>: </td>
<td class="value"> <& /Elements/SelectDate, Name => "Started_Date", Default => $ARGS{Started_Date} || '' &> </td></tr>
<tr><td class="label"> <&|/l&>Make date Told</&>: </td>
<td class="value"> <& /Elements/SelectDate, Name => "Told_Date", Default => $ARGS{Told_Date} || '' &> </td></tr>
<tr><td class="label"> <&|/l&>Make date Due</&>: </td>
<td class="value"> <& /Elements/SelectDate, Name => "Due_Date", Default => $ARGS{Due_Date} || '' &> </td></tr>
</table>

</td>
</tr>
</table>
</&>
<&| /Widgets/TitleBox, title => loc('Add comments or replies to selected tickets') &>
<table>
<tr><td align="right"><&|/l&>Update Type</&>:</td>
<td><select name="UpdateType" id="UpdateType">
  <option value="private" <% $ARGS{UpdateType} && $ARGS{UpdateType} eq 'private' ? 'selected="selected"' : '' %> ><&|/l&>Comments (Not sent to requestors)</&></option>
<option value="response" <% $ARGS{UpdateType} && $ARGS{UpdateType} eq 'response' ? 'selected="selected"' : '' %>><&|/l&>Reply to requestors</&></option>
</select> 
</td></tr>
<tr>
    <td align="right"><&|/l&>Subject</&>:</td>
    <td>
        <input name="UpdateSubject" size="60" value="<% $ARGS{UpdateSubject} || "" %>" />
% $m->callback( %ARGS, CallbackName => 'AfterUpdateSubject' );
    </td>
</tr>
% $m->callback( CallbackName => 'BeforeTransactionCustomFields', CustomFields => $TxnCFs );
% while (my $CF = $TxnCFs->Next()) {
<tr>
<td align="right"><% $CF->Name %>:</td>
<td><& /Elements/EditCustomField,
    CustomField => $CF,
    Object => RT::Transaction->new( $session{'CurrentUser'} ),
    &><em><% $CF->EntryHint // '' %></em></td>
</td></tr>
% } # end if while

 <tr><td class="labeltop"><&|/l&>Message</&>:</td>
 <td class="messagebox-container action-<% $ARGS{UpdateType} || 'private' %>">
% $m->callback( %ARGS, CallbackName => 'BeforeMessageBox' );
%# Currently, bulk update always starts with Comment not Reply selected, so we check this unconditionally
% my $IncludeSignature = RT->Config->Get('MessageBoxIncludeSignatureOnComment');
<& /Elements/MessageBox, Name => "UpdateContent", 
    $ARGS{UpdateContent} ? ( Default => $ARGS{UpdateContent}, IncludeSignature => 0 ) :
                        ( IncludeSignature => $IncludeSignature ),
        &>
 </td></tr>

<& /Ticket/Elements/AddAttachments, %ARGS &>

 </table>

</&>

<%perl>
my $cfs = RT::CustomFields->new($session{'CurrentUser'});
$cfs->LimitToGlobal();
$cfs->LimitToQueue($_) for keys %$seen_queues;
$cfs->SetContextObject( values %$seen_queues ) if keys %$seen_queues == 1;
</%perl>

% if ( $cfs->Count ) {
<&|/Widgets/TitleBox, title => loc('Edit Custom Fields') &>
<& /Elements/BulkCustomFields, $ARGS{'AddMoreAttach'} ? %ARGS : (), CustomFields => $cfs &>
</&>
% }

<&|/Widgets/TitleBox, title => loc('Edit Links'), color => "#336633"&>
<em><&|/l&>Enter tickets or URIs to link tickets to. Separate multiple entries with spaces.</&></em><br />
<& /Elements/BulkLinks, Collection => $Tickets, $ARGS{'AddMoreAttach'} ? %ARGS : () &>
</&>

<&| /Widgets/TitleBox, title => loc('Merge'), color => '#336633' &>
<& /Ticket/Elements/EditMerge, Tickets => $Tickets, %ARGS &>
</&>

<& /Elements/Submit, Label => loc('Update') &>


</form>

%# Keyboard shortcuts info
<div class="clear"></div>
<div class="keyboard-shortcuts footer">
    <p><&|/l_unsafe, '<span class="keyboard-shortcuts-key">?</span>' &>Press [_1] to view keyboard shortcuts.</&></p>
</div>

<%INIT>
my (@results);

$m->callback(CallbackName => 'Initial', ARGSRef => \%ARGS, results_ref => \@results, QueryRef => \$Query, UpdateTicketRef => \@UpdateTicket);

unless ( defined $Rows ) {
    $Rows = $RowsPerPage;
    $ARGS{Rows} = $RowsPerPage;
}
my $title = loc("Update multiple tickets");

# Iterate through the ARGS hash and remove anything with a null value.
map ( $ARGS{$_} =~ /^$/ && ( delete $ARGS{$_} ), keys %ARGS );

ProcessAttachments(ARGSRef => \%ARGS);

$Page ||= 1;

$Format ||= RT->Config->Get('DefaultSearchResultFormat');

my $DisplayFormat = "'__CheckBox.{UpdateTicket}__',". $Format;
$DisplayFormat =~ s/\s*,\s*('?__NEWLINE__'?)/,$1,''/gi;

$DECODED_ARGS->{'UpdateTicketAll'} = 1 unless @UpdateTicket;

my $Tickets = RT::Tickets->new( $session{'CurrentUser'} );
$Tickets->FromSQL($Query);
if ( $OrderBy =~ /\|/ ) {

  # Multiple Sorts
  my @OrderBy = split /\|/, $OrderBy;
  my @Order   = split /\|/, $Order;
  $Tickets->OrderByCols(
    map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } }
      ( 0 .. $#OrderBy ) );
}
else {
  $Tickets->OrderBy( FIELD => $OrderBy, ORDER => $Order );
}

$Tickets->RowsPerPage($Rows) if ($Rows);
$Tickets->GotoPage( $Page - 1 );    # SB uses page 0 as the first page

Abort( loc("No search to operate on.") ) unless ($Tickets);

# build up a list of all custom fields for tickets that we're displaying, so
# we can display sane edit widgets.

my $fields      = {};
my $seen_queues = {};
while ( my $ticket = $Tickets->Next ) {
    next if $seen_queues->{ $ticket->Queue };
    $seen_queues->{ $ticket->Queue } ||= $ticket->QueueObj;

    my $custom_fields = $ticket->CustomFields;
    while ( my $field = $custom_fields->Next ) {
        $fields->{ $field->id } = $field;
    }
}

# determine if any of the queues seen has a SLA active
my $queue = RT::Queue->new($session{CurrentUser});
my $hasSLA = 0;
for (keys %$seen_queues) {
    $queue->Load($_);
    $hasSLA ||= ! $queue->SLADisabled();
}

#Iterate through each ticket we've been handed
my @linkresults;

$Tickets->RedoSearch();

unless ( $ARGS{'AddMoreAttach'} ) {
    $RT::Handle->BeginTransaction;
    while ( my $Ticket = $Tickets->Next ) {
        my $tid = $Ticket->id;
        next unless grep $tid == $_, @UpdateTicket;

        $Ticket->LockForUpdate;

        #Update the links
        $ARGS{'id'} = $Ticket->id;

        my @updateresults = ProcessUpdateMessage(
            TicketObj       => $Ticket,
            ARGSRef         => \%ARGS,
            KeepAttachments => 1,
        );

        #Update the basics.
        my @basicresults =
          ProcessTicketBasics( TicketObj => $Ticket, ARGSRef => \%ARGS );
        my @dateresults =
          ProcessTicketDates( TicketObj => $Ticket, ARGSRef => \%ARGS );

        #Update the watchers
        my @watchresults =
          ProcessTicketWatchers( TicketObj => $Ticket, ARGSRef => \%ARGS );

        @linkresults =
            ProcessTicketLinks( TicketObj => $Ticket, TicketId => 'Ticket', ARGSRef => \%ARGS );

        my @cfresults = ProcessRecordBulkCustomFields( RecordObj => $Ticket, ARGSRef => \%ARGS );

        my @tempresults = (
            grep { defined }
            @watchresults,  @basicresults, @dateresults,
            @updateresults, @linkresults,  @cfresults
        );

        @tempresults =
          map { loc( "Ticket [_1]: [_2]", $Ticket->Id, $_ ) } @tempresults;

        @results = ( @results, @tempresults );
    }
    $RT::Handle->Commit;

    delete $session{'Attachments'}{ $ARGS{'Token'} };

    $Tickets->RedoSearch();
}

my $TxnCFs = RT::CustomFields->new( $session{CurrentUser} );
$TxnCFs->LimitToLookupType( RT::Transaction->CustomFieldLookupType );
$TxnCFs->LimitToGlobalOrObjectId( keys %$seen_queues );
$TxnCFs->SetContextObject( values %$seen_queues ) if keys %$seen_queues == 1;

</%INIT>
<%args>
$Format => undef
$Page => 1
$Rows => undef
$RowsPerPage => undef
$Order => 'ASC'
$OrderBy => 'id'
$Query => undef
@UpdateTicket => ()
</%args>
