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   2346 use strict;
  95         177  
  95         2863  
4 95     95   477 use vars qw( $VERSION );
  95         175  
  95         4646  
5             $VERSION = '0.20';
6              
7 95     95   471 use Carp qw( croak );
  95         185  
  95         5730  
8 95     95   83732 use Wiki::Toolkit;
  95         1391143  
  95         3134  
9 95     95   84393 use Wiki::Toolkit::Formatter::UseMod;
  95         2807961  
  95         3473  
10 95     95   835 use URI::Escape;
  95         193  
  95         5545  
11 95     95   119114 use MIME::Lite;
  95         3423171  
  95         4468  
12 95     95   75277 use Net::Netmask;
  95         548064  
  95         10060  
13 95     95   759 use List::Util qw( first );
  95         204  
  95         5454  
14 95     95   14865 use Data::Validate::URI qw( is_web_uri );
  95         1008473  
  95         341098  
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 121226 my ($class, %args) = @_;
71 155 100       1146 my $config = $args{config} or croak "No config param supplied";
72 154 100       1338 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         1017 my $dbtype = $config->dbtype;
77              
78 153         3879 my %wiki_toolkit_exts = (
79             postgres => "Pg",
80             mysql => "MySQL",
81             sqlite => "SQLite",
82             );
83              
84 153         722 my $wiki_toolkit_module = "Wiki::Toolkit::Store::" . $wiki_toolkit_exts{$dbtype};
85 153         14985 eval "require $wiki_toolkit_module";
86 153 50       1125370 croak "Can't 'require' $wiki_toolkit_module.\n" if $@;
87              
88             # Make store.
89 153         1088 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         237017 my $search;
100 153 50 33     956 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         15968 require Wiki::Toolkit::Search::Plucene;
107 153         815922 my %plucene_args = ( path => $config->indexing_directory );
108 153         2252 my $munger = $config->search_content_munger_module;
109 153 100       2024 if ( $munger ) {
110 2         5 eval {
111 2         194 eval "require $munger";
112             $plucene_args{content_munger} = sub {
113 2     2   196739 my $content = shift;
114 2         36 return $munger->search_content_munger( $content );
115 2         23 };
116             };
117             }
118 153         2257 $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         4229 my $script_name = $config->script_name;
131 153         2137 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   46446 if ( UNIVERSAL::isa( $_[0], "Wiki::Toolkit" ) ) {
142 2         5 shift; # just throw it away
143             }
144 3 50       21 my $type = ( lc( $_[0] ) eq "category" ) ? "cat" : "loc";
145 3   66     26 my $link_title = $_[2] || "View all pages in $_[0] $_[1]";
146 3         26 return qq($link_title);
147             },
148             qr/\@INDEX_LIST\s+\[\[(Category|Locale)\s+([^\]]+)]]/ =>
149             sub {
150 7     7   7913 my ($wiki, $type, $value) = @_;
151 7         76 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   2530 my ($wiki, $type, $value) = @_;
158 4         41 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   636 my ($wiki, $type, $value) = @_;
164 1         12 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   1990 if ( UNIVERSAL::isa( $_[0], "Wiki::Toolkit" ) ) {
170 4         9 shift; # don't need $wiki
171             }
172              
173 4 100       28 my $type = ( lc( $_[0] ) eq "category" ) ? "cat" : "loc";
174 4   66     26 my $link_title = $_[2]
175             || "View map of pages in $_[0] $_[1]";
176 4         37 return qq($link_title);
177             },
178             qr/\@RANDOM_PAGE_LINK(?:\s+\[\[(Category|Locale)\s+([^\]|]+)\|?([^\]]+)?\]\])?/ =>
179             sub {
180 5 50   5   3518 if ( UNIVERSAL::isa( $_[0], "Wiki::Toolkit" ) ) {
181 5         14 shift; # don't need $wiki
182             }
183 5         30 my ( $type, $value, $link_title ) = @_;
184 5         20 my $link = "$script_name?action=random";
185              
186 5 100 66     39 if ( $type && $value ) {
187 4         27 $link .= ";" . lc( uri_escape( $type ) ) . "="
188             . lc( uri_escape( $value ) );
189 4   66     122 $link_title ||= "View a random page in $type $value";
190             } else {
191 1   50     9 $link_title ||= "View a random page on this guide";
192             }
193 5         43 return qq($link_title);
194             },
195             qr/\@INCLUDE_NODE\s+\[\[([^\]|]+)\]\]/ =>
196             sub {
197 2     2   1159 my ($wiki, $node) = @_;
198 2         181 my %node_data = $wiki->retrieve_node( $node );
199 2         3637 return $node_data{content};
200             },
201 153         7985 );
202              
203 153         1401 my $custom_macro_module = $config->custom_macro_module;
204 153 100       2105 if ( $custom_macro_module ) {
205 2         4 eval {
206 2         148 eval "require $custom_macro_module";
207 2         31 %macros = $custom_macro_module->custom_macros(macros => \%macros);
208             };
209             }
210              
211 153         3360 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         15759 my %conf = ( store => $store,
228             search => $search,
229             formatter => $formatter );
230              
231 153         1588 my $wiki = Wiki::Toolkit->new( %conf );
232 153         7038 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 68 my ( $class, %args ) = @_;
259             my ( $wiki, $type, $value, $include_prefix )
260 11         46 = @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       78 if ( !UNIVERSAL::isa( $wiki, "Wiki::Toolkit" ) ) {
265 1 50       9 if ( $args{include_prefix} ) {
266 1         9 return "(unprocessed INDEX_LIST macro)";
267             } else {
268 0         0 return "(unprocessed INDEX_LIST_NO_PREFIX macro)";
269             }
270             }
271              
272 10         57 my @nodes = sort $wiki->list_nodes_by_metadata(
273             metadata_type => $type,
274             metadata_value => $value,
275             ignore_case => 1,
276             );
277 10 100       3631 unless ( scalar @nodes ) {
278 4         50 return "\n* No pages currently in " . lc($type) . " $value\n";
279             }
280 6         15 my $return = "\n";
281 6         18 foreach my $node ( @nodes ) {
282 12         7724 my $title = $node;
283 12 100       59 $title =~ s/^(Category|Locale) // unless $args{include_prefix};
284 12         49 $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         7392 $return =~ s/%2C/,/gs;
291 6         55 return $return;
292             }
293             sub do_node_count {
294 1     1 0 6 my ( $class, %args ) = @_;
295             my ( $wiki, $type, $value )
296 1         5 = @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       10 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         393 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 188     188 0 14138 my ($self, %args) = @_;
326             my ($longitude, $latitude, $config) = ($args{longitude}, $args{latitude},
327             $args{config})
328 188 50       1039 or croak "No longitude supplied to get_wgs84_coords";
329 188 50       980 croak "geo_handler not defined!" unless $config->geo_handler;
330              
331 188 100       2796 if ($config->force_wgs84) {
332             # Only as a rough approximation, good enough for large scale guides
333 164         2134 return ($longitude, $latitude);
334             }
335              
336             # If we don't have a lat and long, return undef right away
337 24 50 66     357 unless($args{longitude} || $args{latitude}) {
338 17         71 return undef;
339             }
340              
341             # Try to load a provider of Helmert Transforms
342 7         15 my $helmert;
343             # First up, try the MySociety Geo::HelmertTransform
344 7 50       30 unless($helmert) {
345 7         16 eval {
346 7         1732 require Geo::HelmertTransform;
347             $helmert = sub($$$) {
348 7     7   26 my ($datum,$oldlat,$oldlong) = @_;
349 7 100       29 if ($datum eq 'Airy') {
350 1         3 $datum = 'Airy1830';
351             }
352 7         47 my $datum_helper = new Geo::HelmertTransform::Datum(Name=>$datum);
353 7         22924 my $wgs84_helper = new Geo::HelmertTransform::Datum(Name=>'WGS84');
354 7 50       981 unless($datum_helper) {
355 0         0 croak("No convertion helper for datum '$datum'");
356 0         0 return undef;
357             }
358              
359 7         32 my ($lat,$long,$h) =
360             Geo::HelmertTransform::convert_datum($datum_helper,$wgs84_helper,$oldlat,$oldlong,0);
361 7         7073 return ($long,$lat);
362 7         8075 };
363             };
364             }
365             # Give up, return undef
366 7 50       32 unless($helmert) {
367 0         0 return undef;
368             }
369              
370              
371 7 100       28 if ($config->geo_handler == 1) {
    50          
    50          
372             # Do conversion here
373 6         81 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       37 if ($config->ellipsoid eq "WGS-84") {
379 0         0 return ($longitude, $latitude);
380             } else {
381             # Do conversion here
382 1         13 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 904 my ( $self, %args ) = @_;
412 15         26 my @nodes = @{$args{nodes}};
  15         49  
413              
414             my @lats = sort
415 39 100       267 grep { defined $_ && /^[-.\d]+$/ }
416 15         46 map { $_->{wgs84_lat} }
  39         91  
417             @nodes;
418             my @longs = sort
419 39 100       229 grep { defined $_ && /^[-.\d]+$/ }
420 15         37 map { $_->{wgs84_long} }
  39         85  
421             @nodes;
422              
423 15 100 66     90 if ( !scalar @lats || !scalar @longs ) {
424 2         9 return;
425             }
426              
427 13         143 my %data = ( min_lat => $lats[0], max_lat => $lats[$#lats],
428             min_long => $longs[0], max_long => $longs[$#longs] );
429 13         80 $data{centre_lat} = ( $data{min_lat} + $data{max_lat} ) / 2;
430 13         44 $data{centre_long} = ( $data{min_long} + $data{max_long} ) / 2;
431 13         118 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 133 my ( $class, %args ) = @_;
449 33 100       140 my $desc = ( $args{format} eq "map" ) ? "Map" : "List";
450 33         102 $desc .= " of all our pages";
451              
452 33         62 my ( @cats, @locs );
453 33         360 foreach my $criterion ( @{$args{criteria}} ) {
  33         109  
454 40         111 my ( $type, $name ) = ( $criterion->{type}, $criterion->{name} );
455 40 100       120 if ( $type eq "category" ) {
456 17         67 $name =~ s/Category //;
457 17         57 push @cats, $name;
458             } else {
459 23         152 $name =~ s/Locale //;
460 23         80 push @locs, $name;
461             }
462             }
463              
464 33 100       119 if ( scalar @cats ) {
465 17         53 $desc .= " labelled with: " . join( ", ", @cats );
466 17 100       57 if ( scalar @locs ) {
467 8         15 $desc .= ", and";
468             }
469             }
470 33 100       117 if ( scalar @locs ) {
471 23         77 $desc .= " located in: " . join( ", ", @locs );
472             }
473 33         63 $desc .= ".";
474 33         158 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 140     140 1 2201 my ( $self, %args ) = @_;
491 140 100       568 return unless $args{content};
492              
493 122 100       917 if ( $args{content} =~ /^#REDIRECT\s+(.+?)\s*$/ ) {
494 9         29 my $redirect = $1;
495              
496             # Strip off enclosing [[ ]] in case this is an extended link.
497 9         40 $redirect =~ s/^\[\[//;
498 9         52 $redirect =~ s/\]\]\s*$//;
499              
500 9         71 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 1125 my ( $self, %args ) = @_;
518 355         740 my $q = $args{cgi_obj};
519 355         600 my @fails;
520 355 50       1142 push @fails, "Content missing" unless $q;
521 355 50       1077 return \@fails if @fails;
522              
523             # Now do our real validation
524 355         1008 foreach my $var (qw(os_x os_y)) {
525 710 100 100     11234 if ($q->param($var) and $q->param($var) !~ /^-?\d+$/) {
526 1         49 push @fails, "$var must be integer, was: " . $q->param($var);
527             }
528             }
529              
530 355         9369 foreach my $var (qw(latitude longitude)) {
531 710 50 66     11763 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     10176 if ( $q->param('website') and $q->param('website') ne 'http://' ) {
537 5 50       214 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         10452 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 18766 my ($comment, $base_url) = @_;
566              
567 83         249 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         278 foreach (@links) {
573 2 100       11 if (/(.*?)\|(.*)/) {
574 1         4 my ($page, $title) = ($1, $2);
575 1         19 $comment =~ s{\[\[$page\|$title\]\]}
576             {$title};
577             } else {
578 1         2 my $page = $_;
579 1         30 $comment =~ s{\[\[$page\]\]}
580             {$page};
581             }
582             }
583              
584 83         463 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 3384 my ( $self, %args ) = @_;
618 4 50       16 my $config = $args{config} or die "config argument not supplied";
619 4         7 my @to;
620 4 100       16 @to = @{$args{to}} if $args{to};
  1         3  
621 4         7 my @bcc;
622 4 100       20 push @to, $config->contact_email if $args{admin};
623 4 100       39 die "No recipients specified" unless $to[0];
624 3 50       10 die "No subject specified" unless $args{subject};
625 3 50       10 die "No body specified" unless $args{body};
626 3         9 my $to_str = join ',', @to;
627 3 50       15 push @bcc, $config->contact_email unless $args{nobcc};
628 3         33 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         10 );
636              
637 3 50       93245 if ( $args{return_output} ) {
638 3         14 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 1127 my ($self, $config, $ip) = @_;
658 353 50       1297 return undef if not defined $ip;
659              
660             # create NetAddr::IP object of the test IP
661 353 100       4146 my $addr = Net::Netmask->new2($ip) or return undef;
662              
663             # load the configured whitelist
664             my @whitelist
665 352         40860 = split ',', $config->moderate_whitelist;
666              
667             # test each entry in the whitelist
668 352         4428 return eval{
669 2     2   8 first { Net::Netmask->new2($_)->match($addr->base) } @whitelist
670 352         4078 };
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;