File Coverage

blib/lib/OpenGuides/Utils.pm
Criterion Covered Total %
statement 223 251 88.8
branch 87 118 73.7
condition 22 35 62.8
subroutine 31 33 93.9
pod 8 12 66.6
total 371 449 82.6


line stmt bran cond sub pod time code
1             package OpenGuides::Utils;
2              
3 95     95   1474 use strict;
  95         109  
  95         2486  
4 95     95   321 use vars qw( $VERSION );
  95         112  
  95         3590  
5             $VERSION = '0.20';
6              
7 95     95   293 use Carp qw( croak );
  95         102  
  95         3356  
8 95     95   42737 use Wiki::Toolkit;
  95         826031  
  95         2305  
9 95     95   42756 use Wiki::Toolkit::Formatter::UseMod;
  95         1706970  
  95         2627  
10 95     95   519 use URI::Escape;
  95         119  
  95         3998  
11 95     95   63540 use MIME::Lite;
  95         2131553  
  95         2653  
12 95     95   35113 use Net::Netmask;
  95         345906  
  95         6742  
13 95     95   473 use List::Util qw( first );
  95         111  
  95         4385  
14 95     95   7833 use Data::Validate::URI qw( is_web_uri );
  95         660187  
  95         222801  
15              
16             =head1 NAME
17              
18             OpenGuides::Utils - General utility methods for OpenGuides scripts.
19              
20             =head1 DESCRIPTION
21              
22             Provides general utility methods for OpenGuides scripts. Distributed
23             and installed as part of the OpenGuides project, not intended for
24             independent installation. This documentation is probably only useful
25             to OpenGuides developers.
26              
27             =head1 SYNOPSIS
28              
29             use OpenGuide::Config;
30             use OpenGuides::Utils;
31              
32             my $config = OpenGuides::Config->new( file => "wiki.conf" );
33             my $wiki = OpenGuides::Utils->make_wiki_object( config => $config );
34              
35             =head1 METHODS
36              
37             =over 4
38              
39             =item B
40              
41             my $config = OpenGuides::Config->new( file => "wiki.conf" );
42             my $wiki = OpenGuides::Utils->make_wiki_object( config => $config );
43              
44             Croaks unless an C object is supplied. Returns a
45             C object made from the given config file on success,
46             croaks if any other error occurs.
47              
48             The config file needs to define at least the following variables:
49              
50             =over
51              
52             =item *
53              
54             dbtype - one of C, C and C
55              
56             =item *
57              
58             dbname
59              
60             =item *
61              
62             indexing_directory - for the L, L,
63             or C files to go in
64              
65             =back
66              
67             =cut
68              
69             sub make_wiki_object {
70 155     155 1 76664 my ($class, %args) = @_;
71 155 100       757 my $config = $args{config} or croak "No config param supplied";
72 154 100       925 croak "config param isn't an OpenGuides::Config object"
73             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
74              
75             # Require in the right database module.
76 153         650 my $dbtype = $config->dbtype;
77              
78 153         2571 my %wiki_toolkit_exts = (
79             postgres => "Pg",
80             mysql => "MySQL",
81             sqlite => "SQLite",
82             );
83              
84 153         471 my $wiki_toolkit_module = "Wiki::Toolkit::Store::" . $wiki_toolkit_exts{$dbtype};
85 153         10284 eval "require $wiki_toolkit_module";
86 153 50       703033 croak "Can't 'require' $wiki_toolkit_module.\n" if $@;
87              
88             # Make store.
89 153         794 my $store = $wiki_toolkit_module->new(
90             dbname => $config->dbname,
91             dbuser => $config->dbuser,
92             dbpass => $config->dbpass,
93             dbhost => $config->dbhost,
94             dbport => $config->dbport,
95             charset => $config->dbencoding,
96             );
97              
98             # Make search.
99 153         174971 my $search;
100 153 50 33     721 if ( $config->use_lucy ) {
    50 33        
101 0         0 $search = $class->make_lucy_searcher( config => $config );
102             } elsif ( $config->use_plucene
103             && ( lc($config->use_plucene) eq "y"
104             || $config->use_plucene == 1 )
105             ) {
106 153         10084 require Wiki::Toolkit::Search::Plucene;
107 153         549916 my %plucene_args = ( path => $config->indexing_directory );
108 153         1586 my $munger = $config->search_content_munger_module;
109 153 100       1129 if ( $munger ) {
110 2         3 eval {
111 2         88 eval "require $munger";
112             $plucene_args{content_munger} = sub {
113 2     2   21348 my $content = shift;
114 2         21 return $munger->search_content_munger( $content );
115 2         15 };
116             };
117             }
118 153         1571 $search = Wiki::Toolkit::Search::Plucene->new( %plucene_args );
119             } else {
120 0         0 require Wiki::Toolkit::Search::SII;
121 0         0 require Search::InvertedIndex::DB::DB_File_SplitHash;
122 0         0 my $indexdb = Search::InvertedIndex::DB::DB_File_SplitHash->new(
123             -map_name => $config->indexing_directory,
124             -lock_mode => "EX"
125             );
126 0         0 $search = Wiki::Toolkit::Search::SII->new( indexdb => $indexdb );
127             }
128              
129             # Make formatter.
130 153         3065 my $script_name = $config->script_name;
131 153         1730 my $search_url = $config->script_url . "search.cgi";
132              
133             my %macros = (
134             '@SEARCHBOX' =>
135             qq(
),
136             qr/\@INDEX_LINK\s+\[\[(Category|Locale)\s+([^\]|]+)\|?([^\]]+)?\]\]/ =>
137             sub {
138             # We may be being called by Wiki::Toolkit::Plugin::Diff,
139             # which doesn't know it has to pass us $wiki - and
140             # we don't use it anyway.
141 3 100   3   22825 if ( UNIVERSAL::isa( $_[0], "Wiki::Toolkit" ) ) {
142 2         4 shift; # just throw it away
143             }
144 3 50       15 my $type = ( lc( $_[0] ) eq "category" ) ? "cat" : "loc";
145 3   66     16 my $link_title = $_[2] || "View all pages in $_[0] $_[1]";
146 3         20 return qq($link_title);
147             },
148             qr/\@INDEX_LIST\s+\[\[(Category|Locale)\s+([^\]]+)]]/ =>
149             sub {
150 7     7   4645 my ($wiki, $type, $value) = @_;
151 7         57 return $class->do_index_list_macro(
152             wiki => $wiki, type => $type, value => $value,
153             include_prefix => 1 );
154             },
155             qr/\@INDEX_LIST_NO_PREFIX\s+\[\[(Category|Locale)\s+([^\]]+)]]/ =>
156             sub {
157 4     4   1426 my ($wiki, $type, $value) = @_;
158 4         29 return $class->do_index_list_macro(
159             wiki => $wiki, type => $type, value => $value );
160             },
161             qr/\@NODE_COUNT\s+\[\[(Category|Locale)\s+([^\]]+)]]/ =>
162             sub {
163 1     1   426 my ($wiki, $type, $value) = @_;
164 1         11 return $class->do_node_count(
165             wiki => $wiki, type => $type, value => $value );
166             },
167             qr/\@MAP_LINK\s+\[\[(Category|Locale)\s+([^\]|]+)\|?([^\]]+)?\]\]/ =>
168             sub {
169 4 50   4   1464 if ( UNIVERSAL::isa( $_[0], "Wiki::Toolkit" ) ) {
170 4         6 shift; # don't need $wiki
171             }
172              
173 4 100       18 my $type = ( lc( $_[0] ) eq "category" ) ? "cat" : "loc";
174 4   66     17 my $link_title = $_[2]
175             || "View map of pages in $_[0] $_[1]";
176 4         27 return qq($link_title);
177             },
178             qr/\@RANDOM_PAGE_LINK(?:\s+\[\[(Category|Locale)\s+([^\]|]+)\|?([^\]]+)?\]\])?/ =>
179             sub {
180 5 50   5   2069 if ( UNIVERSAL::isa( $_[0], "Wiki::Toolkit" ) ) {
181 5         9 shift; # don't need $wiki
182             }
183 5         18 my ( $type, $value, $link_title ) = @_;
184 5         11 my $link = "$script_name?action=random";
185              
186 5 100 66     32 if ( $type && $value ) {
187 4         19 $link .= ";" . lc( uri_escape( $type ) ) . "="
188             . lc( uri_escape( $value ) );
189 4   66     88 $link_title ||= "View a random page in $type $value";
190             } else {
191 1   50     5 $link_title ||= "View a random page on this guide";
192             }
193 5         29 return qq($link_title);
194             },
195             qr/\@INCLUDE_NODE\s+\[\[([^\]|]+)\]\]/ =>
196             sub {
197 2     2   772 my ($wiki, $node) = @_;
198 2         8 my %node_data = $wiki->retrieve_node( $node );
199 2         1994 return $node_data{content};
200             },
201 153         6348 );
202              
203 153         1056 my $custom_macro_module = $config->custom_macro_module;
204 153 100       1384 if ( $custom_macro_module ) {
205 2         3 eval {
206 2         111 eval "require $custom_macro_module";
207 2         23 %macros = $custom_macro_module->custom_macros(macros => \%macros);
208             };
209             }
210              
211 153         2730 my $formatter = Wiki::Toolkit::Formatter::UseMod->new(
212             extended_links => 1,
213             implicit_links => 0,
214             allowed_tags => [qw(a p b strong i em pre small img table td
215             tr th br hr ul li center blockquote kbd
216             div code span strike sub sup font dl dt dd
217             )],
218             macros => \%macros,
219             pass_wiki_to_macros => 1,
220             node_prefix => "$script_name?",
221             edit_prefix => "$script_name?action=edit;id=",
222             munge_urls => 1,
223             external_link_class => "external",
224             escape_url_commas => 0,
225             );
226              
227 153         11147 my %conf = ( store => $store,
228             search => $search,
229             formatter => $formatter );
230              
231 153         1074 my $wiki = Wiki::Toolkit->new( %conf );
232 153         5326 return $wiki;
233             }
234              
235             sub make_lucy_searcher {
236 0     0 0 0 my ( $class, %args ) = @_;
237 0         0 require Wiki::Toolkit::Search::Lucy;
238 0         0 my $config = $args{config};
239 0         0 my %lucy_args = (
240             path => $config->indexing_directory,
241             metadata_fields => [ qw( address category locale ) ],
242             boost => { title => 10 }, # empirically determined (test t/306)
243             );
244 0         0 my $munger = $config->search_content_munger_module;
245 0 0       0 if ( $munger ) {
246 0         0 eval {
247 0         0 eval "require $munger";
248             $lucy_args{content_munger} = sub {
249 0     0   0 my $content = shift;
250 0         0 return $munger->search_content_munger( $content );
251 0         0 };
252             };
253             }
254 0         0 return Wiki::Toolkit::Search::Lucy->new( %lucy_args );
255             }
256              
257             sub do_index_list_macro {
258 11     11 0 44 my ( $class, %args ) = @_;
259             my ( $wiki, $type, $value, $include_prefix )
260 11         30 = @args{ qw( wiki type value include_prefix ) };
261              
262             # We may be being called by Wiki::Toolkit::Plugin::Diff,
263             # which doesn't know it has to pass us $wiki
264 11 100       54 if ( !UNIVERSAL::isa( $wiki, "Wiki::Toolkit" ) ) {
265 1 50       7 if ( $args{include_prefix} ) {
266 1         5 return "(unprocessed INDEX_LIST macro)";
267             } else {
268 0         0 return "(unprocessed INDEX_LIST_NO_PREFIX macro)";
269             }
270             }
271              
272 10         46 my @nodes = sort $wiki->list_nodes_by_metadata(
273             metadata_type => $type,
274             metadata_value => $value,
275             ignore_case => 1,
276             );
277 10 100       2460 unless ( scalar @nodes ) {
278 4         37 return "\n* No pages currently in " . lc($type) . " $value\n";
279             }
280 6         12 my $return = "\n";
281 6         13 foreach my $node ( @nodes ) {
282 12         4622 my $title = $node;
283 12 100       46 $title =~ s/^(Category|Locale) // unless $args{include_prefix};
284 12         42 $return .= "* "
285             . $wiki->formatter->format_link( wiki => $wiki,
286             link => "$node|$title" )
287             . "\n";
288             }
289             # URI::Escape escapes commas in URLs. This is annoying.
290 6         4610 $return =~ s/%2C/,/gs;
291 6         35 return $return;
292             }
293             sub do_node_count {
294 1     1 0 4 my ( $class, %args ) = @_;
295             my ( $wiki, $type, $value )
296 1         4 = @args{ qw( wiki type value ) };
297              
298             # We may be being called by Wiki::Toolkit::Plugin::Diff,
299             # which doesn't know it has to pass us $wiki
300 1 50       7 if ( !UNIVERSAL::isa( $wiki, "Wiki::Toolkit" ) ) {
301 0         0 return "(unprocessed NODE_COUNT macro)";
302             }
303              
304 1         6 my $num_nodes = scalar $wiki->list_nodes_by_metadata(
305             metadata_type => $type,
306             metadata_value => $value,
307             ignore_case => 1,
308             );
309 1         288 return $num_nodes;
310             }
311             =item B
312              
313             Returns coordinate data suitable for use with Google Maps (and other GIS
314             systems that assume WGS-84 data).
315              
316             my ($wgs84_long, $wgs84_lat) = OpenGuides::Utils->get_wgs84_coords(
317             longitude => $longitude,
318             latitude => $latitude,
319             config => $config
320             );
321              
322             =cut
323              
324             sub get_wgs84_coords {
325 189     189 0 7652 my ($self, %args) = @_;
326             my ($longitude, $latitude, $config) = ($args{longitude}, $args{latitude},
327             $args{config})
328 189 50       790 or croak "No longitude supplied to get_wgs84_coords";
329 189 50       677 croak "geo_handler not defined!" unless $config->geo_handler;
330              
331 189 100       2055 if ($config->force_wgs84) {
332             # Only as a rough approximation, good enough for large scale guides
333 165         1333 return ($longitude, $latitude);
334             }
335              
336             # If we don't have a lat and long, return undef right away
337 24 50 66     233 unless($args{longitude} || $args{latitude}) {
338 17         54 return undef;
339             }
340              
341             # Try to load a provider of Helmert Transforms
342 7         11 my $helmert;
343             # First up, try the MySociety Geo::HelmertTransform
344 7 50       19 unless($helmert) {
345 7         11 eval {
346 7         950 require Geo::HelmertTransform;
347             $helmert = sub($$$) {
348 7     7   18 my ($datum,$oldlat,$oldlong) = @_;
349 7 100       23 if ($datum eq 'Airy') {
350 1         1 $datum = 'Airy1830';
351             }
352 7         42 my $datum_helper = new Geo::HelmertTransform::Datum(Name=>$datum);
353 7         7902 my $wgs84_helper = new Geo::HelmertTransform::Datum(Name=>'WGS84');
354 7 50       496 unless($datum_helper) {
355 0         0 croak("No convertion helper for datum '$datum'");
356 0         0 return undef;
357             }
358              
359 7         30 my ($lat,$long,$h) =
360             Geo::HelmertTransform::convert_datum($datum_helper,$wgs84_helper,$oldlat,$oldlong,0);
361 7         4047 return ($long,$lat);
362 7         5056 };
363             };
364             }
365             # Give up, return undef
366 7 50       20 unless($helmert) {
367 0         0 return undef;
368             }
369              
370              
371 7 100       21 if ($config->geo_handler == 1) {
    50          
    50          
372             # Do conversion here
373 6         53 return &$helmert('Airy1830',$latitude,$longitude);
374             } elsif ($config->geo_handler == 2) {
375             # Do conversion here
376 0         0 return &$helmert('Airy1830Modified',$latitude,$longitude);
377             } elsif ($config->geo_handler == 3) {
378 1 50       23 if ($config->ellipsoid eq "WGS-84") {
379 0         0 return ($longitude, $latitude);
380             } else {
381             # Do conversion here
382 1         7 return &$helmert($config->ellipsoid,$latitude,$longitude);
383             }
384             } else {
385 0         0 croak "Invalid geo_handler config option $config->geo_handler";
386             }
387             }
388              
389             =item B
390              
391             Given a set of WGS84 coordinate data, returns the minimum, maximum,
392             and centre latitude and longitude.
393              
394             %data = OpenGuides::Utils->get_wgs84_min_max(
395             nodes => [
396             { wgs84_lat => 51.1, wgs84_long => 1.1 },
397             { wgs84_lat => 51.2, wgs84_long => 1.2 },
398             ]
399             );
400             print "Top right-hand corner is $data{max_lat}, $data{max_long}";
401             print "Centre point is $data{centre_lat}, $data{centre_long}";
402              
403             The hashes in the C argument can include other key/value pairs;
404             these will just be ignored.
405              
406             Returns false if it can't find any valid geodata in the nodes.
407              
408             =cut
409              
410             sub get_wgs84_min_max {
411 15     15 1 606 my ( $self, %args ) = @_;
412 15         22 my @nodes = @{$args{nodes}};
  15         33  
413              
414             my @lats = sort
415 39 100       193 grep { defined $_ && /^[-.\d]+$/ }
416 15         27 map { $_->{wgs84_lat} }
  39         53  
417             @nodes;
418             my @longs = sort
419 39 100       150 grep { defined $_ && /^[-.\d]+$/ }
420 15         28 map { $_->{wgs84_long} }
  39         47  
421             @nodes;
422              
423 15 100 66     71 if ( !scalar @lats || !scalar @longs ) {
424 2         6 return;
425             }
426              
427 13         67 my %data = ( min_lat => $lats[0], max_lat => $lats[$#lats],
428             min_long => $longs[0], max_long => $longs[$#longs] );
429 13         61 $data{centre_lat} = ( $data{min_lat} + $data{max_lat} ) / 2;
430 13         34 $data{centre_long} = ( $data{min_long} + $data{max_long} ) / 2;
431 13         84 return %data;
432             }
433              
434             =item B
435              
436             $tt_vars{page_description} =
437             OpenGuides::Utils->get_index_page_description(
438             format => "map",
439             criteria => [ type => "locale", value => "croydon" ],
440             );
441              
442             Returns a sentence that can be used as a summary of what's shown on an
443             index page.
444              
445             =cut
446              
447             sub get_index_page_description {
448 33     33 1 90 my ( $class, %args ) = @_;
449 33 100       111 my $desc = ( $args{format} eq "map" ) ? "Map" : "List";
450 33         63 $desc .= " of all our pages";
451              
452 33         36 my ( @cats, @locs );
453 33         46 foreach my $criterion ( @{$args{criteria}} ) {
  33         96  
454 40         77 my ( $type, $name ) = ( $criterion->{type}, $criterion->{name} );
455 40 100       86 if ( $type eq "category" ) {
456 17         54 $name =~ s/Category //;
457 17         34 push @cats, $name;
458             } else {
459 23         63 $name =~ s/Locale //;
460 23         52 push @locs, $name;
461             }
462             }
463              
464 33 100       86 if ( scalar @cats ) {
465 17         45 $desc .= " labelled with: " . join( ", ", @cats );
466 17 100       40 if ( scalar @locs ) {
467 8         16 $desc .= ", and";
468             }
469             }
470 33 100       76 if ( scalar @locs ) {
471 23         60 $desc .= " located in: " . join( ", ", @locs );
472             }
473 33         54 $desc .= ".";
474 33         111 return $desc;
475             }
476              
477             =item B
478              
479             $redir = OpenGuides::Utils->detect_redirect( content => "foo" );
480              
481             Checks the content of a node to see if the node is a redirect to another
482             node. If so, returns the name of the node that this one redirects to. If
483             not, returns false.
484              
485             (Also returns false if no content is provided.)
486              
487             =cut
488              
489             sub detect_redirect {
490 141     141 1 2194 my ( $self, %args ) = @_;
491 141 100       415 return unless $args{content};
492              
493 123 100       677 if ( $args{content} =~ /^#REDIRECT\s+(.+?)\s*$/ ) {
494 9         20 my $redirect = $1;
495              
496             # Strip off enclosing [[ ]] in case this is an extended link.
497 9         32 $redirect =~ s/^\[\[//;
498 9         36 $redirect =~ s/\]\]\s*$//;
499              
500 9         50 return $redirect;
501             }
502             }
503              
504             =item B
505              
506             my $fails = OpenGuides::Utils->validate_edit(
507             id => $node,
508             cgi_obj => $q
509             );
510              
511             Checks supplied content for general validity. If anything is invalid,
512             returns an array ref of errors to report to the user.
513              
514             =cut
515              
516             sub validate_edit {
517 355     355 1 816 my ( $self, %args ) = @_;
518 355         529 my $q = $args{cgi_obj};
519 355         385 my @fails;
520 355 50       947 push @fails, "Content missing" unless $q;
521 355 50       845 return \@fails if @fails;
522              
523             # Now do our real validation
524 355         753 foreach my $var (qw(os_x os_y)) {
525 710 100 100     7030 if ($q->param($var) and $q->param($var) !~ /^-?\d+$/) {
526 1         51 push @fails, "$var must be integer, was: " . $q->param($var);
527             }
528             }
529              
530 355         5442 foreach my $var (qw(latitude longitude)) {
531 710 50 66     7224 if ($q->param($var) and $q->param($var) !~ /^-?\d+\.?(\d+)?$/) {
532 0         0 push @fails, "$var must be numeric, was: " . $q->param($var);
533             }
534             }
535              
536 355 100 66     6026 if ( $q->param('website') and $q->param('website') ne 'http://' ) {
537 5 50       231 unless ( is_web_uri( scalar $q->param('website') ) ) {
538 0         0 push @fails, $q->param('website') . ' is not a valid web URI';
539             }
540             }
541              
542 355         6508 return \@fails;
543              
544             };
545              
546             =item B
547              
548             my $change_comment = parse_change_comment($string, $base_url);
549              
550             Given a base URL (for example, C), takes a string,
551             replaces C<[[page]]> and C<[[page|titled link]]> with
552              
553             page
554              
555             and
556              
557             titled link
558              
559             respectively, and returns it. This is a limited subset of wiki markup suitable for
560             use in page change comments.
561              
562             =cut
563              
564             sub parse_change_comment {
565 83     83 1 11337 my ($comment, $base_url) = @_;
566              
567 83         210 my @links = $comment =~ m{\[\[(.*?)\]\]}g;
568              
569             # It's not all that great having to reinvent the wheel in this way, but
570             # Text::WikiFormat won't let you specify the subset of wiki notation that
571             # you're interested in. C'est la vie.
572 83         160 foreach (@links) {
573 2 100       9 if (/(.*?)\|(.*)/) {
574 1         3 my ($page, $title) = ($1, $2);
575 1         14 $comment =~ s{\[\[$page\|$title\]\]}
576             {$title};
577             } else {
578 1         3 my $page = $_;
579 1         25 $comment =~ s{\[\[$page\]\]}
580             {$page};
581             }
582             }
583              
584 83         326 return $comment;
585             }
586              
587             =item B
588              
589             eval { OpenGuides::Utils->send_email(
590             config => $config,
591             subject => "Subject",
592             body => "Test body",
593             admin => 1,
594             nobcc => 1,
595             return_output => 1
596             ) };
597              
598             if ($@) {
599             print "Error mailing admin: $@\n";
600             } else {
601             print "Mailed admin\n";
602             }
603              
604             Send out email. If C is true, the email will be sent to the site
605             admin. If C is defined, email will be sent to addresses in that
606             arrayref. If C is true, there will be no Bcc to the admin.
607              
608             C and C are mandatory arguments.
609              
610             Debugging: if C is true, the message will be returned as
611             a string instead of being sent by email.
612              
613             =cut
614              
615              
616             sub send_email {
617 4     4 1 2350 my ( $self, %args ) = @_;
618 4 50       13 my $config = $args{config} or die "config argument not supplied";
619 4         4 my @to;
620 4 100       9 @to = @{$args{to}} if $args{to};
  1         3  
621 4         5 my @bcc;
622 4 100       13 push @to, $config->contact_email if $args{admin};
623 4 100       28 die "No recipients specified" unless $to[0];
624 3 50       6 die "No subject specified" unless $args{subject};
625 3 50       6 die "No body specified" unless $args{body};
626 3         6 my $to_str = join ',', @to;
627 3 50       9 push @bcc, $config->contact_email unless $args{nobcc};
628 3         22 my $bcc_str = join ',', @bcc;
629             my $msg = MIME::Lite->new(
630             From => $config->contact_email,
631             To => $to_str,
632             Bcc => $bcc_str,
633             Subject => $args{subject},
634             Data => $args{body}
635 3         7 );
636              
637 3 50       57608 if ( $args{return_output} ) {
638 3         8 return $msg->as_string;
639             } else {
640 0 0       0 $msg->send or die "Couldn't send mail!";
641             }
642             }
643              
644             =item B
645              
646             if (OpenGuides::Utils->in_moderate_whitelist( '127.0.0.1' )) {
647             # skip moderation and apply new verson to published site
648             }
649              
650             Admins can supply a comma separated list of IP addresses or CIDR-notation
651             subnets indicating the hosts which can bypass enforced moderation. Any
652             values which cannot be parsed by C will be ignored.
653              
654             =cut
655              
656             sub in_moderate_whitelist {
657 353     353 1 854 my ($self, $config, $ip) = @_;
658 353 50       978 return undef if not defined $ip;
659              
660             # create NetAddr::IP object of the test IP
661 353 100       3050 my $addr = Net::Netmask->new2($ip) or return undef;
662              
663             # load the configured whitelist
664             my @whitelist
665 352         31780 = split ',', $config->moderate_whitelist;
666              
667             # test each entry in the whitelist
668 352         3187 return eval{
669 2     2   6 first { Net::Netmask->new2($_)->match($addr->base) } @whitelist
670 352         3398 };
671             }
672              
673             =back
674              
675             =head1 AUTHOR
676              
677             The OpenGuides Project (openguides-dev@lists.openguides.org)
678              
679             =head1 COPYRIGHT
680              
681             Copyright (C) 2003-2013 The OpenGuides Project. All Rights Reserved.
682              
683             This module is free software; you can redistribute it and/or modify it
684             under the same terms as Perl itself.
685              
686             =cut
687              
688             1;