Changeset 497
- Timestamp:
- 02/01/09 00:06:59 (3 years ago)
- Location:
- wiki-toolkit/trunk
- Files:
-
- 1 added
- 8 modified
-
Changes (modified) (1 diff)
-
MANIFEST (modified) (2 diffs)
-
lib/Wiki/Toolkit.pm (modified) (3 diffs)
-
lib/Wiki/Toolkit/Feed/Atom.pm (modified) (4 diffs)
-
lib/Wiki/Toolkit/Feed/Listing.pm (modified) (13 diffs)
-
lib/Wiki/Toolkit/Feed/RSS.pm (modified) (3 diffs)
-
lib/Wiki/Toolkit/Formatter/WikiLinkFormatterParent.pm (modified) (4 diffs)
-
lib/Wiki/Toolkit/Search/Base.pm (modified) (9 diffs)
-
t/500_pod.t (added)
Legend:
- Unmodified
- Added
- Removed
-
wiki-toolkit/trunk/Changes
r486 r497 1 0.78 2 Fix various POD errors and add POD testing (#45) 3 1 4 0.77 24 December 2008 2 5 Complete support for store->list_metadata_by_type, which -
wiki-toolkit/trunk/MANIFEST
r478 r497 4 4 Makefile.PL 5 5 README 6 SIGNATURE7 6 bin/wiki-toolkit-rename-node 8 7 bin/wiki-toolkit-delete-node … … 82 81 t/400_upgrade.t 83 82 t/401_null_change.t 83 t/500_pod.t 84 84 t/lib/Wiki/Toolkit/Plugin/Bar.pm 85 85 t/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 4 4 5 5 use vars qw( $VERSION ); 6 $VERSION = '0.7 7';6 $VERSION = '0.78'; 7 7 8 8 use Carp qw(croak carp); … … 927 927 } 928 928 929 =back 930 929 931 =head1 SEE ALSO 930 932 … … 1030 1032 1031 1033 Copyright (C) 2002-2004 Kake Pugh. All Rights Reserved. 1032 Copyright (C) 2006-200 8the Wiki::Toolkit team. All Rights Reserved.1034 Copyright (C) 2006-2009 the Wiki::Toolkit team. All Rights Reserved. 1033 1035 1034 1036 This module is free software; you can redistribute it and/or modify it -
wiki-toolkit/trunk/lib/Wiki/Toolkit/Feed/Atom.pm
r432 r497 13 13 use Wiki::Toolkit::Feed::Listing; 14 14 @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 arguments37 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 want46 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 urls55 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 =cut75 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 <feed98 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 =cut118 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 nodes128 129 =cut130 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-clean165 my $title = $node_name;166 $title =~ s/&/&/g;167 $title =~ s/</</g;168 $title =~ s/>/>/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" url175 # 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 it184 my $geo_atom = $self->format_geo($node->{metadata});185 186 # TODO: Find an Atom equivalent of ModWiki, so we can include more info187 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_atom198 $geo_atom199 </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 locations213 (if given), and their distance from a reference location (if given).214 215 Typically used on search feeds.216 217 =cut218 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-clean232 my $title = $node_name;233 $title =~ s/&/&/g;234 $title =~ s/</</g;235 $title =~ s/>/>/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_atom246 </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 available261 262 =cut263 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 =cut285 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__295 15 296 16 =head1 NAME … … 451 171 =back 452 172 173 =cut 174 175 sub 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 "&") { 218 my $new_and = $and; 219 $new_and =~ s/\&/\&/; 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 231 sub 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 270 sub build_feed_end { 271 my ($self,$feed_timestamp) = @_; 272 273 return "</feed>\n"; 274 } 275 276 =head2 C<generate_node_list_feed> 277 278 Generate and return an Atom feed for a list of nodes 279 280 =cut 281 282 sub 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/&/&/g; 318 $title =~ s/</</g; 319 $title =~ s/>/>/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 363 Generate 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 366 Typically used on search feeds. 367 368 =cut 369 370 sub 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/&/&/g; 385 $title =~ s/</</g; 386 $title =~ s/>/>/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 453 408 =head2 C<feed_timestamp()> 454 409 … … 460 415 need this to print a Last-Modified HTTP header so user-agents can determine 461 416 whether they need to reload the feed or not. 462 417 418 =cut 419 420 sub 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 438 Take a feed_timestamp and return a Time::Piece object. 439 440 =cut 441 442 sub 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 } 448 1; 449 450 __END__ 451 452 463 453 =head1 SEE ALSO 464 454 … … 477 467 =head1 COPYRIGHT AND LICENSE 478 468 479 Copyright 2006-200 8Earle Martin and the Wiki::Toolkit team.469 Copyright 2006-2009 Earle Martin and the Wiki::Toolkit team. 480 470 481 471 This module is free software; you can redistribute it and/or modify it -
wiki-toolkit/trunk/lib/Wiki/Toolkit/Feed/Listing.pm
r446 r497 4 4 use Carp qw( croak ); 5 5 6 =item B<fetch_recently_changed_nodes> 6 =head1 NAME 7 8 Wiki::Toolkit::Feed::Listing - parent class for Feeds from Wiki::Toolkit. 9 10 =head1 DESCRIPTION 11 12 Handles common data fetching tasks, so that child classes need only 13 worry about formatting the feeds. 14 15 Also enforces some common methods that must be implemented. 16 17 =head1 METHODS 18 19 =head2 C<fetch_recently_changed_nodes> 7 20 8 21 Based on the supplied criteria, fetch a list of the recently changed nodes … … 40 53 } 41 54 42 = item B<fetch_newest_for_recently_changed>55 =head2 C<fetch_newest_for_recently_changed> 43 56 44 57 Based on the supplied criteria (but not using all of those used by … … 56 69 57 70 58 = item B<fetch_node_all_versions>71 =head2 C<fetch_node_all_versions> 59 72 60 73 For a given node (name or ID), return all the versions there have been, … … 92 105 93 106 94 = item B<recent_changes>107 =head2 C<recent_changes> 95 108 96 109 Build an Atom Feed of the recent changes to the Wiki::Toolkit instance, … … 120 133 121 134 122 = item B<node_all_versions>135 =head2 C<node_all_versions> 123 136 124 137 Build an Atom Feed of all the different versions of a given node. … … 144 157 } 145 158 146 = item B<format_geo>159 =head2 C<format_geo> 147 160 148 161 Using the geo and space xml namespaces, format the supplied node metadata … … 187 200 } 188 201 189 #item B<handle_supply_one_of>190 202 # Utility method, to help with argument passing where one of a list of 191 203 # arguments must be supplied 192 #193 #=cut194 204 195 205 sub handle_supply_one_of { … … 214 224 215 225 216 # The following are methods that any feed renderer must provide 217 218 =item B<feed_timestamp> 226 =pod 227 228 The following are methods that any feed renderer must provide: 229 230 =head2 C<feed_timestamp> 219 231 220 232 All implementing feed renderers must implement a method to produce a … … 225 237 sub feed_timestamp { die("Not implemented by feed renderer!"); } 226 238 227 = item B<generate_node_list_feed>239 =head2 C<generate_node_list_feed> 228 240 229 241 All implementing feed renderers must implement a method to produce a … … 234 246 sub generate_node_list_feed { die("Not implemented by feed renderer!"); } 235 247 236 = item B<generate_node_name_distance_feed>248 =head2 C<generate_node_name_distance_feed> 237 249 238 250 All implementing feed renderers must implement a method to produce a … … 244 256 sub generate_node_name_distance_feed { die("Not implemented by feed renderer!"); } 245 257 246 = item B<parse_feed_timestamp>258 =head2 C<parse_feed_timestamp> 247 259 248 260 Take a feed_timestamp and return a Time::Piece object. … … 256 268 __END__ 257 269 258 =head1 NAME259 260 Wiki::Toolkit::Feed::Listing - parent class for Feeds from Wiki::Toolkit.261 262 =head1 DESCRIPTION263 264 Handles common data fetching tasks, so that child classes need only265 worry about formatting the feeds.266 267 Also enforces some common methods that must be implemented.268 269 270 =head1 MAINTAINER 270 271 … … 273 274 =head1 COPYRIGHT AND LICENSE 274 275 275 Copyright 2006 the Wiki::Toolkit team.276 Copyright 2006-2009 the Wiki::Toolkit team. 276 277 277 278 This module is free software; you can redistribute it and/or modify it -
wiki-toolkit/trunk/lib/Wiki/Toolkit/Feed/RSS.pm
r432 r497 13 13 use Wiki::Toolkit::Feed::Listing; 14 14 @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 arguments37 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 want46 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 =cut63 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:RDF71 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 channel87 88 =cut89 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 feed132 133 =cut134 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 nodes145 146 =cut147 148 sub generate_node_list_feed {149 my ($self,$feed_timestamp,@nodes) = @_;150 151 # Start our feed152 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 entries161 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-clean209 my $title = $node_name;210 $title =~ s/&/&/g;211 $title =~ s/</</g;212 $title =~ s/>/>/g;213 214 # Pop the categories into dublin core subject elements215 # (http://dublincore.org/usage/terms/history/#subject-004)216 # TODO: Decide if we should include the "all categories listing" url217 # 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 it226 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_rss242 $geo_rss243 </item>244 };245 }246 247 # Output the items list248 $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 entries259 $rss .= join('', @items) . "\n";260 261 # Finish up262 $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 locations271 (if given), and their distance from a reference location (if given).272 273 Typically used on search feeds.274 275 =cut276 277 sub generate_node_name_distance_feed {278 my ($self,$feed_timestamp,@nodes) = @_;279 280 # Start our feed281 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 entries290 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-clean303 my $title = $node_name;304 $title =~ s/&/&/g;305 $title =~ s/</</g;306 $title =~ s/>/>/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_rss317 </item>318 };319 }320 321 # Output the items list322 $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 entries333 $rss .= join('', @items) . "\n";334 335 # Finish up336 $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 available345 346 =cut347 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 instead364 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 =cut379 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__390 15 391 16 =head1 NAME … … 563 188 need this to print a Last-Modified HTTP header so user-agents can determine 564 189 whether they need to reload the feed or not. 190 191 =cut 192 193 sub 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 } 565 204 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 237 sub 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 259 sub 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 300 sub 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 309 Generate and return an RSS feed for a list of nodes 310 311 =cut 312 313 sub 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/&/&/g; 376 $title =~ s/</</g; 377 $title =~ s/>/>/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 435 Generate 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 438 Typically used on search feeds. 439 440 =cut 441 442 sub 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/&/&/g; 470 $title =~ s/</</g; 471 $title =~ s/>/>/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 508 Generate the timestamp for the RSS, based on the newest node (if available). 509 Will return a timestamp for now if no node dates are available 510 511 =cut 512 513 sub 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 529 sub 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 541 Take a feed_timestamp and return a Time::Piece object. 542 543 =cut 544 545 sub 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 552 1; 553 554 __END__ 555 556 566 557 =head1 SEE ALSO 567 558 … … 584 575 Copyright 2003-4 Kake Pugh. 585 576 Copyright 2005 Earle Martin. 586 Copyright 2006-200 8the Wiki::Toolkit team577 Copyright 2006-2009 the Wiki::Toolkit team 587 578 588 579 This module is free software; you can redistribute it and/or modify it -
wiki-toolkit/trunk/lib/Wiki/Toolkit/Formatter/WikiLinkFormatterParent.pm
r431 r497 27 27 } 28 28 29 =item B<rename_links> 29 =head1 METHODS 30 31 =head2 C<rename_links> 30 32 31 33 $formatter->rename_links( $from, $to, $content ); … … 56 58 } 57 59 58 = item B<find_internal_links>60 =head2 C<find_internal_links> 59 61 60 62 my @links_to = $formatter->find_internal_links( $content ); … … 95 97 } 96 98 97 =cut98 99 99 =head1 SEE ALSO 100 100 … … 108 108 109 109 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. 111 111 112 112 This module is free software; you can redistribute it and/or modify it -
wiki-toolkit/trunk/lib/Wiki/Toolkit/Search/Base.pm
r431 r497 29 29 =head1 METHODS 30 30 31 =over 4 32 33 =item B<new> 31 =head2 C<new> 34 32 35 33 my $search = Wiki::Toolkit::Search::XXX->new( @args ); … … 53 51 } 54 52 55 = item B<search_nodes>53 =head2 C<search_nodes> 56 54 57 55 # Find all the nodes which contain the word 'expert'. … … 86 84 sub _do_search { shift->_abstract }; 87 85 88 = item B<analyze>86 =head2 C<analyze> 89 87 90 88 @terms = $self->analyze($string) … … 106 104 } 107 105 108 = item B<fuzzy_title_match>106 =head2 C<fuzzy_title_match> 109 107 110 108 $wiki->write_node( "King's Cross St Pancras", "A station." ); … … 133 131 sub _fuzzy_match { shift->_abstract }; 134 132 135 = item B<index_node>133 =head2 C<index_node> 136 134 137 135 $search->index_node($node, $content); … … 175 173 } 176 174 177 = item B<delete_node>175 =head2 C<delete_node> 178 176 179 177 $search->delete_node($node); … … 192 190 sub _delete_node { shift->_abstract }; 193 191 194 = item B<supports_phrase_searches>192 =head2 C<supports_phrase_searches> 195 193 196 194 if ( $search->supports_phrase_searches ) { … … 205 203 sub supports_phrase_searches { shift->_abstract }; 206 204 207 = item B<supports_fuzzy_searches>205 =head2 C<supports_fuzzy_searches> 208 206 209 207 if ( $search->supports_fuzzy_searches ) { … … 218 216 sub supports_fuzzy_searches { shift->_abstract }; 219 217 220 =back221 222 218 =head1 SEE ALSO 223 219
