File Coverage

blib/lib/OpenGuides/CGI.pm
Criterion Covered Total %
statement 156 159 98.1
branch 57 68 83.8
condition 35 40 87.5
subroutine 17 17 100.0
pod 8 9 88.8
total 273 293 93.1


line stmt bran cond sub pod time code
1             package OpenGuides::CGI;
2 93     93   1016 use strict;
  93         98  
  93         2309  
3 93     93   287 use vars qw( $VERSION );
  93         102  
  93         3302  
4             $VERSION = '0.13';
5              
6 93     93   293 use Carp qw( croak );
  93         98  
  93         3092  
7 93     93   1013 use CGI;
  93         20136  
  93         812  
8 93     93   39304 use CGI::Cookie;
  93         273328  
  93         121590  
9              
10             =head1 NAME
11              
12             OpenGuides::CGI - An OpenGuides helper for CGI-related things.
13              
14             =head1 DESCRIPTION
15              
16             Does CGI stuff for OpenGuides. Distributed and installed as part of
17             the OpenGuides project, not intended for independent installation.
18             This documentation is probably only useful to OpenGuides developers.
19              
20             =head1 SYNOPSIS
21              
22             Saving preferences in a cookie:
23              
24             use OpenGuides::CGI;
25             use OpenGuides::Config;
26             use OpenGuides::Template;
27             use OpenGuides::Utils;
28              
29             my $config = OpenGuides::Config->new( file => "wiki.conf" );
30              
31             my $cookie = OpenGuides::CGI->make_prefs_cookie(
32             config => $config,
33             username => "Kake",
34             include_geocache_link => 1,
35             preview_above_edit_box => 1,
36             latlong_traditional => 1,
37             omit_help_links => 1,
38             show_minor_edits_in_rc => 1,
39             default_edit_type => "tidying",
40             cookie_expires => "never",
41             track_recent_changes_views => 1,
42             display_google_maps => 1,
43             is_admin => 1
44             );
45              
46             my $wiki = OpenGuides::Utils->make_wiki_object( config => $config );
47             print OpenGuides::Template->output( wiki => $wiki,
48             config => $config,
49             template => "preferences.tt",
50             cookies => $cookie
51             );
52              
53             # and to retrive prefs later:
54             my %prefs = OpenGuides::CGI->get_prefs_from_cookie(
55             config => $config
56             );
57              
58             Tracking visits to Recent Changes:
59              
60             use OpenGuides::CGI;
61             use OpenGuides::Config;
62             use OpenGuides::Template;
63             use OpenGuides::Utils;
64              
65             my $config = OpenGuides::Config->new( file => "wiki.conf" );
66              
67             my $cookie = OpenGuides::CGI->make_recent_changes_cookie(
68             config => $config,
69             );
70              
71             =head1 METHODS
72              
73             =over 4
74              
75             =item B
76              
77             my $config_file = $ENV{OPENGUIDES_CONFIG_FILE} || "wiki.conf";
78             my $config = OpenGuides::Config->new( file => $config_file );
79             my $guide = OpenGuides->new( config => $config );
80             my $wiki = $guide->wiki;
81              
82             my $q = CGI->new;
83              
84             my $node_param = OpenGuides::CGI->extract_node_param(
85             wiki => $wiki, cgi_obj => $q );
86              
87             Returns the title, id, or keywords parameter from the URL. Normally
88             this will be something like "British_Museum", i.e. with underscores
89             instead of spaces. However if the URL does contain spaces (encoded as
90             %20 or +), the return value will be e.g. "British Museum" instead.
91              
92             Croaks unless a L object is supplied as C and a
93             L object is supplied as C.
94              
95             =cut
96              
97             sub extract_node_param {
98 52     52 1 2502 my ($class, %args) = @_;
99 52 50       148 my $wiki = $args{wiki} or croak "No wiki supplied";
100 52 50       125 croak "wiki not a Wiki::Toolkit object"
101             unless UNIVERSAL::isa( $wiki, "Wiki::Toolkit" );
102 52 50       74 my $q = $args{cgi_obj} or croak "No cgi_obj supplied";
103 52 50       87 croak "cgi_obj not a CGI object"
104             unless UNIVERSAL::isa( $q, "CGI" );
105              
106             # Note $q->param( "keywords" ) gives you the entire param string.
107             # We need this to do URLs like foo.com/wiki.cgi?This_Page
108 52   50     165 my $param = $q->param( "id" )
109             || $q->param( "title" )
110             || join( " ", $q->multi_param( "keywords" ) )
111             || "";
112 52         1295 $param =~ s/%20/ /g;
113 52         58 $param =~ s/\+/ /g;
114 52         91 return $param;
115             }
116              
117             =item B
118              
119             my $config_file = $ENV{OPENGUIDES_CONFIG_FILE} || "wiki.conf";
120             my $config = OpenGuides::Config->new( file => $config_file );
121             my $guide = OpenGuides->new( config => $config );
122             my $wiki = $guide->wiki;
123              
124             my $q = CGI->new;
125              
126             my $node_name = OpenGuides::CGI->extract_node_name(
127             wiki => $wiki, cgi_obj => $q );
128              
129             Returns the name of the node the user wishes to display/manipulate, as
130             we expect it to be stored in the database. Normally this will be
131             something like "British Museum", i.e. with spaces in. Croaks unless a
132             L object is supplied as C and a L object is
133             supplied as C.
134              
135             =cut
136              
137             sub extract_node_name {
138 26     26 1 3780 my ($class, %args) = @_;
139             # The next call will validate our args for us and croak if necessary.
140 26         54 my $param = $class->extract_node_param( %args );
141              
142             # Sometimes people type spaces instead of underscores.
143 26         56 $param =~ s/ /_/g;
144 26         25 $param =~ s/%20/_/g;
145 26         21 $param =~ s/\+/_/g;
146              
147 26         55 my $formatter = $args{wiki}->formatter;
148 26         92 return $formatter->node_param_to_node_name( $param );
149             }
150              
151             =item B
152              
153             my $config_file = $ENV{OPENGUIDES_CONFIG_FILE} || "wiki.conf";
154             my $config = OpenGuides::Config->new( file => $config_file );
155             my $guide = OpenGuides->new( config => $config );
156              
157             my $q = CGI->new;
158              
159             my $url = OpenGuides::CGI->check_spaces_redirect(
160             wiki => $wiki, cgi_obj => $q );
161              
162             If the user seems to have typed a URL with spaces in the node param
163             instead of underscores, this method will return the URL with the
164             underscores put in. Otherwise, it returns false.
165              
166             =cut
167              
168             sub check_spaces_redirect {
169 17     17 1 7729 my ($class, %args) = @_;
170 17         16 my $wiki = $args{wiki};
171 17         15 my $q = $args{cgi_obj};
172              
173 17         31 my $name = $class->extract_node_name( wiki => $wiki, cgi_obj => $q );
174 17         100 my $param = $class->extract_node_param( wiki => $wiki, cgi_obj => $q );
175              
176             # If we can't figure out the name or param, it's safest to do nothing.
177 17 50 33     44 if ( !$name || !$param ) {
178 0         0 return 0;
179             }
180              
181             # If the name has no spaces in, or the name and param differ, we're
182             # probably OK.
183 17 100 100     75 if ( ( $name !~ / / ) || ( $name ne $param ) ) {
184 6         12 return 0;
185             }
186              
187             # Make a new CGI object to manipulate, to avoid action-at-a-distance.
188 11         22 my $new_q = CGI->new( $q );
189 11         2533 my $formatter = $wiki->formatter;
190 11         37 my $real_param = $formatter->node_name_to_node_param( $name );
191              
192 11 100       205 if ( $q->param( "id" ) ) {
    100          
193 4         53 $new_q->param( -name => "id", -value => $real_param );
194             } elsif ( $q->param( "title" ) ) {
195 4         89 $new_q->param( -name => "title", -value => $real_param );
196             } else {
197             # OK, we have the keywords case; the entire param string is the
198             # node param. So just delete all existing parameters and stick
199             # the node param back in.
200 3         57 $new_q->delete_all();
201 3         124 $new_q->param( -name => "id", -value => $real_param );
202             }
203              
204 11         633 my $url = $new_q->self_url;
205              
206             # Escaped commas are ugly.
207 11         2776 $url =~ s/%2C/,/g;
208 11         53 return $url;
209             }
210              
211             =item B
212              
213             my $cookie = OpenGuides::CGI->make_prefs_cookie(
214             config => $config,
215             username => "Kake",
216             include_geocache_link => 1,
217             preview_above_edit_box => 1,
218             latlong_traditional => 1,
219             omit_help_links => 1,
220             show_minor_edits_in_rc => 1,
221             default_edit_type => "tidying",
222             cookie_expires => "never",
223             track_recent_changes_views => 1,
224             display_google_maps => 1,
225             is_admin => 1
226             );
227              
228             Croaks unless an L object is supplied as C.
229             Acceptable values for C are C, C,
230             C; anything else will default to C.
231              
232             =cut
233              
234             sub make_prefs_cookie {
235 47     47 1 227582 my ($class, %args) = @_;
236 47 100       330 my $config = $args{config} or croak "No config object supplied";
237 46 100       431 croak "Config object not an OpenGuides::Config"
238             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
239 45         159 my $cookie_name = $class->_get_cookie_name( config => $config );
240 45         59 my $expires;
241 45 100 100     360 if ( $args{cookie_expires} and $args{cookie_expires} eq "never" ) {
    100 100        
242             # Gosh, a hack. YES I AM ASHAMED OF MYSELF.
243             # Putting no expiry date means cookie expires when browser closes.
244             # Putting a date later than 2037 makes it wrap round, at least on Linux
245             # I will only be 62 by the time I need to redo this hack, so I should
246             # still be alive to fix it.
247 13         21 $expires = "Thu, 31-Dec-2037 22:22:22 GMT";
248             } elsif ( $args{cookie_expires} and $args{cookie_expires} eq "year" ) {
249 1         2 $expires = "+1y";
250             } else {
251 31         57 $args{cookie_expires} = "month";
252 31         129 $expires = "+1M";
253             }
254             # Supply 'default' values to stop CGI::Cookie complaining about
255             # uninitialised values. *Real* default should be applied before
256             # calling this method.
257             my $cookie = CGI::Cookie->new(
258             -name => $cookie_name,
259             -value => { user => $args{username} || "",
260             gclink => $args{include_geocache_link} || 0,
261             prevab => $args{preview_above_edit_box} || 0,
262             lltrad => $args{latlong_traditional} || 0,
263             omithlplks => $args{omit_help_links} || 0,
264             rcmined => $args{show_minor_edits_in_rc} || 0,
265             defedit => $args{default_edit_type} || "normal",
266             exp => $args{cookie_expires},
267             trackrc => $args{track_recent_changes_views} || 0,
268             gmaps => $args{display_google_maps} || 0,
269 45   100     1745 admin => $args{is_admin} || 0
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
270             },
271             -expires => $expires,
272             );
273 45         8128 return $cookie;
274             }
275              
276             =item B
277              
278             my %prefs = OpenGuides::CGI->get_prefs_from_cookie(
279             config => $config,
280             cookies => \@cookies
281             );
282              
283             Croaks unless an L object is supplied as C.
284             Returns default values for any parameter not specified in cookie.
285              
286             If C is provided, and includes a preferences cookie, this overrides
287             any preferences cookie submitted by the browser.
288              
289             =cut
290              
291             sub get_prefs_from_cookie {
292 1439     1439 1 7853 my ($class, %args) = @_;
293 1439 100       3332 my $config = $args{config} or croak "No config object supplied";
294 1438 100       4982 croak "Config object not an OpenGuides::Config"
295             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
296 1437         3284 my $cookie_name = $class->_get_cookie_name( config => $config );
297 1437         1526 my %cookies;
298 1437 100       3116 if ( my $cookies = $args{cookies} ) {
299 11 100       1033 if (ref $cookies ne 'ARRAY') {
300 10         21 $cookies = [ $cookies ];
301             }
302 11         14 %cookies = map { $_->name => $_ } @{ $cookies };
  11         27  
  11         18  
303             }
304 1437 50       2776 if ( !$cookies{$cookie_name} ) {
305 1437         6212 my %stored_cookies = CGI::Cookie->fetch;
306 1437         111902 $cookies{$cookie_name} = $stored_cookies{$cookie_name};
307             }
308 1437         1492 my %data;
309 1437 100       3056 if ( $cookies{$cookie_name} ) {
310 224         58535 %data = $cookies{$cookie_name}->value; # call ->value in list context
311             }
312              
313 1437         11108 my %long_forms = (
314             user => "username",
315             gclink => "include_geocache_link",
316             prevab => "preview_above_edit_box",
317             lltrad => "latlong_traditional",
318             omithlplks => "omit_help_links",
319             rcmined => "show_minor_edits_in_rc",
320             defedit => "default_edit_type",
321             exp => "cookie_expires",
322             trackrc => "track_recent_changes_views",
323             gmaps => "display_google_maps",
324             admin => "is_admin",
325             );
326 1437         3582 my %long_data = map { $long_forms{$_} => $data{$_} } keys %long_forms;
  15807         19885  
327              
328 1437         5364 return $class->get_prefs_from_hash( %long_data );
329             }
330              
331             sub get_prefs_from_hash {
332 1437     1437 0 3800 my ($class, %data) = @_;
333 1437         5986 my %defaults = (
334             username => "Anonymous",
335             include_geocache_link => 0,
336             preview_above_edit_box => 0,
337             latlong_traditional => 0,
338             omit_help_links => 0,
339             # This has been set to 1 to work around
340             # Wiki::Toolkit bug #41 - consider reverting this
341             # when that bug gets fixed
342             show_minor_edits_in_rc => 1,
343             default_edit_type => "normal",
344             cookie_expires => "never",
345             track_recent_changes_views => 0,
346             display_google_maps => 1,
347             is_admin => 0,
348             );
349 1437         1206 my %return;
350 1437         3500 foreach my $key ( keys %data ) {
351 15807 100       20518 $return{$key} = defined $data{$key} ? $data{$key} : $defaults{$key};
352             }
353              
354 1437         14881 return %return;
355             }
356              
357              
358             =item B
359              
360             my $cookie = OpenGuides::CGI->make_recent_changes_cookie(
361             config => $config,
362             );
363              
364             Makes a cookie that stores the time now as the time of the latest
365             visit to Recent Changes. Or, if C is specified and
366             true, makes a cookie with an expiration date in the past:
367              
368             my $cookie = OpenGuides::CGI->make_recent_changes_cookie(
369             config => $config,
370             clear_cookie => 1,
371             );
372              
373             =cut
374              
375             sub make_recent_changes_cookie {
376 19     19 1 1614 my ($class, %args) = @_;
377 19 100       214 my $config = $args{config} or croak "No config object supplied";
378 18 100       142 croak "Config object not an OpenGuides::Config"
379             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
380 17         52 my $cookie_name = $class->_get_rc_cookie_name( config => $config );
381             # See explanation of expiry date hack above in make_prefs_cookie.
382 17         23 my $expires;
383 17 50       36 if ( $args{clear_cookie} ) {
384 0         0 $expires = "-1M";
385             } else {
386 17         27 $expires = "Thu, 31-Dec-2037 22:22:22 GMT";
387             }
388 17         88 my $cookie = CGI::Cookie->new(
389             -name => $cookie_name,
390             -value => {
391             time => time,
392             },
393             -expires => $expires,
394             );
395 17         1805 return $cookie;
396             }
397              
398              
399             =item B
400              
401             my %prefs = OpenGuides::CGI->get_last_recent_changes_visit_from_cookie(
402             config => $config
403             );
404              
405             Croaks unless an L object is supplied as C.
406             Returns the time (as seconds since epoch) of the user's last visit to
407             Recent Changes.
408              
409             =cut
410              
411             sub get_last_recent_changes_visit_from_cookie {
412 13     13 1 2160 my ($class, %args) = @_;
413 13 100       108 my $config = $args{config} or croak "No config object supplied";
414 12 100       117 croak "Config object not an OpenGuides::Config"
415             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
416 11         32 my %cookies = CGI::Cookie->fetch;
417 11         3666 my $cookie_name = $class->_get_rc_cookie_name( config => $config );
418 11         17 my %data;
419 11 100       31 if ( $cookies{$cookie_name} ) {
420 6         513 %data = $cookies{$cookie_name}->value; # call ->value in list context
421             }
422 11         95 return $data{time};
423             }
424              
425              
426             sub _get_cookie_name {
427 1482     1482   2175 my ($class, %args) = @_;
428             my $site_name = $args{config}->site_name
429 1482 50       3897 or croak "No site name in config";
430 1482         12295 return $site_name . "_userprefs";
431             }
432              
433             sub _get_rc_cookie_name {
434 28     28   42 my ($class, %args) = @_;
435             my $site_name = $args{config}->site_name
436 28 50       68 or croak "No site name in config";
437 28         254 return $site_name . "_last_rc_visit";
438             }
439              
440             =item B
441              
442             my @dropdowns = OpenGuides::CGI->make_index_form_dropdowns (
443             guide => $guide,
444             selected => [
445             { type => "category", value => "pubs" },
446             { type => "locale", value => "holborn" },
447             ],
448             );
449             %tt_vars = ( %tt_vars, dropdowns => \@dropdowns );
450              
451             # In the template
452             [% FOREACH dropdown = dropdowns %]
453             [% dropdown.type.ucfirst | html %]:
454             [% dropdown.html %]
455            
456             [% END %]
457              
458             Makes HTML dropdown selects suitable for passing to an indexing template.
459              
460             The C argument is optional; if supplied, it gives default values
461             for the dropdowns. At least one category and one locale dropdown will be
462             returned; if no defaults are given for either then they'll default to
463             everything/everywhere.
464              
465             =cut
466              
467             sub make_index_form_dropdowns {
468 37     37 1 13686 my ( $class, %args ) = @_;
469 37 100       49 my @selected = @{$args{selected} || [] };
  37         139  
470 37         56 my $guide = $args{guide};
471 37         34 my @dropdowns;
472 37         40 my ( $got_cat, $got_loc );
473 37         63 foreach my $criterion ( @selected ) {
474 44   50     116 my $type = $criterion->{type} || "";
475 44   50     102 my $value = $criterion->{value} || "";
476 44         44 my $html;
477 44 100       122 if ( $type eq "category" ) {
    50          
478 19         77 $html = $class->_make_dropdown_html(
479             %$criterion, guide => $guide );
480 19         9425 $got_cat = 1;
481             } elsif ( $type eq "locale" ) {
482 25         99 $html = $class->_make_dropdown_html(
483             %$criterion, guide => $guide );
484 25         12553 $got_loc = 1;
485             } else {
486 0         0 warn "Unknown or missing criterion type: $type";
487             }
488 44 50       114 if ( $html ) {
489 44         163 push @dropdowns, { type => $type, html => $html };
490             }
491             }
492 37 100       93 if ( !$got_cat ) {
493 18         52 push @dropdowns, { type => "category", html =>
494             $class->_make_dropdown_html( type => "category", guide => $guide )
495             };
496             }
497 37 100       6794 if ( !$got_loc ) {
498 12         40 push @dropdowns, { type => "locale", html =>
499             $class->_make_dropdown_html( type => "locale", guide => $guide )
500             };
501             }
502             # List the category dropdowns before the locale dropdowns, for consistency.
503 37         5104 @dropdowns = sort { $a->{type} cmp $b->{type} } @dropdowns;
  37         88  
504 37         149 return @dropdowns;
505             }
506              
507             sub _make_dropdown_html {
508 74     74   196 my ( $class, %args ) = @_;
509 74         70 my ( $field_name, $any_label );
510              
511 74 100       164 if ( $args{type} eq "locale" ) {
512 37         53 $args{type} = "locales"; # hysterical raisins
513 37         44 $any_label = " -- anywhere -- ";
514 37         43 $field_name = "loc";
515             } else {
516 37         40 $any_label = " -- anything -- ";
517 37         46 $field_name = "cat";
518             }
519              
520             my @options = $args{guide}->wiki->list_nodes_by_metadata(
521             metadata_type => "category",
522             metadata_value => $args{type},
523 74         218 ignore_case => 1,
524             );
525 74         18032 @options = map { s/^Category //; s/^Locale //; $_ } @options;
  118         223  
  118         214  
  118         216  
526 74         116 my %labels = map { lc( $_ ) => $_ } @options;
  118         446  
527 74         267 my @values = sort keys %labels;
528 74   100     313 my $default = lc( $args{value} || "");
529              
530 74         315 my $q = CGI->new( "" );
531 74         9913 return $q->popup_menu( -name => $field_name,
532             -class => "$args{type}_index",
533             -values => [ "", @values ],
534             -labels => { "" => $any_label, %labels },
535             -default => $default );
536             }
537              
538             =back
539              
540             =head1 AUTHOR
541              
542             The OpenGuides Project (openguides-dev@lists.openguides.org)
543              
544             =head1 COPYRIGHT
545              
546             Copyright (C) 2003-2013 The OpenGuides Project. All Rights Reserved.
547              
548             This module is free software; you can redistribute it and/or modify it
549             under the same terms as Perl itself.
550              
551             =cut
552              
553             1;
554