%# 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 }}}
<div class="calendar-wrapper p-2 mx-auto">
% if ( @Dates ) {
  <div class="row">
    <div class="calendar-content col">
      <table class="table">
        <tr>
          <td align="left">
% my ($PMonth, $PYear) = ($Month - 1, $Year);
% if ($PMonth < 0) {
%    $PYear--;
%    $PMonth = 11;
% }

%         if ( $BaseURL ) {
            <a href="<% $BaseURL %>&Month=<% $PMonth %>&Year=<% $PYear %><% $CalendarStatusFilterParam ? "&CalendarStatusFilter=$CalendarStatusFilterParamURI" : '' %>">&laquo; <%$rtdate->GetMonth($PMonth)%></a>
%         } else {
            <a onclick="reloadElement(this.closest('[hx-get]'), {'hx-vals': '<% JSON({ Month => $PMonth, Year => $PYear, SearchDisplayMode => 'Calendar', ( $CalendarStatusFilterParam ? (CalendarStatusFilter => $CalendarStatusFilterParam) : () ) }) %>'}); return false;" href="#">&laquo; <%$rtdate->GetMonth($PMonth)%></a>
%         }
          </td>
          <th class="h5 text-center">
            <% $rtdate->GetMonth($Month). " $Year" %>
          </th>
          <td align="right">
% my ($NMonth, $NYear) = ($Month + 1, $Year);
% if ($NMonth > 11) {
%    $NYear++;
%    $NMonth = 0;
% }

%         if ( $BaseURL ) {
            <a href="<% $BaseURL %>&Month=<% $NMonth %>&Year=<% $NYear %><% $CalendarStatusFilterParam ? "&CalendarStatusFilter=$CalendarStatusFilterParamURI" : '' %>"><%$rtdate->GetMonth($NMonth)%> &raquo;</a>
%         } else {
            <a onclick="reloadElement(this.closest('[hx-get]'), {'hx-vals': '<% JSON({ Month => $NMonth, Year => $NYear, SearchDisplayMode => 'Calendar', ( $CalendarStatusFilterParam ? (CalendarStatusFilter => $CalendarStatusFilterParam) : () ) }) %>'}); return false;" href="#"><%$rtdate->GetMonth($NMonth)%> &raquo;</a>
%         }
          </td>
        </tr>
      </table>


      <table class="table table-bordered rt-calendar">
        <thead>
          <tr>
% for ( @{$week{$weekstart}} ) {
            <th width="14%"><%$rtdate->GetWeekday($_)%></th>
% }
          </tr>
        </thead>
        <tbody>
<%perl>
# We use %week_ticket_position to control the display of tickets on the
# calendar. It has the following structure:
# {
#   1 => { id => 123, TicketObj => $t },
#   2 => { id => 312, TicketObj => $t },
#   3 => { id => '', TicketObj => undef }, # empty position
#   4 => { id => 111, TicketObj => $t },
# }
# where the key is the position/line of the ticket in the current week
# when an event ends during the week, it's removed from the hash, openning
# the position for a new ticket to be placed at the same line on the week,
# saving some height on the calendar.
# This variable is cleaned every time we start a new week.
my %week_ticket_position;
my $day_of_week = 1;

while ($date <= $end) {
  my @classes = ();
  push @classes, "offmonth"  if $date->month != ($Month + 1);
  push @classes, "today"     if (DateTime->compare($today,     $date) == 0);
  push @classes, "yesterday" if (DateTime->compare($yesterday, $date) == 0);
  push @classes, "aweekago"  if (DateTime->compare($aweekago,  $date) == 0);
  push @classes, "weekday-$day_of_week";

  if ( $date->day_of_week == $startday_of_week ) {
    $m->out('<tr>');
  }

  for my $event ( @{ $Calendar->{ $date->strftime("%F") } || [] } ) {
    my $t = $event->{ticket};

    # Use span_id for multi-day events to ensure consistent positioning, otherwise use ticket id
    my $position_key = $event->{span_id} || $t->id;

    # check if this event was already displayed this week, if not, we need to find a
    # position for it
    unless ( grep { $week_ticket_position{$_}{id} eq $position_key } keys %week_ticket_position ) {
      # new events should assume the first empty spot.
      my $i = 1;
      my $free_index = 0;
      for my $index ( sort { $a <=> $b } keys %week_ticket_position ) {
        if ( $week_ticket_position{$index}{id} eq "" ) {
          $free_index = $i;
          last;
        }
        $i++;
      }
      # if we found a free spot, we place the event there
      if ( $free_index != 0 ) {
        $week_ticket_position{$free_index}{id} = $position_key;
        $week_ticket_position{$free_index}{TicketObj} = $t;
        $week_ticket_position{$free_index}{EventData} = $event;
      }
      # if not, we add it to the end of the hash
      else {
        $week_ticket_position{((scalar keys %week_ticket_position)+1)}{id} = $position_key;
        $week_ticket_position{((scalar keys %week_ticket_position))}{TicketObj} = $t;
        $week_ticket_position{((scalar keys %week_ticket_position))}{EventData} = $event;
      }
    }
  }
</%perl>

            <td class="<% join(' ', @classes) %>">
              <div class="inside-day">
                <div class="calendar-date"><%$date->day%></div>
                <div class="tickets-container">
%                 for my $index ( sort { $a <=> $b } keys %week_ticket_position ) {
%                   # Find the event for this position on this date
%                   my $current_event;
%                   for my $event ( @{ $Calendar->{ $date->strftime("%F") } || [] } ) {
%                     my $position_key = $event->{span_id} || $event->{ticket}->id;
%                     if ( $position_key eq $week_ticket_position{$index}{id} ) {
%                       $current_event = $event;
%                       last;
%                     }
%                   }
%                   if ( $current_event ) {
%                     my $t = $current_event->{ticket};
%
%                     # Look up status color from the ticket's lifecycle
%                     my $event_color;
%                     if ( $CalendarColorMode eq 'status' ) {
%                       my $lc_name = $t->Lifecycle;
%                       if ( !$SeenLifecycles{$lc_name} ) {
%                         my $lc = $t->LifecycleObj;
%                         my %colors;
%                         for my $status ( $lc->Valid ) {
%                           $colors{ lc $status } = $lc->StatusColor($status) || $DefaultStatusColor;
%                         }
%                         $SeenLifecycles{$lc_name} = \%colors;
%                         push @SeenLifecycleOrder, $lc_name;
%                       }
%                       $event_color = $SeenLifecycles{$lc_name}{ lc $t->Status } || $DefaultStatusColor;
%                     }
                  <& /Search/Elements/CalendarEvent,
                    Object              => $t,
                    Date                => $date,
                    DayOfWeek           => $day_of_week,
                    EventData           => $current_event,
                    WeekTicketPosition  => \%week_ticket_position,
                    CurrentPosition      => $index,
                    SavedSearchId       => ($SavedSearchObj ? $SavedSearchObj->Id : undef),
                    Format              => $Format,
                    CalendarColorMode   => $CalendarColorMode,
                    EventColor          => $event_color,
                  &>
%                   }
%                   else {
%                     # if there's no ticket for this position, we add an empty space
                      <div class="day">&nbsp;</div>
%                   }
%                 }
                </div>
              </div>
            </td>

%   $date = $set->next($date);
%   if ( $date->day_of_week == $startday_of_week ) {
% #   we start a new week but preserve spanning tickets to maintain consistent ordering
%     my %preserved_positions = ();
%     my $date_str = $date->strftime("%F");
%
%     # Keep events that are still spanning into this week
%     for my $pos (sort { $a <=> $b } keys %week_ticket_position) {
%       my $position_key = $week_ticket_position{$pos}{id};
%       if ($position_key && $position_key ne "") {
%         # Check if this event appears on this day (start of new week)
%         my $found = 0;
%         for my $event (@{ $Calendar->{$date_str} || [] }) {
%           my $event_key = $event->{span_id} || $event->{ticket}->id;
%           if ($event_key eq $position_key) {
%             $found = 1;
%             last;
%           }
%         }
%         if ($found) {
%           # adjust $pos for any empty previous positions
%           while ( ( $pos > 1 ) && ( ! exists( $preserved_positions{ $pos - 1 } ) ) ) {
%               $pos--;
%           }
%           $preserved_positions{$pos} = $week_ticket_position{$pos};
%         }
%       }
%     }
%     %week_ticket_position = %preserved_positions;
%     $day_of_week=1;
          </tr>
%   }
%   else {
%     $day_of_week = $day_of_week + 1;
%   }
% }
        </tbody>
      </table>

      <table class="table">
        <tr>
          <td align="left">
%         if ( $BaseURL ) {
            <a href="<% $BaseURL %>&Month=<% $PMonth %>&Year=<% $PYear %><% $CalendarStatusFilterParam ? "&CalendarStatusFilter=$CalendarStatusFilterParamURI" : '' %>">&laquo; <%$rtdate->GetMonth($PMonth)%></a>
%         } else {
            <a onclick="reloadElement(this.closest('[hx-get]'), {'hx-vals': '<% JSON({ Month => $PMonth, Year => $PYear, SearchDisplayMode => 'Calendar', ( $CalendarStatusFilterParam ? (CalendarStatusFilter => $CalendarStatusFilterParam) : () ) }) %>'}); return false;" href="#">&laquo; <%$rtdate->GetMonth($PMonth)%></a>
%         }
          </td>
          <td valign="top" align="center">

            <form class="row justify-content-center"
%           if ( $BaseURL ) {
              action="<% $BaseURL %>">
%           } else {
                  hx-get="<% RT->Config->Get('WebPath')
        . ( $session{CurrentUser}->Privileged ? '' : '/SelfService' )
        %>/Views/Component/SavedSearch"
                  hx-target="#<% $ARGS{TargetId} %>"
                  hx-trigger="submit">
% if ($SavedSearchObj && $SavedSearchObj->Id) {
              <input type="hidden" name="SavedSearch" value="<% $SavedSearchObj->Id %>">
% }
% if ($ARGS{sc}) {
              <input type="hidden" name="sc" value="<% $ARGS{sc} %>">
% }
              <input type="hidden" name="SearchDisplayMode" value="Calendar">
              <input type="hidden" name="TargetId" value="<% $ARGS{TargetId} %>">
              <input type="hidden" name="Rows" value="<% $RowsPerPage || 10 %>">
% if ($CalendarStatusFilterParam) {
              <input type="hidden" name="CalendarStatusFilter" value="<% $CalendarStatusFilterParam %>">
% }
%           }
              <div class="col-auto">
                <select name="SelectedMonth" class="form-select selectpicker">
% for (0..11) {
                  <option value="<%$_%>" <% $_ == $Month ? 'selected' : ''%> ><%$rtdate->GetMonth($_)%></option>
% }
                </select>
              </div>
              <div class="col-auto">
% my $year = (localtime)[5] + 1900;
                <select name="SelectedYear" class="form-select selectpicker">
% for ( ($year-5) .. ($year+5)) {
                  <option value="<%$_%>" <% $_ == $Year ? 'selected' : ''%>><%$_%></option>
% }
                </select>
              </div>
              <div class="col-auto">
                <input type="submit" value="<% loc('Submit') %>" class="btn btn-primary" />
              </div>
            </form>
          </td>
          <td align="right">
%         if ( $BaseURL ) {
            <a href="<% $BaseURL %>&Month=<% $NMonth %>&Year=<% $NYear %><% $CalendarStatusFilterParam ? "&CalendarStatusFilter=$CalendarStatusFilterParamURI" : '' %>"><%$rtdate->GetMonth($NMonth)%> &raquo;</a>
%         } else {
            <a onclick="reloadElement(this.closest('[hx-get]'), {'hx-vals': '<% JSON({ Month => $NMonth, Year => $NYear, SearchDisplayMode => 'Calendar', ( $CalendarStatusFilterParam ? (CalendarStatusFilter => $CalendarStatusFilterParam) : () ) }) %>'}); return false;" href="#"><%$rtdate->GetMonth($NMonth)%> &raquo;</a>
%         }
          </td>
        </tr>
      </table>
    </div>
  </div>
</div>

% if ( $CalendarColorMode eq 'status' && @FilterLifecycleOrder ) {
<div class="calendar-status-filter-container d-none">
  <& /Search/Elements/CalendarStatusFilterDropdown,
    FilterLifecycleOrder  => \@FilterLifecycleOrder,
    FilterStatuses        => \%FilterStatuses,
    CalendarStatusFilter  => \@CalendarStatusFilter,
    DefaultStatusColor    => $DefaultStatusColor,
  &>
</div>
% }

<!-- Bootstrap Modal for Calendar Color Legend -->
<div class="modal fade" id="calendar-date-colors-modal" aria-labelledby="calendar-date-type-colors-<% $ARGS{SavedSearchId} || 0 %>">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
% if ( $CalendarColorMode eq 'status' ) {
        <h5 class="modal-title" id="calendar-date-type-colors-<% $ARGS{SavedSearchId} || 0 %>"><&|/l&>Status Colors</&></h5>
% } else {
        <h5 class="modal-title" id="calendar-date-type-colors-<% $ARGS{SavedSearchId} || 0 %>"><&|/l&>Date Type Colors</&></h5>
% }
        <a href="javascript:void(0)" class="close" data-bs-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </a>
      </div>
      <div class="modal-body">
% if ( $CalendarColorMode eq 'status' ) {
%   my @merged = RT::Lifecycle->MergeLifecycleStatuses(@SeenLifecycleOrder);
%   my $group_by_lifecycle = @merged > 1;
%   for my $entry ( @merged ) {
%     my $lc_name = $entry->{lifecycle};
%     my $lc = RT::Lifecycle->Load($lc_name);
%     if ( $group_by_lifecycle ) {
        <h6 class="mt-3 mb-1"><% $entry->{label} %></h6>
%     }
%     for my $category (qw(initial active inactive)) {
%       for my $status ( sort $lc->Valid($category) ) {
%         my $color = $SeenLifecycles{$lc_name}{ lc $status } || $DefaultStatusColor;
        <div class="ticket-entry status-color mb-2 p-2 rounded" style="--event-color: <% $color %>">
          <span><% loc($status) %></span>
        </div>
%       }
%     }
%   }

% } else {
        <div class="ticket-entry starts mb-2 p-2 rounded">
          <span><&|/l&>Starts</&></span>
        </div>
        <div class="ticket-entry due mb-2 p-2 rounded">
          <span><&|/l&>Due</&></span>
        </div>
        <div class="ticket-entry created mb-2 p-2 rounded">
          <span><&|/l&>Created</&></span>
        </div>
        <div class="ticket-entry started mb-2 p-2 rounded">
          <span><&|/l&>Started</&></span>
        </div>
        <div class="ticket-entry resolved mb-2 p-2 rounded">
          <span><&|/l&>Resolved</&></span>
        </div>
        <div class="ticket-entry last-updated mb-2 p-2 rounded">
          <span><&|/l&>Last Updated</&></span>
        </div>
        <div class="ticket-entry custom-field mb-2 p-2 rounded">
          <span><&|/l&>Custom Field Dates</&></span>
        </div>
% }
      </div>
    </div>
  </div>

% } else {
<p class="description mt-1 ms-3">
<&|/l&>No date columns were found. Please add one or more date columns and try again.</&>
</p>
% }
</div>

<%init>
use RT::Search::Calendar;

# Bootstrap modal for date color legend will be added to template output

my $Month = $DECODED_ARGS->{SelectedMonth} // $DECODED_ARGS->{Month} // (localtime)[4];
my $Year = $DECODED_ARGS->{SelectedYear} // $DECODED_ARGS->{Year}  || (localtime)[5] + 1900;
my $Query = $DECODED_ARGS->{Query};
my $Format = $DECODED_ARGS->{Format};
my $Order = $DECODED_ARGS->{Order};
my $OrderBy = $DECODED_ARGS->{OrderBy};
my $RowsPerPage = $DECODED_ARGS->{RowsPerPage};
my $NewQuery = $DECODED_ARGS->{NewQuery};
my $BaseQuery = $DECODED_ARGS->{BaseQuery};

$BaseQuery ||= $Query;

my $rtdate = RT::Date->new($session{'CurrentUser'});

my $weekstart = 'Sunday'; #RT::SiteConfig?  user pref?
my %week = (
  'Saturday' => [6,0..5],
  'Sunday'   => [0..6],
  'Monday'   => [1..6,0],
);
my $startday_of_week = ${$week{$weekstart}}[0]  || 7;
my $endday_of_week   = ${$week{$weekstart}}[-1] || 7;

my $today = DateTime->today;
my $yesterday = $today->clone->subtract( days=>1 );
my $aweekago  = $today->clone->subtract( days=>7 );
my $date = RT::Search::Calendar::CalendarFirstDay($Year, $Month + 1, $startday_of_week );
my $end  = RT::Search::Calendar::CalendarLastDay ($Year, $Month + 1, $endday_of_week );

# use this to loop over days until $end
my $set = DateTime::Set->from_recurrence(
    next => sub { $_[0]->truncate( to => 'day' )->add( days => 1 ) }
);

# Default Query and Format
my $TempFormat = "__Starts__ __Due__";
my $TempQuery = "( Status = 'new' OR Status = 'open' OR Status = 'stalled')
 AND ( Owner = '" . $session{CurrentUser}->Id ."' OR Owner = 'Nobody'  )
 AND ( Type = 'reminder' OR 'Type' = 'ticket' )";

if ( $SavedSearchObj && $SavedSearchObj->Id ) {
    my $content = $SavedSearchObj->Content || {};
    $TempFormat = $content->{'Format'};
    $TempQuery = $content->{'Query'};
}

# Load saved search for reading options when not already provided
my $SavedSearchForOptions = $SavedSearchObj;
if ( !$SavedSearchForOptions && $ARGS{SavedSearchId} ) {
    $SavedSearchForOptions = RT::SavedSearch->new( $session{'CurrentUser'} );
    $SavedSearchForOptions->Load( $ARGS{SavedSearchId} );
    undef $SavedSearchForOptions unless $SavedSearchForOptions->Id;
}

my $CalendarColorMode = $DECODED_ARGS->{CalendarColorMode} || 'date';
if ( $CalendarColorMode eq 'date' && $SavedSearchForOptions && $SavedSearchForOptions->Id ) {
    $CalendarColorMode = $SavedSearchForOptions->GetOption('CalendarColorMode') || 'date';
}

# Collect status colors from ticket lifecycles during rendering.
# @SeenLifecycleOrder preserves the order lifecycles were first encountered.
# %SeenLifecycles maps lifecycle name => { status => color, ... }
my @SeenLifecycleOrder;
my %SeenLifecycles;
my $DefaultStatusColor = '#6c757d';

# we overide them if needed
$TempQuery  = $Query  if $Query;
$TempFormat = $Format if $Format;
$Format = $TempFormat unless $Format;

# Extract available statuses from queues referenced in the query for the filter dropdown
my @FilterLifecycleOrder;
my %FilterStatuses;

if ( $CalendarColorMode eq 'status' ) {
    my $query = $TempQuery || '';
    my $filter_data = ProcessQueryForFilters( Class => 'RT::Tickets', Query => $query, BaseQuery => $query );
    my @queues = @{ ( $filter_data || {} )->{queues} || [] };

    my %seen_lc;
    for my $queue (@queues) {
        my $lc_name = $queue->Lifecycle || 'default';
        next if $seen_lc{$lc_name}++;
        my $lc = $queue->LifecycleObj;
        next unless $lc;
        push @FilterLifecycleOrder, $lc_name;
        my %colors;
        for my $status ( $lc->Valid ) {
            next if $lc->IsInactive($status) && lc($status) eq 'deleted';
            $colors{ lc $status } = $lc->StatusColor($status) || $DefaultStatusColor;
        }
        $FilterStatuses{$lc_name} = \%colors;
    }
}

# Read CalendarStatusFilter and apply to query
my @CalendarStatusFilter;
if ( $CalendarColorMode eq 'status' && $DECODED_ARGS->{CalendarStatusFilter} ) {
    # Collect all valid statuses across lifecycles
    my %valid_statuses;
    for my $lc_name ( @FilterLifecycleOrder ) {
        $valid_statuses{$_} = 1 for keys %{ $FilterStatuses{$lc_name} || {} };
    }

    # Only keep statuses that exist in the known lifecycles
    @CalendarStatusFilter = grep { $valid_statuses{ lc $_ } } split /,/, $DECODED_ARGS->{CalendarStatusFilter};

    # Only apply filter if fewer statuses selected than available
    if ( @CalendarStatusFilter && @CalendarStatusFilter < scalar keys %valid_statuses ) {
        my $status_clause = join ' OR ', map { "Status = '$_'" } @CalendarStatusFilter;
        $TempQuery = "( $TempQuery ) AND ( $status_clause )";
    }
}

my $CalendarStatusFilterParam = join(',', @CalendarStatusFilter);
my $CalendarStatusFilterParamURI = $m->interp->apply_escapes($CalendarStatusFilterParam, 'u');

my $QueryString =
      $m->comp(
        '/Elements/QueryString',
        Query   => $BaseQuery,
        Format  => $Format,
        Order   => $Order,
        OrderBy => $OrderBy,
        Rows    => $RowsPerPage,
      );

# we search all date types in Format string
my @formats = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $TempFormat);
my @Dates;
my ($explicit_starts, $explicit_ends);
for my $format (@formats) {
    if ( my $attr = $format->{attribute} ) {
        my $table_column = $m->comp(
            '/Elements/ColumnMap',
            Class => 'RT__Ticket',
            Name  => $attr,
            Attr  => 'attribute',
        );

        my $date_field;
        if ( $attr =~ /^(?:CF|CustomField(?:View)?)\./ ) {
            my $cf = RT::CustomField->new( RT->SystemUser );
            my $name = $table_column->();
            $cf->LoadByName( Name => $name, LookupType => 'RT::Queue-RT::Ticket' );
            if ( $cf->id && ( $cf->Type eq 'Date' || $cf->Type eq 'DateTime' ) ) {
                $date_field = "CF.{$name}";
                push @Dates, $date_field;
            }
        }
        elsif (RT::Ticket->_ClassAccessible->{$table_column}
            && RT::Ticket->_ClassAccessible->{$table_column}{type} eq 'datetime' )
        {
            $date_field = $table_column;
            push @Dates, $date_field;
        }

        # Check for /CALENDAR:start or /CALENDAR:end modifier
        if ( $date_field && $format->{calendar} ) {
            if ( lc $format->{calendar} eq 'start' ) {
                $explicit_starts //= $date_field;
            }
            elsif ( lc $format->{calendar} eq 'end' ) {
                $explicit_ends //= $date_field;
            }
        }
    }
}

@Dates = List::MoreUtils::uniq(@Dates);

# Use explicit /CALENDAR modifiers if both present, otherwise auto-detect
my ($starts_field, $ends_field);
if ( $explicit_starts && $explicit_ends ) {
    ($starts_field, $ends_field) = ($explicit_starts, $explicit_ends);
}
else {
    ($starts_field, $ends_field) = RT::Search::Calendar::GetMultipleDayFields(\@Dates);
}

$TempQuery .= RT::Search::Calendar::DatesClauses(\@Dates, $date->strftime("%F"), $end->strftime("%F"), $starts_field, $ends_field);

$m->callback( CallbackName => 'BeforeFindTickets', ARGSRef => \%ARGS, QueryRef => \$TempQuery, FormatRef => \$TempFormat );

my $Calendar;

# This is the expensive part of the processing, so only run this when
# htmx is rendering the main content.
if ( $m->request_path =~ /^(?:\/SelfService)?\/Views|Calendar.html/ ) {
    $Calendar = RT::Search::Calendar::GetCalendarTickets($session{'CurrentUser'}, $TempQuery, \@Dates, $date->strftime("%F"), $end->strftime("%F"), $starts_field, $ends_field);

    $m->callback(
        CallbackName   => 'ModifyCalendar',
        CalendarRef    => \$Calendar,
        BeginDate      => $date->strftime("%F"),
        EndDate        => $end->strftime("%F"),
        SavedSearchObj => $SavedSearchObj,
    );
}

my $BaseURL;
if ( $m->request_path =~ /Calendar\.html$/ ) {
    $BaseURL
        = RT->Config->Get('WebPath')
        . $m->request_path . '?'
        . QueryString( ShortenSearchQuery( map { $_ => $ARGS{$_} } grep { $_ !~ /^(?:Year|Month)$/ } keys %ARGS ) );
}

# Calendar uses unified event structure for improved performance and consistency

</%init>
<%args>
$Class => 'RT::Tickets'
$SavedSearchObj => undef
</%args>
