Changeset 8063


Ignore:
Timestamp:
07/07/10 15:44:23 (23 months ago)
Author:
CrawfordCurrie
Message:

Item8421: add redefined output formats to better support FullCalendarPlugin and other calendering apps (e.g. by generating JSON for client-side)

Location:
trunk/CalDAVPlugin
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/CalDAVPlugin/data/System/CalDAVPlugin.txt

    r7999 r8063  
    88[[http://en.wikipedia.org/wiki/CalDAV][CalDAV]] server and the 
    99CalendarPlugin. !CalDAV is a protocol used by many calendaring applications, 
    10 the most notable being iCal on Apple products. 
     10two of the most notable being Apple's iCal, and Google calendar. 
    1111 
    1212The plugin works by accessing a calendar on the !CalDAV server and 
    1313generating an event list in the "bulleted event list" format that 
    14 the !CalendarPlugin uses. 
     14the !CalendarPlugin (and HolidaylistPlugin) can use. 
     15 
     16Note that the !CalDAVPlugin will generate individual events for recurrences. 
     17The limits for the number of events generated for each recurrence can be 
     18configured using the =stop= parameter. 
    1519 
    1620%TOC% 
    1721 
    1822---++ Usage 
    19 Put the macro =%<nop>CALDAV{"calendar"}% anywhere on a topic. The 
    20 CalendarPlugin will automatically pick up the events from it. 
     23Put the macro =%<nop>CALDAV{"calendar"}%= anywhere on a topic. This will 
     24generate a bulleted list when the macro is expanded. The 
     25CalendarPlugin (and HolidaylistPlugin) will automatically pick up the 
     26events from this list. 
    2127 
    22 | *Parameter* | *Meaning* | 
    23 | ="calendar"= | The name of a declared calendar | 
    24 | =url= | The url of a remote calendar you want to view | 
    25 | =user= | Username used to access the remote calendar | 
    26 | =pass= | Password for the user used to access the remote calendar | 
    27 | =stop= | Hard stop for recurrences. This takes two values, separated by commas. The first value specifies the maximum number of events generated by a single recurrence, and the second value specifies the maximum number of days to generate events for. For example, =stop="50,365"= (the default) will prevent any recurrence generating more than 50 events, or any events more than 365 days from now. | 
     28*Source parameters* 
     29| *Parameter* | *Meaning* | *Default* | 
     30| ="calendar"= | The name of a pre-declared calendar (see Installation, below) | none; if you don't provide this, you have to specify =url=, =user= and =pass=. | 
     31| =url= | The url of a remote calendar you want to view | from =calendar= | 
     32| =user= | Username used to access the remote calendar | from =calendar= | 
     33| =pass= | Password for the user used to access the remote calendar | from =calendar= | 
    2834 
    29 For obvious security reasons, you are recommended to use the ="calendar"= method to specify the calendar to retrieve. 
     35*Generator parameters* 
     36| *Parameter* | *Meaning* | *Default* | 
     37| =stop= | Hard stop for recurrences. This takes two values, separated by commas. The first value specifies the maximum number of events generated by a single recurrence, and the second value specifies the maximum number of days to generate events for. For example, =stop="50,365"= will prevent any recurrence generating more than 50 events, or any events more than 365 days from now. | =50,365= | 
     38| =target= | Name of a predefined target format e.g. =calendar= for the !CalendarPlugin or =holidaylist= for the !HolidayListPlugin. | =calendar= | 
     39| =event= | Format of an individual event. If =target= is given, this parameter will override the default provided by the =target=. | =target= | 
     40| =range= | Format of an event range. If =target= is given, this parameter will override the default provided by the =target=. | =target= | 
     41| =separator= | String used to separate events in the output | =$n= | 
     42Any other named parameters will be passed on to the output via the corresponding format token. 
    3043 
    31 Note that the !CalendarPlugin does not support a wide range of recurrences, 
    32 and the !CalDAVPlugin will generate individual events for recurrences that 
    33 it can't map to the !CalendarPlugin. The limits for the number of events 
    34 generated for each recurrence can be configured using the plugin parameters. 
     44The output is generated by expanding format tokens in the =event= and =range= format parameters as appropriate. The tokens expanded from the event data are: 
     45| *Token* | *Meaning* | 
     46| =$second= (seconds, 00..59)<br/> =$minute= (minutes, 00..59)<br/> =$hour= (hours, 00..23)<br/> =$day= (day of month, 01..31)<br/> =$month= (month, 01..12)<br/> =$mon= (month in text format, Jan..Dec)<br/> =$year= (4 digit year, 1999)<br/> =$ye= (2 digit year, 99)<br/> =$wd= (day of the week, 1 for Sunday, 2 for Monday, etc)<br/> =$wday= (day of the week, Sun..Sat)<br/> =$weekday= (day of the week, Sunday..Saturday)<br/> =$yearday= (day of the year) | Event time (or start time for a range) | 
     47| As above, but with =e= at the start of the name e.g. =$esecond=, =$eminute= etc. | Range end time | 
     48| =$summary= | Event summary | 
     49| =$description= | Event (full) description | 
     50| =$param= | where =param= is the name of a parameter to %<nop>CALDAV e.g. =$icon= | 
     51 
     52All [[FormatTokens][standard format tokens]] are supported (once all the above format tokens have been expanded). 
     53 
     54__Tip__ you can hide the bulleted list that the %<nop>CALDAV macro generates 
     55by enclosing it in HTML comments e.g. 
     56<verbatim> 
     57<!-- 
     58%CALDAV{"GeorgeWShrub" target="holidaylist"}% 
     59--> 
     60</verbatim> 
     61 
     62---+++ !CalendarPlugin 
     63The events generated when using the =target="calendar"= format use the following formats: 
     64   * =event='$day $month $year - $summary'= 
     65   * =range='$day $month $year - $eday $emonth $eyear - $summary'= 
     66Example: =%<nop>CALDAV{"Jean" target="calendar"}%= 
     67 
     68---+++ !HolidaylistPlugin 
     69   * =event='$day $month $year - $name - $summary - $icon'= 
     70   * =range='$day $month $year - $eday $emonth $eyear - $name - $summary - $icon'= 
     71=$name= is the calendar name, or can be overridden by a =name= parameter. 
     72=$icon= is taken from the parameter of the same name, and is a fragment of TML that generates an icon. It defaults to the empty string. 
     73 
     74Example: =%<nop>CALDAV{"UK" icon="$percntICON{ukflag}$percnt" target="holidaylist"}%= 
    3575 
    3676---++ Installation Instructions 
     
    3878%$INSTALL_INSTRUCTIONS% 
    3979 
    40 Use =configure= to set up the calendars you want to access. 
     80Use =configure= to set up any pre-declared calendars you want to access. Example 
     81configuration: 
     82<verbatim> 
     83$Foswiki::cfg{Plugins}{CalDAVPlugin}{Calendars} = { 
     84   home => { 
     85      url => 'http://127.0.0.1/caldav.php/simon/home/Home.ics', 
     86      user => 'simon', 
     87      pass => 'xxx' 
     88   }, 
     89   office => { 
     90      url => 'http://calendar.corp.com/simple/simon', 
     91      user => 'caladmin', 
     92      pass => 'yyy' 
     93}; 
     94</verbatim> 
     95This configures two calendars, 'home' and 'office'. The parameters specify 
     96the !CalDAV server - for more information on what these parameters mean, see [[http://search.cpan.org/~simonw/Cal-DAV-0.6/lib/Cal/DAV.pm][the !CalDAV module on CPAN]] 
     97 
     98Pre-declaring calendars is obviously important from a perspective of 
     99protecting sensitive username and password information. Unfortunately there 
     100is no way to limit access to remote calendars based on the logged-in user. 
    41101 
    42102---++ Info 
    43103Many thanks to the following sponsors for supporting this work: 
    44    * Apple Inc. 
     104   * Apple, Inc. 
    45105 
    46106|  Author(s): | Crawford Currie http://c-dot.co.uk | 
     
    50110|  Version: | %$VERSION% | 
    51111|  Change History: | <!-- versions below in reverse order -->&nbsp; | 
     112|  1.000 (7 Jul 2010) | Initial release | 
    52113|  Dependencies: | %$DEPENDENCIES% | 
    53114|  Home page: | http://foswiki.org/bin/view/Extensions/CalDAVPlugin | 
  • trunk/CalDAVPlugin/lib/Foswiki/Plugins/CalDAVPlugin.pm

    r7999 r8063  
    55 
    66---+ package Foswiki::Plugins::CalDAVPlugin 
    7  
    8 When developing a plugin it is important to remember that 
    9 Foswiki is tolerant of plugins that do not compile. In this case, 
    10 the failure will be silent but the plugin will not be available. 
    11 See %SYSTEMWEB%.InstalledPlugins for error messages. 
    12  
    13 __NOTE:__ Foswiki:Development.StepByStepRenderingOrder helps you decide which 
    14 rendering handler to use. When writing handlers, keep in mind that these may 
    15 be invoked on included topics. For example, if a plugin generates links to the 
    16 current topic, these need to be generated before the =afterCommonTagsHandler= 
    17 is run. After that point in the rendering loop we have lost the information 
    18 that the text had been included from another topic. 
    19  
    20 __NOTE:__ Not all handlers (and not all parameters passed to handlers) are 
    21 available with all versions of Foswiki. Where a handler has been added 
    22 (or deprecated) the POD comment will indicate this with a "Since" line 
    23 e.g. *Since:* Foswiki::Plugins::VERSION 1.1 
    24  
    25 See http://foswiki.org/Download/ReleaseDates for a breakdown of release 
    26 versions. 
    277 
    288=cut 
     
    3414 
    3515our $VERSION = '$Rev: 7888 $'; 
    36 our $RELEASE = '1.1.1'; 
     16our $RELEASE = '1.000'; 
    3717our $SHORTDESCRIPTION = 'Extract a list of events from a CalDAV (iCal) server'; 
    3818our $NO_PREFS_IN_TOPIC = 1; 
    3919 
    4020sub initPlugin { 
    41     my ( $topic, $web, $user, $installWeb ) = @_; 
    42  
     21    # my ( $topic, $web, $user, $installWeb ) = @_; 
    4322    Foswiki::Func::registerTagHandler( 'CALDAV', \&_CALDAV ); 
    44  
    4523    return 1; 
    4624} 
    4725 
    4826sub _CALDAV { 
    49     my($session, $params, $theTopic, $theWeb) = @_; 
     27    # my($session, $params, $topic, $web) = @_; 
    5028 
    5129    require Foswiki::Plugins::CalDAVPlugin::Core; 
  • trunk/CalDAVPlugin/lib/Foswiki/Plugins/CalDAVPlugin/Config.spec

    r7999 r8063  
    22#---++ CalDAVPlugin 
    33# **PERL** 
    4 # Set of calendars that can be accessed using shortcuts frm the CALDAV macro. 
     4# Set of calendars that can be accessed using shortcuts form the CALDAV macro. 
    55# Each calendar specification must include a user, password and url. 
    66$Foswiki::cfg{Plugins}{CalDAVPlugin}{Calendars} = { 
    7    home => { 
    8       url => 'http://simian:x@192.168.1.12/caldav.php/simian/home/Home.ics', 
    9       user => 'simian', 
    10       pass => 'x' 
     7   example => { 
     8      url => 'http://example.com/caldav.php/example', 
     9      user => 'example', 
     10      pass => 'example' 
    1111   }, 
    1212}; 
  • trunk/CalDAVPlugin/lib/Foswiki/Plugins/CalDAVPlugin/Core.pm

    r7999 r8063  
    11# See bottom of file for license and copyright information 
    22package Foswiki::Plugins::CalDAVPlugin::Core; 
     3 
     4use strict; 
     5use warnings; 
    36 
    47use Cal::DAV; 
     
    69use DateTime::Format::ICal; 
    710 
    8 # Target formats 
    9 # dd MMM yyyy - description 
    10 #   * 09 Dec 2002 - Expo 
    11 # dd MMM yyyy - dd MMM yyyy - description 
    12 #   * 02 Feb 2002 - 04 Feb 2002 - Vacation 
    13 # dd MMM - description 
    14 #   * 05 Jun - Every 5th of June 
    15 # w DDD MMM - description 
    16 #   * 2 Tue Mar - Every 2nd Tuesday of March 
    17 # rrule: FREQ=MONTHLY 
    18 # L DDD MMM - description 
    19 #   * L Mon May - The last Monday of May 
    20 # rrule: FREQ=MONTHLY 
    21 # A dd MMM yyyy - description 
    22 #   * A 20 Jul 1969 - First moon landing 
    23 # FREQ=YEARLY 
    24 # w DDD - description 
    25 #   * 1 Fri - Every 1st Friday of the month 
    26 # FREQ=MONTHLY,BYDAY=FR 
    27 # L DDD - description 
    28 #   * L Mon - The last Monday of each month 
    29 # dd - description       
    30 #   * 14 - The 14th of every month 
    31 # E DDD - description 
    32 #   * E Wed - Every Wednesday 
    33 # E DDD dd MMM yyyy - description 
    34 #   * E Wed 27 Jan 2005 - Every Wednesday Starting 27 Jan 2005 
    35 # E DDD dd MMM yyyy - dd MMM yyyy - description 
    36 #   * E Wed 1 Jan 2005 - 27 Jan 2005 - Every Wednesday from 1 Jan 2005 through 27 Jan 2005 (inclusive) 
    37 # En dd MMM yyyy - description 
    38 #   * E3 02 Dec 2002 - Every three days starting 02 Dec 2002 
    39 # En dd MMM yyyy - dd MMM yyyy - description 
    40 #   * E3 12 Apr 2005 - 31 Dec 2005 - Every three days from 12 Apr 2005 through 31 Dec 2005 (inclusive)  
     11my %formats = ( 
     12    calendar => { 
     13        event => '   * $day $month $year - $summary', 
     14        range => '   * $day $month $year - $eday $emonth $eyear - $summary', 
     15    }, 
     16    holidaylist => { 
     17        event => '   * $day $month $year - $name - $summary - $icon', 
     18        range => '   * $day $month $year - $eday $emonth $eyear - $name - $summary', 
     19    }, 
     20   ); 
     21 
     22use constant TRACE => 1; 
     23 
     24=begin TML 
     25 
     26---++ StaticMethod CALDAV($session, $params, $topic, $web) 
     27 
     28Macro handler, indirected via CalDAVPlugin.pm 
     29 
     30Read CalDAV data from a remote server and generate output in a format 
     31suitable for use by different plugins. 
     32 
     33You can define an output format (using the 'target' parameter), or 
     34select from a range of predefined formats. 'header', 'footer' and 
     35'separator' have conventional interpretations, and all standard formatting 
     36tokens are supported. 
     37 
     38=cut 
    4139 
    4240sub CALDAV { 
     
    6765    my $cal; 
    6866    eval { 
    69        $cal = Cal::DAV->new( user => $user, pass => $pass, url => $url); 
     67        $cal = Cal::DAV->new( user => $user, pass => $pass, url => $url); 
    7068    }; 
    7169    unless ($cal) { 
     
    7371        return "<span class='foswikiAlert'>Calendar could not be opened</span>"; 
    7472    } 
     73 
     74    $params->{name} = $calendar unless defined $params->{name}; 
     75    $params->{name} = $params->{url} unless defined $params->{name}; 
     76    $params->{separator} = '$n()' unless defined $params->{separator}; 
    7577 
    7678    my $stop = $params->{stop}; 
     
    9294                $event{start} = 
    9395                  DateTime::Format::ICal->parse_datetime($p->value); 
    94                 push(@events, "# dtstart ".$event{start}); 
     96                push(@events, " dtstart ".$event{start}) if (TRACE); 
    9597            } elsif ($k eq 'dtend') { 
    9698                $event{end} = 
    9799                  DateTime::Format::ICal->parse_datetime($p->value); 
    98                 push(@events, "# dtend ".$event{end}); 
     100                push(@events, " dtend ".$event{end}) if (TRACE); 
    99101            } elsif ($k eq 'summary') { 
     102                $event{summary} = $p->{value}; 
     103                push(@events, " summary ".$event{summary}) if (TRACE); 
     104            } elsif ($k eq 'description') { 
    100105                $event{description} = $p->{value}; 
    101                 $event{description} =~ s/\n/<br>/gs; 
    102106            } elsif ($k eq 'rrule') { 
    103                 push(@events, "# rrule $p->{value}"); 
     107                push(@events, " rrule $p->{value}") if (TRACE); 
    104108                $event{recurrence} = $p->{value}; 
    105109            } 
     
    113117                recurrence => $event{recurrence}, 
    114118                dtstart => $event{start}); 
    115             # Limit to the request range *or* $stopDays *or* $stopCount repeats 
     119            # Limit to the request range *or* $stopDays *or* 
     120            # $stopCount repeats 
    116121            my $span = DateTime::Span->from_datetimes( 
    117122                start => $event{start} || 
     
    122127            $event{count} = $stopCount if ($event{count} > $stopCount); 
    123128 
    124             push(@events, "   * ".Foswiki::Time::formatTime( 
    125                 Foswiki::Time::parseTime($event{start})) 
    126                    . ' - '.$event{description}); 
     129            my $s = Foswiki::Time::parseTime($event{start}); 
     130            push(@events, 
     131                 { 
     132                     start => $s, 
     133                     summary => $event{summary}, 
     134                     description => $event{description}, 
     135                 }); 
    127136            $event{count}--; 
    128137            my $iter = $recur->iterator(span => $span); 
    129138            while ( $event{count} && (my $dt = $iter->next) ) { 
    130                 push(@events, "   * " . Foswiki::Time::formatTime( 
    131                     Foswiki::Time::parseTime($dt)) 
    132                    . ' - '.$event{description}); 
     139                my $d = Foswiki::Time::parseTime($dt); 
     140                next if $d == $s; 
     141                push(@events, 
     142                     { 
     143                         start => $d, 
     144                         summary => $event{summary}, 
     145                         description => $event{description}, 
     146                     }); 
    133147                $event{count}--; 
    134148            } 
    135149        } else { 
    136             my $evs = Foswiki::Time::formatTime( 
    137                     Foswiki::Time::parseTime($event{start})); 
    138             if (defined $event{end}) { 
    139                 $evs .= ' - '.Foswiki::Time::formatTime( 
    140                     Foswiki::Time::parseTime($event{end})); 
    141             } 
    142             $evs .= ' - '.$event{description}; 
    143             push(@events, "   * $evs"); 
     150            push(@events, 
     151                 { 
     152                     start => Foswiki::Time::parseTime($event{start}), 
     153                     end => (defined $event{end}) ? 
     154                       Foswiki::Time::parseTime($event{end}) : undef, 
     155                     summary => $event{summary}, 
     156                     description => $event{description}, 
     157                 }); 
    144158        } 
    145159    } 
    146     return "\n".join("\n", @events)."\n"; 
     160    return _formatEvents(\@events, $params); 
     161} 
     162 
     163sub _formatEvents { 
     164    my ($events, $params) = @_; 
     165 
     166    my $format; 
     167    my $target = $params->{target} || 'calendar'; 
     168    if ($target) { 
     169        $format = $formats{$target} || $formats{calendar}; 
     170    } 
     171    my $es = $params->{event}; 
     172    $es = $format->{event} unless defined $es; 
     173    my $rs = $params->{range}; 
     174    $rs = $format->{range} unless defined $rs; 
     175    my @r = map { _formatEvent($_, $params, $es, $rs) } @$events; 
     176    return Foswiki::Func::decodeFormatTokens(join($params->{separator}, @r)); 
     177} 
     178 
     179sub _formatEvent { 
     180    my ($event, $params, $es, $rs) = @_; 
     181    return $event unless ref($event); 
     182    my $s = (defined $event->{end}) ? $rs : $es; 
     183    $s = Foswiki::Time::formatTime($event->{start}, $s); 
     184    if (defined $event->{end}) { 
     185        $s =~ s/\$e(seco?n?d?s?|minu?t?e?s?|hour?s?|day|w(eek|day)|dow 
     186                |mo(?:nt?h?)?|ye(?:ar)?)(\(\)|(?=\W|$))/\$$1/gx; 
     187        $s = Foswiki::Time::formatTime($event->{end}, $s); 
     188    } 
     189    foreach my $f (keys %$event) { 
     190        $s =~ s/\$$f(\(\)|(?=\W|$))/$event->{$f}/; 
     191    } 
     192    foreach my $f (keys %$params) { 
     193        next if $f =~ /^_/; 
     194        $s =~ s/\$$f(\(\)|(?=\W|$))/$params->{$f}/; 
     195    } 
     196    return $s; 
    147197} 
    148198 
Note: See TracChangeset for help on using the changeset viewer.