4 use base qw( Exporter );
10 RT->AddStyleSheets('calendar.css')
11 if RT->can('AddStyleSheets');
13 our @EXPORT_OK = qw( FirstDay LastDay LastDayOfWeek );
16 my ($year, $month, $matchday) = @_;
17 my $set = DateTime::Set->from_recurrence(
18 next => sub { $_[0]->truncate( to => 'day' )->subtract( days => 1 ) }
21 my $day = DateTime->new( year => $year, month => $month );
23 $day = $set->next($day) while $day->day_of_week != $matchday;
29 my ($year, $month, $matchday) = @_;
30 my $set = DateTime::Set->from_recurrence(
31 next => sub { $_[0]->truncate( to => 'day' )->add( days => 1 ) }
34 my $day = DateTime->last_day_of_month( year => $year, month => $month );
36 $day = $set->next($day) while $day->day_of_week != $matchday;
41 my ($year, $month, $day, $matchday) = @_;
42 my $set = DateTime::Set->from_recurrence(
43 next => sub { $_[0]->truncate( to => 'day' )->add( days => 1 ) }
46 my $day = DateTime->new( year => $year, month => $month, day => $day );
48 $day = $set->next($day) while $day->day_of_week != $matchday;
53 # we can't use RT::Date::Date because it uses gmtime
54 # and we need localtime
57 my ($d,$m,$y) = (localtime($ts))[3..5];
58 sprintf "%4d-%02d-%02d", ($y + 1900), ++$m, $d;
62 my ($Dates, $begin, $end) = @_;
66 my @DateClauses = map {
67 "($_ >= '" . $begin . " 00:00:00' AND $_ <= '" . $end . " 23:59:59')"
69 $clauses .= " AND " . " ( " . join(" OR ", @DateClauses) . " ) "
76 my ($CurrentUser, $Query, $Dates, $begin, $end) = @_;
78 $Query .= DatesClauses($Dates, $begin, $end)
81 my $Tickets = RT::Tickets->new($CurrentUser);
82 $Tickets->FromSQL($Query);
87 while ( my $Ticket = $Tickets->Next()) {
89 # How to find the LastContacted date ?
90 for my $Date (@$Dates) {
91 my $DateObj = $Date . "Obj";
92 push @{ $Tickets{ LocalDate($Ticket->$DateObj->Unix) } }, $Ticket
93 # if reminder, check it's refering to a ticket
94 unless ($Ticket->Type eq 'reminder' and not $Ticket->RefersTo->First)
95 or $AlreadySeen{ LocalDate($Ticket->$DateObj->Unix) }{ $Ticket }++;
102 # Take a user object and return the search with Description "calendar" if it exists
104 sub SearchDefaultCalendar {
105 my $CurrentUser = shift;
106 my $Description = "calendar";
108 # I'm quite sure the loop isn't usefull but...
109 my @Objects = $CurrentUser->UserObj;
110 for my $object (@Objects) {
111 next unless ref($object) eq 'RT::User' && $object->id == $CurrentUser->Id;
112 my @searches = $object->Attributes->Named('SavedSearch');
113 for my $search (@searches) {
114 next if ($search->SubValue('SearchType')
115 && $search->SubValue('SearchType') ne 'Ticket');
118 if "calendar" eq $search->Description;
123 package RT::Interface::Web::Menu;
125 # we should get an add_after method in 4.0.6 (hopefully), but until then
126 # shim this in so I don't copy the code.
127 unless (RT::Interface::Web::Menu->can('add_after')) {
128 *RT::Interface::Web::Menu::add_after = sub {
130 my $parent = $self->parent;
132 for my $contemporary ($parent->children) {
133 if ( $contemporary->key eq $self->key ) {
134 $sort_order = $contemporary->sort_order + 1;
138 $contemporary->sort_order( $contemporary->sort_order + 1 );
141 $parent->child( @_, sort_order => $sort_order );
152 RTx::Calendar - Calendar for RT due tasks
156 This RT extension provides a calendar view for your tickets and your
157 reminders so you see when is your next due ticket. You can find it in
158 the menu Search->Calendar.
160 There's a portlet to put on your home page (see Prefs/MyRT.html)
162 You can also enable ics (ICal) feeds for your default calendar and all
163 your private searches in Prefs/Calendar.html. Authentication is magic
164 number based so that you can give those feeds to other people.
168 If you upgrade from 0.02, see next part before.
170 You need to install those two modules :
175 Install it like a standard perl module
181 If your RT is not in the default path (/opt/rt3) you must set RTHOME
182 before doing the Makefile.PL
186 =head2 Base configuration
188 In RT 3.8 and later, to enable calendar plugin, you must add something
189 like that in your etc/RT_SiteConfig.pm :
191 Set(@Plugins,(qw(RTx::Calendar)));
193 To use MyCalendar portlet you must add MyCalendar to
194 $HomepageComponents in etc/RT_SiteConfig.pm like that :
196 Set($HomepageComponents, [qw(QuickCreate Quicksearch MyCalendar
197 MyAdminQueues MySupportQueues MyReminders RefreshHomepage)]);
199 To enable private searches ICal feeds, you need to give
200 CreateSavedSearch and LoadSavedSearch rights to your users.
202 =head2 Display configuration
204 You can show the owner in each day box by adding this line to your
205 etc/RT_SiteConfig.pm :
207 Set($CalendarDisplayOwner, 1);
209 You can change which fields show up in the popup display when you
210 mouse over a date in etc/RT_SiteConfig.pm :
212 @CalendarPopupFields = ('Status', 'OwnerObj->Name', 'DueObj->ISO');
214 =head2 ICAL feed configuration
216 By default, tickets are todo and reminders event. You can change this
217 by setting $RT::ICalTicketType and $RT::ICalReminderType in etc/RT_SiteConfig.pm :
219 Set($ICalTicketType, "Data::ICal::Entry::Event");
220 Set($ICalReminderType ,"Data::ICal::Entry::Todo");
224 A small help section is available in /Prefs/Calendar.html
226 =head1 UPGRADE FROM 0.02
228 As I've change directory structure, if you upgrade from 0.02 you need
229 to delete old files manually. Go in RTHOME/share/html (by default
230 /opt/rt3/share/html) and delete those files :
232 rm -rf Callbacks/RTx-Calendar
233 rm Tools/Calendar.html
235 RTx-Calendar may work without this but it's not very clean.
239 All bugs should be reported via
240 L<http://rt.cpan.org/Public/Dist/Display.html?Name=RTx-Calendar>
241 or L<bug-RTx-Calendar@rt.cpan.org>.
245 Best Practical Solutions
247 Nicolas Chuche E<lt>nchuche@barna.beE<gt>
249 Idea borrowed from redmine's calendar (Thanks Jean-Philippe).
253 Copyright 2007-2009 by Nicolas Chuche E<lt>nchuche@barna.beE<gt>
255 Copyright 2010-2012 by Best Practical Solutions.
257 This program is free software; you can redistribute it and/or
258 modify it under the same terms as Perl itself.
260 See L<http://www.perl.com/perl/misc/Artistic.html>