[Bps-public-commit] rtx-calendar branch allow-custom-field-based-events-2 created. 1.05-9-g7447aae

BPS Git Server git at git.bestpractical.com
Wed Aug 30 00:33:02 UTC 2023


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rtx-calendar".

The branch, allow-custom-field-based-events-2 has been created
        at  7447aae64acbe03c2fdc9979a18a49f482dcd0a6 (commit)

- Log -----------------------------------------------------------------
commit 7447aae64acbe03c2fdc9979a18a49f482dcd0a6
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 21:05:11 2023 -0300

    Update calendar POD
    
    Refactor POD adding an extra header level to describe better the Display
    configuration options.
    
    Add sections and descriptions for the new features and options:
    Custom Field displaying, Event sorting, Custom event colors, custom
    event icons and extra filter on status on Calendar context.

diff --git a/lib/RTx/Calendar.pm b/lib/RTx/Calendar.pm
index 9e344bb..f6fbe63 100644
--- a/lib/RTx/Calendar.pm
+++ b/lib/RTx/Calendar.pm
@@ -280,15 +280,76 @@ C<$HomepageComponents> in F<etc/RT_SiteConfig.pm>:
 
 =head2 Display configuration
 
+=head3 Displaying the owner
+
 You can show the owner in each day box by adding this line to your
 F<etc/RT_SiteConfig.pm>:
 
     Set($CalendarDisplayOwner, 1);
 
+=head3 Choosing the fields to be displayed in the popup
+
 You can change which fields show up in the popup display when you
 mouse over a date in F<etc/RT_SiteConfig.pm>:
 
-    Set(@CalendarPopupFields, ('Status', 'OwnerObj->Name', 'DueObj->ISO'));
+    Set(@CalendarPopupFields,
+        ('Status',
+         'OwnerObj->Name',
+         'DueObj->ISO',
+         'CustomField.{Maintenance Estimated Start Date/Time - ET}'));
+
+=head3 Event sorting
+
+You can set the order that the events will presented in the day cell with
+the C<$CalendarSortEvents> setting.
+
+This setting takes a subroutine reference that will receive an array of
+L<RT::Ticket> objects and should return a sorted array of L<RT::Ticket>.
+
+The following example sorts the events by status:
+
+    Set($CalendarSortEvents, sub {
+        my @Tickets = @_;
+        my @SortedTickets = sort { lc($a->Status) cmp lc($b->Status) } @Tickets;
+        return @SortedTickets;
+    });
+
+=head3 Event colors
+
+It's also possible to change the color of the events in the calendar by
+adding the C<$CalendarStatusColorMap> setting to your F<etc/RT_SiteConfig.pm>:
+
+    Set(%CalendarStatusColorMap, (
+        'new'                                   => 'blue',
+        'open'                                  => 'blue',
+        'approved'                              => 'green',
+        'rejected'                              => 'red',
+        'resolved'                              => '#aaa',
+    ));
+
+You can use any color declaration that CSS supports, including hex codes,
+color names, and RGB values.
+
+=head3 Event filtering by status
+
+You can change the statuses available for filtering on the calendar by
+adding the C<@CalendarFilterStatuses> setting to your
+F<etc/RT_SiteConfig.pm>:
+
+    Set(@CalendarFilterStatuses, qw(new open stalled rejected resolved));
+
+
+=head3 Custom icons
+
+Custom Icons can be defined for the events in the calendar by adding the
+C<$CalendarIcons> setting to your F<etc/RT_SiteConfig.pm>:
+
+    Set(%CalendarIcons, (
+        'CF.{Maintenance Estimated Start Date/Time - ET}'
+            => 'maint.png',
+    ));
+
+The images should be placed on F<local/static/images>.
 
 =head1 USAGE
 

commit 125dd21971ea36b39ac090b8f0e1b06ee65b4c72
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 20:54:29 2023 -0300

    Refactor calendar Legend to show custom icons
    
    Refactor calendar Legend to show custom icons and removed the hard coded
    icons for the default events.
    
    Moved the legend to the sidebar.

diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index 35b8002..5312065 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -52,6 +52,39 @@ $FilterOnStatusClear => undef
 </&>
 
   <&| /Widgets/TitleBox,
+     title => loc('Event Types'),
+     &>
+
+<%perl>
+my %CalendarIcons = RT->Config->Get('CalendarIcons');
+# Sort the legend after translation
+my %CalendarIconsTranslated;
+my $LegendLabel;
+foreach my $legend (sort { lc($a) cmp lc($b) } keys %CalendarIcons) {
+  my @LegendLabels = split ',', $legend;
+  $LegendLabel = join ', ',
+    map {
+      $_ =~ s/^\s+|\s+$//g;
+      $_ =~ s/^CF\.\{(.*)\}/$1/;
+      $_ = 'Last Updated' if $_ eq 'LastUpdated';
+      loc($_)
+    } @LegendLabels;
+  $CalendarIconsTranslated{$LegendLabel} = $legend;
+}
+foreach my $TranslatedLegend (sort keys %CalendarIconsTranslated) {
+</%perl>
+  <span class="tip">
+    <span class="tipimg">
+      <img
+        src="<% $RT::WebImagesURL %>/<%
+        $CalendarIcons{$CalendarIconsTranslated{$TranslatedLegend}}|n %>" />
+    </span>
+    <span class="tiplegend">
+      <% $TranslatedLegend %>
+    </span>
+  </span>
+%     }
+</&>
 
 <&| /Widgets/TitleBox,
      title => loc('State Colors'),
@@ -166,24 +199,6 @@ $FilterOnStatusClear => undef
 </tr>
 </table>
 
-<table width="100%">
-% foreach my $legend (sort keys %legend) {
-    <tr>
-      <td align="right">
-        <img src="<%$RT::WebImagesURL%>/<%$legend%>.png" />
-      </td>
-      <td align="left">
-%       my $more = 0;
-%       foreach ( @{$legend{$legend}} ) {
-          <% $more++ ? ', ' : '' %>
-          <&|/l&><% $_ %></&>
-%       }
-      </td>
-    </tr>
-% }
-
-</table>
-
 </&>
 
 <&| /Widgets/TitleBox, title => loc('Help') &>
@@ -235,20 +250,7 @@ portlet by saving a query with the name <code>calendar</code> in the [_1].
     });
   });
 </script>
-<%ONCE>
-
-my %legend = (
-  'created'     => ['Created'],
-  'due'         => ['Due'],
-  'resolved'    => ['Resolved'],
-  'updated'     => ['Last Updated'],
-  'created_due' => ['Created','Due'],
-  'reminder'    => ['Reminders'],
-  'started'     => ['Started'],
-  'starts_due'  => ['Starts','Due'],
-);
 
-</%ONCE>
 <%INIT>
 if ($FilterOnStatusClear) {
   $Query = $BaseQuery if $BaseQuery;

commit 9bdeab22f3d33a2244a83a3821dcb7eac7e8f19d
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 21:30:47 2023 -0300

    Add feature to customize Icon of events on the calendar
    
    Since we allow now to add custom fields to the calendar, we need to
    allow to customize the icon of the event based on its type.
    
    If custom icon is not set to a Custom Field based event, then we will
    not show a standard icon for that.

diff --git a/etc/RTxCalendar_Config.pm b/etc/RTxCalendar_Config.pm
index 253bd78..d042d47 100644
--- a/etc/RTxCalendar_Config.pm
+++ b/etc/RTxCalendar_Config.pm
@@ -1,3 +1,15 @@
+Set(%CalendarIcons, (
+    'Reminder'     => 'reminder.png',
+    'Resolved'     => 'resolved.png',
+    'Starts, Due'  => 'starts_due.png',
+    'Created, Due' => 'created_due.png',
+    'Created'      => 'created.png',
+    'Due'          => 'due.png',
+    'Starts'       => 'starts.png',
+    'Started'      => 'started.png',
+    'LastUpdated'  => 'updated.png',
+));
+
 Set(%CalendarStatusColorMap, (
     'new'                                   => '#87873c',
     'open'                                  => '#5555f8',
diff --git a/html/Elements/CalendarEvent b/html/Elements/CalendarEvent
index 6d3d874..b8023d9 100644
--- a/html/Elements/CalendarEvent
+++ b/html/Elements/CalendarEvent
@@ -6,43 +6,7 @@ $DateTypes => undef
 <div class="day">
 <small>
 
-% if ($IsReminder and RTx::Calendar::LocalDate($Object->DueObj->Unix) eq $today) {
-     <img src="<%$RT::WebImagesURL%>/reminder.png" />
-
-% } elsif ($DateTypes->{Resolved}
-%           and RTx::Calendar::LocalDate($Object->ResolvedObj->Unix) eq $today) {
-         <img src="<%$RT::WebImagesURL%>/resolved.png" />
-
-% } elsif ($DateTypes->{Starts} and $DateTypes->{Due}
-%           and RTx::Calendar::LocalDate($Object->StartsObj->Unix) eq $today and RTx::Calendar::LocalDate($Object->DueObj->Unix) eq $today ) {
-    <img src="<%$RT::WebImagesURL%>/starts_due.png" />
-
-% } elsif ($DateTypes->{Due} and $DateTypes->{Created}
-%           and RTx::Calendar::LocalDate($Object->DueObj->Unix) eq $today and RTx::Calendar::LocalDate($Object->CreatedObj->Unix) eq $today ) {
-    <img src="<%$RT::WebImagesURL%>/created_due.png" />
-
-% } elsif ($DateTypes->{Starts}
-%           and RTx::Calendar::LocalDate($Object->StartsObj->Unix) eq $today) {
-    <img src="<%$RT::WebImagesURL%>/starts.png" />
-
-% } elsif ($DateTypes->{Due}
-%           and RTx::Calendar::LocalDate($Object->DueObj->Unix) eq $today) {
-    <img src="<%$RT::WebImagesURL%>/due.png" />
-
-% } elsif ($DateTypes->{Created}
-%           and RTx::Calendar::LocalDate($Object->CreatedObj->Unix) eq $today) {
-    <img src="<%$RT::WebImagesURL%>/created.png" />
-
-% } elsif ($DateTypes->{Started}
-%           and RTx::Calendar::LocalDate($Object->StartedObj->Unix) eq $today) {
-    <img src="<%$RT::WebImagesURL%>/started.png" />
-
-% } elsif ($DateTypes->{LastUpdated}
-%           and RTx::Calendar::LocalDate($Object->LastUpdatedObj->Unix) eq $today) {
-    <img src="<%$RT::WebImagesURL%>/updated.png" />
-
-% }
-
+    <% RTx::Calendar::GetEventImg($Object, $today, $DateTypes, $IsReminder)|n %>
 	<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$TicketId%>"
 % if ( $CalendarStatusColorMap{$status} ) {
     style="color: <%$CalendarStatusColorMap{$status}%>;"
diff --git a/lib/RTx/Calendar.pm b/lib/RTx/Calendar.pm
index 06b501a..9e344bb 100644
--- a/lib/RTx/Calendar.pm
+++ b/lib/RTx/Calendar.pm
@@ -160,6 +160,57 @@ sub SearchDefaultCalendar {
     }
 }
 
+sub GetEventImg {
+    my $Object      = shift;
+    my $CurrentDate = shift;
+    my $DateTypes   = shift;
+    my $IsReminder  = shift;
+    my $EventIcon;
+    my %CalendarIcons = RT->Config->Get('CalendarIcons');
+CALENDAR_ICON:
+    for my $DateField ( keys %CalendarIcons ) {
+
+      # Icon can be a combination of two dates such as Due-Created,
+      # or CF.{Date Field}-Created. It can be also single date such as Created
+        my @DatesToCompare = split( /,/, $DateField );
+    DATE_COMPARE:
+        for my $ComparedDate (@DatesToCompare) {
+            # trim spaces
+            $ComparedDate =~ s/^\s+|\s+$//g;
+            if ( $DateField eq 'Reminder' ) {
+                if ( $IsReminder
+                    && RTx::Calendar::LocalDate( $Object->DueObj->Unix ) eq
+                    $CurrentDate )
+                {
+                    $EventIcon = 'reminder.png';
+                    last CALENDAR_ICON;
+                }
+            } elsif ( $DateField =~ /^CF\./ ) {
+                my $cf = $DateField;
+                $cf =~ s/^CF\.\{(.*)\}/$1/;
+                my $DateValue = $Object->FirstCustomFieldValue($cf);
+                next CALENDAR_ICON unless $DateValue;
+                $DateValue =~ s/(.*) (.*)/$1/;
+                next CALENDAR_ICON unless $DateValue eq $CurrentDate;
+            } else {
+                my $DateObj = $ComparedDate . "Obj";
+                my $DateValue
+                    = RTx::Calendar::LocalDate( $Object->$DateObj->Unix );
+                next CALENDAR_ICON unless $DateValue eq $CurrentDate;
+            }
+
+            # If we are here, it means that all comparissons are true
+            $EventIcon = $CalendarIcons{$DateField};
+        }
+    }
+    if ($EventIcon) {
+        return '<img src="' . $RT::WebImagesURL . '/' . $EventIcon . '" />';
+    } else {
+        return '';
+    }
+}
+
+
 1;
 
 __END__

commit 845139a77d2ba7a929ed4e4820e3a3dfa97f0fbf
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 21:26:35 2023 -0300

    Add feature to customize color of events on the calendar

diff --git a/etc/RTxCalendar_Config.pm b/etc/RTxCalendar_Config.pm
index 0f16304..253bd78 100644
--- a/etc/RTxCalendar_Config.pm
+++ b/etc/RTxCalendar_Config.pm
@@ -1,3 +1,11 @@
+Set(%CalendarStatusColorMap, (
+    'new'                                   => '#87873c',
+    'open'                                  => '#5555f8',
+    'rejected'                              => '#FF0000',
+    'resolved'                              => '#72b872',
+    'stalled'                               => '#FF0000',
+));
+
 Set($CalendarSortEvents, sub {
     my @Tickets = @_;
     my @SortedTickets = sort { lc($a->Status) cmp lc($b->Status) } @Tickets;
diff --git a/html/Elements/CalendarEvent b/html/Elements/CalendarEvent
index 11d090d..6d3d874 100644
--- a/html/Elements/CalendarEvent
+++ b/html/Elements/CalendarEvent
@@ -43,7 +43,11 @@ $DateTypes => undef
 
 % }
 
-	<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$TicketId%>">
+	<a href="<%$RT::WebPath%>/Ticket/Display.html?id=<%$TicketId%>"
+% if ( $CalendarStatusColorMap{$status} ) {
+    style="color: <%$CalendarStatusColorMap{$status}%>;"
+% }
+    >
            <% $Object->QueueObj->Name %> #<% $TicketId %>
            <% $display_owner ? 'by ' . $Object->OwnerObj->Name : '' %>
            <% length($Object->Subject) > 80 ? substr($Object->Subject, 0, 77) . "..." : $Object->Subject %></a></small><br />
@@ -86,6 +90,7 @@ my $TicketId;
 my $ticket;
 my $subject;
 my $IsReminder;
+my $status;
 
 if ($Object->Type eq 'reminder') {
     $IsReminder = 1;
@@ -93,12 +98,16 @@ if ($Object->Type eq 'reminder') {
 	$ticket   = $Object->RefersTo->First->TargetObj;
 	$TicketId = $ticket->Id;
 	$subject = $Object->Subject . " (" . $ticket->Subject . ")";
+        $status = $Object->Status;
     }
 } else {
     $TicketId = $Object->Id;
     $subject = $Object->Subject;
+    $status = $Object->Status;
 }
 
+my %CalendarStatusColorMap = RT->Config->Get('CalendarStatusColorMap');
+
 my $display_owner = $RT::CalendarDisplayOwner;
 $display_owner ||= RT->Config->Get('CalendarDisplayOwner')
     if RT->can('Config');
diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index 33f14d2..35b8002 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -52,6 +52,14 @@ $FilterOnStatusClear => undef
 </&>
 
   <&| /Widgets/TitleBox,
+
+<&| /Widgets/TitleBox,
+     title => loc('State Colors'),
+     &>
+% my %ColorStatusMap = RT->Config->Get('CalendarStatusColorMap');
+% foreach my $Status (sort { lc($a) cmp lc($b) } keys %ColorStatusMap) {
+    <span style="color: <% $ColorStatusMap{$Status} %>"><% $Status %><span><br />
+% }
 </&>
 
 </div>

commit 621cbed0caf8c3b6f39caef21125d4f13657a204
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 20:38:07 2023 -0300

    Add link to Download Calendar events
    
    Add link on the top right of the Calendar to download the events that
    are shown in the current view as a Spreadsheet.

diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index 3e4432b..33f14d2 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -58,6 +58,11 @@ $FilterOnStatusClear => undef
 </div>
 
 <div class="calendar-content">
+<&| /Widgets/TitleBox,
+     title => loc('Calendar for [_1] [_2]', $rtdate->GetMonth($Month), $Year),
+     titleright => loc('Download Spreadsheet'),
+     titleright_href => $RT::WebPath. "/Search/Results.tsv?". $DownloadQueryString
+     &>
 
 <table width="100%">
 <tr>
@@ -330,5 +335,14 @@ $m->callback( CallbackName => 'BeforeFindTickets', ARGSRef => \%ARGS, QueryRef =
 
 my %Tickets = RTx::Calendar::FindTickets($session{'CurrentUser'}, $TempQuery, \@Dates, $date->strftime("%F"), $end->strftime("%F"));
 
+my $DownloadQueryString =
+      $m->comp(
+        '/Elements/QueryString',
+        Query   => $TempQuery,
+        Format  => $Format,
+        Order   => $Order,
+        OrderBy => $OrderBy,
+      );
+
 my $SortCalendarEvents = RT->Config->Get("CalendarSortEvents");
 </%INIT>

commit 930845e7db591e167a1132fac002dfc38b5db613
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 20:27:21 2023 -0300

    Add Status Filter to Calendar sidebar
    
    Allow user to filter events by status.
    
    Add setting to define available status to filter on.

diff --git a/etc/RTxCalendar_Config.pm b/etc/RTxCalendar_Config.pm
index 17f12ae..0f16304 100644
--- a/etc/RTxCalendar_Config.pm
+++ b/etc/RTxCalendar_Config.pm
@@ -4,4 +4,6 @@ Set($CalendarSortEvents, sub {
     return @SortedTickets;
 });
 
+Set(@CalendarFilterStatuses, qw(new open stalled rejected resolved));
+
 1;
diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index 20625e2..3e4432b 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -7,6 +7,9 @@ $Order => undef
 $OrderBy => undef
 $RowsPerPage => undef
 $NewQuery => 0
+ at FilterOnStatus => undef
+$BaseQuery => undef
+$FilterOnStatusClear => undef
 </%args>
 
 <& /Elements/Header, Title => $title &>
@@ -23,6 +26,33 @@ $NewQuery => 0
 <a title="Toggle Filter" href="javascript:;" class="calendar-toggle-sidebar"></a>
 <div class="calendar-sidebar">
 <&| /Widgets/TitleBox,
+    title => loc('Filter on Status'),
+    class => 'calendar-filter-status-box',
+    &>
+
+  <form id="FilterOnStatusForm"
+  action="<%$RT::WebPath%>/Search/Calendar.html?<%$QueryString%>" method="post">
+  <input type="hidden" name="BaseQuery" value="<%$BaseQuery%>" />
+  <select name="FilterOnStatus" id="FilterOnStatus"
+    class="selectpicker filteronstatus mt-3 mb-3" multiple="multiple" size="6">
+% for my $Status (sort {loc($a) cmp loc($b)} @{RT->Config->Get('CalendarFilterStatuses')}) {
+    <option value="<% $Status %>"
+% if (@FilterOnStatus && $FilterOnStatus[0]) {
+      <% (grep { $Status eq $_ } @FilterOnStatus) ? 'selected="selected"':''%>
+% }
+      ><% loc($Status) %></option>
+% }
+  </select>
+  <div class="text-center">
+    <input type="submit" value="<% loc('Filter') %>" class="mr-2 button btn btn-primary" />
+    <button type="submit" id="FilterOnStatusClear" name="FilterOnStatusClear"
+      value="1" class="button btn btn-primary"><% loc('Clear Filter') %></button>
+  </div>
+</form>
+</&>
+
+  <&| /Widgets/TitleBox,
+</&>
 
 </div>
 </div>
@@ -207,6 +237,11 @@ my %legend = (
 
 </%ONCE>
 <%INIT>
+if ($FilterOnStatusClear) {
+  $Query = $BaseQuery if $BaseQuery;
+  @FilterOnStatus = ();
+}
+$BaseQuery ||= $Query;
 my $title = loc("Calendar");
 
 my @DateTypes = qw/Created Starts Started Due LastUpdated Resolved/;
@@ -233,10 +268,16 @@ my $set = DateTime::Set->from_recurrence(
     next => sub { $_[0]->truncate( to => 'day' )->add( days => 1 ) }
 );
 
+if ($FilterOnStatus[0]) {
+  my $StatusClause = join " OR ", map { "Status = '$_'" } @FilterOnStatus;
+  $Query .= " AND " if $Query;
+  $Query .= "($StatusClause)";
+}
+
 my $QueryString =
       $m->comp(
         '/Elements/QueryString',
-        Query   => $Query,
+        Query   => $BaseQuery,
         Format  => $Format,
         Order   => $Order,
         OrderBy => $OrderBy,
diff --git a/static/css/calendar.css b/static/css/calendar.css
index 60cc225..96e9086 100644
--- a/static/css/calendar.css
+++ b/static/css/calendar.css
@@ -128,6 +128,12 @@ a.calendar-toggle-sidebar.sidebar-off::before {
 .calendar-content.sidebar-off {
     margin-left: 20px;
 }
+.filteronstatus {
+    width: 100% !important;
+}
+.calendar-filter-status-box {
+    margin-top: 0px;
+}
 .calendar-sidebar {
     margin-right: 10px;
 }

commit 5a9fb7f03e9b53ce0fa6f99142b7c9ce88fcf1d9
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 21:16:42 2023 -0300

    Add sidebar to calendar
    
    Add a sidebar to hold legend, filters, and other future controls.
    
    Add a toggle to show/hide the sidebar.

diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index bc44d26..20625e2 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -19,8 +19,15 @@ $NewQuery => 0
     <& /Elements/Tabs &>
 % }
 
+<div class="calendar-sidebar-toggle-content">
+<a title="Toggle Filter" href="javascript:;" class="calendar-toggle-sidebar"></a>
+<div class="calendar-sidebar">
 <&| /Widgets/TitleBox,
-     title => loc('Calendar for [_1] [_2]', $rtdate->GetMonth($Month), $Year) &>
+
+</div>
+</div>
+
+<div class="calendar-content">
 
 <table width="100%">
 <tr>
@@ -174,7 +181,17 @@ portlet by saving a query with the name <code>calendar</code> in the [_1].
 </p>
 
 </&>
-
+</div>
+
+<script type="text/javascript">
+  jQuery(document).ready(function() {
+    jQuery('.calendar-toggle-sidebar').click(function() {
+      jQuery('.calendar-sidebar').toggle();
+      jQuery('.calendar-sidebar-toggle-content,.calendar-toggle-sidebar').toggleClass('sidebar-off');
+      jQuery('.calendar-content').toggleClass('sidebar-off');
+    });
+  });
+</script>
 <%ONCE>
 
 my %legend = (
diff --git a/static/css/calendar.css b/static/css/calendar.css
index 4adfc4a..60cc225 100644
--- a/static/css/calendar.css
+++ b/static/css/calendar.css
@@ -72,3 +72,62 @@ table.rtxcalendar td.yesterday {
 table.rtxcalendar td.aweekago {
     border-bottom: none;
 }
+
+.calendar-sidebar {
+    padding-right: 0.5rem;
+}
+
+.calendar-content {
+    width: auto;
+}
+
+.calendar-sidebar .tipimg {
+    display: table-cell;
+    width: 25px;
+}
+.calendar-sidebar .tiplegend {
+    display: table-cell;
+}
+.calendar-sidebar .tip {
+    display: table-row;
+}
+
+a.calendar-toggle-sidebar,
+a.calendar-toggle-sidebar.off {
+    background: none !important;
+    width: 0px !important;
+    position: relative;
+    float: right;
+    top: 300px;
+    margin-right: 10px;
+}
+a.calendar-toggle-sidebar::before {
+    content: '';
+    display: block;
+    width: 8px;
+    height: 8px;
+    border: solid #B0B3BC;
+    border-width: 0 2px 2px 0;
+    -webkit-transform: rotate(135deg);
+    -ms-transform: rotate(135deg);
+    transform: rotate(135deg);
+    transition: transform .25s;
+    position: absolute;
+    top: 1.2em;
+}
+a.calendar-toggle-sidebar.sidebar-off::before {
+    transform: rotate(315deg);
+}
+.calendar-sidebar-toggle-content {
+    float: left;
+    width: 230px;
+}
+.calendar-sidebar-toggle-content.sidebar-off {
+    width:unset;
+}
+.calendar-content.sidebar-off {
+    margin-left: 20px;
+}
+.calendar-sidebar {
+    margin-right: 10px;
+}

commit 51daf420feed23b1f7934ef3f25381f5d4fe3f94
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 19:31:43 2023 -0300

    Add setting to allow customize the sort order of the events
    
    Add $CalendarSortEvents setting to allow customize the sort order
    of the events in a day cell of the calendar. This is a sub that takes
    the Objects list (that can be tickets or reminders) and returns a sorted
    list of the objects.

diff --git a/etc/RTxCalendar_Config.pm b/etc/RTxCalendar_Config.pm
new file mode 100644
index 0000000..17f12ae
--- /dev/null
+++ b/etc/RTxCalendar_Config.pm
@@ -0,0 +1,7 @@
+Set($CalendarSortEvents, sub {
+    my @Tickets = @_;
+    my @SortedTickets = sort { lc($a->Status) cmp lc($b->Status) } @Tickets;
+    return @SortedTickets;
+});
+
+1;
diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index fcaf314..bc44d26 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -68,7 +68,7 @@ $NewQuery => 0
     <td class="<% @classes %>"><div class="inside-day">
       <div class="calendardate"><%$date->day%></div>
 
-%     for my $t ( @{ $Tickets{$date->strftime("%F")} } ) {
+%     for my $t ( $SortCalendarEvents->( @{ $Tickets{ $date->strftime("%F") } || [] } )) {
         <& /Elements/CalendarEvent, Object => $t, Date => $date, DateTypes => \%DateTypes &>
 %     }
 
@@ -272,4 +272,5 @@ $m->callback( CallbackName => 'BeforeFindTickets', ARGSRef => \%ARGS, QueryRef =
 
 my %Tickets = RTx::Calendar::FindTickets($session{'CurrentUser'}, $TempQuery, \@Dates, $date->strftime("%F"), $end->strftime("%F"));
 
+my $SortCalendarEvents = RT->Config->Get("CalendarSortEvents");
 </%INIT>

commit 5cdffd11c94d64737002de69714f37486a7d0f08
Author: Ronaldo Richieri <ronaldo at bestpractical.com>
Date:   Tue Aug 29 19:22:28 2023 -0300

    Allow Date and Date/Time Custom Fields to be used in Calendar
    
    It's possible now to add __CustomField.{Custom Field Name}__ to search
    format so it will appear on calendar.

diff --git a/html/Elements/CalendarEvent b/html/Elements/CalendarEvent
index 5b8a6c5..11d090d 100644
--- a/html/Elements/CalendarEvent
+++ b/html/Elements/CalendarEvent
@@ -60,6 +60,9 @@ $DateTypes => undef
 %
 %    if ($attr =~ /(.*)->ISO$/ and $Object->$1->Unix <= 0) {
 %        $value = '-';
+%    } elsif ($attr =~ /CustomField\.\{(.*)\}$/) {
+%        my $cf = $1;
+%        $value = $Object->FirstCustomFieldValue($cf);
 %    } else {
 %        my $method = '$Object->'.$attr.'()';
 %        $method =~ s/->ISO\(\)$/->ISO( Timezone => 'user' )/;
@@ -123,6 +126,7 @@ for my $field (@display_fields) {
     my $label = $field;
     $label =~ s'Obj-.(?:AsString|Name|ISO)''g;
     $label =~ s'-\>MemberEmailAddressesAsString''g;
+    $label =~ s/CustomField\.\{(.*)\}/$1/g;
     $label_of{$field} = $label;
 }
 
diff --git a/html/Search/Calendar.html b/html/Search/Calendar.html
index 9c5e71e..fcaf314 100644
--- a/html/Search/Calendar.html
+++ b/html/Search/Calendar.html
@@ -154,12 +154,14 @@ and add something to the Query like that:
 <&|/l_unsafe, qq{<a href="$RT::WebPath/Search/Build.html">} . loc("Query Builder") . '</a>'&>
 By default RTx::Calendar display Due and Starts dates. You can select other
 kind of events you want with the Display Columns section in the [_1].
-The following one will display the two latter and LastUpdated dates:
+The following one will display the two latter, LastUpdated and an additional
+date from a custom field called Maintenance Date:
 </&>
 <pre>
   '<small>__Due__</small>',
   '<small>__Starts__</small>',
-  '<small>__LastUpdated__</small>'
+  '<small>__LastUpdated__</small>',
+  '<small>__CustomField.{Maintenance Date}__</small>'
 </pre>
 </p>
 
@@ -243,7 +245,23 @@ $TempQuery  = $Query  if $Query;
 $TempFormat = $Format if $Format;
 
 # we search all date types in Format string
-my @Dates = grep { $TempFormat =~ m/__${_}(Relative)?__/ } @DateTypes;
+my @CoreDates    = grep { $TempFormat =~ m/__${_}(Relative)?__/ } @DateTypes;
+my @CustomFields = ( $TempFormat =~ /__(CustomField\.\{.*\})__/g );
+my @DateCustomFields;
+
+for my $CustomField (@CustomFields) {
+    my $LintCustomField = $CustomField;
+    $LintCustomField =~ s/CustomField\.\{(.*)\}/$1/;
+    my $CustomFieldObj = RT::CustomField->new( RT->SystemUser );
+    $CustomFieldObj->LoadByName( Name => $LintCustomField );
+    push @DateCustomFields, $CustomField
+        if $CustomFieldObj->id
+        && ( $CustomFieldObj->Type eq 'Date'
+        || $CustomFieldObj->Type eq 'DateTime' );
+}
+
+my @Dates = (@CoreDates, @DateCustomFields);
+ at Dates = map { $_ =~ s/^CustomField\.(.*)$/CF.$1/; $_ } @Dates;
 
 # used to display or not a date in Element/CalendarEvent
 my %DateTypes = map { $_ => 1 } @Dates;
diff --git a/lib/RTx/Calendar.pm b/lib/RTx/Calendar.pm
index 9bb375d..06b501a 100644
--- a/lib/RTx/Calendar.pm
+++ b/lib/RTx/Calendar.pm
@@ -71,14 +71,43 @@ sub FindTickets {
 
         # How to find the LastContacted date ?
         for my $Date (@$Dates) {
-            my $DateObj = $Date . "Obj";
-            push @{ $Tickets{ LocalDate( $Ticket->$DateObj->Unix ) } },
+
+            # $dateindex is the date to use as key in the Tickets Hash
+            # in the YYYY-MM-DD format
+            # Tickets are then groupd by date in the %Tickets hash
+            my $dateindex;
+            if ($Date =~ /^CF\./){
+                my $cf = $Date;
+                $cf =~ s/^CF\.\{(.*)\}/$1/;
+
+                my $CFDateValue = $Ticket->FirstCustomFieldValue($cf);
+                next unless $CFDateValue;
+                my $CustomFieldObj = RT::CustomField->new($CurrentUser);
+                $CustomFieldObj->LoadByName( Name => $cf );
+                my $CustomFieldObjType = $CustomFieldObj->Type;
+                my $DateObj            = RT::Date->new($CurrentUser);
+                if ( $CustomFieldObjType eq 'Date' ) {
+                    $DateObj->Set(
+                        Format   => 'unknown',
+                        Value    => $CFDateValue,
+                        Timezone => 'utc'
+                    );
+                } else {
+                    $DateObj->Set( Format => 'ISO', Value => $CFDateValue );
+                }
+                $dateindex = LocalDate( $DateObj->Unix );
+            } else {
+                my $DateObj = $Date . "Obj";
+                $dateindex = LocalDate( $Ticket->$DateObj->Unix );
+            }
+
+            push @{ $Tickets{$dateindex } },
                 $Ticket
 
                 # if reminder, check it's refering to a ticket
                 unless ( $Ticket->Type eq 'reminder'
                 and not $Ticket->RefersTo->First )
-                or $AlreadySeen{ LocalDate( $Ticket->$DateObj->Unix ) }
+                or $AlreadySeen{ $dateindex }
                 {$Ticket}++;
         }
     }

-----------------------------------------------------------------------


hooks/post-receive
-- 
rtx-calendar


More information about the Bps-public-commit mailing list