| 1 | package Wiki::Toolkit::Feed::Listing; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use Carp qw( croak ); |
|---|
| 5 | |
|---|
| 6 | =item B<fetch_recently_changed_nodes> |
|---|
| 7 | |
|---|
| 8 | Based on the supplied criteria, fetch a list of the recently changed nodes |
|---|
| 9 | |
|---|
| 10 | =cut |
|---|
| 11 | |
|---|
| 12 | sub fetch_recently_changed_nodes { |
|---|
| 13 | my ($self, %args) = @_; |
|---|
| 14 | |
|---|
| 15 | my $wiki = $self->{wiki}; |
|---|
| 16 | |
|---|
| 17 | my %criteria = ( |
|---|
| 18 | ignore_case => 1, |
|---|
| 19 | ); |
|---|
| 20 | |
|---|
| 21 | # If we're not passed any parameters to limit the items returned, |
|---|
| 22 | # default to 15. |
|---|
| 23 | $args{days} ? $criteria{days} = $args{days} |
|---|
| 24 | : $criteria{last_n_changes} = $args{items} || 15; |
|---|
| 25 | |
|---|
| 26 | $criteria{metadata_wasnt} = { major_change => 0 } if $args{ignore_minor_edits}; |
|---|
| 27 | $criteria{metadata_was} = $args{filter_on_metadata} if $args{filter_on_metadata}; |
|---|
| 28 | |
|---|
| 29 | my @changes = $wiki->list_recent_changes(%criteria); |
|---|
| 30 | |
|---|
| 31 | return @changes; |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | =item B<fetch_oldest_for_recently_changed> |
|---|
| 35 | |
|---|
| 36 | Based on the supplied criteria (but not using all of those used by |
|---|
| 37 | B<fetch_recently_changed_nodes>), find the newest node from the recently |
|---|
| 38 | changed nodes set. Normally used for dating the whole of a Feed. |
|---|
| 39 | |
|---|
| 40 | =cut |
|---|
| 41 | |
|---|
| 42 | sub fetch_newest_for_recently_changed { |
|---|
| 43 | my ($self, %args) = @_; |
|---|
| 44 | |
|---|
| 45 | my %criteria = (ignore_case => 1); |
|---|
| 46 | |
|---|
| 47 | $args{days} ? $criteria{days} = $args{days} |
|---|
| 48 | : $criteria{last_n_changes} = $args{items} || 15; |
|---|
| 49 | |
|---|
| 50 | $criteria{metadata_wasnt} = { major_change => 0 } if $args{ignore_minor_edits}; |
|---|
| 51 | $criteria{metadata_was} = $args{filter_on_metadata} if $args{filter_on_metadata}; |
|---|
| 52 | |
|---|
| 53 | my @changes = $self->{wiki}->list_recent_changes(%criteria); |
|---|
| 54 | |
|---|
| 55 | return $changes[0]; |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | |
|---|
| 59 | =item B<fetch_node_all_versions> |
|---|
| 60 | |
|---|
| 61 | For a given node (name or ID), return all the versions there have been, |
|---|
| 62 | including all metadata required for it to go into a "recent changes" |
|---|
| 63 | style listing. |
|---|
| 64 | |
|---|
| 65 | =cut |
|---|
| 66 | |
|---|
| 67 | sub fetch_node_all_versions { |
|---|
| 68 | my ($self, %args) = @_; |
|---|
| 69 | |
|---|
| 70 | # Check we got the right options |
|---|
| 71 | unless($args{'name'}) { |
|---|
| 72 | return (); |
|---|
| 73 | } |
|---|
| 74 | |
|---|
| 75 | # Do the fetch |
|---|
| 76 | my @nodes = $self->{wiki}->list_node_all_versions( |
|---|
| 77 | name => $args{'name'}, |
|---|
| 78 | with_content => 0, |
|---|
| 79 | with_metadata => 1, |
|---|
| 80 | ); |
|---|
| 81 | |
|---|
| 82 | # Ensure that all the metadata fields are arrays and not strings |
|---|
| 83 | foreach my $node (@nodes) { |
|---|
| 84 | foreach my $mdk (keys %{$node->{'metadata'}}) { |
|---|
| 85 | unless(ref($node->{'metadata'}->{$mdk}) eq "ARRAY") { |
|---|
| 86 | $node->{'metadata'}->{$mdk} = [ $node->{'metadata'}->{$mdk} ]; |
|---|
| 87 | } |
|---|
| 88 | } |
|---|
| 89 | } |
|---|
| 90 | |
|---|
| 91 | return @nodes; |
|---|
| 92 | } |
|---|
| 93 | |
|---|
| 94 | |
|---|
| 95 | =item B<recent_changes> |
|---|
| 96 | |
|---|
| 97 | Build an Atom Feed of the recent changes to the Wiki::Toolkit instance, |
|---|
| 98 | using any supplied parameters to narrow the results. |
|---|
| 99 | |
|---|
| 100 | If the argument "also_return_timestamp" is supplied, it will return an |
|---|
| 101 | array of the feed, and the feed timestamp. Otherwise it just returns the feed. |
|---|
| 102 | |
|---|
| 103 | =cut |
|---|
| 104 | |
|---|
| 105 | sub recent_changes |
|---|
| 106 | { |
|---|
| 107 | my ($self, %args) = @_; |
|---|
| 108 | |
|---|
| 109 | my @changes = $self->fetch_recently_changed_nodes(%args); |
|---|
| 110 | my $feed_timestamp = $self->feed_timestamp( |
|---|
| 111 | $self->fetch_newest_for_recently_changed(%args) |
|---|
| 112 | ); |
|---|
| 113 | |
|---|
| 114 | my $feed = $self->generate_node_list_feed($feed_timestamp, @changes); |
|---|
| 115 | |
|---|
| 116 | if($args{'also_return_timestamp'}) { |
|---|
| 117 | return ($feed,$feed_timestamp); |
|---|
| 118 | } else { |
|---|
| 119 | return $feed; |
|---|
| 120 | } |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | |
|---|
| 124 | =item B<node_all_versions> |
|---|
| 125 | |
|---|
| 126 | Build an Atom Feed of all the different versions of a given node. |
|---|
| 127 | |
|---|
| 128 | If the argument "also_return_timestamp" is supplied, it will return an |
|---|
| 129 | array of the feed, and the feed timestamp. Otherwise it just returns the feed. |
|---|
| 130 | |
|---|
| 131 | =cut |
|---|
| 132 | |
|---|
| 133 | sub node_all_versions |
|---|
| 134 | { |
|---|
| 135 | my ($self, %args) = @_; |
|---|
| 136 | |
|---|
| 137 | my @all_versions = $self->fetch_node_all_versions(%args); |
|---|
| 138 | my $feed_timestamp = $self->feed_timestamp( $all_versions[0] ); |
|---|
| 139 | |
|---|
| 140 | my $feed = $self->generate_node_list_feed($feed_timestamp, @all_versions); |
|---|
| 141 | |
|---|
| 142 | if($args{'also_return_timestamp'}) { |
|---|
| 143 | return ($feed,$feed_timestamp); |
|---|
| 144 | } else { |
|---|
| 145 | return $feed; |
|---|
| 146 | } |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | =item B<format_geo> |
|---|
| 150 | |
|---|
| 151 | Using the geo and space xml namespaces, format the supplied node metadata |
|---|
| 152 | into geo: and space: tags, suitable for inclusion in a feed with those |
|---|
| 153 | namespaces imported. |
|---|
| 154 | |
|---|
| 155 | =cut |
|---|
| 156 | |
|---|
| 157 | sub format_geo { |
|---|
| 158 | my ($self, @args) = @_; |
|---|
| 159 | |
|---|
| 160 | my %metadata; |
|---|
| 161 | if(ref($args[0]) eq "HASH") { |
|---|
| 162 | %metadata = %{$_[1]}; |
|---|
| 163 | } else { |
|---|
| 164 | %metadata = @args; |
|---|
| 165 | } |
|---|
| 166 | |
|---|
| 167 | my %mapping = ( |
|---|
| 168 | "os_x" => "space:os_x", |
|---|
| 169 | "os_y" => "space:os_y", |
|---|
| 170 | "latitude" => "geo:lat", |
|---|
| 171 | "longitude" => "geo:long", |
|---|
| 172 | "distance" => "space:distance", |
|---|
| 173 | ); |
|---|
| 174 | |
|---|
| 175 | my $feed = ""; |
|---|
| 176 | |
|---|
| 177 | foreach my $geo (keys %metadata) { |
|---|
| 178 | my $geo_val = $metadata{$geo}; |
|---|
| 179 | if(ref($geo_val) eq "ARRAY") { |
|---|
| 180 | $geo_val = $geo_val->[0]; |
|---|
| 181 | } |
|---|
| 182 | |
|---|
| 183 | if($mapping{$geo}) { |
|---|
| 184 | my $tag = $mapping{$geo}; |
|---|
| 185 | $feed .= " <$tag>$geo_val</$tag>\n"; |
|---|
| 186 | } |
|---|
| 187 | } |
|---|
| 188 | |
|---|
| 189 | return $feed; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | #item B<handle_supply_one_of> |
|---|
| 193 | # Utility method, to help with argument passing where one of a list of |
|---|
| 194 | # arguments must be supplied |
|---|
| 195 | #=cut |
|---|
| 196 | sub handle_supply_one_of { |
|---|
| 197 | my ($self,$mref,$aref) = @_; |
|---|
| 198 | my %mustoneof = %{$mref}; |
|---|
| 199 | my %args = %{$aref}; |
|---|
| 200 | |
|---|
| 201 | foreach my $oneof (keys %mustoneof) { |
|---|
| 202 | my $val = undef; |
|---|
| 203 | foreach my $poss (@{$mustoneof{$oneof}}) { |
|---|
| 204 | unless($val) { |
|---|
| 205 | if($args{$poss}) { $val = $args{$poss}; } |
|---|
| 206 | } |
|---|
| 207 | } |
|---|
| 208 | if($val) { |
|---|
| 209 | $self->{$oneof} = $val; |
|---|
| 210 | } else { |
|---|
| 211 | croak "No $oneof supplied, or one of its equivalents (".join(",", @{$mustoneof{$oneof}}).")"; |
|---|
| 212 | } |
|---|
| 213 | } |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | |
|---|
| 217 | # The following are methods that any feed renderer must provide |
|---|
| 218 | |
|---|
| 219 | =item B<feed_timestamp> |
|---|
| 220 | |
|---|
| 221 | All implementing feed renderers must implement a method to produce a |
|---|
| 222 | feed specific timestamp, based on the supplied node |
|---|
| 223 | |
|---|
| 224 | =cut |
|---|
| 225 | |
|---|
| 226 | sub feed_timestamp { die("Not implemented by feed renderer!"); } |
|---|
| 227 | |
|---|
| 228 | =item B<generate_node_list_feed> |
|---|
| 229 | |
|---|
| 230 | All implementing feed renderers must implement a method to produce a |
|---|
| 231 | feed from the supplied list of nodes |
|---|
| 232 | |
|---|
| 233 | =cut |
|---|
| 234 | |
|---|
| 235 | sub generate_node_list_feed { die("Not implemented by feed renderer!"); } |
|---|
| 236 | |
|---|
| 237 | =item B<generate_node_name_distance_feed> |
|---|
| 238 | |
|---|
| 239 | All implementing feed renderers must implement a method to produce a |
|---|
| 240 | stripped down feed from the supplied list of node names, and optionally |
|---|
| 241 | locations and distance from a reference point. |
|---|
| 242 | |
|---|
| 243 | =cut |
|---|
| 244 | |
|---|
| 245 | sub generate_node_name_distance_feed { die("Not implemented by feed renderer!"); } |
|---|
| 246 | |
|---|
| 247 | =item B<parse_feed_timestamp> |
|---|
| 248 | |
|---|
| 249 | Take a feed_timestamp and return a Time::Piece object. |
|---|
| 250 | |
|---|
| 251 | =cut |
|---|
| 252 | sub parse_feed_timestamp { die("Not implemented by feed renderer!"); } |
|---|
| 253 | |
|---|
| 254 | |
|---|
| 255 | 1; |
|---|
| 256 | |
|---|
| 257 | __END__ |
|---|
| 258 | |
|---|
| 259 | =head1 NAME |
|---|
| 260 | |
|---|
| 261 | Wiki::Toolkit::Feed::Listing - parent class for Feeds from Wiki::Toolkit. |
|---|
| 262 | |
|---|
| 263 | =head1 DESCRIPTION |
|---|
| 264 | |
|---|
| 265 | Handles common data fetching tasks, so that child classes need only |
|---|
| 266 | worry about formatting the feeds. |
|---|
| 267 | |
|---|
| 268 | Also enforces some common methods that must be implemented. |
|---|
| 269 | |
|---|
| 270 | =head1 MAINTAINER |
|---|
| 271 | |
|---|
| 272 | The Wiki::Toolkit team, http://www.wiki-toolkit.org/. |
|---|
| 273 | |
|---|
| 274 | =head1 COPYRIGHT AND LICENSE |
|---|
| 275 | |
|---|
| 276 | Copyright 2006 the Wiki::Toolkit team. |
|---|
| 277 | |
|---|
| 278 | This module is free software; you can redistribute it and/or modify it |
|---|
| 279 | under the same terms as Perl itself. |
|---|
| 280 | |
|---|
| 281 | |
|---|
| 282 | =cut |
|---|