—package
WebService::Blogger::Blog;
our
$VERSION
=
'0.23'
;
use
warnings;
use
strict;
use
Moose;
use
XML::Simple ();
use
URI::Escape ();
use
Encode ();
# Blog properties, non-updatable.
has
id
=> (
is
=>
'ro'
,
isa
=>
'Str'
,
required
=> 1 );
has
numeric_id
=> (
is
=>
'ro'
,
isa
=>
'Str'
,
required
=> 1 );
has
title
=> (
is
=>
'ro'
,
isa
=>
'Str'
,
required
=> 1 );
has
public_url
=> (
is
=>
'ro'
,
isa
=>
'Str'
,
required
=> 1 );
has
id_url
=> (
is
=>
'ro'
,
isa
=>
'Str'
,
required
=> 1 );
has
post_url
=> (
is
=>
'ro'
,
isa
=>
'Str'
,
required
=> 1 );
# Service attributes.
has
source_xml_tree
=> (
is
=>
'ro'
,
isa
=>
'HashRef'
,
required
=> 1 );
has
blogger
=> (
is
=>
'ro'
,
isa
=>
'WebService::Blogger'
,
required
=> 1 );
# Blog entries.
has
max_results
=> (
is
=>
'rw'
,
isa
=>
'Num'
,
required
=> 1,
default
=> 30, );
has
entries
=> (
is
=>
'rw'
,
isa
=>
'ArrayRef[WebService::Blogger::Blog::Entry]'
,
lazy_build
=> 1,
auto_deref
=> 1,
);
# Speed Moose up.
__PACKAGE__->meta->make_immutable;
sub
BUILDARGS {
## Parses source XML into initial attribute values.
my
$class
=
shift
;
my
%params
=
@_
;
my
$tree
=
$params
{source_xml_tree};
my
$id
=
$tree
->{id}[0];
# Extract attributes from XML tree and return them to be set in the instance.
return
{
id
=>
$id
,
numeric_id
=>
$id
=~ /(\d+)$/,
title
=>
$tree
->{title}[0]{content},
id_url
=>
$class
->get_link_href_by_rel(
$tree
,
'self'
),
public_url
=>
$class
->get_link_href_by_rel(
$tree
,
'alternate'
),
post_url
=>
$class
->get_link_href_by_rel(
$tree
,
qr/#post$/
),
%params
,
};
}
sub
_build_entries {
## Populates the entries attribute, loading all entries for the blog.
my
$self
=
shift
;
# Search with no parameters.
return
$self
->search_entries;
}
sub
search_entries {
## Returns entries matching search criteria.
my
$self
=
shift
;
my
%params
=
@_
;
# Construct request URL, incorporating category criteria into it, if given.
$url
.=
'/-/'
.
join
'/'
,
map
URI::Escape::uri_escape(
$_
), @{
$params
{categories} }
if
$params
{categories};
# Map our parameter names to Blogger's.
my
%params_to_req_args_map
= (
max_results
=>
'max-results'
,
published_min
=>
'published-min'
,
published_max
=>
'published-max'
,
updated_min
=>
'updated-min'
,
updated_max
=>
'updated-max'
,
order_by
=>
'orderby'
,
offset
=>
'start-index'
,
);
# Map our sort mode parameter names to Blogger's.
my
%sort_mode_map
= (
last_modified
=>
'lastmodified'
,
start_time
=>
'starttime'
,
updated
=>
'updated'
,
);
# Populate request arguments hash WRT above mappings.
my
%req_args
= (
alt
=>
'atom'
,
);
foreach
my
$param
(
keys
%params_to_req_args_map
) {
my
$value
=
$self
->
$param
if
$self
->meta->has_attribute(
$param
);
$value
=
$params
{
$param
}
if
exists
$params
{
$param
};
$req_args
{
$params_to_req_args_map
{
$param
}} =
$value
if
defined
$value
;
}
if
(
my
$sort_mode
=
$params
{sort_by}) {
$req_args
{orderby} =
$sort_mode_map
{
$sort_mode
};
}
# Execute request and parse the response.
my
$uri_obj
= URI->new(
$url
);
$uri_obj
->query_form(
%req_args
);
my
$response
=
$self
->blogger->http_get(
$uri_obj
);
my
$response_tree
= XML::Simple::XMLin(
$response
->content,
ForceArray
=> 1);
# Return list of entry objects constructed from list of hashes in parsed data.
my
@entries
=
map
WebService::Blogger::Blog::Entry->new(
source_xml_tree
=>
$_
,
blog
=>
$self
,
),
@{
$response_tree
->{entry} };
return
wantarray
?
@entries
: \
@entries
;
}
sub
add_entry {
## Adds new entry with specified properties to the blog and returns it.
my
$self
=
shift
;
my
%params
=
@_
;
# Get the XML for creation of new entry and post it to appropriate URL.
my
$creation_xml
= WebService::Blogger::Blog::Entry->xml_for_creation(
%params
);
my
$response
=
$self
->blogger->http_post(
$self
->post_url,
'Content-Type'
=>
'application/atom+xml'
,
Content
=> Encode::encode_utf8(
$creation_xml
),
);
die
'Unable to add entry to blog: '
.
$response
->status_line
unless
$response
->is_success;
# Create new entry object from the response.
my
$xml_tree
= XML::Simple::XMLin(
$response
->content,
ForceArray
=> 1);
return
WebService::Blogger::Blog::Entry->new(
source_xml_tree
=>
$xml_tree
,
blog
=>
$self
);
}
sub
delete_entry {
## Deletes given entry from server as well as list of entries held in blog object.
my
$self
=
shift
;
my
(
$entry
) =
@_
;
# Execute deletion request, with a workaround for proxies blocking DELETE method.
my
$response
=
$self
->blogger->http_post(
$entry
->edit_url,
'X-HTTP-Method-Override'
=>
'DELETE'
,
);
die
'Could not delete entry from server: '
.
$response
->status_line
unless
$response
->is_success;
# Remove the entry from local list of entries.
$self
->entries([
grep
$_
ne
$entry
,
$self
->entries ]);
}
sub
destroy {
## Removes references to the blog from child entries, so they're
## no longer circular. Blog object as well as entries can then be
## garbage-collected.
my
$self
=
shift
;
$_
->blog(
undef
)
foreach
$self
->entries;
}
1;
__END__
=head1 NAME
WebService::Blogger::Blog - represents blog entity of Google Blogger service.
=head1 SYNOPSIS
Please see L<WebService::Blogger>.
=head1 DESCRIPTION
This class represents a blog in WebService::Blogger package, and is
not designed to be instantiated directly.
=head1 METHODS
=head3 C<add_entry(%properties)>
=over
Adds given entry to the blog:
my $new_entry = $blog->add_entry(
title => 'New entry',
content => 'New content',
categories => [ 'news', 'testing', 'perl examples' ],
);
=back
=head3 C<search_entries(%criteria)>
=over
Returns entries matching specified criteria. The following example
contains all possible search conditions:
my @entries = $blog->search_entries(
published_min => '2010-08-10T23:25:00+04:00'
published_max => '2010-07-17T23:25:00+04:00',
updated_min => '2010-09-17T12:25:00+04:00',
updated_max => '2010-09-17T14:00:00+04:00',
order_by => 'start_time', # can also be: 'last_modified' or 'updated'
max_results => 20,
offset => 10, # skip first 10 entries
);
=back
=head3 C<destroy()>
=over
Removes references to the blog from child entries, so they're no
longer circular. Blog object as well as entries can then be
garbage-collected.
=back
=head1 ATTRIBUTES
=head3 C<id>
=over
Unique ID of the blog, a string in Blogger-specific format as present
in the Atom entry.
=back
=head3 C<numeric_id>
=over
Numeric ID of the blog.
=back
=head3 C<title>
=over
Title of the blog.
=back
=head3 C<public_url>
=over
The human-readable, SEO-friendly URL of the blog.
=back
=head3 C<id_url>
=over
URL of the blog based on its numeric ID. Never changes.
=back
=head3 C<post_url>
=over
URL for publishing new posts.
=back
=head3 C<entries>
=over
List of blog entries, lazily populated.
=back
=head1 AUTHOR
Kedar Warriner, C<< <kedar at cpan.org> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-webservice-blogger at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=WebService-Blogger>. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc WebService::Blogger
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
=item * AnnoCPAN: Annotated CPAN documentation
=item * CPAN Ratings
=item * Search CPAN
=back
=head1 ACKNOWLEDGEMENTS
Many thanks to:
- Egor Shipovalov who wrote the original version of this module
- Everyone involved with CPAN.
=head1 LICENSE AND COPYRIGHT
Copyright 2010 Kedar Warriner.
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
=cut