%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2026 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 }}}
<%INIT>
if (!$Collection) {
    $Collection = $Class->new( $session{'CurrentUser'} );
    if ( $Class eq 'RT::Transactions' ) {
        $Query = PreprocessTransactionSearchQuery( Query => $Query, ObjectType => $ObjectType );
    }
    $Collection->FromSQL($Query);
}

# flip HasResults from undef to 0 to indicate there was a search, so
# dashboard mail can be suppressed if there are no results
$$HasResults = 0 if $HasResults && !defined($$HasResults);

# XXX: ->{'order_by'} is hacky, but there is no way to check if
# collection is ordered or not
if ( @OrderBy && ($AllowSorting || $PreferOrderBy || !$Collection->{'order_by'}) ) {
    if ( $OrderBy[0] =~ /\|/ || ( $Order[0] // '' ) =~ /\|/ ) {
        @OrderBy = split /\|/, $OrderBy[0];
        @Order = split /\|/,$Order[0];
    }
    @OrderBy = grep length, @OrderBy;
    $Collection->OrderByCols(
        map { { FIELD => $OrderBy[$_], ORDER => $Order[$_] } }
        ( 0 .. $#OrderBy )
    );
}

$Collection->RowsPerPage( $Rows ) if $Rows;
$Page = 1 unless $Page && $Page =~ /\A\d+\z/ && $Page > 1;    # workaround problems with $Page = '' or undef
$Collection->GotoPage( $Page - 1 ); # SB uses page 0 as the first page

# $Collection might not have CombineSearchAndCount method, like dashboards and saved searches.
# Don't combine search and count for unpaginated searches, as we will progressively generate the result.
$Collection->CombineSearchAndCount(1) if $Rows && $Collection->can('CombineSearchAndCount');
$TotalFound = $Collection->CountAll() unless defined $TotalFound;
return '' if !$TotalFound && !$ShowEmpty;

# Adjust page after CountAll, otherwise $TotalFound could be not defined.
# On the other hand, CountAll needs page info if it's a combined search,
# thus we have to set page first and then adjust it here.
if ( $Rows ) {
    if ( $TotalFound <= $Rows ) {
        $Page = 1;
    }
    else {
        my $MaxPage = int( $TotalFound / $Rows ) + ( $TotalFound % $Rows ? 1 : 0 );
        $Page = $MaxPage if $Page > $MaxPage;
    }
}
$Collection->GotoPage( $Page - 1 );

# DisplayFormat lets us use a "temporary" format for display, while 
# still using our original format for next/prev page links.
# bulk update uses this feature to add checkboxes

$DisplayFormat ||= $Format;

# Scrub the html of the format string to remove any potential nasties.
$Format = $m->comp('/Elements/ScrubHTML', Content => $Format, SkipStructureCheck => 1, Restrictive => 1);
$DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $DisplayFormat, SkipStructureCheck => 1, Restrictive => 1);

my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat);

# Find the maximum number of items in any row, so we can pad the table.
my ($maxitems, $item) = (0, 0);
foreach my $col (@Format) {
    $item++;
    if ( $col->{title} && ($col->{title} eq 'NEWLINE') ) {
        $item = 0;
    }
    else {
        $maxitems = $item if $item > $maxitems;
    }
}

$Class ||= $Collection->ColumnMapClassName;

$InlineEdit = 0
    unless $session{CurrentUser}->Privileged && ( $Collection->isa('RT::Tickets') || $Collection->isa('RT::Assets') );

$m->out('<div class="table-responsive">');
$m->out('<table cellspacing="0"');
$m->out(' class="table ' . RT->Config->Get('TableAccent') . ' ' . ($Collection->isa('RT::Tickets') ? 'ticket-list' : 'collection') . ($InlineEdit ? ' inline-edit' : '') . ' collection-as-table"');
$m->out(' data-display-format="' . $m->interp->apply_escapes($DisplayFormat, 'h') . '"');
$m->out(' data-max-items="' . $maxitems . '"');
$m->out(' data-class="' . $Collection->RecordClass . '"');
$m->out('>');

if ( $ShowCols ) {
    $m->out('<colgroup>');
    $m->out('<col>') for 1 .. $maxitems;
    $m->out('</colgroup>');
}

if ( $ShowHeader ) {
    $m->comp('/Elements/CollectionAsTable/Header',
        %ARGS,
        Class        => $Class,
        Format       => \@Format,
        FormatString => $Format,
        Order        => \@Order,
        OrderBy      => \@OrderBy,
        Rows         => $Rows,
        Page         => $Page,
        AllowSorting => $AllowSorting,
        BaseURL      => $BaseURL,
        GenericQueryArgs => $GenericQueryArgs,
        maxitems     => $maxitems,
        PassArguments => \@PassArguments,
    );
}

my ($i, $column_map) = (0, {});
$m->out('<tbody>');
if ( !$Rows ) {
    $Collection->RowsPerPage(100);
}

while ( my $record = $Rows ? $Collection->Next : ( $Collection->Next || ( $Collection->NextPage && $Collection->Next ) ) ) {
    # Every ten rows, flush the buffer and put something on the page.
    $m->flush_buffer unless ++$i % 10;

    my $warning = 0;
    my $Classes = '';

    $m->callback(
        CallbackName => 'EachRow',
        Record       => $record,
        Warning      => \$warning,
        Classes      => \$Classes,
        Format       => \@Format,
    );

    $m->comp('/Elements/CollectionAsTable/Row',
        i          => $i,
        Format     => \@Format,
        record     => $record,
        maxitems   => $maxitems,
        ColumnMap  => $column_map,
        Class      => $Class,
        Warning    => $warning,
        Classes    => $Classes,
        InlineEdit => $InlineEdit,
    );

    $$HasResults++ if $HasResults;
}
$m->out('</tbody>');
$m->out('</table>');
$m->out('</div>');
if ( $Rows && $ShowNavigation && $TotalFound > $Rows ) {
  my $oddRows = ($TotalFound && $TotalFound % $Rows == 0 )? 0 : 1;
  my $pages = int( $TotalFound / $Rows ) + $oddRows;
  $pages = 1 if $pages < 1;


  my %query_args = map { $_ => $ARGS{$_} } @PassArguments;
  $m->comp(
        '/Elements/CollectionListPaging',
        BaseURL     => $BaseURL,
        Rows        => $Rows,
        TotalFound  => $TotalFound,
        CurrentPage => $Page,
        Pages       => $pages,
        PageParam   => $PageParam,
        URLParams   => \%query_args,
        ShowJumpToPage => $ShowJumpToPage,
        UseHtmx     => $UseHtmx,
        HtmxTarget  => $HtmxTarget,
        $HtmxSwap ? (HtmxSwap => $HtmxSwap) : (),
        $HtmxSelect ? (HtmxSelect => $HtmxSelect) : (),
    );

}

# Restore unpaginated state so the internal RowsPerPage=100 we set above
# doesn't leak into later renders.
if ( !$Rows ) {
    $Collection->RowsPerPage(0);
}

</%INIT>
<%ARGS>
$Class         => ''
$Collection    => undef
$TotalFound    => undef
$Format        => undef
$DisplayFormat => undef
@Order         => ()
@OrderBy       => ()
$GenericQueryArgs => undef
$Rows          => $Collection ? $Collection->RowsPerPage : undef
$Page          => 1
$PageParam     => 'Page'
$Title         => loc('Ticket Search')
$BaseURL       => RT->Config->Get('WebPath') . $m->request_comp->path .'?'
@PassArguments => qw( Query Format Rows Page Order OrderBy Class ObjectType )

$AllowSorting   => 0  # Make headers in table links that will resort results
$PreferOrderBy  => 0  # Prefer the passed-in @OrderBy to the collection default
$ShowNavigation => 1
$ShowHeader     => 1
$ShowCols       => 1
$ShowEmpty      => 0
$InlineEdit     => RT->Config->Get('InlineEdit', $session{CurrentUser})
$Query => 0
$HasResults     => undef
$ObjectType     => $Class eq 'RT::Transactions' ? 'RT::Ticket' : ''
$ShowJumpToPage => 0
$UseHtmx        => 0
$HtmxTarget     => undef
$HtmxSwap       => undef
$HtmxSelect     => undef
</%ARGS>
