Cal-DAV
view release on metacpan or search on metacpan
lib/Cal/DAV.pm view on Meta::CPAN
package Cal::DAV;
use strict;
use Data::ICal;
use HTTP::DAV;
our $VERSION="0.6";
=head1 NAME
Cal::DAV - a CalDAV client
=head1 SYNOPSIS
my $cal = Cal::DAV->new( user => $user, pass => $pass, url => $url);
# the ics data will be fetched automatically if it's there
# ... or you can parse some ics
$cal->parse(filename => $data);
# cal now has all the methods of Data::ICal
# you can now monkey around with the object
# saves the updated calendar
$cal->save;
# deletes the calendar
$cal->delete;
# lock the file on the server
$cal->lock;
# unlock the file on the server
$cal->unlock
# steal the lock
$cal->steal_lock;
# also
$cal->forcefully_unlock_all
# and
$cal->lockdiscovery
# resyncs it with the server
$cal->get;
# Get the underlying HTTP::DAV object
my $dav = $cal->dav;
=head1 DESCRIPTION
C<Cal::DAV> is actually a very thin wrapper round C<HTTP::DAV> and
C<Data::ICal> but it may gain more functionality later and, in the mean
time, serves as something that
=head1 TESTING
In order to test you need to define three environment variables:
C<CAL_DAV_USER>, C<CAL_DAV_PASS> and C<CAL_DAV_URL_BASE> which
points to a DAV collection that the user supplied has write
permissions for.
It should be noted that, at the moment, I'm having problems finding
a CalDAV server that allows me to create files and so I can't run all
the tests.
=head1 METHODS
=cut
=head2 new <arg[s]>
Must have at least C<user>, C<pass> and C<url> args where
C<url> is the url of a remote, DAV accessible C<.ics> file.
Can optionally take an C<auto_commit> option. See C<auto_commit()> method below.
=cut
# TODO if we remove the option to do operations with other urls
# we could then cache the resource object
sub new {
my $class = shift;
my %args = @_;
my %opts;
for (qw(user pass url)) {
die "You must pass in a $_ param\n" unless defined $args{$_};
$opts{"-${_}"} = $args{$_};
}
my $dav = HTTP::DAV->new;
$dav->credentials(%opts);
return bless { _dav => $dav, url => $args{url}, _auto_commit => $args{auto_commit} }, $class;
}
=head2 parse <arg[s]>
Make a new calendar object using same arguments as C<Data::ICal>'s C<new()> or C<parse()> methods.
Does not auto save for you.
Returns 1 on success and 0 on failure.
=cut
sub parse {
my $self = shift;
my %args = @_;
$self->{_cal} = Data::ICal->new(%args);
return (defined $self->{_cal}) ?
$self->dav->ok("Loaded data successfully") :
$self->dav->err('ERR_GENERIC', "Failed to load calendar: parse error $@");
}
=head2 save [url]
Save the calendar back to the server (or optionally to another path).
Returns 1 on success and 0 on failure.
=cut
sub save {
my $self = shift;
my $url = shift || $self->{url};
my $cal = $self->{_cal}; # TODO should this be cal()
return 1 unless defined $cal;
my $res = $self->dav->new_resource( -uri => $url );
#unless ($self->{_fetched}) {
#my $ret = $res->mkcol;
#unless ($ret->is_success) {
# return $self->dav->err( 'ERR_RESP_FAIL',"mkcol in put failed ".$ret->message(), $url);
#}
#$self->{_fetched} = 1;
#}
my $data = $cal->as_string;
my $ret = $res->put($data);
if ($ret->is_success) {
return $self->dav->ok( "put $url (" . length($data) ." bytes)",$url );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',"put failed ".$ret->message(), $url);
}
}
=head2 delete [url]
Delete the file on the server or optionally another url.
Returns 1 on success and 0 on failure.
=cut
sub delete {
my $self = shift;
my $url = shift || $self->{url};
my $res = $self->dav->new_resource( -uri => $url );
my $ret = $res->delete();
if ($ret->is_success) {
return $self->dav->ok( "deleted $url successfully", $url );
} else {
return $self->dav->err( 'ERR_RESP_FAIL',$ret->message(), $url);
}
}
=head2 get [url]
Refetch the file from the sever to sync it -
Alternatively fetch an alternative url.
These will lose any local changes.
=cut
sub get {
lib/Cal/DAV.pm view on Meta::CPAN
} else {
return $self->dav->err( 'ERR_RESP_FAIL',$resp->message(),$self->{url} );
}
}
=head2 lockdiscovery
Same options as C<HTTP::DAV::Response>'s C<lockdiscovery>.
=cut
sub lockdiscovery {
my $self = shift;
my $resp = $self->_do_on_dav('lockdiscovery', @_);
}
=head2 forcefully_unlock_all
Same options as C<HTTP::DAV::Response>'s C<forcefully_unlock_all>.
=cut
sub forcefully_unlock_all {
my $self = shift;
$self->_do_on_dav('forcefully_unlock_all', @_);
}
sub _do_on_dav {
my $self = shift;
my $meth = shift;
my $url = $self->{url};
my $res = $self->dav->new_resource( -uri => $url );
$res->$meth(@_);
}
=head2 dav [HTTP::DAV]
Get the underlying C<HTTP::DAV> object or, alterntively, replace it with
a a new one.
=cut
sub dav {
my $self = shift;
if (@_) {
$self->{_dav} = shift;
}
return $self->{_dav};
}
=head2 cal
Get the underlying cal object
=cut
sub cal {
my $self = shift;
if (!defined $self->{_cal}) {
my $ret = $self->get || die "Couldn't autofetch calendar: ".$self->dav->message;
}
return $self->{_cal};
}
=head2 auto_commit [boolean]
Whether to auto save on desctruction or not.
Defaults to 0.
=cut
sub auto_commit {
my $self = shift;
if (@_) {
$self->{_auto_commit} = shift;
}
return $self->{_auto_commit};
}
=head2 message
Same as C<HTTP::DAV>'s C<message> function.
=cut
sub message {
my $self = shift;
return $self->dav->message;
}
=head2 errors
Same as C<HTTP::DAV>'s C<errors> function.
=cut
sub errors {
my $self = shift;
return $self->dav->errors;
}
use Carp qw(croak confess cluck);
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my $method = $AUTOLOAD;
$method =~ s/.*://; # strip fully-qualified portion
# TODO should we cache this in a glob?
$self->cal->$method(@_)
}
sub DESTROY {
my $self = shift;
$self->save if $self->auto_commit;
}
( run in 3.516 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )