Further utilization of the Events Calendar

Dont create your support topics here! No new topics with questions allowed!

Moderator: crythias

Forum rules
Dont create your support topics here! No new topics with questions allowed!
Post Reply
RStraub
Znuny guru
Posts: 2210
Joined: 13 Mar 2014, 09:16
Znuny Version: 6.0.14
Real Name: Rolf Straub

Further utilization of the Events Calendar

Post by RStraub »

Hello forum,

we recently wanted to use the events calendar for vacation planning, showing events etc.

What we disliked was the default view that didn't visualize vacation days or weekends. So, as OTRS is very neatly moddable, here's our solution.

To color in the weekends, simply add to your skin folder (you are using custom skins, right?), or append to the defaults skin folder:
~otrs/var/httpd/htdocs/skins/Agent/[NameOfYourSkin|default]/css/thirdparty/fullcalendar-2.4.0/fullcalendar.min.css

Code: Select all

.fc-sat { background-color:#D1D0CE;}
.fc-sun { background-color:#D1D0CE;}
Note: Pre 5 the version of the fullcalendar is 1.6.1

Now, to show all the vacation days you have configured in the SysConfig, create a copy of these files in your custom folder (this only works from 5.x +):
~otrs/Kernel/Output/HTML/Dashboard/EventsTicketCalendar.pm
~otrs/Kernel/Output/HTML/Templates/Standard/DashboardEventsTicketCalendar.tt

in the perl file, add after these lines:

Code: Select all

    my @EventsDisplayed;

    my $Counter = 1;
    my $Limit   = scalar keys %Tickets;
    
    # get needed objects
    my $TimeObject   = $Kernel::OM->Get('Kernel::System::Time');
    my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');

this code (and feel free to improve it):

Code: Select all

# Begin of Customization
    my $TimeVacationDaysOneTime = $ConfigObject->Get('TimeVacationDaysOneTime');
    for my $CurYear (keys $TimeVacationDaysOneTime) {
        for my $CurMonth (keys $TimeVacationDaysOneTime->{$CurYear}) {
            my $SMonth = ($CurMonth < 10) ? "0$CurMonth" : $CurMonth;
            for my $CurDay (keys $TimeVacationDaysOneTime->{$CurYear}->{$CurMonth}) {
                my $SDay = ($CurDay < 10) ? "0$CurDay" : $CurDay;
                $LayoutObject->Block(
                    Name => 'CalendarEventVacationDay',
                    Data => {
                        Date => "$CurYear-$SMonth-$SDay",
                        Color => "red",
                    },
                );
            }
        }
    }
    # Now do the same, but for recurring vacation days. As fullcalendar does not support
    # yearly recurring events, let's just create these for 3 years in the future and 1 year in the past
    my $TimeVacationDays = $ConfigObject->Get('TimeVacationDays');
    for my $CurMonth (keys $TimeVacationDays) {
        my $SMonth = ($CurMonth < 10) ? "0$CurMonth" : $CurMonth;
        for my $CurDay (keys $TimeVacationDays->{$CurMonth}) {
            my @Now = localtime();
            my $SYear = $Now[5] + 1900;
            my $SDay = ($CurDay < 10) ? "0$CurDay" : $CurDay;
            foreach ( -1, 0, 1, 2, 3) {
                 my $CurYear = $SYear + $_;
                $LayoutObject->Block(
                    Name => 'CalendarEventVacationDay',
                    Data => {
                        Date => "$CurYear-$SMonth-$SDay",
                        Color => "red",
                    },
                );
            }
        }
    }
# This Block also puts an comma after each event. It's however not allowed on the last one
# Thus we output a "normal"-dummy event
# I found this simpler than counting two nested hashes for their length
# and decreasing a counter and so on
    $LayoutObject->Block(
        Name => 'CalendarEventVacationDayEND',
        Data => {
            Date => "2000-01-01",
            Color => "red",
        },
    );
Then, add to the template file after these lines:

Code: Select all

            ButtonText: {
                today: [% Translate("Today") | JSON %],
                month: [% Translate("month") | JSON %],
                week: [% Translate("week") | JSON %],
                day: [% Translate("day") | JSON %]
            },
            FirstDay: [% Data.FirstDay | html %],
            Events: [
this code:

Code: Select all

[% RenderBlockStart("CalendarEventVacationDay") %]
                {
                    start: "[% Data.Date | html %]",
                    color: "[% Data.Color | html %]",
                    rendering: 'background',
                    allDay: true
                },
[% RenderBlockEnd("CalendarEventVacationDay") %]
[% RenderBlockStart("CalendarEventVacationDayEND") %]
                {
                    start: "[% Data.Date | html %]",
                    color: "[% Data.Color | html %]",
                    rendering: 'background',
                    allDay: true
                }
[% RenderBlockEnd("CalendarEventVacationDayEND") %]
Enjoy your new colorful calendar!

Next project is to implement some kind of ordering to events, such that certain types of events are always rendered on the top.

EDIT:
Just noticed that this breaks when normal events are added. In that case a comma after the "CalendarEventVacationDayEND"-Block is needed.
Currently using: OTRS 6.0.14 -- MariaDB -- Ubuntu 16 LTS
RStraub
Znuny guru
Posts: 2210
Joined: 13 Mar 2014, 09:16
Znuny Version: 6.0.14
Real Name: Rolf Straub

Re: Further utilization of the Events Calendar

Post by RStraub »

Off to the next part. The FullCalendar sorts events according to these parameters (vertical sorting):
- Events lasting for multiple days get sorted higher
- Events that are full-day get sorted higher
- Events lasting for more hours get sorted higher
- If they still match, they get sorted alphabetically

We wanted to have different colors for different event types and a certain fixed ordering. So, open up the modified perl module (mentioned in the thread above) and find the block where %Data is getting defined:

Code: Select all

                my %Data;
                $Data{ID}    = $TicketID;
                $Data{Title} = $TicketDetail{Title};
                $TicketDetail{ 'DynamicField_' . $StartTimeDynamicField }
                    =~ /(\d+)\-(\d+)\-(\d+)\ (\d+)\:(\d+)\:(\d+)/;
                $Data{SYear}   = $1;
                $Data{SMonth}  = $2 - 1;
                $Data{SDay}    = $3;
                [....]
There, replace the line $Data{Color} with (replace with your actual types):

Code: Select all

                if ($TicketDetail{Type} eq 'Vacation'){
                    $Data{Color} = "#930";
                    $Data{Prio} = "20";
                } elsif ($TicketDetail{Type} eq 'Event') {
                    $Data{Color} = "blue";
                    $Data{Prio} = "1";
                } else {
                    $Data{Color}     = "#09C";
                    $Data{Prio} = "25";
                }
Next, we have to hack the ordering of the calender. For that, locate the file /var/httpd/htdocs/js/thirdparty/fullcalendar-2.4.0/fullcalendar.js, create a backup and modify it. Search in it for this function:

Code: Select all

        // A cmp function for determining which segments should take visual priority
        // DOES NOT WORK ON INVERTED BACKGROUND EVENTS because they have no eventStartMS/eventDurationMS
        compareSegs: function(seg1, seg2) {
                return seg1.eventStartMS - seg2.eventStartMS || // earlier events go first
                        seg2.eventDurationMS - seg1.eventDurationMS || // tie? longer events go first
                        seg2.event.allDay - seg1.event.allDay || // tie? put all-day events first (booleans cast to 0/1)
                        compareByFieldSpecs(seg1.event, seg2.event, this.view.eventOrderSpecs);
        }
and replace it with this one:

Code: Select all

        // A cmp function for determining which segments should take visual priority
        // DOES NOT WORK ON INVERTED BACKGROUND EVENTS because they have no eventStartMS/eventDurationMS
        compareSegs: function(seg1, seg2) {
            if (seg1.event.prio != null && seg2.event.prio != null)
                return seg1.event.prio - seg2.event.prio;
            else
                return seg1.eventStartMS - seg2.eventStartMS || // earlier events go first
                        seg2.eventDurationMS - seg1.eventDurationMS || // tie? longer events go first
                        seg2.event.allDay - seg1.event.allDay || // tie? put all-day events first (booleans cast to 0/1)
                        compareByFieldSpecs(seg1.event, seg2.event, this.view.eventOrderSpecs);
        }
Save it, minify it and replace it with the fullcalendar.min.js (or link it).
Currently using: OTRS 6.0.14 -- MariaDB -- Ubuntu 16 LTS
Post Reply