File Coverage

blib/lib/XML/Atom/App.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package XML::Atom::App;
2              
3 7     7   201193 use warnings;
  7         18  
  7         260  
4 7     7   40 use strict;
  7         14  
  7         230  
5 7     7   37 use Carp ();
  7         17  
  7         110  
6 7     7   8565 use Time::HiRes;
  7         27134  
  7         125  
7              
8 7     7   8199 use version; our $VERSION = qv('0.0.5');
  7         18520  
  7         45  
9              
10 7     7   4090 use XML::Atom;
  0            
  0            
11             use XML::Atom::Entry;
12             use XML::Atom::Feed;
13              
14             use base qw(XML::Atom::Feed);
15             $XML::Atom::DefaultVersion = '1.0'; # $feed->version()
16              
17             # not currently in use but if ever needed the logic is in place...
18             my %new_map;
19             my %key_map;
20             my %link_key_map;
21             my %author_key_map;
22              
23             sub new {
24             my ($self, $args_hr, $opt_hr) = @_;
25             local $XML::Atom::DefaultVersion = '1.0';
26             $args_hr = {} if !defined $args_hr || ref $args_hr ne 'HASH';
27             $opt_hr = {} if !defined $opt_hr || ref $opt_hr ne 'HASH';
28            
29             my $feed = $self->SUPER::new( 'Version' => delete($args_hr->{'Version'}) || $XML::Atom::DefaultVersion );
30              
31             my $particles = delete $args_hr->{'particles'} || '';
32             my $link = exists $args_hr->{'link'} ? delete $args_hr->{'link'} : undef;
33             $link ||= $opt_hr->{no_self_link} ? undef : [{ 'rel' => 'self' }];
34             my $contrib = exists $args_hr->{'contributor'} ? delete $args_hr->{'contributor'} : undef;
35            
36             $feed->{'alert_cant'} = delete $args_hr->{'alert_cant'} || '';
37             $feed->{'alert_cant'} = '' if ref $feed->{'alert_cant'} ne 'CODE';
38              
39             for my $item ( sort keys %{ $args_hr } ) {
40             $item = $new_map{$item} if exists $new_map{$item};
41             if ( $feed->can($item) ) {
42             $feed->$item( ref $args_hr->{$item} eq 'ARRAY' ? @{$args_hr->{$item}} : ($args_hr->{$item}) );
43             }
44             else {
45             $feed->alert_cant( $item );
46             }
47             }
48            
49             $feed->_do_app_author( $feed, delete $args_hr->{'author'} );
50             $feed->_do_app_link( $feed, $link );
51             $feed->_do_app_contributor( $feed, $contrib ) if defined $contrib;
52            
53             $feed->{'time_of_last_create_from_atomic_structure'} = 0;
54             $feed->create_from_atomic_structure( $particles ) if ref $particles eq 'ARRAY';
55             return $feed;
56             }
57              
58             sub clear_particles {
59             my ($feed, $dx) = @_;
60             $feed->{'time_of_last_create_from_atomic_structure'} = 0;
61              
62             # would love to know of a better way to remove $feed->entries, anyone ?
63             my $author = $feed->author();
64             my @links = $feed->link();
65             my @contribs = $feed->contributors();
66              
67             use XML::Simple ();
68             my $xml_struct = XML::Simple::XMLin( $feed->as_xml );
69             for my $key (qw(xmlns entry link author contributor)) {
70             delete $xml_struct->{$key};
71             }
72              
73             $feed->init; # resets 'elem' key to new empty object, wipes out everything not just entries..
74            
75             for my $item ( sort keys %{ $xml_struct } ) {
76             $item = $new_map{$item} if exists $new_map{$item};
77             if ( $feed->can($item) ) {
78             $xml_struct->{$item} = $xml_struct->{$item}{'content'} if ref $xml_struct->{$item} eq 'HASH';
79             $feed->$item( ref $xml_struct->{$item} eq 'ARRAY' ? @{$xml_struct->{$item}} : ($xml_struct->{$item}) );
80             }
81             else {
82             $feed->alert_cant( $item );
83             }
84             }
85            
86             $feed->author( $author ) if $author;
87             $feed->add_link($_) for @links;
88             $feed->add_contributor($_) for @contribs;
89              
90             return $feed;
91             }
92              
93             sub alert_cant {
94             my ($feed, $cant, $obj) = @_;
95             $obj = $feed if !defined $obj || !$obj;
96            
97             if ( ref $feed->{'alert_cant'} eq 'CODE' ) {
98             return $feed->{'alert_cant'}->( $feed, $cant, $obj );
99             }
100             else {
101             my $msg = sprintf q{Can't locate object method "%s" via package "%s"}, $cant, ref($obj);
102             if ( exists $INC{'CGI/Carp.pm'} ) {
103             return CGI::Carp::carp( $msg );
104             }
105             else {
106             return Carp::carp( $msg );
107             }
108             }
109             }
110              
111             sub atom_date_string {
112             goto &datetime_as_rfc3339;
113             }
114              
115             sub datetime_as_rfc3339 {
116             my ($feed, $dt) = @_;
117              
118             if (ref $dt eq 'ARRAY') {
119             require DateTime if !exists $INC{'DateTime.pm'};
120             $dt = DateTime->new(@{ $dt });
121             }
122            
123             my $offset = $dt->offset != 0 ? '%z' : 'Z';
124             return $dt->strftime('%FT%T' . $offset);
125             }
126              
127             sub create_entry_from_atomic_structure {
128             my ( $feed, $entry_hr ) = @_;
129             local $XML::Atom::DefaultVersion = $feed->version();
130              
131             my $entry = XML::Atom::Entry->new;
132            
133             for my $item (keys %{ $entry_hr } ) {
134             next if $item eq 'author' || $item eq 'link' || $item eq 'contributor' || $item eq 'source';
135             $item = $key_map{$item} if exists $key_map{$item};
136             if ( $entry->can($item) ) {
137             $entry->$item( ref $entry_hr->{$item} eq 'ARRAY' ? @{$entry_hr->{$item}} : $entry_hr->{$item} );
138             }
139             else {
140             $feed->alert_cant( $item, $entry );
141             }
142             }
143              
144             $feed->_do_app_author( $entry, $entry_hr->{'author'} );
145             $feed->_do_app_contributor( $entry, $entry_hr->{'contributor'} );
146             $feed->_do_app_link( $entry, $entry_hr->{'link'} );
147             $feed->_do_app_source( $entry, $entry_hr->{'source'} ) if defined $entry_hr->{'source'};
148              
149             return $entry;
150             }
151              
152             sub create_from_atomic_structure {
153             my ( $feed, $particles, $opts_hr ) = @_;
154             $opts_hr = {} if !defined $opts_hr || ref $opts_hr ne 'HASH';
155             local $XML::Atom::DefaultVersion = $feed->version();
156            
157             $feed->clear_particles() if !$opts_hr->{'do_not_clear_particles'};
158            
159             for my $entry_hr ( @{ $particles } ) {
160             my $entry = $feed->create_entry_from_atomic_structure( $entry_hr );
161             $feed->add_entry($entry);
162             }
163            
164             $feed->{'time_of_last_create_from_atomic_structure'} = Time::HiRes::time();
165             return $feed;
166             }
167              
168             sub _do_app_contributor {
169             my ($feed, $thing, $aref) = @_;
170             return unless defined $aref;
171             $aref = [ $aref ] unless ref $aref eq 'ARRAY';
172              
173             foreach my $contrib_ds ( @{$aref} ) {
174             if ( ref $contrib_ds eq 'HASH' ) {
175             my $contrib = XML::Atom::App::Contributor->new;
176             for my $item ( keys %{ $contrib_ds } ) {
177             $item = $author_key_map{$item} if exists $author_key_map{$item};
178             if ( $contrib->can( $item ) ) {
179             $contrib->$item( ref $contrib_ds->{$item} eq 'ARRAY' ? @{$contrib_ds->{$item}} : $contrib_ds->{$item} );
180             }
181             else {
182             $feed->alert_cant( $item, $contrib );
183             }
184             }
185              
186             $thing->add_contributor($contrib);
187             }
188             }
189             }
190              
191             sub _do_app_author {
192             my ($feed, $thing, $author_ds) = @_;
193             if ( ref $author_ds eq 'HASH' ) {
194             my $author = XML::Atom::Person->new;
195             for my $item ( keys %{ $author_ds } ) {
196             $item = $author_key_map{$item} if exists $author_key_map{$item};
197             if ( $author->can( $item ) ) {
198             $author->$item( ref $author_ds->{$item} eq 'ARRAY' ? @{$author_ds->{$item}} : $author_ds->{$item} );
199             }
200             else {
201             $feed->alert_cant( $item, $author );
202             }
203             }
204              
205             $thing->author($author);
206             }
207             }
208              
209             sub _do_app_link {
210             my ($feed, $thing, $link_ds) = @_;
211            
212             if ( ref $link_ds eq 'ARRAY' ) {
213             for my $link_hr ( @{ $link_ds } ) {
214             next if ref $link_hr ne 'HASH';
215            
216             my $link = XML::Atom::Link->new;
217             for my $item ( keys %{ $link_hr } ) {
218             $item = $link_key_map{$item} if exists $link_key_map{$item};
219             if ( $link->can( $item ) ) {
220             $link->$item( ref $link_hr->{$item} eq 'ARRAY' ? @{$link_hr->{$item}} : $link_hr->{$item} );
221             }
222             else {
223             $feed->alert_cant( $item, $link);
224             }
225             }
226             $thing->add_link($link);
227             }
228             }
229             }
230              
231              
232             sub _do_app_source {
233             my ($feed, $entry, $source_ds) = @_;
234              
235             if ( ref $source_ds ne 'HASH' ) {
236             $entry->source( $source_ds );
237             return;
238             }
239             delete $source_ds->{particles};
240             my $src = __PACKAGE__-> new( $source_ds, {no_self_link=>1} );
241             $entry->source( $src );
242             }
243              
244             sub output_with_headers {
245             my ($feed, $xml) = @_;
246             # local $XML::Atom::DefaultVersion = $feed->version();
247            
248             $xml = $feed->as_xml() if !defined $xml || !$xml; # get $xml if non provided
249             {
250             use bytes;
251             my $len = length($xml);
252             if (defined wantarray) {
253             return "Content-length: $len\nContent-type: application/atom+xml\n\n$xml";
254             }
255             else {
256             # print in void context
257             print "Content-length: $len\nContent-type: application/atom+xml\n\n$xml";
258             }
259             }
260             }
261              
262             sub orange_atom_icon_32_32_base64 {
263             # my ($feed) = @_;
264             return q{data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAUzSURBVHjavFdbbFRVFF3nPjoz7dTWTittaW0jUDRAUqaNojyqREnEQKgfUj9MqqAmhqRt/OCD4CuY+Kckoh+aiGKC+gMJbdHoRysJ8dkhhmJLNdDKtJU+6GMK87j3Hs85d2Z6HzNtMYWb3Dn3NWftvfba+5xNYDl+e6Fkj6yqb/oDRbWq14vlPBLRKCITkxf0ROLt+hNjp1PPSRK4kA3vF1dXNRcWlyA2OQU9eos9opAkAiKxD+XkKO6t15aRWO7J/MgmAZU8MEgexgZHMX518Dh72sYMmVKShnxWuWHdHtxKIDIYTgMuDzgfmSOIQkYMpdUF8OY92Hytt4/jvkg47czzU16iQovM3QFwmNck+Yyduu7D6NA0Z6JR4THntFs9V4tWQg6Ui3s6MwKDncsFTnXKLJhDSeUK3AgPtyhccDzmVs999buRt/1Vm4i0od+hX7+MRG87jPGB/w1u8FPj9xEw7McVrnYuOCvtpjTth3J/nTg99c8LRhKhr6D3dTB5R24bXFwbMXBsyZzeoXaycEpJ95TB09AGX/NpqLVNtw8urnVzLvHjFNxiFqRy2OOHuqUVnue+ACkoWzo4O6lGzTmuHq6nPvY2m9rVqjrIK2rMEKxqyG5NPAKt+wjo0LklgfNxJkZMA3KJvqRUk3z5UFY3QH14P0h+WUY79HPvgv7VuSg4ZRGY1YgZgqXmORccF17sy2ehnf9AeO085K2HQFbtXBScj0LcpgF2cN+WV+DZ/LJQu6gD4R7oV7pBJwbSgtMvfiPoVp56DySwxm7EtkMs1WdAB7qzggsDJKQYsHucSkOudrkiCPWR/fA2nYCn8SNIK4NptSMyAu3sAdDRkIsJdfth0LzSrODUoPNZ4KI9SxJI5UHk7D4GdQfz2us31c7CoHMjRkKuDPHseCMrONVhNcDJwMJpKFVvg9L4OaTiNWm1x789KCqkrXhVBiEz0WYCT2nAzQAD1/vaETv1GrRfP4Vx5cfMNcDPwvP0h0DhanPym7OIf/+O67vcJ1/PCJ4KgdzaUP6Wz+dU+5yIL6fV+PsHGAOdwlPpvvUOyeeAVGyCdqkDNB6DPjsBSrnndfOGevOh3RhGItxvA+fX1CtbGFhgYUFkFMZPR6F1HnClHq8HyubWtJexX06CRmdt33hrd7nA7SFY4qoGpnYuOKcRykPPgDCBcsHx9Iv+fNL2PueBehCWUfYQIIMGLOCcOmXDXsh1+yCt35tUPfvzGFuSvzvoinXOxqa02qOhM6733nVP2MAdaej2XN11DPKjLZCD+yBvahGCo7JfTKAN9UD7s8Oe9zUNIhz8fWI8DG2k38WCFdxugANcXrvTVd1IEbuv3Jour7Hzn7jLMBNfKs7R3i67gRVrbeCOEDhinmWhAatsqdquM2XzHZINhK2cqTjHr/XZdVJUbgN3MWAVXKbSyg9jesRW2xP9di+lwrL5ojM3m2H/kG9hwcIA37c71W6wJdW2J2S5nrjYbq/t1AHAhJsKQeyfPvf6IMJgghPJhFZ4x0KlfLFvt22du45Au/A1SOlGc0P672XXwhLtOcM0kTTEMMd0qkVmMNXxMd/tsedUjInr4SQDgOfeXMSiN0FCL5WHah4L1qqYXPJOJlttd+a5M+YpcG5poLYKQ5f+6JJ4r8bbJYP47hq4r7QAs9PjYNhHJd4o8l5taiwuOpa7AS4XKqI/5NjJbTnaWK92nLdLuhQAJayRNMiygXPBeQN+Qbvu0zDc3y+aUzhbkGR73sI7ljvUnndx2q3t+X8CDAD66FtrIL864AAAAABJRU5ErkJggg%3D%3D};
265             }
266              
267             sub orange_atom_icon_32_32_body_with_headers {
268             # my ($feed) = @_;
269             my $base64 = orange_atom_icon_32_32_base64();
270             my ($ctype) = $base64 =~ m{data:([^/]+[/][^;]+);base64,};
271             $ctype = 'image/png' if !$ctype;
272             $base64 =~ s{^data:image/png;base64,}{};
273            
274             require MIME::Base64;
275             my $binary = MIME::Base64::decode_base64( $base64 );
276            
277             {
278             use bytes;
279             my $len = length $binary;
280             return "Content-length: $len\nContent-type: image/png\n\n$binary";
281             }
282             }
283              
284             sub orange_atom_icon_32_32_img_tag {
285             my ($feed, $attr) = @_; # !! make sure your $attr are XSS safe !!
286             $attr = $attr ? " $attr" : '';
287             return q{};
288             }
289              
290             sub orange_atom_icon_16_16_base64 {
291             # my ($feed) = @_;
292             return q{data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAKOSURBVHjadJNNSFRRGIbfc+6dO6OOOplDykSZaRCtnKRc+ANRUBFEm0gicxG0bGoVhERRYIvIjRAtW+UmW5QQQS6qRWQSWJRaUcjkT0LiVWfm/pzTd869M5LShe+eufC973m+n2Fj55KJymTt9ZgVy3AGMHoxvvEs/qZgDL4AlhbsAfv34g1TiRPxRMZbWwU3GQzO9clNhGcYBpkY4UlGiWRd5scnMvx5tUWano9IMoXy1pOUBPgz4xC/xv8r1hSexNKiB1Nhq6RIbQoVhy6i+MiCDffDEPyJITBvZZPYLwiQFFzVZ0Q45Moc8q8fwP34FGJ5FixaCevgBZT1DMNs7Nok9iikL8GyfWkZ3VgvhbljP6yOy2BbmzWR/+o2vM/PSmLfkchTaAIlsHa1ourSS5Sfvg8r3Q38mYb7+DzE9Ig2MDquAan2kth3AgKu0JSBapLCNranEWnPIHp2GDzZDEE3+1OBiXW4D4LHtViZCB/rBHJuHPmHp+CO3oK0Zyk7DvP4IGR1EwrPb673JX0mELslgvVRYW0O8usIvCe9AboyOdIPn1Ug9+Kupihr69ZiZSJFsYRwztbRO4icGARv6IT3ZkCTsKp68J2dyE2MQuZtsFgllbZHTyMsAVqsaldCVt8CtvsYPHsZzvtH+tZIc5e+sfBtLOhFYytRFAlYuGGED2clGNn8lO62m53U37y6XmM7M8E3onEIIpCSLp/vPyC31Bp6SUSsjmIbnO9jpVEZqbROzn15F5RDZs5CFs58FmaNBTZ5Ze+9hn11Genl/1mS0qjChumaCVvo2iViNXHkuD1g9Daxt7lVEQPMNpcSdah1pQb5kqho4yVXQc2iacHiMMpNFPia/jv/FWAAUTVTOunExzkAAAAASUVORK5CYII%3D};
293             }
294              
295             sub orange_atom_icon_16_16_body_with_headers {
296             # my ($feed) = @_;
297             my $base64 = orange_atom_icon_16_16_base64();
298             my ($ctype) = $base64 =~ m{data:([^/]+[/][^;]+);base64,};
299             $ctype = 'image/png' if !$ctype;
300             $base64 =~ s{^data:image/png;base64,}{};
301            
302             require MIME::Base64;
303             my $binary = MIME::Base64::decode_base64( $base64 );
304            
305             {
306             use bytes;
307             my $len = length $binary;
308             return "Content-length: $len\nContent-type: image/png\n\n$binary";
309             }
310             }
311              
312             sub orange_atom_icon_16_16_img_tag {
313             my ($feed, $attr) = @_; # !! make sure your $attr are XSS safe !!
314             $attr = $attr ? " $attr" : '';
315             return q{};
316             }
317              
318             #
319             # This is a workaround for the fact that XML::Atom::Person always pretends
320             # to by an author. This minimal change allows a contributor.
321             {
322             package XML::Atom::App::Contributor;
323             use base 'XML::Atom::Person';
324              
325             sub element_name { 'contributor' }
326             }
327              
328              
329             1;
330              
331             __END__