%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2023 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 }}}
<%ARGS>
$Class        => 'RT::Tickets'

@Format       => undef
$FormatString => undef
@OrderBy      => ()
@Order        => ()
$Query        => undef
$Rows         => undef
$Page         => undef
$GenericQueryArgs => undef
$maxitems     => undef

$AllowSorting  => undef
$AllowFiltering => undef
$BaseURL       => undef
@PassArguments => qw(Query Format Rows Page Order OrderBy)
</%ARGS>
<tr class="collection-as-table">
<%PERL>

my $generic_query_args = $GenericQueryArgs || {map { $_ => $ARGS{$_} } @PassArguments};
# backward compatibility workaround
$generic_query_args->{'Format'} = $FormatString if grep $_ eq 'Format', @PassArguments;

my $column_map_class;
if ( $Class =~ /::/ ) {
    $column_map_class = $Class->ColumnMapClassName;
}
else {
    # For back compatibility
    $column_map_class = $Class;
}

my $item = 0;


# "Requestor" has alias "Requestors", this is to handle the case where
# OrderBy is "Requestor.Name" and result Format is "Requestors"
my %field_alias;
if ( $Class->can('RecordClass') && $Class->RecordClass->DOES("RT::Record::Role::Roles") ) {
    for my $role ( $Class->RecordClass->Roles( UserDefined => 0 ) ) {
        my $attrs = $Class->RecordClass->Role($role);
        $field_alias{$role} = $role . 's' unless $attrs->{Single};
    }
}

my %order_by;
for my $i ( 0 .. $#OrderBy ) {
    my $field = $OrderBy[$i];
    $order_by{$field}{index} = $i;
    $order_by{$field}{order} = $Order[$i] || 'ASC';
    $order_by{$field}{class} = $order_by{ $OrderBy[$i] }{order} =~ /ASC/i ? 'fa-sort-up' : 'fa-sort-down';

    if ( $field =~ m!^(\w+)\.! && $field_alias{$1} ) {
        my $alias = $field;
        $alias =~ s!^(\w+)\.!$field_alias{$1}.!;
        $order_by{$alias} = $order_by{$field};
    }
}

foreach my $col ( @Format ) {
    my $attr = $col->{'attribute'} || $col->{'last_attribute'};

    my $title = $col->{'title'} || '';
    if ( $title eq 'NEWLINE' ) {
        while ( $item < $maxitems ) {
            $m->out(qq{<th class="collection-as-table">&nbsp;</th>\n});
            $item++;
        }

        $item = 0;
        $m->out(qq{</tr>\n<tr class="collection-as-table">});
        next;
    } elsif ( $title eq 'NBSP' ) {
        $item++;
        $m->out(qq{<th class="collection-as-table">&nbsp;</th>\n});
        next;
    }

    my $span = $col->{'span'};
    $item += ($span || 1);

    my $class = 'collection-as-table';
    $class .= ' allow-sorting' if $AllowSorting;
    $class .= ' allow-filtering' if $AllowFiltering;

    $m->out(qq{<th class="$class"});
    $m->out(' colspan="' . $m->interp->apply_escapes($span  => 'h') . '"')
        if $span;

    my $align = $col->{'align'} || do {
        my $tmp_columnmap = $m->comp( '/Elements/ColumnMap',
            Class => $column_map_class,
            Name => $attr,
            Attr => 'align',
        );
        ProcessColumnMapValue( $tmp_columnmap, Arguments => [ $attr ] );
    };
    $m->out(qq{ style="text-align: $align"}) if $align;
    $m->out('>');

    my $loc_title;
    # if title is not defined then use defined attribute or last
    # one we saw in the format
    unless ( defined $col->{'title'} ) {
        my $tmp = $m->comp( '/Elements/ColumnMap',
            Class => $column_map_class,
            Name  => $attr,
            Attr  => 'title',
        );
        $title = ProcessColumnMapValue( $tmp, Arguments => [ $attr ] );

        # in case title is not defined in ColumnMap 
        # the following regex changes $attr like from "ReferredToBy" to "Referred To By"
        $title = join ' ', split /(?<=[a-z])(?=[A-Z])/, $attr unless defined $title;
        $loc_title = $attr =~ /^(?:CustomField|CF)\./ ? $title : loc($title);
    } else {
        $loc_title = loc($m->comp('/Elements/ScrubHTML', Content => $title));
    }

    my $attribute;
    if ( $col->{'attribute'} ) {
        $attribute = $m->comp(
            "/Elements/ColumnMap",
            Class => $column_map_class,
            Name  => $col->{'attribute'},
            Attr  => 'attribute'
        );
    }

    if ( $AllowSorting and $attribute ) {
        my $attr = ProcessColumnMapValue( $attribute, Arguments => [ $col->{'attribute'} ], Escape => 0 );


        my @new_order_by = @OrderBy;
        my @new_order = @Order;

        # ASC -> DESC -> UNSET -> ASC, etc.
        if ( $order_by{$attr} ) {
             splice @new_order_by, $order_by{$attr}{index}, 1;
             splice @new_order,   $order_by{$attr}{index}, 1;

             if ( $order_by{$attr}{order} eq 'ASC' ) {
                 unshift @new_order_by, $attr;
                 unshift @new_order,   'DESC';
             }
        }
        else {
            unshift @new_order_by, $attr;
            unshift @new_order, 'ASC';
        }

        $m->out(
            '<span class="title"><a href="' . $m->interp->apply_escapes($BaseURL
            . $m->comp( '/Elements/QueryString',
                ShortenSearchQuery(%$generic_query_args),
                OrderBy => join( '|', @new_order_by ), Order => join( '|', @new_order ),
            ), 'h')
            . '">'. $loc_title .'</a></span>'
        );

        if ( $order_by{$attr} ) {
            $m->out( qq!&nbsp;<span class="fas $order_by{$attr}{class}"></span>! );
        }
        else {
            $m->out( qq!&nbsp;<span class="fas fa-sort"></span>! );
        }
    }
    else {
        $m->out( qq{<span class="title">$loc_title</span>} );
    }

    if ( $AllowFiltering && $Class eq 'RT::Tickets' ) {
        my $attr = ProcessColumnMapValue( $attribute, Arguments => [ $col->{'attribute'} ], Escape => 1 );
        my $field;
        my %supported = map { $_ => 1 }
            qw/id Subject Status Queue Owner Type Creator LastUpdatedBy SLA InitialPriority FinalPriority Priority TimeLeft TimeWorked TimeEstimated Created LastUpdated Told Starts Started Due Resolved Requestors Requestor Cc AdminCc/;

        if ( ( $attr || '' ) =~ /^(\w+)/ && $supported{$1} ) {
            $field = $1;
        }
        elsif ( ( $attr || '' ) =~ /^(CustomRole\.\{.+?\})/ ) {
            $field = $1;
        }
        elsif ( ( $attr || '' ) =~ /^(?:CF|CustomField(?:View)?)\.(?:(\w+)|\{(.+?)\})/ ) {
            my $name = $1 || $2;
            $field = "CustomField.{$name}";
        }

        if ( $field ) {
            my $tooltip = loc( 'Filter on [_1]', loc($field) );
            my $icon = exists $filter_data{filter}{$field} ? 'funnel-fill' : 'funnel';
            $icon = RT::Interface::Web::MenuBuilder::GetSVGImage($icon);
            $m->out(
                qq{&nbsp;<a href="javascript:void(0)" class="btn btn-primary button search-filter" data-toggle="tooltip" data-placement="bottom" data-original-title="$tooltip">$icon</a>}
            );
            $m->out( $m->scomp( '/Elements/SearchFilter', Attribute => $field, FilterData => \%filter_data, %ARGS ) );
        }
    }
    $m->out('</th>');
}
</%PERL>
</tr>

<%INIT>

my %filter_data;
if ( $AllowFiltering && $Class eq 'RT::Tickets' && $ARGS{Query} && $ARGS{BaseQuery} ) {

    my $tickets = RT::Tickets->new( $session{CurrentUser} );
    my ($ok) = $tickets->FromSQL( $ARGS{Query} );
    return unless $ok && ( $ARGS{BaseQuery} || $tickets->Count );

    my @queues;

    my $tree = RT::Interface::Web::QueryBuilder::Tree->new;
    $tree->ParseSQL( Query => $ARGS{BaseQuery} || $ARGS{Query}, CurrentUser => $session{'CurrentUser'} );
    my $referenced_queues = $tree->GetReferencedQueues;
    for my $name_or_id ( keys %$referenced_queues ) {
        my $queue = RT::Queue->new( $session{CurrentUser} );
        $queue->Load($name_or_id);
        if ( $queue->id ) {
            push @queues, $queue;
        }
    }

    my %status;
    my @lifecycles;

    if (@queues) {
        my %lifecycle;
        for my $queue (@queues) {
            next if $lifecycle{ $queue->Lifecycle }++;
            push @lifecycles, $queue->LifecycleObj;
        }
    }
    else {
        @lifecycles = map { RT::Lifecycle->Load( Type => 'ticket', Name => $_ ) } RT::Lifecycle->List('ticket');
    }

    for my $lifecycle (@lifecycles) {
        $status{$_} = 1 for $lifecycle->Valid;
    }
    delete $status{deleted};

    if ( !@queues ) {
        my $queues = RT::Queues->new( $session{CurrentUser} );
        $queues->UnLimit;

        while ( my $queue = $queues->Next ) {
            push @queues, $queue;
            last if @queues == 100;    # TODO make a config for it
        }
    }

    my %filter;

    if ( $ARGS{BaseQuery} && $ARGS{BaseQuery} ne $ARGS{Query} ) {
        my $query = $ARGS{Query};
        $query =~ s!^\s*\(?\s*\Q$ARGS{BaseQuery}\E\s*\)? AND !!;
        my $tree = RT::Interface::Web::QueryBuilder::Tree->new;
        $tree->ParseSQL( Query => $query, CurrentUser => $session{'CurrentUser'} );
        $tree->traverse(
            sub {
                my $node = shift;

                return if $node->isRoot;
                return unless $node->isLeaf;

                my $clause = $node->getNodeValue();
                if ( $clause->{Key} =~ /^Queue/ ) {
                    my $queue = RT::Queue->new( $session{CurrentUser} );
                    $queue->Load( $clause->{Value} );
                    if ( $queue->id ) {
                        $filter{ $clause->{Key} }{ $queue->id } = 1;
                    }
                }
                elsif ( $clause->{Key} =~ /^(?:Status|SLA|Type)/ ) {
                    $filter{ $clause->{Key} }{ $clause->{Value} } = 1;
                }
                elsif ( $clause->{Key}
                    =~ /^(?:(?:Initial|Final)?Priority|Time(?:Worked|Estimated|Left)|id|Told|Starts|Started|Due|Resolved|Created|LastUpdated\b)/
                    )
                {
                    $filter{ $clause->{Key} }{ $clause->{Op} } = $clause->{Value};
                }
                else {
                    my $value = $clause->{Value};
                    $value =~ s!\\([\\"])!$1!g;
                    my $key = $clause->{Key};
                    my $cf;
                    if ( $key eq 'CustomField' ) {
                        $key .= ".$clause->{Subkey}";
                        my ($cf_name) = $clause->{Subkey} =~ /{(.+)}/;
                        $cf = RT::CustomField->new( RT->SystemUser );
                        $cf->Load($cf_name);
                    }
                    elsif ( $key eq 'CustomRole' ) {
                        $key .= ".$1" if $clause->{Subkey} =~ /(\{.+?\})/;
                    }
                    if ( $cf && $cf->id && $cf->Type eq 'Select' ) {
                        $filter{$key}{$value} = 1;
                    }
                    else {
                        $filter{$key} = $value;
                    }
                }
            }
        );
    }
    %filter_data = ( status => \%status, queues => \@queues, filter => \%filter );
}
</%INIT>
