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

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

Add method for generating a feed from an arbitary list of nodes

File size: 9.9 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
107    # Grab our feed maker
108    my $maker = $self->fetch_maker($format);
109
110    # Find our newest node, so we can use that for the feed timestamp
111    my $newest_node;
112    foreach my $node (@nodes) {
113        if($node->{last_modified}) {
114            if((!$newest_node) || $node->{last_modified} < $newest_node->{last_modified}) {
115                $newest_node = $node;
116            }
117        }
118    }
119
120    # Grab the timestamp, and do our header
121    my $timestamp = $maker->feed_timestamp($newest_node);
122
123    my $feed = "Last-Modified: ".$timestamp."\n\n";
124
125    # Generate the feed itself
126    $feed .= $maker->generate_node_list_feed($timestamp, @nodes);
127
128    return $feed;
129}
130
131=item B<fetch_maker>
132For the given feed type, identify and return the maker routine for feeds
133of that type.
134
135my $maker = $feed->fetch_maker("rss");
136my $feed_contents = maker->node_all_versions(%options);
137
138Will always return something of type Wiki::Toolkit::Feed::Listing
139=cut
140sub fetch_maker {
141    my ($self,$feed_type) = @_;
142
143    my %known_types = (
144                          'atom'  => \&atom_maker,
145                          'rss' => \&rss_maker,
146                      );
147
148    croak "No feed type specified" unless $feed_type;
149    croak "Unknown feed type: $feed_type" unless $known_types{$feed_type};
150
151    return &{$known_types{$feed_type}};
152}
153
154sub atom_maker {
155    my $self = shift;
156 
157    unless ($self->{atom_maker}) {
158        $self->{atom_maker} = Wiki::Toolkit::Feed::Atom->new(
159            wiki                => $self->{wiki},
160            site_name           => $self->{site_name},
161            site_url            => $self->{config}->script_url,
162            site_description    => $self->{site_description},
163            make_node_url       => $self->{make_node_url},
164            recent_changes_link => $self->{config}->script_url . '?action=rc',
165            atom_link           => $self->{config}->script_url . '?action=rc&format=atom',
166            software_name       => 'OpenGuides',
167            software_homepage   => 'http://openguides.org/',
168            software_version    => $self->{og_version},
169        );
170    }
171   
172    $self->{atom_maker};
173}
174
175sub rss_maker {
176    my $self = shift;
177
178    unless ($self->{rss_maker}) {
179        $self->{rss_maker} = Wiki::Toolkit::Feed::RSS->new(
180            wiki                => $self->{wiki},
181            site_name           => $self->{site_name},
182            site_url            => $self->{config}->script_url,
183            site_description    => $self->{site_description},
184            make_node_url       => $self->{make_node_url},
185            recent_changes_link => $self->{config}->script_url . '?action=rc',
186            software_name       => 'OpenGuides',
187            software_homepage   => 'http://openguides.org/',
188            software_version    => $self->{og_version},
189        );
190    }
191   
192    $self->{rss_maker};
193}
194
195sub feed_timestamp {
196    my ($self, %args) = @_;
197
198    # Call the compatability timestamping method on the RSS Feed.
199    # People should really just pass in also_return_timestamp to the
200    #  feed method, and get the timestamp at the same time as their data
201    $self->rss_maker->rss_timestamp(%args);
202}
203
204=head1 NAME
205
206OpenGuides::Feed - generate data feeds for OpenGuides in various formats.
207
208=head1 DESCRIPTION
209
210Produces RSS 1.0 and Atom 1.0 feeds for OpenGuides.  Distributed and
211installed as part of the OpenGuides project, not intended for independent
212installation.  This documentation is probably only useful to OpenGuides
213developers.
214
215=head1 SYNOPSIS
216
217    use Wiki::Toolkit;
218    use OpenGuides::Config;
219    use OpenGuides::Feed;
220
221    my $wiki = Wiki::Toolkit->new( ... );
222    my $config = OpenGuides::Config->new( file => "wiki.conf" );
223    my $feed = OpenGuides::Feed->new( wiki       => $wiki,
224                                      config     => $config,
225                                      og_version => '1.0', );
226
227    # Ten most recent changes in RSS format.
228    my %args = ( items     => 10,
229                 feed_type => 'rss',
230                 also_return_timestamp => 1 );
231    my ($feed_output,$feed_timestamp) = $feed->make_feed( %args );
232
233    print "Content-Type: application/rdf+xml\n";
234    print "Last-Modified: " . $feed_timestamp . "\n\n";
235    print $feed_output;
236
237=head1 METHODS
238
239=over 4
240
241=item B<new>
242
243    my $feed = OpenGuides::Feed->new( wiki       => $wiki,
244                                      config     => $config,
245                                      og_version => '1.0', );
246
247C<wiki> must be a L<Wiki::Toolkit> object and C<config> must be an
248L<OpenGuides::Config> object.  Both of these arguments are mandatory.
249C<og_version> is an optional argument specifying the version of
250OpenGuides for inclusion in the feed.
251
252=item B<rss_maker>
253
254Returns a raw L<Wiki::Toolkit::Feed::RSS> object created with the values you
255invoked this module with.
256
257=item B<atom_maker>
258
259Returns a raw L<Wiki::Toolkit::Feed::Atom> object created with the values you
260invoked this module with.
261
262=item B<make_feed>
263
264    # Ten most recent changes in RSS format.
265    my %args = ( items     => 10,
266                 feed_type => 'rss',
267                 also_return_timestamp => 1 );
268    my ($feed_output,$feed_timestamp) = $rdf_writer->make_feed( %args );
269
270    print "Content-Type: application/rdf+xml\n";
271    print "Last-Modified: " . $feed_timestamp . "\n\n";
272    print $feed_output;
273    print $rdf_writer->make_feed( %args );
274
275
276    # All the changes made by bob in the past week, ignoring minor edits, in Atom.
277    $args{days}               = 7;
278    $args{ignore_minor_edits  = 1;
279    $args{filter_on_metadata} => { username => "bob" };
280    $args{also_return_timestamp} => 1;
281
282    my ($feed_output,$feed_timestamp) = $rdf_writer->make_feed( %args );
283    print "Content-Type: application/atom+xml\n";
284    print "Last-Modified: " . $feed_timestamp . "\n\n";
285    print $feed_output;
286
287=item B<feed_timestamp>
288
289Instead of calling this, you should instead pass in the 'also_return_timestamp'
290option. You will then get back the feed timestamp, along with the feed output.
291
292This method will be removed in future, and currently will only return
293meaningful values if your arguments relate to recent changes.
294
295    print "Last-Modified: " . $feed->feed_timestamp( %args ) . "\n\n";
296
297Returns the timestamp of something in POSIX::strftime style ("Tue, 29 Feb 2000
29812:34:56 GMT"). Takes the same arguments as make_recentchanges_rss().
299You will most likely need this to print a Last-Modified HTTP header so
300user-agents can determine whether they need to reload the feed or not.
301
302=back
303
304=head1 SEE ALSO
305
306=over 4
307
308=item * L<Wiki::Toolkit>, L<Wiki::Toolkit::Feed::RSS> and L<Wiki::Toolkit::Feed::Atom>
309
310=item * L<http://openguides.org/>
311
312=back
313
314=head1 AUTHOR
315
316The OpenGuides Project (openguides-dev@openguides.org)
317
318=head1 COPYRIGHT
319
320Copyright (C) 2003-2006 The OpenGuides Project.  All Rights Reserved.
321
322This module is free software; you can redistribute it and/or modify it
323under the same terms as Perl itself.
324
325=head1 CREDITS
326
327Written by Earle Martin, based on the original OpenGuides::RDF by Kake Pugh.
328
329=cut
330
3311;
Note: See TracBrowser for help on using the repository browser.