package # hide from PAUSE
MyBlog::Controller::MediaCollection;
use strict;
use warnings;
use Atompub::DateTime qw(datetime);
use Atompub::MediaType qw(media_type);
use MIME::Base64;
use HTTP::Status;
use POSIX qw(strftime);
use String::CamelCase qw(camelize);
use Time::HiRes qw(gettimeofday);
use base qw(Catalyst::Controller::Atompub::Collection);
my $ENTRIES_PER_PAGE = 10;
my $TABLE_NAME = 'medias';
my $MODEL = join '::', 'DBIC', camelize($TABLE_NAME);
sub get_feed :Atompub(list) {
my($self, $c) = @_;
## URI without parameters
my $uri = $self->collection_resource->uri;
my $feed = $self->collection_resource->body;
my $page = $c->req->param('page') || 1;
my $attr = {
offset => ($page - 1) * $ENTRIES_PER_PAGE,
rows => $ENTRIES_PER_PAGE,
order_by => 'edited desc',
};
my $rs = $c->model($MODEL)->search({}, $attr);
while (my $resource = $rs->next) {
my $entry = XML::Atom::Entry->new(\$resource->entry_body);
$feed->add_entry($entry);
}
$feed->alternate_link($c->req->base.'html');
$feed->first_link($uri);
$feed->previous_link("$uri?page=".($page-1)) if $page > 1;
$feed->next_link("$uri?page=".($page+1)) if $rs->count >= $ENTRIES_PER_PAGE;
1;
}
sub create_resource :Atompub(create) {
my($self, $c) = @_;
# URIs were determined by C::C::Atompub
my $entry_uri = $self->media_link_entry->uri;
my $media_uri = $self->media_resource->uri;
return $self->error($c, RC_CONFLICT, "Resource name is used (change Slug): $entry_uri")
if $c->model($MODEL)->search({ entry_uri => $entry_uri })->count;
# Edit $entry and $media if needed ...
my $vals = {
edited => $self->edited->epoch,
entry_uri => $entry_uri,
entry_etag => $self->calculate_new_etag($c, $entry_uri),
entry_body => $self->media_link_entry->body->as_xml,
media_uri => $media_uri,
media_etag => $self->calculate_new_etag($c, $media_uri),
media_body => MIME::Base64::encode($self->media_resource->body),
media_type => $self->media_resource->type,
};
$c->model($MODEL)->create($vals)
or return $self->error($c, RC_INTERNAL_SERVER_ERROR,
'Cannot create new media resource');
1;
}
sub get_resource :Atompub(read) {
my($self, $c) = @_;
my $uri = $c->req->uri;
my $cond = {
'-or' => [
{ entry_uri => $uri },
{ media_uri => $uri },
],
};
my $rs = $c->model($MODEL)->search($cond)->first
or return $self->error($c, RC_NOT_FOUND);
if ($rs->entry_uri eq $uri) {
$self->media_link_entry->body( XML::Atom::Entry->new(\$rs->entry_body) );
$self->media_resource->type(media_type('entry'));
}
else {
$self->media_resource->body( MIME::Base64::decode($rs->media_body) );
$self->media_resource->type($rs->media_type);
}
1;
}
sub update_resource :Atompub(update) {
my($self, $c) = @_;
my $uri = $c->req->uri;
my $cond = {
'-or' => [
{ entry_uri => $uri },
{ media_uri => $uri },
],
};
my $rs = $c->model($MODEL)->search($cond)->first
or return $self->error($c, RC_NOT_FOUND);
my $vals = { edited => $self->edited->epoch };
my($media_link_entry, $media_type);
if ($rs->entry_uri eq $uri) {
$media_link_entry = $self->media_link_entry->body;
$media_type = $rs->media_type;
# Don't update the Last-Modified value of the corresponding
# Media Resource if you use it
}
else {
$media_link_entry = XML::Atom::Entry->new(\$rs->entry_body)
or return $self->error($c);
$vals->{media_etag} = $self->calculate_new_etag($c, $rs->media_uri);
$vals->{media_body} = MIME::Base64::encode($self->media_resource->body);
$vals->{media_type} = $media_type = $self->media_resource->type;
# Do update the Last-Modified value of the Media Resource if you use it
}
# app:edited and atom:content in Media Link Entry MUST be updated
$media_link_entry->edited($self->edited->w3c);
my $content = XML::Atom::Content->new;
$content->src($rs->media_uri);
$content->type($media_type);
$media_link_entry->content($content);
$vals->{entry_body} = $media_link_entry->as_xml;
$vals->{entry_etag} = $self->calculate_new_etag($c, $rs->entry_uri);
$rs->update($vals)
or return $self->error($c, RC_INTERNAL_SERVER_ERROR, "Cannot update resource: $uri");
1;
}
sub delete_resource :Atompub(delete) {
my($self, $c) = @_;
my $uri = $c->req->uri;
my $cond = {
'-or' => [
{ entry_uri => $uri },
{ media_uri => $uri },
],
};
my $rs = $c->model($MODEL)->search($cond)->first
or return $self->error($c, RC_NOT_FOUND);
# delete entry and media resources at once
$rs->delete
or return $self->error($c, RC_INTERNAL_SERVER_ERROR, "Cannot delete resource: $uri");
1;
}
sub make_edit_uri {
my($self, $c, @args) = @_;
my @uris = $self->SUPER::make_edit_uri($c, @args);
my $cond = {
'-or' => [
{ entry_uri => $uris[0] },
{ media_uri => $uris[0] },
],
};
# return, if $uris[0] is not used
return wantarray ? @uris : $uris[0]
unless $c->model($MODEL)->search($cond)->count;
my($sec, $usec) = gettimeofday;
my $dt = strftime '%Y%m%d-%H%M%S', localtime($sec);
$usec = sprintf '%06d', $usec;
# insert $dt-$usec before extension
$_ =~ s{(\.[^./?]+)$}{-$dt-$usec$1} for @uris;
@uris;
}
sub find_version {
my($self, $c, $uri) = @_;
my $cond = {
'-or' => [
{ entry_uri => $uri },
{ media_uri => $uri },
],
};
my $rs = $c->model($MODEL)->search($cond)->first or return;
if ($rs->entry_uri eq $uri) {
return (etag => $rs->entry_etag);
}
else {
return (etag => $rs->media_etag);
}
}
sub calculate_new_etag {
my($self, $c, $uri) = @_;
my($sec, $usec) = gettimeofday;
my $dt = join '-', strftime('%Y%m%d-%H%M%S', localtime($sec)), sprintf('%06d', $usec);
join '/', $uri, $dt;
}
1;