File Coverage

blib/lib/Net/Google/Calendar/Entry.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Net::Google::Calendar::Entry;
2             {
3             $Net::Google::Calendar::Entry::VERSION = '1.05';
4             }
5              
6 1     1   4070 use strict;
  1         3  
  1         35  
7 1     1   1065 use Data::Dumper;
  1         8482  
  1         85  
8 1     1   1621 use DateTime;
  1         184386  
  1         36  
9 1     1   413 use XML::Atom;
  0            
  0            
10             use XML::Atom::Entry;
11             use XML::Atom::Util qw( set_ns first nodelist childlist iso2dt create_element);
12             use base qw(XML::Atom::Entry Net::Google::Calendar::Base);
13             use Net::Google::Calendar::Person;
14             use Net::Google::Calendar::Comments;
15              
16              
17             =head1 NAME
18              
19             Net::Google::Calendar::Entry - entry class for Net::Google::Calendar
20              
21             =head1 SYNOPSIS
22              
23             my $event = Net::Google::Calendar::Entry->new();
24             $event->title('Party!');
25             $event->content('P-A-R-T-Why? Because we GOTTA!');
26             $event->location("My Flat, London, England");
27             $event->status('confirmed');
28             $event->transparency('opaque');
29             $event->visibility('private');
30              
31             my $author = Net::Google::Calendar::Person->new;
32             $author->name('Foo Bar');
33             $author->email('foo@bar.com');
34             $entry->author($author);
35              
36              
37              
38             =head1 DESCRIPTION
39              
40             =head1 METHODS
41              
42             =head2 new
43              
44             Create a new Event object
45              
46             =cut
47              
48             sub new {
49             my ($class, %opts) = @_;
50             my $self = $class->SUPER::new( Version => '1.0', %opts );
51             $self->_initialize();
52             return $self;
53             }
54              
55             sub _initialize {
56             my ($self) = @_;
57             $self->SUPER::_initialize();
58             $self->category({ scheme => 'http://schemas.google.com/g/2005#kind', term => 'http://schemas.google.com/g/2005#event' } );
59             $self->set_attr('xmlns:gd', 'http://schemas.google.com/g/2005');
60             $self->set_attr('xmlns:gCal', 'http://schemas.google.com/gCal/2005');
61             unless ( $self->{_gd_ns} ) {
62             $self->{_gd_ns} = XML::Atom::Namespace->new(gd => 'http://schemas.google.com/g/2005');
63             }
64             unless ( $self->{_gcal_ns} ) {
65             $self->{_gcal_ns} = XML::Atom::Namespace->new(gCal => 'http://schemas.google.com/gCal/2005');
66             }
67              
68             }
69              
70             =head2 id [id]
71              
72             Get or set the id.
73              
74             =cut
75              
76             =head2 title [title]
77              
78             Get or set the title.
79              
80             =cut
81              
82             =head2 content [content]
83              
84             Get or set the content.
85              
86             =cut
87              
88             sub content {
89             my $self= shift;
90             if (@_) {
91             $self->set($self->ns, 'content', shift);
92             }
93             return $self->SUPER::content;
94             }
95              
96             =head2 author [author]
97              
98             Get or set the author
99              
100             =cut
101              
102             =head2 transparency [transparency]
103              
104             Get or set the transparency. Transparency should be one of
105              
106             opaque
107             transparent
108              
109             =cut
110              
111             sub transparency {
112             my $self = shift;
113             return $self->_gd_element('transparency', @_);
114             }
115              
116              
117             =head2 visibility [visibility]
118              
119             Get or set the visibility. Visibility should be one of
120              
121             confidential
122             default
123             private
124             public
125              
126             =cut
127              
128             sub visibility {
129             my $self = shift;
130             return $self->_gd_element('visibility', @_);
131             }
132              
133             =head2 status [status]
134              
135             Get or set the status. Status should be one of
136              
137             canceled
138             confirmed
139             tentative
140              
141             =cut
142              
143             sub status {
144             my $self = shift;
145             return $self->_gd_element('eventStatus', @_);
146             }
147              
148              
149              
150             =head2 is_allday
151            
152             Get the allday flag.
153            
154             Returns 1 of event is an All Day event, 0 if not, undef if it can't be
155             determined.
156            
157             =cut
158            
159             sub is_allday {
160             my $self = shift;
161            
162             my $start = $self->_attribute_get($self->{_gd_ns}, 'when', 'startTime');
163             my $end = $self->_attribute_get($self->{_gd_ns}, 'when', 'endTime');
164            
165             my $startok = undef;
166             my $endok = undef;
167            
168             if ($start =~ /^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/) { $startok = 1; }
169             if ($end =~ /^[0-9]{4}-[0-1][0-9]-[0-3][0-9]$/) { $endok = 1; }
170            
171             if ($startok && $endok) { return 1; }
172             if (!$startok && !$endok) { return 0; }
173             return undef;
174             }
175            
176              
177             =head2 extended_property [property]
178              
179             Get or set an extended property
180              
181             =cut
182              
183             sub extended_property {
184             my $self = shift;
185             return $self->_multi_gd_element('extendedProperty', @_);
186             }
187              
188             sub _multi_gd_element {
189             my $self = shift;
190             $self->_gd_elem_generic(1, @_);
191             }
192              
193             sub _gd_element{
194             my $self = shift;
195             $self->_gd_elem_generic(0, @_);
196             }
197              
198             sub _gd_elem_generic{
199             my $self = shift;
200             my $multi = shift;
201             my $elem = shift;
202              
203             if ($elem eq "extendedProperty") {
204             if (@_) {
205             my $name = shift;
206             my $val = shift;
207             my $op = $multi ? 'add' : 'set';
208             $self->$op($self->{_gd_ns}, "${elem}" => "", { name => $name, value => $val } );
209             return $val;
210             }
211             my $ret = {};
212             for my $item ($self->_my_getlist($self->{_gd_ns} ,$elem)) {
213             $ret->{$item->getAttribute('name')} = $item->getAttribute('value');
214             }
215             return $ret;
216             }
217              
218             if (@_) {
219             my $val = lc(shift);
220             my $op = ($multi)? 'add' : 'set';
221             $self->$op($self->{_gd_ns}, "${elem}", '', { value => "http://schemas.google.com/g/2005#event.${val}" });
222             return $val;
223             }
224             my $val = $self->_attribute_get($self->{_gd_ns}, $elem, 'value');
225             $val =~ s!^http://schemas.google.com/g/2005#event\.!!;
226             return $val;
227             }
228              
229             sub _attribute_get {
230             my ($self, $ns, $what, $key) = @_;
231             my $elem = $self->_my_get($self->{_gd_ns}, $what, $key);
232            
233             if (defined($elem) && $elem->hasAttribute($key)) {
234             return $elem->getAttribute($key);
235             } else {
236             return $elem;
237             }
238             }
239              
240             =head2 location [location]
241              
242             Get or set the location
243              
244             =cut
245              
246             sub location {
247             my $self = shift;
248              
249             if (@_) {
250             my $val = shift;
251             $self->set($self->{_gd_ns}, 'where' => '', { valueString => $val});
252             return $val;
253             }
254            
255             return $self->_attribute_get($self->{_gd_ns}, 'where', 'valueString');
256             }
257              
258              
259             =head2 quick_add [bool]
260              
261             Get or set whether this is a a quick add entry or not.
262              
263             =cut
264             sub quick_add {
265             my $self = shift;
266              
267             if (@_) {
268             my $val = ($_[0])? 'true' : 'false';
269             $self->set( $self->{_gcal_ns}, quickadd => '', { value => $val } );
270             return $_[0];
271             }
272             my $val = $self->_attribute_get($self->{_gcal_ns}, 'quickadd', 'valueString');
273             return ($val eq 'true');
274             }
275              
276              
277              
278             =head2 when [ [allday]]
279              
280             Get or set the start and end time as supplied as DateTime objects.
281             End must be more than start.
282              
283             You may optionally pass a paramter in designating if this is an all day event or not.
284              
285             Returns two DateTime objects depicting the start and end and a flag noting whether it's an all day event.
286              
287              
288             =cut
289              
290             sub when {
291             my $self = shift;
292              
293             if (@_) {
294             my ($start, $end, $allday) = @_;
295             $allday = 0 unless defined $allday;
296             unless ($end>=$start) {
297             $@ = "End is not less than start";
298             return undef;
299             }
300             $start->set_time_zone('UTC');
301             $end->set_time_zone('UTC');
302            
303             my $format = $allday ? "%F" : "%FT%TZ";
304              
305             $self->set($self->{_gd_ns}, "when", '', {
306             startTime => $start->strftime($format),
307             endTime => $end->strftime($format),
308             });
309             }
310             my $start = $self->_attribute_get($self->{_gd_ns}, 'when', 'startTime');
311             my $end = $self->_attribute_get($self->{_gd_ns}, 'when', 'endTime');
312             my @rets;
313             if (defined $start) {
314             push @rets, $start;
315             } else {
316             return @rets;
317             #die "No start date ".$self->as_xml;
318             }
319             if (defined $end) {
320             push @rets, $end;
321             }
322             return (map { iso2dt($_) } @rets), $self->is_allday;
323              
324             }
325              
326             =head2 reminder
327              
328             Sets a reminder on this entry.
329              
330             C must be one of:
331              
332             alert email sms
333              
334             C must be one of
335              
336             days hours minutes absoluteTime
337              
338             If the type is C then C should be either a iso formatted date string or a DateTime object.
339              
340             =cut
341              
342             sub reminder {
343             my $self = shift;
344             my ($method, $type, $time) = @_;
345             return undef unless ($method =~ /alert|email|sms/);
346             return undef unless ($type =~ /days|hours|minutes|absoluteTime/);
347             $time = $time->strftime("%FT%TZ") if ref($time) && $time->isa('DateTime');
348             for my $item ($self->_my_getlist($self->{_gd_ns} ,'when')) {
349             my $elem = create_element($self->{_gd_ns}, 'reminder');
350             $elem->setAttribute('method', $method);
351             $elem->setAttribute($type, $time);
352             $item->appendChild($elem);
353             }
354             return 1;
355             }
356              
357              
358              
359              
360              
361             =head2 who [Net::Google::Calendar::Person[s]]
362              
363             Get or set the list of event invitees.
364              
365             If no parameters are passed then it returns a list containing zero
366             or more Net::Google::Calendar::Person objects.
367              
368             If you pass in one or more Net::Google::Calendar::Person objects then
369             they get set as the invitees.
370              
371             =cut
372              
373             # http://code.google.com/apis/gdata/elements.html#gdWho
374             sub who {
375             my $self = shift;
376              
377             my $ns_uri = ""; # $self->{_gd_ns};
378             my $name = 'gd:who';
379             foreach my $who (@_) {
380             $self->add($ns_uri,"${name}", $who, {});
381             }
382             my @who = map {
383             my $person = Net::Google::Calendar::Person->new();
384             for my $attr ($_->attributes) {
385             my $name = $attr->nodeName;
386             my $val = $attr->value || "";
387             #print "$name = $val\n";
388             eval { $person->_do('@'.$name, $val) };
389             next if $@;
390             }
391             foreach my $child ($_->childNodes) {
392             my $name = $child->nodeName;
393             my $val = $child->getAttribute('value');
394             #print "$name = $val\n";
395             $person->_do($name, $val);
396             }
397             #print $person->as_xml;
398             #print "\n\n";
399             $person;
400             } $self->_my_getlist($ns_uri,$name);
401             }
402              
403             =head2 comments [comment[s]]
404              
405             Get or set Comments object.
406              
407             =cut
408              
409             sub comments {
410             my $self = shift;
411              
412             my $ns_uri = $self->{_gd_ns};
413             my $name = 'gd:comments';
414             if (@_) {
415             $self->add($ns_uri,"${name}", shift, {});
416             }
417              
418             my $tmp = $self->_my_get($ns_uri, $name);
419             my $comment = Net::Google::Calendar::Comments->new();
420             for my $attr ($tmp->attributes) {
421             my $name = $attr->nodeName;
422             my $val = $attr->value || "";
423             eval { $comment->_do('@'.$name, $val) };
424             next if $@;
425             }
426             my $feed = Net::Google::Calendar::FeedLink->new(Elem => $tmp->firstChild);
427             $comment->feed_link($feed) if $feed;
428             return $comment;
429             }
430              
431              
432              
433              
434             =head2 edit_url
435              
436             Return the edit url of this event.
437              
438             =cut
439              
440              
441             sub edit_url {
442             return $_[0]->_generic_url('edit');
443             }
444              
445              
446             =head2 self_url
447              
448             Return the self url of this event.
449              
450             =cut
451              
452              
453              
454             sub self_url {
455             return $_[0]->_generic_url('self');
456             }
457              
458              
459             =head2 html_url
460              
461             Return the 'alternate' browser-friendly url of this event.
462              
463             =cut
464              
465             sub html_url {
466             return $_[0]->_generic_url('alternate');
467             }
468              
469              
470              
471             =head2 recurrence [ Data::ICal::Entry::Event ]
472              
473             Get or set a recurrence for an entry - this is in the form of a Data::ICal::Entry::Event object.
474              
475             Returns undef if there's no recurrence event
476              
477             This will not work if C is not installed and will return undef.
478              
479             For example ...
480              
481             $event->title('Pay Day');
482             $event->start(DateTime->now);
483              
484             my $recurrence = Data::ICal::Entry::Event->new();
485              
486              
487             my $last_day_of_the_month = DateTime::Event::Recurrence->monthly( days => -1 );
488             $recurrence->add_properties(
489             dtstart => DateTime::Format::ICal->format_datetime(DateTime->now),
490             rrule => DateTime::Format::ICal->format_recurrence($last_day_of_the_month),
491             );
492              
493             $entry->recurrence($recurrence);
494              
495             To get the recurrence back:
496              
497             print $entry->recurrence->as_string;
498              
499             See
500              
501             http://code.google.com/apis/gdata/common-elements.html#gdRecurrence
502              
503             For more details
504              
505             =cut
506              
507             sub recurrence {
508             my $self = shift;
509            
510             # we need Data::ICal for this but we don't wnat to require it
511             eval {
512             require Data::ICal;
513             Data::ICal->import;
514             require Data::ICal::Entry::Event;
515             Data::ICal::Entry::Event->import;
516            
517             };
518             if ($@) {
519             $@ = "Couldn't load Data::ICal or Data::ICal::Entry::Event: $@";
520             return;
521             }
522              
523             # this is all one massive hack.
524             # I hate myself for writing this.
525             if (@_) {
526             my $event = shift;
527             # pesky Google Calendar needs you to remove the BEGIN:VEVENT END:VEVENT. TSSSK
528             my $recur = $event->as_string;
529              
530             $recur =~ s!(^BEGIN:VEVENT\n|END:VEVENT\n$)!!sg;
531             $self->set($self->{_gd_ns}, 'recurrence', $recur);
532              
533             return $event;
534             }
535             my $string = $self->get($self->{_gd_ns}, 'recurrence');
536             return undef unless defined $string;
537             $string =~ s!\n+$!!g;
538             $string = "BEGIN:VEVENT\n${string}\nEND:VEVENT";
539             my $vfile = Text::vFile::asData->new->parse_lines( split(/\n/, $string) );
540             my $event = Data::ICal::Entry::Event->new();
541             #return $event;
542              
543             $event->parse_object($vfile->{objects}->[0]);
544             return $event->entries->[0];
545              
546             }
547              
548             =head2 add_link
549              
550             Adds the link $link, which must be an XML::Atom::Link object, to the entry as a new tag. For example:
551              
552             my $link = XML::Atom::Link->new;
553             $link->type('text/html');
554             $link->rel('alternate');
555             $link->href('http://www.example.com/2003/12/post.html');
556             $entry->add_link($link);
557              
558             =cut
559              
560             sub add_link {
561             my ($self, $link) = @_;
562             # workaround bug in XML::Atom
563             $link = bless $link, 'XML::Atom::Link' if ref($link) && $link->isa('XML::Atom::Link');
564             $self->SUPER::add_link($link);
565             }
566              
567             =head2 original_event [event]
568              
569             Get or set the original event ID.
570              
571             =cut
572              
573             sub original_event {
574             my $self = shift;
575             return $self->_gd_element('originalEvent', @_);
576             }
577              
578             =head1 TODO
579              
580             =over 4
581              
582             =item more complex content
583              
584             =item more complex locations
585              
586             =item recurrency
587              
588             =item comments
589              
590             =back
591              
592             See http://code.google.com/apis/gdata/common-elements.html for details
593              
594             =head1 AUTHOR
595              
596             Simon Wistow
597              
598             =head1 COPYRIGHT
599              
600             Copyright Simon Wistow, 2006
601              
602             Distributed under the same terms as Perl itself.
603              
604             =head1 SEE ALSO
605              
606             http://code.google.com/apis/gdata/common-elements.html
607              
608             L
609              
610             L
611              
612             =cut
613              
614              
615              
616             1;