Show
Ignore:
Timestamp:
02/01/09 00:06:59 (3 years ago)
Author:
dom
Message:

fix up pod and add pod testing (fixes #45)

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • wiki-toolkit/trunk/lib/Wiki/Toolkit/Feed/RSS.pm

    r432 r497  
    1313use Wiki::Toolkit::Feed::Listing; 
    1414@ISA = qw( Wiki::Toolkit::Feed::Listing ); 
    15  
    16 sub new { 
    17     my $class = shift; 
    18     my $self  = {}; 
    19     bless $self, $class; 
    20  
    21     my %args = @_; 
    22     my $wiki = $args{wiki}; 
    23  
    24     unless ($wiki && UNIVERSAL::isa($wiki, 'Wiki::Toolkit')) { 
    25         croak 'No Wiki::Toolkit object supplied'; 
    26     } 
    27    
    28     $self->{wiki} = $wiki; 
    29    
    30     # Mandatory arguments. 
    31     foreach my $arg (qw/site_name site_url make_node_url/) { 
    32         croak "No $arg supplied" unless $args{$arg}; 
    33         $self->{$arg} = $args{$arg}; 
    34     } 
    35  
    36     # Must-supply-one-of arguments 
    37     my %mustoneof = ( 'html_equiv_link' => ['html_equiv_link','recent_changes_link'] ); 
    38     $self->handle_supply_one_of(\%mustoneof,\%args); 
    39    
    40     # Optional arguments. 
    41     foreach my $arg (qw/site_description interwiki_identifier make_diff_url make_history_url encoding software_name software_version software_homepage/) { 
    42         $self->{$arg} = $args{$arg} || ''; 
    43     } 
    44  
    45     # Supply some defaults, if a blank string isn't what we want 
    46     unless($self->{encoding}) { 
    47         $self->{encoding} = $self->{wiki}->store->{_charset}; 
    48     } 
    49  
    50     $self->{timestamp_fmt} = $Wiki::Toolkit::Store::Database::timestamp_fmt; 
    51     $self->{utc_offset} = strftime "%z", localtime; 
    52     $self->{utc_offset} =~ s/(..)(..)$/$1:$2/; 
    53  
    54     $self; 
    55 } 
    56  
    57 =item <build_feed_start> 
    58  
    59 Internal method, to build all the stuff that will go at the start of a feed. 
    60 Generally will output namespaces, headers and so on. 
    61  
    62 =cut 
    63  
    64 sub build_feed_start { 
    65     my ($self,$feed_timestamp) = @_; 
    66  
    67     #"http://purl.org/rss/1.0/modules/wiki/" 
    68     return qq{<?xml version="1.0" encoding="}. $self->{encoding} .qq{"?> 
    69  
    70 <rdf:RDF 
    71  xmlns         = "http://purl.org/rss/1.0/" 
    72  xmlns:dc      = "http://purl.org/dc/elements/1.1/" 
    73  xmlns:doap    = "http://usefulinc.com/ns/doap#" 
    74  xmlns:foaf    = "http://xmlns.com/foaf/0.1/" 
    75  xmlns:rdf     = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
    76  xmlns:rdfs    = "http://www.w3.org/2000/01/rdf-schema#" 
    77  xmlns:modwiki = "http://www.usemod.com/cgi-bin/mb.pl?ModWiki" 
    78  xmlns:geo     = "http://www.w3.org/2003/01/geo/wgs84_pos#" 
    79  xmlns:space   = "http://frot.org/space/0.1/" 
    80 > 
    81 }; 
    82 } 
    83  
    84 =item <build_feed_mid> 
    85  
    86 Internal method, to build all the stuff (except items) to go inside the channel 
    87  
    88 =cut 
    89  
    90 sub build_feed_mid { 
    91     my ($self,$feed_timestamp) = @_; 
    92  
    93     my $rss .= qq{<dc:publisher>} . $self->{site_url} . qq{</dc:publisher>\n}; 
    94  
    95     if ($self->{software_name}) { 
    96         $rss .= qq{<foaf:maker> 
    97         <doap:Project> 
    98         <doap:name>} . $self->{software_name} . qq{</doap:name>\n}; 
    99     } 
    100  
    101     if ($self->{software_name} && $self->{software_homepage}) { 
    102         $rss .= qq{    <doap:homepage rdf:resource="} . $self->{software_homepage} . qq{" />\n}; 
    103     } 
    104  
    105     if ($self->{software_name} && $self->{software_version}) { 
    106         $rss .= qq{    <doap:release> 
    107       <doap:Version> 
    108       <doap:revision>} . $self->{software_version} . qq{</doap:revision> 
    109       </doap:Version> 
    110     </doap:release>\n}; 
    111     } 
    112  
    113     if ($self->{software_name}) { 
    114         $rss .= qq{  </doap:Project> 
    115 </foaf:maker>\n}; 
    116     } 
    117  
    118     $feed_timestamp ||= ''; 
    119  
    120     $rss .= qq{<title>}   . $self->{site_name}             . qq{</title> 
    121 <link>}               . $self->{html_equiv_link}       . qq{</link> 
    122 <description>}        . $self->{site_description}      . qq{</description> 
    123 <dc:date>}            . $feed_timestamp                . qq{</dc:date> 
    124 <modwiki:interwiki>}     . $self->{interwiki_identifier} . qq{</modwiki:interwiki>}; 
    125  
    126    return $rss; 
    127 } 
    128  
    129 =item <build_feed_end> 
    130  
    131 Internal method, to build all the stuff that will go at the end of a feed 
    132  
    133 =cut 
    134  
    135 sub build_feed_end { 
    136     my ($self,$feed_timestamp) = @_; 
    137  
    138     return "</rdf:RDF>\n"; 
    139 } 
    140  
    141  
    142 =item <generate_node_list_feed> 
    143  
    144 Generate and return an RSS feed for a list of nodes 
    145  
    146 =cut 
    147  
    148 sub generate_node_list_feed { 
    149     my ($self,$feed_timestamp,@nodes) = @_; 
    150  
    151     # Start our feed 
    152     my $rss = $self->build_feed_start($feed_timestamp); 
    153     $rss .= qq{ 
    154  
    155 <channel rdf:about=""> 
    156  
    157 }; 
    158     $rss .= $self->build_feed_mid($feed_timestamp); 
    159  
    160     # Generate the items list, and the individiual item entries 
    161     my (@urls, @items); 
    162     foreach my $node (@nodes) { 
    163         my $node_name = $node->{name}; 
    164  
    165         my $timestamp = $node->{last_modified}; 
    166      
    167         # Make a Time::Piece object. 
    168         my $time = Time::Piece->strptime($timestamp, $self->{timestamp_fmt}); 
    169  
    170         my $utc_offset = $self->{utc_offset}; 
    171      
    172         $timestamp = $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
    173  
    174         my $author      = $node->{metadata}{username}[0] || $node->{metadata}{host}[0] || ''; 
    175         my $description = $node->{metadata}{comment}[0]  || ''; 
    176  
    177         $description .= " [$author]" if $author; 
    178  
    179         my $version = $node->{version}; 
    180         my $status  = (1 == $version) ? 'new' : 'updated'; 
    181  
    182         my $major_change = $node->{metadata}{major_change}[0]; 
    183         $major_change = 1 unless defined $major_change; 
    184         my $importance = $major_change ? 'major' : 'minor'; 
    185  
    186         my $url = $self->{make_node_url}->($node_name, $version); 
    187  
    188         push @urls, qq{    <rdf:li rdf:resource="$url" />\n}; 
    189  
    190         my $diff_url = ''; 
    191      
    192         if ($self->{make_diff_url}) { 
    193             $diff_url = $self->{make_diff_url}->($node_name); 
    194         } 
    195  
    196         my $history_url = ''; 
    197      
    198         if ($self->{make_history_url}) { 
    199             $history_url = $self->{make_history_url}->($node_name); 
    200         } 
    201  
    202         my $node_url = $self->{make_node_url}->($node_name); 
    203  
    204         my $rdf_url =  $node_url; 
    205         $rdf_url =~ s/\?/\?id=/; 
    206         $rdf_url .= ';format=rdf'; 
    207  
    208         # make XML-clean 
    209         my $title =  $node_name; 
    210         $title =~ s/&/&amp;/g; 
    211         $title =~ s/</&lt;/g; 
    212         $title =~ s/>/&gt;/g; 
    213  
    214         # Pop the categories into dublin core subject elements 
    215         #  (http://dublincore.org/usage/terms/history/#subject-004) 
    216         # TODO: Decide if we should include the "all categories listing" url 
    217         #        as the scheme (URI) attribute? 
    218         my $category_rss = ""; 
    219         if($node->{metadata}->{category}) { 
    220             foreach my $cat (@{ $node->{metadata}->{category} }) { 
    221                 $category_rss .= "  <dc:subject>$cat</dc:subject>\n"; 
    222             } 
    223         } 
    224  
    225         # Include geospacial data, if we have it 
    226         my $geo_rss = $self->format_geo($node->{metadata}); 
    227  
    228         push @items, qq{ 
    229 <item rdf:about="$url"> 
    230   <title>$title</title> 
    231   <link>$url</link> 
    232   <description>$description</description> 
    233   <dc:date>$timestamp</dc:date> 
    234   <dc:contributor>$author</dc:contributor> 
    235   <modwiki:status>$status</modwiki:status> 
    236   <modwiki:importance>$importance</modwiki:importance> 
    237   <modwiki:diff>$diff_url</modwiki:diff> 
    238   <modwiki:version>$version</modwiki:version> 
    239   <modwiki:history>$history_url</modwiki:history> 
    240   <rdfs:seeAlso rdf:resource="$rdf_url" /> 
    241 $category_rss 
    242 $geo_rss 
    243 </item> 
    244 }; 
    245     } 
    246    
    247     # Output the items list 
    248     $rss .= qq{ 
    249  
    250 <items> 
    251   <rdf:Seq> 
    252 } . join('', @urls) . qq{  </rdf:Seq> 
    253 </items> 
    254  
    255 </channel> 
    256 }; 
    257  
    258     # Output the individual item entries 
    259     $rss .= join('', @items) . "\n"; 
    260  
    261     # Finish up 
    262     $rss .= $self->build_feed_end($feed_timestamp); 
    263   
    264     return $rss;    
    265 } 
    266  
    267  
    268 =item B<generate_node_name_distance_feed> 
    269  
    270 Generate a very cut down rss feed, based just on the nodes, their locations 
    271 (if given), and their distance from a reference location (if given).  
    272  
    273 Typically used on search feeds. 
    274  
    275 =cut 
    276  
    277 sub generate_node_name_distance_feed { 
    278     my ($self,$feed_timestamp,@nodes) = @_; 
    279  
    280     # Start our feed 
    281     my $rss = $self->build_feed_start($feed_timestamp); 
    282     $rss .= qq{ 
    283  
    284 <channel rdf:about=""> 
    285  
    286 }; 
    287     $rss .= $self->build_feed_mid($feed_timestamp); 
    288  
    289     # Generate the items list, and the individiual item entries 
    290     my (@urls, @items); 
    291     foreach my $node (@nodes) { 
    292         my $node_name = $node->{name}; 
    293  
    294         my $url = $self->{make_node_url}->($node_name); 
    295  
    296         push @urls, qq{    <rdf:li rdf:resource="$url" />\n}; 
    297  
    298         my $rdf_url =  $url; 
    299         $rdf_url =~ s/\?/\?id=/; 
    300         $rdf_url .= ';format=rdf'; 
    301  
    302         # make XML-clean 
    303         my $title =  $node_name; 
    304         $title =~ s/&/&amp;/g; 
    305         $title =~ s/</&lt;/g; 
    306         $title =~ s/>/&gt;/g; 
    307  
    308         # What location stuff do we have? 
    309         my $geo_rss = $self->format_geo($node); 
    310  
    311         push @items, qq{ 
    312 <item rdf:about="$url"> 
    313   <title>$title</title> 
    314   <link>$url</link> 
    315   <rdfs:seeAlso rdf:resource="$rdf_url" /> 
    316 $geo_rss 
    317 </item> 
    318 }; 
    319     } 
    320    
    321     # Output the items list 
    322     $rss .= qq{ 
    323  
    324 <items> 
    325   <rdf:Seq> 
    326 } . join('', @urls) . qq{  </rdf:Seq> 
    327 </items> 
    328  
    329 </channel> 
    330 }; 
    331  
    332     # Output the individual item entries 
    333     $rss .= join('', @items) . "\n"; 
    334  
    335     # Finish up 
    336     $rss .= $self->build_feed_end($feed_timestamp); 
    337   
    338     return $rss;    
    339 } 
    340  
    341 =item B<feed_timestamp> 
    342  
    343 Generate the timestamp for the RSS, based on the newest node (if available). 
    344 Will return a timestamp for now if no node dates are available 
    345  
    346 =cut 
    347  
    348 sub feed_timestamp { 
    349     my ($self, $newest_node) = @_; 
    350  
    351     my $time; 
    352     if ($newest_node->{last_modified}) { 
    353         $time = Time::Piece->strptime( $newest_node->{last_modified}, $self->{timestamp_fmt} ); 
    354     } else { 
    355         $time = localtime; 
    356     } 
    357  
    358     my $utc_offset = $self->{utc_offset}; 
    359  
    360     return $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
    361 } 
    362  
    363 # Compatibility method - use feed_timestamp with a node instead 
    364 sub rss_timestamp { 
    365     my ($self, %args) = @_; 
    366  
    367     warn("Old style method used - please convert to calling feed_timestamp with a node!"); 
    368     my $feed_timestamp = $self->feed_timestamp( 
    369                               $self->fetch_newest_for_recently_changed(%args) 
    370     ); 
    371     return $feed_timestamp; 
    372 } 
    373  
    374 =item B<parse_feed_timestamp> 
    375  
    376 Take a feed_timestamp and return a Time::Piece object.  
    377  
    378 =cut 
    379  
    380 sub parse_feed_timestamp { 
    381     my ($self, $feed_timestamp) = @_; 
    382     
    383     $feed_timestamp = substr($feed_timestamp, 0, -length( $self->{utc_offset})); 
    384     return Time::Piece->strptime( $feed_timestamp, '%Y-%m-%dT%H:%M:%S' ); 
    385 } 
    386  
    387 1; 
    388  
    389 __END__ 
    39015 
    39116=head1 NAME 
     
    563188need this to print a Last-Modified HTTP header so user-agents can determine 
    564189whether they need to reload the feed or not. 
     190 
     191=cut 
     192 
     193sub new { 
     194    my $class = shift; 
     195    my $self  = {}; 
     196    bless $self, $class; 
     197 
     198    my %args = @_; 
     199    my $wiki = $args{wiki}; 
     200 
     201    unless ($wiki && UNIVERSAL::isa($wiki, 'Wiki::Toolkit')) { 
     202        croak 'No Wiki::Toolkit object supplied'; 
     203    } 
    565204   
     205    $self->{wiki} = $wiki; 
     206   
     207    # Mandatory arguments. 
     208    foreach my $arg (qw/site_name site_url make_node_url/) { 
     209        croak "No $arg supplied" unless $args{$arg}; 
     210        $self->{$arg} = $args{$arg}; 
     211    } 
     212 
     213    # Must-supply-one-of arguments 
     214    my %mustoneof = ( 'html_equiv_link' => ['html_equiv_link','recent_changes_link'] ); 
     215    $self->handle_supply_one_of(\%mustoneof,\%args); 
     216   
     217    # Optional arguments. 
     218    foreach my $arg (qw/site_description interwiki_identifier make_diff_url make_history_url encoding software_name software_version software_homepage/) { 
     219        $self->{$arg} = $args{$arg} || ''; 
     220    } 
     221 
     222    # Supply some defaults, if a blank string isn't what we want 
     223    unless($self->{encoding}) { 
     224        $self->{encoding} = $self->{wiki}->store->{_charset}; 
     225    } 
     226 
     227    $self->{timestamp_fmt} = $Wiki::Toolkit::Store::Database::timestamp_fmt; 
     228    $self->{utc_offset} = strftime "%z", localtime; 
     229    $self->{utc_offset} =~ s/(..)(..)$/$1:$2/; 
     230 
     231    $self; 
     232} 
     233 
     234# Internal method, to build all the stuff that will go at the start of a feed. 
     235# Generally will output namespaces, headers and so on. 
     236 
     237sub build_feed_start { 
     238    my ($self,$feed_timestamp) = @_; 
     239 
     240    #"http://purl.org/rss/1.0/modules/wiki/" 
     241    return qq{<?xml version="1.0" encoding="}. $self->{encoding} .qq{"?> 
     242 
     243<rdf:RDF 
     244 xmlns         = "http://purl.org/rss/1.0/" 
     245 xmlns:dc      = "http://purl.org/dc/elements/1.1/" 
     246 xmlns:doap    = "http://usefulinc.com/ns/doap#" 
     247 xmlns:foaf    = "http://xmlns.com/foaf/0.1/" 
     248 xmlns:rdf     = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
     249 xmlns:rdfs    = "http://www.w3.org/2000/01/rdf-schema#" 
     250 xmlns:modwiki = "http://www.usemod.com/cgi-bin/mb.pl?ModWiki" 
     251 xmlns:geo     = "http://www.w3.org/2003/01/geo/wgs84_pos#" 
     252 xmlns:space   = "http://frot.org/space/0.1/" 
     253> 
     254}; 
     255} 
     256 
     257# Internal method, to build all the stuff (except items) to go inside the channel 
     258 
     259sub build_feed_mid { 
     260    my ($self,$feed_timestamp) = @_; 
     261 
     262    my $rss .= qq{<dc:publisher>} . $self->{site_url} . qq{</dc:publisher>\n}; 
     263 
     264    if ($self->{software_name}) { 
     265        $rss .= qq{<foaf:maker> 
     266        <doap:Project> 
     267        <doap:name>} . $self->{software_name} . qq{</doap:name>\n}; 
     268    } 
     269 
     270    if ($self->{software_name} && $self->{software_homepage}) { 
     271        $rss .= qq{    <doap:homepage rdf:resource="} . $self->{software_homepage} . qq{" />\n}; 
     272    } 
     273 
     274    if ($self->{software_name} && $self->{software_version}) { 
     275        $rss .= qq{    <doap:release> 
     276      <doap:Version> 
     277      <doap:revision>} . $self->{software_version} . qq{</doap:revision> 
     278      </doap:Version> 
     279    </doap:release>\n}; 
     280    } 
     281 
     282    if ($self->{software_name}) { 
     283        $rss .= qq{  </doap:Project> 
     284</foaf:maker>\n}; 
     285    } 
     286 
     287    $feed_timestamp ||= ''; 
     288 
     289    $rss .= qq{<title>}   . $self->{site_name}             . qq{</title> 
     290<link>}               . $self->{html_equiv_link}       . qq{</link> 
     291<description>}        . $self->{site_description}      . qq{</description> 
     292<dc:date>}            . $feed_timestamp                . qq{</dc:date> 
     293<modwiki:interwiki>}     . $self->{interwiki_identifier} . qq{</modwiki:interwiki>}; 
     294 
     295   return $rss; 
     296} 
     297 
     298# Internal method, to build all the stuff that will go at the end of a feed 
     299 
     300sub build_feed_end { 
     301    my ($self,$feed_timestamp) = @_; 
     302 
     303    return "</rdf:RDF>\n"; 
     304} 
     305 
     306 
     307=head2 C<generate_node_list_feed> 
     308 
     309Generate and return an RSS feed for a list of nodes 
     310 
     311=cut 
     312 
     313sub generate_node_list_feed { 
     314    my ($self,$feed_timestamp,@nodes) = @_; 
     315 
     316    # Start our feed 
     317    my $rss = $self->build_feed_start($feed_timestamp); 
     318    $rss .= qq{ 
     319 
     320<channel rdf:about=""> 
     321 
     322}; 
     323    $rss .= $self->build_feed_mid($feed_timestamp); 
     324 
     325    # Generate the items list, and the individiual item entries 
     326    my (@urls, @items); 
     327    foreach my $node (@nodes) { 
     328        my $node_name = $node->{name}; 
     329 
     330        my $timestamp = $node->{last_modified}; 
     331     
     332        # Make a Time::Piece object. 
     333        my $time = Time::Piece->strptime($timestamp, $self->{timestamp_fmt}); 
     334 
     335        my $utc_offset = $self->{utc_offset}; 
     336     
     337        $timestamp = $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
     338 
     339        my $author      = $node->{metadata}{username}[0] || $node->{metadata}{host}[0] || ''; 
     340        my $description = $node->{metadata}{comment}[0]  || ''; 
     341 
     342        $description .= " [$author]" if $author; 
     343 
     344        my $version = $node->{version}; 
     345        my $status  = (1 == $version) ? 'new' : 'updated'; 
     346 
     347        my $major_change = $node->{metadata}{major_change}[0]; 
     348        $major_change = 1 unless defined $major_change; 
     349        my $importance = $major_change ? 'major' : 'minor'; 
     350 
     351        my $url = $self->{make_node_url}->($node_name, $version); 
     352 
     353        push @urls, qq{    <rdf:li rdf:resource="$url" />\n}; 
     354 
     355        my $diff_url = ''; 
     356     
     357        if ($self->{make_diff_url}) { 
     358            $diff_url = $self->{make_diff_url}->($node_name); 
     359        } 
     360 
     361        my $history_url = ''; 
     362     
     363        if ($self->{make_history_url}) { 
     364            $history_url = $self->{make_history_url}->($node_name); 
     365        } 
     366 
     367        my $node_url = $self->{make_node_url}->($node_name); 
     368 
     369        my $rdf_url =  $node_url; 
     370        $rdf_url =~ s/\?/\?id=/; 
     371        $rdf_url .= ';format=rdf'; 
     372 
     373        # make XML-clean 
     374        my $title =  $node_name; 
     375        $title =~ s/&/&amp;/g; 
     376        $title =~ s/</&lt;/g; 
     377        $title =~ s/>/&gt;/g; 
     378 
     379        # Pop the categories into dublin core subject elements 
     380        #  (http://dublincore.org/usage/terms/history/#subject-004) 
     381        # TODO: Decide if we should include the "all categories listing" url 
     382        #        as the scheme (URI) attribute? 
     383        my $category_rss = ""; 
     384        if($node->{metadata}->{category}) { 
     385            foreach my $cat (@{ $node->{metadata}->{category} }) { 
     386                $category_rss .= "  <dc:subject>$cat</dc:subject>\n"; 
     387            } 
     388        } 
     389 
     390        # Include geospacial data, if we have it 
     391        my $geo_rss = $self->format_geo($node->{metadata}); 
     392 
     393        push @items, qq{ 
     394<item rdf:about="$url"> 
     395  <title>$title</title> 
     396  <link>$url</link> 
     397  <description>$description</description> 
     398  <dc:date>$timestamp</dc:date> 
     399  <dc:contributor>$author</dc:contributor> 
     400  <modwiki:status>$status</modwiki:status> 
     401  <modwiki:importance>$importance</modwiki:importance> 
     402  <modwiki:diff>$diff_url</modwiki:diff> 
     403  <modwiki:version>$version</modwiki:version> 
     404  <modwiki:history>$history_url</modwiki:history> 
     405  <rdfs:seeAlso rdf:resource="$rdf_url" /> 
     406$category_rss 
     407$geo_rss 
     408</item> 
     409}; 
     410    } 
     411   
     412    # Output the items list 
     413    $rss .= qq{ 
     414 
     415<items> 
     416  <rdf:Seq> 
     417} . join('', @urls) . qq{  </rdf:Seq> 
     418</items> 
     419 
     420</channel> 
     421}; 
     422 
     423    # Output the individual item entries 
     424    $rss .= join('', @items) . "\n"; 
     425 
     426    # Finish up 
     427    $rss .= $self->build_feed_end($feed_timestamp); 
     428  
     429    return $rss;    
     430} 
     431 
     432 
     433=head2 C<generate_node_name_distance_feed> 
     434 
     435Generate a very cut down rss feed, based just on the nodes, their locations 
     436(if given), and their distance from a reference location (if given).  
     437 
     438Typically used on search feeds. 
     439 
     440=cut 
     441 
     442sub generate_node_name_distance_feed { 
     443    my ($self,$feed_timestamp,@nodes) = @_; 
     444 
     445    # Start our feed 
     446    my $rss = $self->build_feed_start($feed_timestamp); 
     447    $rss .= qq{ 
     448 
     449<channel rdf:about=""> 
     450 
     451}; 
     452    $rss .= $self->build_feed_mid($feed_timestamp); 
     453 
     454    # Generate the items list, and the individiual item entries 
     455    my (@urls, @items); 
     456    foreach my $node (@nodes) { 
     457        my $node_name = $node->{name}; 
     458 
     459        my $url = $self->{make_node_url}->($node_name); 
     460 
     461        push @urls, qq{    <rdf:li rdf:resource="$url" />\n}; 
     462 
     463        my $rdf_url =  $url; 
     464        $rdf_url =~ s/\?/\?id=/; 
     465        $rdf_url .= ';format=rdf'; 
     466 
     467        # make XML-clean 
     468        my $title =  $node_name; 
     469        $title =~ s/&/&amp;/g; 
     470        $title =~ s/</&lt;/g; 
     471        $title =~ s/>/&gt;/g; 
     472 
     473        # What location stuff do we have? 
     474        my $geo_rss = $self->format_geo($node); 
     475 
     476        push @items, qq{ 
     477<item rdf:about="$url"> 
     478  <title>$title</title> 
     479  <link>$url</link> 
     480  <rdfs:seeAlso rdf:resource="$rdf_url" /> 
     481$geo_rss 
     482</item> 
     483}; 
     484    } 
     485   
     486    # Output the items list 
     487    $rss .= qq{ 
     488 
     489<items> 
     490  <rdf:Seq> 
     491} . join('', @urls) . qq{  </rdf:Seq> 
     492</items> 
     493 
     494</channel> 
     495}; 
     496 
     497    # Output the individual item entries 
     498    $rss .= join('', @items) . "\n"; 
     499 
     500    # Finish up 
     501    $rss .= $self->build_feed_end($feed_timestamp); 
     502  
     503    return $rss;    
     504} 
     505 
     506=head2 C<feed_timestamp> 
     507 
     508Generate the timestamp for the RSS, based on the newest node (if available). 
     509Will return a timestamp for now if no node dates are available 
     510 
     511=cut 
     512 
     513sub feed_timestamp { 
     514    my ($self, $newest_node) = @_; 
     515 
     516    my $time; 
     517    if ($newest_node->{last_modified}) { 
     518        $time = Time::Piece->strptime( $newest_node->{last_modified}, $self->{timestamp_fmt} ); 
     519    } else { 
     520        $time = localtime; 
     521    } 
     522 
     523    my $utc_offset = $self->{utc_offset}; 
     524 
     525    return $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
     526} 
     527 
     528# Compatibility method - use feed_timestamp with a node instead 
     529sub rss_timestamp { 
     530    my ($self, %args) = @_; 
     531 
     532    warn("Old style method used - please convert to calling feed_timestamp with a node!"); 
     533    my $feed_timestamp = $self->feed_timestamp( 
     534                              $self->fetch_newest_for_recently_changed(%args) 
     535    ); 
     536    return $feed_timestamp; 
     537} 
     538 
     539=head2 C<parse_feed_timestamp> 
     540 
     541Take a feed_timestamp and return a Time::Piece object.  
     542 
     543=cut 
     544 
     545sub parse_feed_timestamp { 
     546    my ($self, $feed_timestamp) = @_; 
     547    
     548    $feed_timestamp = substr($feed_timestamp, 0, -length( $self->{utc_offset})); 
     549    return Time::Piece->strptime( $feed_timestamp, '%Y-%m-%dT%H:%M:%S' ); 
     550} 
     551 
     5521; 
     553 
     554__END__ 
     555 
     556 
    566557=head1 SEE ALSO 
    567558 
     
    584575Copyright 2003-4 Kake Pugh. 
    585576Copyright 2005 Earle Martin. 
    586 Copyright 2006-2008 the Wiki::Toolkit team 
     577Copyright 2006-2009 the Wiki::Toolkit team 
    587578 
    588579This module is free software; you can redistribute it and/or modify it