Changeset 497

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

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

Location:
wiki-toolkit/trunk
Files:
1 added
8 modified

Legend:

Unmodified
Added
Removed
  • wiki-toolkit/trunk/Changes

    r486 r497  
     10.78 
     2        Fix various POD errors and add POD testing (#45) 
     3 
    140.77    24 December 2008 
    25        Complete support for store->list_metadata_by_type, which 
  • wiki-toolkit/trunk/MANIFEST

    r478 r497  
    44Makefile.PL 
    55README 
    6 SIGNATURE 
    76bin/wiki-toolkit-rename-node 
    87bin/wiki-toolkit-delete-node 
     
    8281t/400_upgrade.t 
    8382t/401_null_change.t 
     83t/500_pod.t 
    8484t/lib/Wiki/Toolkit/Plugin/Bar.pm 
    8585t/lib/Wiki/Toolkit/Plugin/Foo.pm 
    86  
    87 META.yml                                 Module meta-data (added by MakeMaker) 
  • wiki-toolkit/trunk/lib/Wiki/Toolkit.pm

    r486 r497  
    44 
    55use vars qw( $VERSION ); 
    6 $VERSION = '0.77'; 
     6$VERSION = '0.78'; 
    77 
    88use Carp qw(croak carp); 
     
    927927} 
    928928 
     929=back 
     930 
    929931=head1 SEE ALSO 
    930932 
     
    10301032 
    10311033     Copyright (C) 2002-2004 Kake Pugh.  All Rights Reserved. 
    1032      Copyright (C) 2006-2008 the Wiki::Toolkit team. All Rights Reserved. 
     1034     Copyright (C) 2006-2009 the Wiki::Toolkit team. All Rights Reserved. 
    10331035 
    10341036This module is free software; you can redistribute it and/or modify it 
  • wiki-toolkit/trunk/lib/Wiki/Toolkit/Feed/Atom.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 atom_link/) { 
    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 software_name software_version software_homepage encoding/) { 
    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     # Escape any &'s in the urls 
    55     foreach my $key qw(site_url atom_link) { 
    56         my @ands = ($self->{$key} =~ /(\&.{1,6})/g); 
    57         foreach my $and (@ands) { 
    58             if($and ne "&") { 
    59                 my $new_and = $and; 
    60                 $new_and =~ s/\&/\&/; 
    61                 $self->{$key} =~ s/$and/$new_and/; 
    62             } 
    63         } 
    64     } 
    65  
    66     $self; 
    67 } 
    68  
    69 =item <build_feed_start> 
    70  
    71 Internal method, to build all the stuff that will go at the start of a feed. 
    72 Outputs the feed header, and initial feed info. 
    73  
    74 =cut 
    75  
    76 sub build_feed_start { 
    77     my ($self,$atom_timestamp) = @_; 
    78  
    79     my $generator = ''; 
    80    
    81     if ($self->{software_name}) { 
    82         $generator  = '  <generator'; 
    83         $generator .= ' uri="' . $self->{software_homepage} . '"'   if $self->{software_homepage}; 
    84         $generator .= ' version=' . $self->{software_version} . '"' if $self->{software_version}; 
    85         $generator .= ">\n"; 
    86         $generator .= $self->{software_name} . "</generator>\n"; 
    87     }                           
    88  
    89     my $subtitle = $self->{site_description} 
    90                  ? '<subtitle>' . $self->{site_description} . "</subtitle>\n" 
    91                  : ''; 
    92  
    93     $atom_timestamp ||= ''; 
    94  
    95     my $atom = qq{<?xml version="1.0" encoding="} . $self->{encoding} . qq{"?> 
    96  
    97 <feed  
    98  xmlns         = "http://www.w3.org/2005/Atom" 
    99  xmlns:geo     = "http://www.w3.org/2003/01/geo/wgs84_pos#" 
    100  xmlns:space   = "http://frot.org/space/0.1/" 
    101 > 
    102  
    103   <link href="}            . $self->{site_url}     . qq{" /> 
    104   <title>}                 . $self->{site_name}    . qq{</title> 
    105   <link rel="self" href="} . $self->{atom_link}    . qq{" /> 
    106   <updated>}               . $atom_timestamp       . qq{</updated> 
    107   <id>}                    . $self->{site_url}     . qq{</id> 
    108   $subtitle}; 
    109    
    110     return $atom; 
    111 } 
    112  
    113 =item <build_feed_end> 
    114  
    115 Internal method, to build all the stuff that will go at the end of a feed. 
    116  
    117 =cut 
    118  
    119 sub build_feed_end { 
    120     my ($self,$feed_timestamp) = @_; 
    121  
    122     return "</feed>\n"; 
    123 } 
    124  
    125 =item <generate_node_list_feed> 
    126    
    127 Generate and return an Atom feed for a list of nodes 
    128    
    129 =cut 
    130  
    131 sub generate_node_list_feed { 
    132     my ($self,$atom_timestamp,@nodes) = @_; 
    133  
    134     my $atom = $self->build_feed_start($atom_timestamp); 
    135  
    136     my (@urls, @items); 
    137  
    138     foreach my $node (@nodes) { 
    139         my $node_name = $node->{name}; 
    140  
    141         my $item_timestamp = $node->{last_modified}; 
    142      
    143         # Make a Time::Piece object. 
    144         my $time = Time::Piece->strptime($item_timestamp, $self->{timestamp_fmt}); 
    145  
    146         my $utc_offset = $self->{utc_offset}; 
    147      
    148         $item_timestamp = $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
    149  
    150         my $author      = $node->{metadata}{username}[0] || $node->{metadata}{host}[0] || 'Anonymous'; 
    151         my $description = $node->{metadata}{comment}[0]  || 'No description given for node'; 
    152  
    153         $description .= " [$author]" if $author; 
    154  
    155         my $version = $node->{version}; 
    156         my $status  = (1 == $version) ? 'new' : 'updated'; 
    157  
    158         my $major_change = $node->{metadata}{major_change}[0]; 
    159         $major_change = 1 unless defined $major_change; 
    160         my $importance = $major_change ? 'major' : 'minor'; 
    161  
    162         my $url = $self->{make_node_url}->($node_name, $version); 
    163  
    164         # make XML-clean 
    165         my $title =  $node_name; 
    166         $title =~ s/&/&amp;/g; 
    167         $title =~ s/</&lt;/g; 
    168         $title =~ s/>/&gt;/g; 
    169  
    170         # Pop the categories into atom:category elements (4.2.2) 
    171         # We can do this because the spec says: 
    172         #   "This specification assigns no meaning to the content (if any)  
    173         #    of this element." 
    174         # TODO: Decide if we should include the "all categories listing" url 
    175         #        as the scheme (URI) attribute? 
    176         my $category_atom = ""; 
    177         if ($node->{metadata}->{category}) { 
    178             foreach my $cat (@{ $node->{metadata}->{category} }) { 
    179                 $category_atom .= "    <category term=\"$cat\" />\n"; 
    180             } 
    181         } 
    182  
    183         # Include geospacial data, if we have it 
    184         my $geo_atom = $self->format_geo($node->{metadata}); 
    185  
    186         # TODO: Find an Atom equivalent of ModWiki, so we can include more info 
    187  
    188      
    189         push @items, qq{ 
    190   <entry> 
    191     <title>$title</title> 
    192     <link href="$url" /> 
    193     <id>$url</id> 
    194     <summary>$description</summary> 
    195     <updated>$item_timestamp</updated> 
    196     <author><name>$author</name></author> 
    197 $category_atom 
    198 $geo_atom 
    199   </entry> 
    200 }; 
    201  
    202     } 
    203    
    204     $atom .= join('', @items) . "\n"; 
    205     $atom .= $self->build_feed_end($atom_timestamp); 
    206  
    207     return $atom;    
    208 } 
    209  
    210 =item <generate_node_name_distance_feed> 
    211    
    212 Generate a very cut down atom feed, based just on the nodes, their locations 
    213 (if given), and their distance from a reference location (if given). 
    214  
    215 Typically used on search feeds. 
    216    
    217 =cut 
    218  
    219 sub generate_node_name_distance_feed { 
    220     my ($self,$atom_timestamp,@nodes) = @_; 
    221  
    222     my $atom = $self->build_feed_start($atom_timestamp); 
    223  
    224     my (@urls, @items); 
    225  
    226     foreach my $node (@nodes) { 
    227         my $node_name = $node->{name}; 
    228  
    229         my $url = $self->{make_node_url}->($node_name); 
    230  
    231         # make XML-clean 
    232         my $title =  $node_name; 
    233         $title =~ s/&/&amp;/g; 
    234         $title =~ s/</&lt;/g; 
    235         $title =~ s/>/&gt;/g; 
    236  
    237         # What location stuff do we have? 
    238         my $geo_atom = $self->format_geo($node); 
    239  
    240         push @items, qq{ 
    241   <entry> 
    242     <title>$title</title> 
    243     <link href="$url" /> 
    244     <id>$url</id> 
    245 $geo_atom 
    246   </entry> 
    247 }; 
    248  
    249     } 
    250    
    251     $atom .= join('', @items) . "\n"; 
    252     $atom .= $self->build_feed_end($atom_timestamp); 
    253  
    254     return $atom;    
    255 } 
    256  
    257 =item B<feed_timestamp> 
    258  
    259 Generate the timestamp for the Atom, based on the newest node (if available). 
    260 Will return a timestamp for now if no node dates are available 
    261  
    262 =cut 
    263  
    264 sub feed_timestamp { 
    265     my ($self, $newest_node) = @_; 
    266    
    267     my $time; 
    268     if ($newest_node->{last_modified}) { 
    269         $time = Time::Piece->strptime( $newest_node->{last_modified}, $self->{timestamp_fmt} ); 
    270     } else { 
    271         $time = localtime; 
    272     } 
    273  
    274     my $utc_offset = $self->{utc_offset}; 
    275      
    276     return $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
    277 } 
    278  
    279  
    280 =item B<parse_feed_timestamp> 
    281  
    282 Take a feed_timestamp and return a Time::Piece object.  
    283  
    284 =cut 
    285  
    286 sub parse_feed_timestamp { 
    287     my ($self, $feed_timestamp) = @_; 
    288     
    289     $feed_timestamp = substr($feed_timestamp, 0, -length( $self->{utc_offset})); 
    290     return Time::Piece->strptime( $feed_timestamp, '%Y-%m-%dT%H:%M:%S' ); 
    291 } 
    292 1; 
    293  
    294 __END__ 
    29515 
    29616=head1 NAME 
     
    451171=back 
    452172 
     173=cut 
     174 
     175sub new { 
     176    my $class = shift; 
     177    my $self  = {}; 
     178    bless $self, $class; 
     179 
     180    my %args = @_; 
     181    my $wiki = $args{wiki}; 
     182 
     183    unless ($wiki && UNIVERSAL::isa($wiki, 'Wiki::Toolkit')) { 
     184        croak 'No Wiki::Toolkit object supplied'; 
     185    } 
     186   
     187    $self->{wiki} = $wiki; 
     188   
     189    # Mandatory arguments. 
     190    foreach my $arg (qw/site_name site_url make_node_url atom_link/) { 
     191        croak "No $arg supplied" unless $args{$arg}; 
     192        $self->{$arg} = $args{$arg}; 
     193    } 
     194 
     195    # Must-supply-one-of arguments 
     196    my %mustoneof = ( 'html_equiv_link' => ['html_equiv_link','recent_changes_link'] ); 
     197    $self->handle_supply_one_of(\%mustoneof,\%args); 
     198   
     199    # Optional arguments. 
     200    foreach my $arg (qw/site_description software_name software_version software_homepage encoding/) { 
     201        $self->{$arg} = $args{$arg} || ''; 
     202    } 
     203 
     204    # Supply some defaults, if a blank string isn't what we want 
     205    unless($self->{encoding}) { 
     206        $self->{encoding} = $self->{wiki}->store->{_charset}; 
     207    } 
     208 
     209    $self->{timestamp_fmt} = $Wiki::Toolkit::Store::Database::timestamp_fmt; 
     210    $self->{utc_offset} = strftime "%z", localtime; 
     211    $self->{utc_offset} =~ s/(..)(..)$/$1:$2/; 
     212   
     213    # Escape any &'s in the urls 
     214    foreach my $key qw(site_url atom_link) { 
     215        my @ands = ($self->{$key} =~ /(\&.{1,6})/g); 
     216        foreach my $and (@ands) { 
     217            if($and ne "&amp;") { 
     218                my $new_and = $and; 
     219                $new_and =~ s/\&/\&amp;/; 
     220                $self->{$key} =~ s/$and/$new_and/; 
     221            } 
     222        } 
     223    } 
     224 
     225    $self; 
     226} 
     227 
     228# Internal method, to build all the stuff that will go at the start of a feed. 
     229# Outputs the feed header, and initial feed info. 
     230 
     231sub build_feed_start { 
     232    my ($self,$atom_timestamp) = @_; 
     233 
     234    my $generator = ''; 
     235   
     236    if ($self->{software_name}) { 
     237        $generator  = '  <generator'; 
     238        $generator .= ' uri="' . $self->{software_homepage} . '"'   if $self->{software_homepage}; 
     239        $generator .= ' version=' . $self->{software_version} . '"' if $self->{software_version}; 
     240        $generator .= ">\n"; 
     241        $generator .= $self->{software_name} . "</generator>\n"; 
     242    }                           
     243 
     244    my $subtitle = $self->{site_description} 
     245                 ? '<subtitle>' . $self->{site_description} . "</subtitle>\n" 
     246                 : ''; 
     247 
     248    $atom_timestamp ||= ''; 
     249 
     250    my $atom = qq{<?xml version="1.0" encoding="} . $self->{encoding} . qq{"?> 
     251 
     252<feed  
     253 xmlns         = "http://www.w3.org/2005/Atom" 
     254 xmlns:geo     = "http://www.w3.org/2003/01/geo/wgs84_pos#" 
     255 xmlns:space   = "http://frot.org/space/0.1/" 
     256> 
     257 
     258  <link href="}            . $self->{site_url}     . qq{" /> 
     259  <title>}                 . $self->{site_name}    . qq{</title> 
     260  <link rel="self" href="} . $self->{atom_link}    . qq{" /> 
     261  <updated>}               . $atom_timestamp       . qq{</updated> 
     262  <id>}                    . $self->{site_url}     . qq{</id> 
     263  $subtitle}; 
     264   
     265    return $atom; 
     266} 
     267 
     268# Internal method, to build all the stuff that will go at the end of a feed. 
     269 
     270sub build_feed_end { 
     271    my ($self,$feed_timestamp) = @_; 
     272 
     273    return "</feed>\n"; 
     274} 
     275 
     276=head2 C<generate_node_list_feed> 
     277   
     278Generate and return an Atom feed for a list of nodes 
     279   
     280=cut 
     281 
     282sub generate_node_list_feed { 
     283    my ($self,$atom_timestamp,@nodes) = @_; 
     284 
     285    my $atom = $self->build_feed_start($atom_timestamp); 
     286 
     287    my (@urls, @items); 
     288 
     289    foreach my $node (@nodes) { 
     290        my $node_name = $node->{name}; 
     291 
     292        my $item_timestamp = $node->{last_modified}; 
     293     
     294        # Make a Time::Piece object. 
     295        my $time = Time::Piece->strptime($item_timestamp, $self->{timestamp_fmt}); 
     296 
     297        my $utc_offset = $self->{utc_offset}; 
     298     
     299        $item_timestamp = $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
     300 
     301        my $author      = $node->{metadata}{username}[0] || $node->{metadata}{host}[0] || 'Anonymous'; 
     302        my $description = $node->{metadata}{comment}[0]  || 'No description given for node'; 
     303 
     304        $description .= " [$author]" if $author; 
     305 
     306        my $version = $node->{version}; 
     307        my $status  = (1 == $version) ? 'new' : 'updated'; 
     308 
     309        my $major_change = $node->{metadata}{major_change}[0]; 
     310        $major_change = 1 unless defined $major_change; 
     311        my $importance = $major_change ? 'major' : 'minor'; 
     312 
     313        my $url = $self->{make_node_url}->($node_name, $version); 
     314 
     315        # make XML-clean 
     316        my $title =  $node_name; 
     317        $title =~ s/&/&amp;/g; 
     318        $title =~ s/</&lt;/g; 
     319        $title =~ s/>/&gt;/g; 
     320 
     321        # Pop the categories into atom:category elements (4.2.2) 
     322        # We can do this because the spec says: 
     323        #   "This specification assigns no meaning to the content (if any)  
     324        #    of this element." 
     325        # TODO: Decide if we should include the "all categories listing" url 
     326        #        as the scheme (URI) attribute? 
     327        my $category_atom = ""; 
     328        if ($node->{metadata}->{category}) { 
     329            foreach my $cat (@{ $node->{metadata}->{category} }) { 
     330                $category_atom .= "    <category term=\"$cat\" />\n"; 
     331            } 
     332        } 
     333 
     334        # Include geospacial data, if we have it 
     335        my $geo_atom = $self->format_geo($node->{metadata}); 
     336 
     337        # TODO: Find an Atom equivalent of ModWiki, so we can include more info 
     338 
     339     
     340        push @items, qq{ 
     341  <entry> 
     342    <title>$title</title> 
     343    <link href="$url" /> 
     344    <id>$url</id> 
     345    <summary>$description</summary> 
     346    <updated>$item_timestamp</updated> 
     347    <author><name>$author</name></author> 
     348$category_atom 
     349$geo_atom 
     350  </entry> 
     351}; 
     352 
     353    } 
     354   
     355    $atom .= join('', @items) . "\n"; 
     356    $atom .= $self->build_feed_end($atom_timestamp); 
     357 
     358    return $atom;    
     359} 
     360 
     361=head2 C<generate_node_name_distance_feed> 
     362   
     363Generate a very cut down atom feed, based just on the nodes, their locations 
     364(if given), and their distance from a reference location (if given). 
     365 
     366Typically used on search feeds. 
     367   
     368=cut 
     369 
     370sub generate_node_name_distance_feed { 
     371    my ($self,$atom_timestamp,@nodes) = @_; 
     372 
     373    my $atom = $self->build_feed_start($atom_timestamp); 
     374 
     375    my (@urls, @items); 
     376 
     377    foreach my $node (@nodes) { 
     378        my $node_name = $node->{name}; 
     379 
     380        my $url = $self->{make_node_url}->($node_name); 
     381 
     382        # make XML-clean 
     383        my $title =  $node_name; 
     384        $title =~ s/&/&amp;/g; 
     385        $title =~ s/</&lt;/g; 
     386        $title =~ s/>/&gt;/g; 
     387 
     388        # What location stuff do we have? 
     389        my $geo_atom = $self->format_geo($node); 
     390 
     391        push @items, qq{ 
     392  <entry> 
     393    <title>$title</title> 
     394    <link href="$url" /> 
     395    <id>$url</id> 
     396$geo_atom 
     397  </entry> 
     398}; 
     399 
     400    } 
     401   
     402    $atom .= join('', @items) . "\n"; 
     403    $atom .= $self->build_feed_end($atom_timestamp); 
     404 
     405    return $atom;    
     406} 
     407 
    453408=head2 C<feed_timestamp()> 
    454409 
     
    460415need this to print a Last-Modified HTTP header so user-agents can determine 
    461416whether they need to reload the feed or not. 
    462    
     417 
     418=cut 
     419 
     420sub feed_timestamp { 
     421    my ($self, $newest_node) = @_; 
     422   
     423    my $time; 
     424    if ($newest_node->{last_modified}) { 
     425        $time = Time::Piece->strptime( $newest_node->{last_modified}, $self->{timestamp_fmt} ); 
     426    } else { 
     427        $time = localtime; 
     428    } 
     429 
     430    my $utc_offset = $self->{utc_offset}; 
     431     
     432    return $time->strftime( "%Y-%m-%dT%H:%M:%S$utc_offset" ); 
     433} 
     434 
     435 
     436=head2 C<parse_feed_timestamp> 
     437 
     438Take a feed_timestamp and return a Time::Piece object.  
     439 
     440=cut 
     441 
     442sub parse_feed_timestamp { 
     443    my ($self, $feed_timestamp) = @_; 
     444    
     445    $feed_timestamp = substr($feed_timestamp, 0, -length( $self->{utc_offset})); 
     446    return Time::Piece->strptime( $feed_timestamp, '%Y-%m-%dT%H:%M:%S' ); 
     447} 
     4481; 
     449 
     450__END__ 
     451 
     452 
    463453=head1 SEE ALSO 
    464454 
     
    477467=head1 COPYRIGHT AND LICENSE 
    478468 
    479 Copyright 2006-2008 Earle Martin and the Wiki::Toolkit team. 
     469Copyright 2006-2009 Earle Martin and the Wiki::Toolkit team. 
    480470 
    481471This module is free software; you can redistribute it and/or modify it 
  • wiki-toolkit/trunk/lib/Wiki/Toolkit/Feed/Listing.pm

    r446 r497  
    44use Carp qw( croak ); 
    55 
    6 =item B<fetch_recently_changed_nodes> 
     6=head1 NAME 
     7 
     8Wiki::Toolkit::Feed::Listing - parent class for Feeds from Wiki::Toolkit. 
     9 
     10=head1 DESCRIPTION 
     11 
     12Handles common data fetching tasks, so that child classes need only 
     13worry about formatting the feeds. 
     14 
     15Also enforces some common methods that must be implemented. 
     16 
     17=head1 METHODS 
     18 
     19=head2 C<fetch_recently_changed_nodes> 
    720 
    821Based on the supplied criteria, fetch a list of the recently changed nodes 
     
    4053} 
    4154 
    42 =item B<fetch_newest_for_recently_changed> 
     55=head2 C<fetch_newest_for_recently_changed> 
    4356 
    4457Based on the supplied criteria (but not using all of those used by 
     
    5669 
    5770 
    58 =item B<fetch_node_all_versions> 
     71=head2 C<fetch_node_all_versions> 
    5972 
    6073For a given node (name or ID), return all the versions there have been, 
     
    92105 
    93106 
    94 =item B<recent_changes> 
     107=head2 C<recent_changes> 
    95108 
    96109Build an Atom Feed of the recent changes to the Wiki::Toolkit instance, 
     
    120133 
    121134 
    122 =item B<node_all_versions> 
     135=head2 C<node_all_versions> 
    123136 
    124137Build an Atom Feed of all the different versions of a given node. 
     
    144157}  
    145158 
    146 =item B<format_geo> 
     159=head2 C<format_geo> 
    147160 
    148161Using the geo and space xml namespaces, format the supplied node metadata 
     
    187200} 
    188201 
    189 #item B<handle_supply_one_of> 
    190202# Utility method, to help with argument passing where one of a list of  
    191203#  arguments must be supplied 
    192 # 
    193 #=cut 
    194204 
    195205sub handle_supply_one_of { 
     
    214224 
    215225 
    216 # The following are methods that any feed renderer must provide 
    217  
    218 =item B<feed_timestamp> 
     226=pod 
     227 
     228The following are methods that any feed renderer must provide: 
     229 
     230=head2 C<feed_timestamp> 
    219231 
    220232All implementing feed renderers must implement a method to produce a 
     
    225237sub feed_timestamp          { die("Not implemented by feed renderer!"); } 
    226238 
    227 =item B<generate_node_list_feed> 
     239=head2 C<generate_node_list_feed> 
    228240 
    229241All implementing feed renderers must implement a method to produce a 
     
    234246sub generate_node_list_feed { die("Not implemented by feed renderer!"); } 
    235247 
    236 =item B<generate_node_name_distance_feed> 
     248=head2 C<generate_node_name_distance_feed> 
    237249 
    238250All implementing feed renderers must implement a method to produce a 
     
    244256sub generate_node_name_distance_feed { die("Not implemented by feed renderer!"); } 
    245257 
    246 =item B<parse_feed_timestamp> 
     258=head2 C<parse_feed_timestamp> 
    247259 
    248260Take a feed_timestamp and return a Time::Piece object.  
     
    256268__END__ 
    257269 
    258 =head1 NAME 
    259  
    260 Wiki::Toolkit::Feed::Listing - parent class for Feeds from Wiki::Toolkit. 
    261  
    262 =head1 DESCRIPTION 
    263  
    264 Handles common data fetching tasks, so that child classes need only 
    265 worry about formatting the feeds. 
    266  
    267 Also enforces some common methods that must be implemented. 
    268  
    269270=head1 MAINTAINER 
    270271 
     
    273274=head1 COPYRIGHT AND LICENSE 
    274275 
    275 Copyright 2006 the Wiki::Toolkit team. 
     276Copyright 2006-2009 the Wiki::Toolkit team. 
    276277 
    277278This module is free software; you can redistribute it and/or modify it 
  • 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 
  • wiki-toolkit/trunk/lib/Wiki/Toolkit/Formatter/WikiLinkFormatterParent.pm

    r431 r497  
    2727} 
    2828 
    29 =item B<rename_links> 
     29=head1 METHODS 
     30 
     31=head2 C<rename_links> 
    3032 
    3133  $formatter->rename_links( $from, $to, $content ); 
     
    5658} 
    5759 
    58 =item B<find_internal_links> 
     60=head2 C<find_internal_links> 
    5961 
    6062  my @links_to = $formatter->find_internal_links( $content ); 
     
    9597} 
    9698 
    97 =cut 
    98  
    9999=head1 SEE ALSO 
    100100 
     
    108108 
    109109     Copyright (C) 2002-2003 Kake Pugh.  All Rights Reserved. 
    110      Copyright (C) 2006 the Wiki::Toolkit team. All Rights Reserved. 
     110     Copyright (C) 2006-2009 the Wiki::Toolkit team. All Rights Reserved. 
    111111 
    112112This module is free software; you can redistribute it and/or modify it 
  • wiki-toolkit/trunk/lib/Wiki/Toolkit/Search/Base.pm

    r431 r497  
    2929=head1 METHODS 
    3030 
    31 =over 4 
    32  
    33 =item B<new> 
     31=head2 C<new> 
    3432 
    3533  my $search = Wiki::Toolkit::Search::XXX->new( @args ); 
     
    5351} 
    5452 
    55 =item B<search_nodes> 
     53=head2 C<search_nodes> 
    5654 
    5755  # Find all the nodes which contain the word 'expert'. 
     
    8684sub _do_search { shift->_abstract }; 
    8785 
    88 =item B<analyze> 
     86=head2 C<analyze> 
    8987 
    9088    @terms = $self->analyze($string) 
     
    106104} 
    107105 
    108 =item B<fuzzy_title_match> 
     106=head2 C<fuzzy_title_match> 
    109107 
    110108  $wiki->write_node( "King's Cross St Pancras", "A station." ); 
     
    133131sub _fuzzy_match { shift->_abstract }; 
    134132 
    135 =item B<index_node> 
     133=head2 C<index_node> 
    136134 
    137135  $search->index_node($node, $content); 
     
    175173} 
    176174 
    177 =item B<delete_node> 
     175=head2 C<delete_node> 
    178176 
    179177  $search->delete_node($node); 
     
    192190sub _delete_node { shift->_abstract }; 
    193191 
    194 =item B<supports_phrase_searches> 
     192=head2 C<supports_phrase_searches> 
    195193 
    196194  if ( $search->supports_phrase_searches ) { 
     
    205203sub supports_phrase_searches { shift->_abstract }; 
    206204 
    207 =item B<supports_fuzzy_searches> 
     205=head2 C<supports_fuzzy_searches> 
    208206 
    209207  if ( $search->supports_fuzzy_searches ) { 
     
    218216sub supports_fuzzy_searches { shift->_abstract }; 
    219217 
    220 =back 
    221  
    222218=head1 SEE ALSO 
    223219