File Coverage

blib/lib/OpenGuides/Feed.pm
Criterion Covered Total %
statement 108 113 95.5
branch 31 40 77.5
condition 7 15 46.6
subroutine 20 21 95.2
pod 11 11 100.0
total 177 200 88.5


line stmt bran cond sub pod time code
1             package OpenGuides::Feed;
2              
3 92     92   504 use strict;
  92         160  
  92         2656  
4              
5 92     92   455 use vars qw( $VERSION );
  92         310  
  92         4480  
6             $VERSION = '0.03';
7              
8 92     92   76773 use Wiki::Toolkit::Feed::Atom;
  92         2429070  
  92         2840  
9 92     92   78176 use Wiki::Toolkit::Feed::RSS;
  92         200896  
  92         2694  
10 92     92   572 use Time::Piece;
  92         190  
  92         470  
11 92     92   5487 use URI::Escape;
  92         190  
  92         4785  
12 92     92   462 use Carp 'croak';
  92         175  
  92         4123  
13              
14 92     92   526 use base qw( Class::Accessor );
  92         161  
  92         93878  
15             # Add more here if we need them - this one added for testing purposes.
16             my @variables = qw( html_equiv_link );
17             OpenGuides::Feed->mk_accessors( @variables );
18              
19             sub new {
20 21     21 1 174 my ($class, @args) = @_;
21 21         55 my $self = {};
22 21         49 bless $self, $class;
23 21         96 $self->_init(@args);
24             }
25              
26             sub _init {
27 21     21   95 my ($self, %args) = @_;
28              
29 21         48 my $wiki = $args{wiki};
30              
31 21 50 33     211 unless ( $wiki && UNIVERSAL::isa( $wiki, "Wiki::Toolkit" ) ) {
32 0         0 croak "No Wiki::Toolkit object supplied.";
33             }
34 21         105 $self->{wiki} = $wiki;
35              
36 21         47 my $config = $args{config};
37              
38 21 50 33     180 unless ( $config && UNIVERSAL::isa( $config, "OpenGuides::Config" ) ) {
39 0         0 croak "No OpenGuides::Config object supplied.";
40             }
41 21         49 $self->{config} = $config;
42              
43             $self->{make_node_url} = sub {
44 36     36   15438 my ($node_name, $version) = @_;
45              
46 36         76 my $config = $self->{config};
47              
48 36         124 my $node_url = $config->script_url . uri_escape($config->script_name) . '?';
49 36 100       903 $node_url .= 'id=' if defined $version;
50 36         128 $node_url .= uri_escape($self->{wiki}->formatter->node_name_to_node_param($node_name));
51 36 100       1299 $node_url .= ';version=' . uri_escape($version) if defined $version;
52              
53 36         320 $node_url;
54 21         165 };
55 21         104 $self->{site_name} = $config->site_name . " - Recent Changes";
56 21   50     315 $self->{default_city} = $config->default_city || "";
57 21   50     333 $self->{default_country} = $config->default_country || "";
58 21   50     323 $self->{site_description} = $config->site_desc || "";
59 21         269 $self->{og_version} = $args{og_version};
60             $self->{html_equiv_link} = $self->{config}->script_url
61 21         94 . $self->{config}->script_name . '?action=rc';
62              
63 21         295 $self;
64             }
65              
66             =over 4
67              
68             =item B
69             Overrides the default feed name and default feed http equivalent url.
70             Useful on custom feeds, where the defaults are incorrect.
71              
72             $feed->set_feed_name_and_url("Search Results", "search=pub");
73             $feed->build_mini_feed_for_nodes("rss", @search_results);
74              
75             =cut
76              
77             sub set_feed_name_and_url_params {
78 8     8 1 19 my ($self, $name, $url) = @_;
79              
80 8 50       31 unless($url =~ /^http/) {
81 8         32 my $b_url = $self->{config}->script_url;
82 8 100       30 unless($url =~ /\.cgi\?/) { $b_url .= "?"; }
  6         14  
83 8         16 $b_url .= $url;
84 8         19 $url = $b_url;
85             }
86              
87 8         27 $self->{site_name} = $self->{config}->{site_name} . " - " . $name;
88 8         20 $self->{html_equiv_link} = $url;
89             }
90              
91             =item B
92              
93             Produce one of the standard feeds, in the requested format.
94              
95              
96             my $feed_contents = feeds->make_feed(
97             feed_type => 'rss',
98             feed_listing => 'recent_changes'
99             );
100              
101             Passes additional arguments through to the underlying Wiki::Toolkit::Feed
102              
103             =cut
104              
105             sub make_feed {
106 12     12 1 1631 my ($self, %args) = @_;
107              
108 12         38 my $feed_type = $args{feed_type};
109 12         29 my $feed_listing = $args{feed_listing};
110              
111 12         53 my %known_listings = (
112             'recent_changes' => 1,
113             'node_all_versions' => 1,
114             );
115              
116 12 50       41 croak "No feed listing specified" unless $feed_listing;
117 12 50       63 croak "Unknown feed listing: $feed_listing" unless $known_listings{$feed_listing};
118              
119              
120             # Tweak any settings, as required by our feed listing
121 12 100       43 if ($feed_listing eq 'node_all_versions') {
122             $self->set_feed_name_and_url_params(
123             "All versions of ".$args{'name'},
124 4         21 "action=list_all_versions;id=".$args{'name'}
125             );
126             }
127              
128              
129             # Fetch the right Wiki::Toolkit::Feeds::Listing instance to use
130 12         51 my $maker = $self->fetch_maker($feed_type);
131              
132              
133             # Call the appropriate feed listing from it
134 12 100       56 if ($feed_listing eq 'recent_changes') {
    50          
135 8         75 return $maker->recent_changes(%args);
136             }
137             elsif ($feed_listing eq 'node_all_versions') {
138 4         73 return $maker->node_all_versions(%args);
139             }
140             }
141              
142             =item B
143              
144             For the given feed type, build a feed from the supplied list of nodes.
145             Will figure out the feed timestamp from the newest node, and output a
146             last modified header based on this.
147              
148             my @nodes = $wiki->fetch_me_nodes_I_like();
149             my $feed_contents = $feed->build_feed_for_nodes("rss", @nodes);
150              
151             =cut
152              
153             sub build_feed_for_nodes {
154 4     4 1 25333 my ($self, $format, @nodes) = @_;
155 4         21 return $self->render_feed_for_nodes($format, undef, 1, @nodes);
156             }
157              
158             =item B
159              
160             For the given feed type, build a mini feed (name and distance) from the
161             supplied list of nodes.
162             Will figure out the feed timestamp from the newest node, and output a
163             last modified header based on this.
164              
165             my @nodes = $wiki->search_near_here();
166             my $feed_contents = $feed->build_mini_feed_for_nodes("rss", @nodes);
167              
168             =cut
169              
170             sub build_mini_feed_for_nodes {
171 4     4 1 6436 my ($self, $format, @nodes) = @_;
172 4         19 return $self->render_feed_for_nodes($format, undef, 0, @nodes);
173             }
174              
175             =item B
176              
177             Normally internal method to perform the appropriate building of a feed
178             based on a list of nodes.
179              
180             =cut
181              
182             sub render_feed_for_nodes {
183 8     8 1 21 my ($self, $format, $html_url, $is_full, @nodes) = @_;
184              
185             # Grab our feed maker
186 8         29 my $maker = $self->fetch_maker($format);
187              
188             # Find our newest node, so we can use that for the feed timestamp
189 8         17 my $newest_node;
190 8         22 foreach my $node (@nodes) {
191 17 100       55 if($node->{last_modified}) {
192 14 100 66     158 if((!$newest_node) || ($node->{last_modified} gt $newest_node->{last_modified})) {
193 6         13 $newest_node = $node;
194             }
195             }
196             }
197              
198             # Grab the timestamp, and do our header
199 8         45 my $timestamp = $maker->feed_timestamp($newest_node);
200              
201 8         732 my $feed = "Last-Modified: ".$timestamp."\n\n";
202              
203             # Generate the feed itself
204 8 100       29 if($is_full) {
205 4         23 $feed .= $maker->generate_node_list_feed($timestamp, @nodes);
206             } else {
207 4         23 $feed .= $maker->generate_node_name_distance_feed($timestamp, @nodes);
208             }
209              
210 8         780 return $feed;
211             }
212              
213             =item B
214              
215             For the given feed type, return the default content type for that feed.
216              
217             my $content_type = $feed->default_content_type("rss");
218              
219             =cut
220              
221             sub default_content_type {
222 12     12 1 38 my ($self,$feed_type) = @_;
223              
224 12         26 my $content_type;
225              
226 12 100       55 if ($feed_type eq 'rss') {
    50          
227 6         13 $content_type = "application/rdf+xml";
228             }
229             elsif ($feed_type eq 'atom') {
230 6         15 $content_type = "application/atom+xml";
231             }
232             else {
233 0         0 croak "Unknown feed type given: $feed_type";
234             }
235              
236 12         49 return $content_type;
237             }
238              
239             =item B
240              
241             For the given feed type, identify and return the maker routine for feeds
242             of that type.
243              
244             my $maker = $feed->fetch_maker("rss");
245             my $feed_contents = maker->node_all_versions(%options);
246              
247             Will always return something of type Wiki::Toolkit::Feed::Listing
248              
249             =cut
250              
251             sub fetch_maker {
252 26     26 1 59 my ($self,$feed_type) = @_;
253              
254 26         208 my %known_types = (
255             'atom' => \&atom_maker,
256             'rss' => \&rss_maker,
257             );
258              
259 26 50       93 croak "No feed type specified" unless $feed_type;
260 26 50       78 croak "Unknown feed type: $feed_type" unless $known_types{$feed_type};
261              
262 26         43 return &{$known_types{$feed_type}};
  26         78  
263             }
264              
265             sub atom_maker {
266 13     13 1 28 my $self = shift;
267              
268 13 100       64 unless ($self->{atom_maker}) {
269             $self->{atom_maker} = Wiki::Toolkit::Feed::Atom->new(
270             wiki => $self->{wiki},
271             site_name => $self->{site_name},
272             site_url => $self->{config}->script_url,
273             site_description => $self->{site_description},
274             make_node_url => $self->{make_node_url},
275             html_equiv_link => $self->{html_equiv_link},
276             atom_link => $self->{html_equiv_link} . ";format=atom",
277             software_name => 'OpenGuides',
278             software_homepage => 'http://openguides.org/',
279             software_version => $self->{og_version},
280             encoding => $self->{config}->http_charset,
281 8         57 );
282             }
283              
284 13         2575 $self->{atom_maker};
285             }
286              
287             sub rss_maker {
288 13     13 1 21 my $self = shift;
289              
290 13 100       49 unless ($self->{rss_maker}) {
291             $self->{rss_maker} = Wiki::Toolkit::Feed::RSS->new(
292             wiki => $self->{wiki},
293             site_name => $self->{site_name},
294             site_url => $self->{config}->script_url,
295             site_description => $self->{site_description},
296             make_node_url => $self->{make_node_url},
297             html_equiv_link => $self->{html_equiv_link},
298             software_name => 'OpenGuides',
299             software_homepage => 'http://openguides.org/',
300             software_version => $self->{og_version},
301             encoding => $self->{config}->http_charset,
302 8         47 );
303             }
304              
305 13         2347 $self->{rss_maker};
306             }
307              
308             sub feed_timestamp {
309 0     0 1   my ($self, %args) = @_;
310              
311             # Call the compatability timestamping method on the RSS Feed.
312             # People should really just pass in also_return_timestamp to the
313             # feed method, and get the timestamp at the same time as their data
314 0           $self->rss_maker->rss_timestamp(%args);
315             }
316              
317             =back
318              
319             =head1 NAME
320              
321             OpenGuides::Feed - generate data feeds for OpenGuides in various formats.
322              
323             =head1 DESCRIPTION
324              
325             Produces RSS 1.0 and Atom 1.0 feeds for OpenGuides. Distributed and
326             installed as part of the OpenGuides project, not intended for independent
327             installation. This documentation is probably only useful to OpenGuides
328             developers.
329              
330             =head1 SYNOPSIS
331              
332             use Wiki::Toolkit;
333             use OpenGuides::Config;
334             use OpenGuides::Feed;
335              
336             my $wiki = Wiki::Toolkit->new( ... );
337             my $config = OpenGuides::Config->new( file => "wiki.conf" );
338             my $feed = OpenGuides::Feed->new( wiki => $wiki,
339             config => $config,
340             og_version => '1.0', );
341              
342             # Ten most recent changes in RSS format.
343             my %args = ( items => 10,
344             feed_type => 'rss',
345             also_return_timestamp => 1 );
346             my ($feed_output,$feed_timestamp) = $feed->make_feed( %args );
347              
348             print "Content-Type: application/rdf+xml\n";
349             print "Last-Modified: " . $feed_timestamp . "\n\n";
350             print $feed_output;
351              
352             =head1 METHODS
353              
354             =over 4
355              
356             =item B
357              
358             my $feed = OpenGuides::Feed->new( wiki => $wiki,
359             config => $config,
360             og_version => '1.0', );
361              
362             C must be a L object and C must be an
363             L object. Both of these arguments are mandatory.
364             C is an optional argument specifying the version of
365             OpenGuides for inclusion in the feed.
366              
367             =item B
368              
369             Returns a raw L object created with the values you
370             invoked this module with.
371              
372             =item B
373              
374             Returns a raw L object created with the values you
375             invoked this module with.
376              
377             =item B
378              
379             # Ten most recent changes in RSS format.
380             my %args = ( items => 10,
381             feed_type => 'rss',
382             also_return_timestamp => 1 );
383             my ($feed_output,$feed_timestamp) = $rdf_writer->make_feed( %args );
384              
385             print "Content-Type: application/rdf+xml\n";
386             print "Last-Modified: " . $feed_timestamp . "\n\n";
387             print $feed_output;
388             print $rdf_writer->make_feed( %args );
389              
390              
391             # All the changes made by bob in the past week, ignoring minor edits, in Atom.
392             $args{days} = 7;
393             $args{ignore_minor_edits = 1;
394             $args{filter_on_metadata} => { username => "bob" };
395             $args{also_return_timestamp} => 1;
396              
397             my ($feed_output,$feed_timestamp) = $rdf_writer->make_feed( %args );
398             print "Content-Type: application/atom+xml\n";
399             print "Last-Modified: " . $feed_timestamp . "\n\n";
400             print $feed_output;
401              
402             =item B
403              
404             Instead of calling this, you should instead pass in the 'also_return_timestamp'
405             option. You will then get back the feed timestamp, along with the feed output.
406              
407             This method will be removed in future, and currently will only return
408             meaningful values if your arguments relate to recent changes.
409              
410             print "Last-Modified: " . $feed->feed_timestamp( %args ) . "\n\n";
411              
412             Returns the timestamp of something in POSIX::strftime style ("Tue, 29 Feb 2000
413             12:34:56 GMT"). Takes the same arguments as make_recentchanges_rss().
414             You will most likely need this to print a Last-Modified HTTP header so
415             user-agents can determine whether they need to reload the feed or not.
416              
417             =back
418              
419             =head1 SEE ALSO
420              
421             =over 4
422              
423             =item * L, L and L
424              
425             =item * L
426              
427             =back
428              
429             =head1 AUTHOR
430              
431             The OpenGuides Project (openguides-dev@lists.openguides.org)
432              
433             =head1 COPYRIGHT
434              
435             Copyright (C) 2003-2012 The OpenGuides Project. All Rights Reserved.
436              
437             This module is free software; you can redistribute it and/or modify it
438             under the same terms as Perl itself.
439              
440             =head1 CREDITS
441              
442             Written by Earle Martin, based on the original OpenGuides::RDF by Kake Pugh.
443              
444             =cut
445              
446             1;