File Coverage

blib/lib/Net/ICal/ETJ.pm
Criterion Covered Total %
statement 30 56 53.5
branch 4 16 25.0
condition 6 18 33.3
subroutine 8 10 80.0
pod 2 2 100.0
total 50 102 49.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             # vi:sts=4:shiftwidth=4
3             # -*- Mode: perl -*-
4             #======================================================================
5             #
6             # This package is free software and is provided "as is" without
7             # express or implied warranty. It may be used, redistributed and/or
8             # modified under the same terms as perl itself. ( Either the Artistic
9             # License or the GPL. )
10             #
11             # $Id: ETJ.pm,v 1.40 2001/08/04 04:59:36 srl Exp $
12             #
13             # (C) COPYRIGHT 2000-2001, Reefknot developers.
14             #
15             # See the AUTHORS file included in the distribution for a full list.
16             #======================================================================
17              
18             =head1 NAME
19              
20             Net::ICal::ETJ - iCalendar event, todo and journal entry base class
21              
22             =head1 SYNOPSIS
23              
24             use Net::ICal::ETJ;
25              
26             my $c = new Net::ICal::ETJ(%arguments);
27              
28             =cut
29              
30             package Net::ICal::ETJ;
31 1     1   5 use strict;
  1         1  
  1         30  
32              
33 1     1   5 use base qw(Net::ICal::Component);
  1         1  
  1         57  
34              
35 1     1   4 use Net::ICal::Duration;
  1         1  
  1         5  
36 1     1   532 use Net::ICal::Period;
  1         2  
  1         10  
37 1     1   24 use Net::ICal::Time;
  1         2  
  1         3  
38 1     1   21 use Net::ICal::Util qw(:all);
  1         2  
  1         1145  
39              
40             # make sure that this object has the bare minimum requirements
41             # specified by the RFC.
42              
43             #TODO: according to http://www.imc.org/ietf-calendar/mail-archive/msg02603.html
44             # uid isn't required for iCal, just for iTIP. How do we handle this?
45             # (Suggestion: make Net::iTIP a child class, and have its new() routine
46             # validate UIDs. --srl)
47             #BUG: 424113
48              
49             =pod
50              
51             =head1 DESCRIPTION
52              
53             Net::ICal::ETJ represents iCalendar events, todo items and
54             journal entries. It's a base class for N::I::Event, N::I::Todo,
55             and N::I::Journal.
56              
57             Casual users shouldn't ever need to actually make an ETJ object. The
58             following documentation is for developers only.
59              
60             =head1 DEVELOPER DOCUMENTATION
61              
62             =head2 $self->validate
63              
64             Performs basic validation on a Net::ICal event, todo or journal object.
65             Returns 1 for success, undef for failure.
66              
67             Required properties include:
68              
69             =over 4
70              
71             =item *
72              
73             organizer
74              
75             =back
76              
77             =for testing
78             use Net::ICal::ETJ;
79             use Net::ICal::Attendee;
80             my $c = new Net::ICal::ETJ;
81              
82             my $args = { attendee => new Net::ICal::Attendee('mailto:foo@example.com');
83              
84             ok( $c->validate($args) , "Validation of simple, correct ETJ");
85             ok( not($c->validate()) , "ETJ with no organizer should fail");
86              
87             =cut
88              
89             sub validate {
90 25     25 1 36 my ($self) = @_;
91              
92             #unless (defined $self->organizer) {
93             # push (@{$@}, "Need an organizer");
94             #}
95 25 50       135 unless (defined $self->uid) {
96 0         0 add_validation_error ($self, "Need a uid");
97             }
98             # 4.8.7.1
99 25 50 66     666 if ($self->created and $self->created->as_ical_value !~ /Z$/) {
100 0         0 add_validation_error ($self, "The created date/time MUST be a UTC value");
101             }
102             # 4.8.7.2
103 25 50 66     1018 if ($self->dtstamp and $self->dtstamp->as_ical_value !~ /Z$/) {
104 0         0 add_validation_error ($self, "The dtstamp date/time MUST be a UTC value");
105             }
106             # 4.8.7.3
107 25 50 66     1365 if ($self->last_modified and $self->last_modified->as_ical_value !~ /Z$/) {
108 0         0 add_validation_error ($self, "The last_modified date/time MUST be a UTC value");
109             }
110              
111             # ugly hardcoded call because $self->SUPER is ::ETJ, since $self is
112             # one of Event, Todo or Journal.
113 25         941 return Net::ICal::Component::validate ($self);
114             }
115              
116              
117             sub _init {
118 0     0   0 my ($self) = @_;
119              
120 0         0 my $time = Net::ICal::Time->new (epoch => time);
121              
122 0         0 $self->uid (create_uuid ($time));
123              
124             # since DTSTAMP is required by the RFC, we'll create it
125             # if it's not given to us.
126 0 0       0 unless (defined ($self->dtstamp) ) {
127 0         0 $self->dtstamp ($time);
128             }
129             }
130              
131             =pod
132              
133             =head2 _create($foo, $class, %args)
134              
135             Creates the ETJ object map using Class::MethodMapper
136              
137             =for testing
138             ok(Net::ICal::ETJ::_create($foo, $class, %args), "Simple call to _create");
139              
140             =cut
141              
142             sub _create {
143 25     25   52 my ($foo, $class, %args) = @_;
144              
145 25         1729 my $map = {
146             alarms => { # RFC 4.6.6 - optional in VEVENT and VTODO.
147             type => 'parameter',
148             doc => 'the alarms related to this event',
149             domain => 'ref',
150             options => 'ARRAY',
151             value => undef,
152             },
153             class => { # RFC2445 4.8.1.3 - optional 1x in VEVENT, VTODO, VJOURNAL
154             # this is *not* an access control system; this is the intention of
155             # the calendar owner. see the RFC.
156             type => 'parameter',
157             doc => 'who can see this?',
158             domain => 'enum',
159             options => [qw(PUBLIC PRIVATE CONFIDENTIAL)],
160             value => undef,
161             },
162             created => { # RFC2445 4.8.7.1 - optional 1x in VEVENT, VTODO, VJOURNAL
163             type => 'parameter',
164             doc => 'when was this first created',
165             domain => 'ref',
166             options => 'Net::ICal::Time',
167             value => undef,
168             },
169             description => { # RFC2445 4.8.1.5 - optional 1x in VEVENT, VTODO,
170             # VJOURNAL, VALARM - can occur more in VJOURNAL
171             type => 'parameter',
172             doc => 'more details about this event',
173             domain => 'param',
174             options => [qw(altrep language)],
175             value => undef,
176             },
177             #TODO: needs to be in UTC. how to enforce?
178             #BUG: 424114
179             dtstart => { # RFC2445 4.8.2.4 - optional in VTODO, VFREEBUSY,
180             # VTIMEZONE; required in VEVENT; not specified in
181             # VJOURNAL
182             type => 'parameter',
183             doc => '',
184             domain => 'ref',
185             options => 'Net::ICal::Time',
186             value => undef,
187             },
188             duration => { # RFC2445 4.8.2.5 - optional in VEVENT or VTODO,
189             # meaningless in VJOURNAL
190             type => 'parameter',
191             doc => 'how long this task lasts',
192             domain => 'ref',
193             options => 'Net::ICal::Duration',
194             value => undef,
195             },
196             due => { # RFC2445 4.8.2.3 - optional in a VTODO, used nowhere else
197             type => 'parameter',
198             doc => 'when this TODO item is done',
199             domain => 'ref',
200             options => 'Net::ICal::Time',
201             value => undef,
202             },
203             #FIXME: must be a float pair (latitude;longitude) where event/todo is
204             #BUG: 424115
205             geo => { # RFC2445 4.8.1.6 - optional in VEVENT or VTODO.
206             type => 'parameter',
207             doc => '',
208             value => undef,
209             },
210             #TODO: needs to be in UTC. how to enforce?
211             #BUG: 424116
212             #TODO: a server will need to keep track of this automatically.
213             #BUG: 424117
214             last_modified => { # RFC2445 4.8.7.3 - optional in VEVENT, VTODO,
215             # and VJOURNAL
216             type => 'parameter',
217             doc => '',
218             domain => 'ref',
219             options => 'Net::ICal::Time',
220             value => undef,
221             },
222             location => { # RFC2445 4.8.1.7 - optional in VEVENT or VTODO
223             type => 'parameter',
224             doc => '',
225             domain => 'param',
226             options => [qw(altrep language)],
227             value => undef,
228             },
229             organizer => { # RFC2445 4.8.4.3 - REQUIRED in VEVENT, VTODO,
230             # VJOURNAL, VFREEBUSY
231             type => 'parameter',
232             doc => '',
233             domain => 'ref',
234             options => 'Net::ICal::Attendee',
235             value => undef,
236             },
237             # 0=undefined; 1=highest priority; 9=lowest priority, but CUAs
238             # can use other schemes. See the RFC.
239             priority => { # RFC2445 4.8.1.9 - optional in VEVENT or VTODO
240             type => 'parameter',
241             doc => 'How high a priority is this?',
242             value => 0,
243             },
244             #TODO: this is the date/time this object was created; should we
245             # set it by default if the user doesn't set it? Does this
246             # have to be in UTC?
247             #BUG: 424118
248             dtstamp => { # RFC2445 4.8.7.2 - REQUIRED in VEVENT, VTODO,
249             # VJOURNAL, VFREEBUSY
250             type => 'parameter',
251             doc => '',
252             domain => 'ref',
253             options => 'Net::ICal::Time',
254             value => undef,
255             },
256             #FIXME: these differ radically for VEVENT, VTODO, and VJOURNAL.
257             # we need to override this in subclasses or something.
258             #BUG: 424120
259             status => { # RFC2445 4.8.1.1 - optional in VEVENT, VTODO, and VJOURNAL.
260             type => 'parameter',
261             doc => 'overall status or confirmation value',
262             domain => 'enum',
263             options => [qw(TENTATIVE CONFIRMED CANCELLED
264             NEEDS-ACTION IN-PROGRESS COMPLETED CANCELLED
265             DRAFT FINAL CANCELLED)],
266             value => undef,
267             },
268             summary => { # RFC2445 4.8.1.12 - optional in VEVENT, VTODO,
269             # VJOURNAL, and VALARM.
270             type => 'parameter',
271             doc => 'a one-line summary',
272             options => [qw(altrep language)],
273             value => undef,
274             },
275             uid => { # RFC2445 4.8.4.7 - REQUIRED in VEVENT, VTODO, VJOURNAL,
276             # VFREEBUSY
277             type => 'parameter',
278             doc => 'global-unique identifier for a generated event',
279             value => undef,
280             },
281             url => { # RFC2445 4.8.4.6 - optional 1x in VEVENT, VTODO, VJOURNAL,
282             # VFREEBUSY.
283             type => 'parameter',
284             doc => 'a url associated with this event',
285             value => undef,
286             },
287             # This keeps track of *which* Monday at 10am meeting
288             # this one is. it's used together with UID.
289             recurrence_id => { # RFC2445 4.8.4.4 - optional in any recurring
290             # calendar component.
291             type => 'parameter',
292             doc => 'which occurrence of a recurring event is this?',
293             domain => 'ref',
294             options => 'Net::ICal::Time',
295             value => undef,
296             },
297             #TODO: there can be one or more of these, and it should be a
298             # N::I::Attach
299             #BUG: 424123
300             attach => { # RFC2445 4.8.1.1 - optional in VEVENT, VTODO,
301             # VJOURNAL, VALARM
302             type => 'parameter',
303             doc => '',
304             domain => 'ref',
305             options => 'ARRAY',
306             value => undef,
307             },
308             attendee => { # RFC2445 4.8.4.1 - optional in VEVENT, VTODO, VJOURNAL;
309             # PROHIBITED in VFREEBUSY and VALARM
310             type => 'parameter',
311             doc => 'who is coming to this meeting',
312             domain => 'ref',
313             options => 'ARRAY',
314             value => undef,
315             },
316             categories => { # RFC2445 4.8.1.2 - optional in VEVENT, VTODO, VJOURNAL
317             type => 'parameter',
318             doc => 'ref',
319             options => 'ARRAY', # there can be more than one of these, just text
320             value => undef,
321             },
322             #FIXME: there can be more than one of these in an event/todo/journal.
323             # do they need to be ordered in an array?
324             #BUG: 424124
325             comment => { # RFC2445 4.8.1.4 - optional in VEVENT, VTODO, VJOURNAL,
326             # VTIMEZONE, VFREEBUSY
327             type => 'parameter',
328             doc => '',
329             domain => 'param',
330             options => [qw(altrep param)],
331             value => undef,
332             },
333             # i'm really surprised this isn't an Attendee type. but i guess
334             # it makes sense.
335             contact => { # RFC2445 4.8.4.2 - optional in VEVENT, VTODO, VJOURNAL,
336             # VFREEBUSY
337             type => 'parameter',
338             doc => 'who to contact about this event',
339             value => undef,
340             },
341             exdate => { # RFC2445 4.8.5.1 - optional in any recurring component.
342             type => 'parameter',
343             doc => 'a 1-date exception to a recurrence rule',
344             domain => 'ref',
345             options => 'Net::ICal::Time',
346             value => undef,
347             },
348             exrule => { # RFC2445 4.8.5.2 - optional in VEVENT, VTODO, VJOURNAL
349             # should be a set of Net::ICal::Recurrence objects
350             type => 'parameter',
351             doc => 'A rule that defines the exceptions to a recurrence rule',
352             domain => 'ref',
353             options => 'ARRAY',
354             value => undef,
355             },
356             # This is a varying-granularity field; read the RFC.
357             # 1.x = preliminary success, pending completion
358             # 2.x = request completed successfully, possibly with a fallback.
359             # 3.x = request failed, syntax or semantic error in client req format
360             # 4.x = scheduling error; some kind of failure in the scheduling system.
361             # Values can look like "x.y.z" to give even more granularity.
362             #TODO: I think we have to define our own error subcodes. --srl
363             #BUG: 424125
364             # See the CAP draft section 8 for one possible set.
365             request_status => { # RFC2445 4.8.8.2 - optional in VEVENT, VTODO,
366             # VJOURNAL, VFREEBUSY
367             type => 'parameter',
368             doc => 'how successful have we been at scheduling this',
369             value => undef,
370             },
371             related_to => { # RFC2445 4.8.4.5 - optional multiple times in
372             # VEVENT, VTODO, or VJOURNAL
373             type => 'parameter',
374             doc => ' other events/todos/journals this item relates to',
375             domain => 'ref',
376             options => 'ARRAY', # could be a hash, i guess.
377             value => undef,
378             },
379             #TODO: only related to VEVENT or VTODO, not VJOURNAL; should
380             # there be an error generated if someone requests this in a
381             # VJOURNAL?
382             #BUG: 424126
383             #TODO: is this a Net::ICal::Attendee?
384             #BUG: 424127
385             resources => { # RFC2445 4.8.1.10 - optional in VEVENT or VTODO
386             type => 'parameter',
387             doc => '',
388             value => undef,
389             },
390             # should be a set of N::I::Times, i think.
391             rdate => { # RFC2445 4.8.5.3 - optional in VEVENT, VTODO, VJOURNAL, VTIMEZONE
392             type => 'parameter',
393             doc => 'define a set of dates as part of a recurrence set',
394             domain => 'ref',
395             options => 'ARRAY',
396             value => undef,
397             },
398             # should be a set of Net::ICal::Recurrence objects
399             rrule => { # RFC2445 4.8.5.4 - optional multiple times in recurring
400             # VEVENT, VTODO, VJOURNALs. optional 1x in STANDARD or
401             # DAYLIGHT parts of VTIMEZONE.
402             type => 'parameter',
403             doc => 'a rule to describe when this event, todo, or journal repeats',
404             domain => 'ref',
405             options => 'ARRAY',
406             value => undef,
407             },
408             # Whenever DTSTART, DTEND, DUE, RDATE, RRULE,
409             # EXDATE, EXRULE, or STATUS are changed, this has to be incremented.
410             # starts at 0 and counts up.
411             #TODO: we should be handling this internally, not letting the user
412             # manipulate it.
413             sequence => { # RFC2445 4.8.7.4 - optional in VEVENT, VTODO, VJOURNAL
414             type => 'parameter',
415             doc => 'version number of this event/todo/journal',
416             value => undef,
417             }
418              
419             #TODO: look also at rfc2445 4.2.15, RELTYPE - is that something that should
420             # go here?
421             #BUG: 424133
422             };
423 25         73 my $myclass = __PACKAGE__;
424 25         110 my $self = $myclass->SUPER::new ($class, $map, %args);
425 25         59 bless $self, $foo;
426 25         164 return $self;
427             }
428              
429             =pod
430              
431             =head2 $self->occurrences($reqperiod)
432              
433             Given a period of time, determines occurences of an Event/Todo/Journal
434             within that period. Returns an arrayref of... what exactly?
435              
436             =for testing
437             ok( $c->occurences($period) , "Simple call to occurrences");
438             ok( $c->occurences($empty) , "Empty period");
439             ok( not($c->occurences($bogusperiod)) , "Bogus period");
440              
441             =cut
442              
443             sub occurrences ($) {
444 0     0 1   my ($self, $reqperiod) = @_;
445              
446             # Get this event's dtstart, and bump up req
447             #TODO: What do we do if dtstart isn't defined? Should dtstart be required?
448             #BUG: 424135
449 0           my $dtstart = $self->dtstart;
450              
451             # Does this event have any recurrence rules? If not, just throw back this
452             # period for now.
453             #TODO: add EXRULE, EXDATE, and RDATE support
454 0           my $ar_rrules = $self->rrule();
455 0           my $ar_exrules = $self->exrule();
456 0           my $dtend = $self->dtend;
457 0           my $duration = $self->duration;
458             #FIXME -- missing the rest
459 0 0 0       if (!$ar_rrules || !@$ar_rrules) {
460 0           my $dstartint = $dtstart->epoch;
461 0 0 0       if ($dstartint >= $reqperiod->start->epoch &&
462             $dstartint <= $reqperiod->end->epoch) {
463 0   0       return [ Net::ICal::Period->new($dtstart, $duration || $dtend) ];
464             } else {
465 0           return [ ];
466             }
467             }
468              
469             # Naive for now -- just collect up the RRULE stuff and don't do anything
470             # to it.
471 0           my @occurrences;
472 0 0         if (@$ar_rrules) {
473 0           foreach my $rrule (@$ar_rrules) {
474 0           push(@occurrences, @{$rrule->occurrences($self, $reqperiod)});
  0            
475             }
476             } else {
477             #FIXME: Event must currently start within period
478             }
479              
480 0           return \@occurrences;
481             }
482              
483             1;
484             __END__