source: trunk/lib/OpenGuides/Feed.pm @ 817

Last change on this file since 817 was 817, checked in by nick, 15 years ago

Shift the content type method
Add mini feed method
Start to do urls, names etc for feeds properly
New tests

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