4 use base qw( Exporter );
10 RT->AddStyleSheets('calendar.css')
11 if RT->can('AddStyleSheets');
13 our @EXPORT_OK = qw( FirstDay LastDay LastDayOfWeek DatesClauses LocalDate
14 SearchDefaultCalendar FindTickets );
17 my ($year, $month, $matchday) = @_;
18 my $set = DateTime::Set->from_recurrence(
19 next => sub { $_[0]->truncate( to => 'day' )->subtract( days => 1 ) }
22 my $day = DateTime->new( year => $year, month => $month );
24 $day = $set->next($day) while $day->day_of_week != $matchday;
30 my ($year, $month, $matchday) = @_;
31 my $set = DateTime::Set->from_recurrence(
32 next => sub { $_[0]->truncate( to => 'day' )->add( days => 1 ) }
35 my $day = DateTime->last_day_of_month( year => $year, month => $month );
37 $day = $set->next($day) while $day->day_of_week != $matchday;
42 my ($year, $month, $day, $matchday) = @_;
43 my $set = DateTime::Set->from_recurrence(
44 next => sub { $_[0]->truncate( to => 'day' )->add( days => 1 ) }
47 my $dt = DateTime->new( year => $year, month => $month, day => $day );
49 $dt = $set->next($dt) while $dt->day_of_week != $matchday;
54 # we can't use RT::Date::Date because it uses gmtime
55 # and we need localtime
58 my ($d,$m,$y) = (localtime($ts))[3..5];
59 sprintf "%4d-%02d-%02d", ($y + 1900), ++$m, $d;
63 my ($Dates, $begin, $end) = @_;
67 my @DateClauses = map {
68 "($_ >= '" . $begin . " 00:00:00' AND $_ <= '" . $end . " 23:59:59')"
70 $clauses .= " AND " . " ( " . join(" OR ", @DateClauses) . " ) "
77 my ($CurrentUser, $Query, $Dates, $begin, $end) = @_;
79 $Query .= DatesClauses($Dates, $begin, $end)
82 my $Tickets = RT::Tickets->new($CurrentUser);
83 $Tickets->FromSQL($Query);
88 while ( my $Ticket = $Tickets->Next()) {
90 # How to find the LastContacted date ?
91 for my $Date (@$Dates) {
92 my $DateObj = $Date . "Obj";
93 push @{ $Tickets{ LocalDate($Ticket->$DateObj->Unix) } }, $Ticket
94 # if reminder, check it's refering to a ticket
95 unless ($Ticket->Type eq 'reminder' and not $Ticket->RefersTo->First)
96 or $AlreadySeen{ LocalDate($Ticket->$DateObj->Unix) }{ $Ticket }++;
103 # Take a user object and return the search with Description "calendar" if it exists
105 sub SearchDefaultCalendar {
106 my $CurrentUser = shift;
107 my $Description = "calendar";
109 # I'm quite sure the loop isn't usefull but...
110 my @Objects = $CurrentUser->UserObj;
111 for my $object (@Objects) {
112 next unless ref($object) eq 'RT::User' && $object->id == $CurrentUser->Id;
113 my @searches = $object->Attributes->Named('SavedSearch');
114 for my $search (@searches) {
115 next if ($search->SubValue('SearchType')
116 && $search->SubValue('SearchType') ne 'Ticket');
119 if "calendar" eq $search->Description;
124 package RT::Interface::Web::Menu;
126 # we should get an add_after method in 4.0.6 (hopefully), but until then
127 # shim this in so I don't copy the code.
128 unless (RT::Interface::Web::Menu->can('add_after')) {
129 *RT::Interface::Web::Menu::add_after = sub {
131 my $parent = $self->parent;
133 for my $contemporary ($parent->children) {
134 if ( $contemporary->key eq $self->key ) {
135 $sort_order = $contemporary->sort_order + 1;
139 $contemporary->sort_order( $contemporary->sort_order + 1 );
142 $parent->child( @_, sort_order => $sort_order );
153 RTx::Calendar - Calendar for RT due tasks
157 This RT extension provides a calendar view for your tickets and your
158 reminders so you see when is your next due ticket. You can find it in
159 the menu Search->Calendar.
161 There's a portlet to put on your home page (see Prefs/MyRT.html)
163 You can also enable ics (ICal) feeds for your default calendar and all
164 your private searches in Prefs/Calendar.html. Authentication is magic
165 number based so that you can give those feeds to other people.
169 If you upgrade from 0.02, see next part before.
171 You need to install those two modules :
176 Install it like a standard perl module
182 If your RT is not in the default path (/opt/rt3) you must set RTHOME
183 before doing the Makefile.PL
187 =head2 Base configuration
189 In RT 3.8 and later, to enable calendar plugin, you must add something
190 like that in your etc/RT_SiteConfig.pm :
192 Set(@Plugins,(qw(RTx::Calendar)));
194 To use MyCalendar portlet you must add MyCalendar to
195 $HomepageComponents in etc/RT_SiteConfig.pm like that :
197 Set($HomepageComponents, [qw(QuickCreate Quicksearch MyCalendar
198 MyAdminQueues MySupportQueues MyReminders RefreshHomepage)]);
200 To enable private searches ICal feeds, you need to give
201 CreateSavedSearch and LoadSavedSearch rights to your users.
203 =head2 Display configuration
205 You can show the owner in each day box by adding this line to your
206 etc/RT_SiteConfig.pm :
208 Set($CalendarDisplayOwner, 1);
210 You can change which fields show up in the popup display when you
211 mouse over a date in etc/RT_SiteConfig.pm :
213 @CalendarPopupFields = ('Status', 'OwnerObj->Name', 'DueObj->ISO');
215 =head2 ICAL feed configuration
217 By default, tickets are todo and reminders event. You can change this
218 by setting $RT::ICalTicketType and $RT::ICalReminderType in etc/RT_SiteConfig.pm :
220 Set($ICalTicketType, "Data::ICal::Entry::Event");
221 Set($ICalReminderType ,"Data::ICal::Entry::Todo");
225 A small help section is available in /Prefs/Calendar.html
227 =head1 UPGRADE FROM 0.02
229 As I've change directory structure, if you upgrade from 0.02 you need
230 to delete old files manually. Go in RTHOME/share/html (by default
231 /opt/rt3/share/html) and delete those files :
233 rm -rf Callbacks/RTx-Calendar
234 rm Tools/Calendar.html
236 RTx-Calendar may work without this but it's not very clean.
240 All bugs should be reported via
241 L<http://rt.cpan.org/Public/Dist/Display.html?Name=RTx-Calendar>
242 or L<bug-RTx-Calendar@rt.cpan.org>.
246 Best Practical Solutions
248 Nicolas Chuche E<lt>nchuche@barna.beE<gt>
250 Idea borrowed from redmine's calendar (Thanks Jean-Philippe).
254 Copyright 2007-2009 by Nicolas Chuche E<lt>nchuche@barna.beE<gt>
256 Copyright 2010-2012 by Best Practical Solutions.
258 This program is free software; you can redistribute it and/or
259 modify it under the same terms as Perl itself.
261 See L<http://www.perl.com/perl/misc/Artistic.html>