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   1694 use strict;
  93         177  
  93         3913  
3 93     93   530 use vars qw( $VERSION );
  93         240  
  93         4295  
4             $VERSION = '0.12';
5              
6 93     93   464 use Carp qw( croak );
  93         172  
  93         4309  
7 93     93   1824 use CGI;
  93         32613  
  93         1288  
8 93     93   77642 use CGI::Cookie;
  93         436528  
  93         191755  
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 3818 my ($class, %args) = @_;
99 52 50       251 my $wiki = $args{wiki} or croak "No wiki supplied";
100 52 50       195 croak "wiki not a Wiki::Toolkit object"
101             unless UNIVERSAL::isa( $wiki, "Wiki::Toolkit" );
102 52 50       131 my $q = $args{cgi_obj} or croak "No cgi_obj supplied";
103 52 50       161 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     214 my $param = $q->param( "id" )
109             || $q->param( "title" )
110             || join( " ", $q->multi_param( "keywords" ) )
111             || "";
112 52         2299 $param =~ s/%20/ /g;
113 52         102 $param =~ s/\+/ /g;
114 52         145 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 5324 my ($class, %args) = @_;
139             # The next call will validate our args for us and croak if necessary.
140 26         83 my $param = $class->extract_node_param( %args );
141              
142             # Sometimes people type spaces instead of underscores.
143 26         72 $param =~ s/ /_/g;
144 26         48 $param =~ s/%20/_/g;
145 26         38 $param =~ s/\+/_/g;
146              
147 26         95 my $formatter = $args{wiki}->formatter;
148 26         161 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 12561 my ($class, %args) = @_;
170 17         30 my $wiki = $args{wiki};
171 17         31 my $q = $args{cgi_obj};
172              
173 17         49 my $name = $class->extract_node_name( wiki => $wiki, cgi_obj => $q );
174 17         168 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     73 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     93 if ( ( $name !~ / / ) || ( $name ne $param ) ) {
184 6         22 return 0;
185             }
186              
187             # Make a new CGI object to manipulate, to avoid action-at-a-distance.
188 11         43 my $new_q = CGI->new( $q );
189 11         4440 my $formatter = $wiki->formatter;
190 11         65 my $real_param = $formatter->node_name_to_node_param( $name );
191              
192 11 100       301 if ( $q->param( "id" ) ) {
    100          
193 4         97 $new_q->param( -name => "id", -value => $real_param );
194             } elsif ( $q->param( "title" ) ) {
195 4         164 $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         107 $new_q->delete_all();
201 3         234 $new_q->param( -name => "id", -value => $real_param );
202             }
203              
204 11         1107 my $url = $new_q->self_url;
205              
206             # Escaped commas are ugly.
207 11         4927 $url =~ s/%2C/,/g;
208 11         81 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 272007 my ($class, %args) = @_;
236 47 100       427 my $config = $args{config} or croak "No config object supplied";
237 46 100       651 croak "Config object not an OpenGuides::Config"
238             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
239 45         231 my $cookie_name = $class->_get_cookie_name( config => $config );
240 45         108 my $expires;
241 45 100 100     550 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         36 $expires = "Thu, 31-Dec-2037 22:22:22 GMT";
248             } elsif ( $args{cookie_expires} and $args{cookie_expires} eq "year" ) {
249 1         3 $expires = "+1y";
250             } else {
251 31         96 $args{cookie_expires} = "month";
252 31         381 $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     2676 admin => $args{is_admin} || 0
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
      100        
270             },
271             -expires => $expires,
272             );
273 45         11573 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 1434     1434 1 9350 my ($class, %args) = @_;
293 1434 100       4829 my $config = $args{config} or croak "No config object supplied";
294 1433 100       6614 croak "Config object not an OpenGuides::Config"
295             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
296 1432         4663 my $cookie_name = $class->_get_cookie_name( config => $config );
297 1432         2318 my %cookies;
298 1432 100       4235 if ( my $cookies = $args{cookies} ) {
299 11 100       1698 if (ref $cookies ne 'ARRAY') {
300 10         30 $cookies = [ $cookies ];
301             }
302 11         28 %cookies = map { $_->name => $_ } @{ $cookies };
  11         41  
  11         25  
303             }
304 1432 50       4192 if ( !$cookies{$cookie_name} ) {
305 1432         8776 my %stored_cookies = CGI::Cookie->fetch;
306 1432         187847 $cookies{$cookie_name} = $stored_cookies{$cookie_name};
307             }
308 1432         2088 my %data;
309 1432 100       4295 if ( $cookies{$cookie_name} ) {
310 224         109459 %data = $cookies{$cookie_name}->value; # call ->value in list context
311             }
312              
313 1432         17000 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 1432         5276 my %long_data = map { $long_forms{$_} => $data{$_} } keys %long_forms;
  15752         37186  
327              
328 1432         8897 return $class->get_prefs_from_hash( %long_data );
329             }
330              
331             sub get_prefs_from_hash {
332 1432     1432 0 6470 my ($class, %data) = @_;
333 1432         9272 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 1432         2213 my %return;
350 1432         4891 foreach my $key ( keys %data ) {
351 15752 100       37969 $return{$key} = defined $data{$key} ? $data{$key} : $defaults{$key};
352             }
353              
354 1432         24643 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 1673 my ($class, %args) = @_;
377 19 100       269 my $config = $args{config} or croak "No config object supplied";
378 18 100       190 croak "Config object not an OpenGuides::Config"
379             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
380 17         58 my $cookie_name = $class->_get_rc_cookie_name( config => $config );
381             # See explanation of expiry date hack above in make_prefs_cookie.
382 17         34 my $expires;
383 17 50       53 if ( $args{clear_cookie} ) {
384 0         0 $expires = "-1M";
385             } else {
386 17         42 $expires = "Thu, 31-Dec-2037 22:22:22 GMT";
387             }
388 17         181 my $cookie = CGI::Cookie->new(
389             -name => $cookie_name,
390             -value => {
391             time => time,
392             },
393             -expires => $expires,
394             );
395 17         2757 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 2168 my ($class, %args) = @_;
413 13 100       153 my $config = $args{config} or croak "No config object supplied";
414 12 100       152 croak "Config object not an OpenGuides::Config"
415             unless UNIVERSAL::isa( $config, "OpenGuides::Config" );
416 11         43 my %cookies = CGI::Cookie->fetch;
417 11         6428 my $cookie_name = $class->_get_rc_cookie_name( config => $config );
418 11         25 my %data;
419 11 100       41 if ( $cookies{$cookie_name} ) {
420 6         877 %data = $cookies{$cookie_name}->value; # call ->value in list context
421             }
422 11         135 return $data{time};
423             }
424              
425              
426             sub _get_cookie_name {
427 1477     1477   3717 my ($class, %args) = @_;
428             my $site_name = $args{config}->site_name
429 1477 50       5973 or croak "No site name in config";
430 1477         18457 return $site_name . "_userprefs";
431             }
432              
433             sub _get_rc_cookie_name {
434 28     28   70 my ($class, %args) = @_;
435             my $site_name = $args{config}->site_name
436 28 50       97 or croak "No site name in config";
437 28         355 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 21945 my ( $class, %args ) = @_;
469 37 100       71 my @selected = @{$args{selected} || [] };
  37         197  
470 37         81 my $guide = $args{guide};
471 37         67 my @dropdowns;
472 37         59 my ( $got_cat, $got_loc );
473 37         93 foreach my $criterion ( @selected ) {
474 44   50     150 my $type = $criterion->{type} || "";
475 44   50     139 my $value = $criterion->{value} || "";
476 44         61 my $html;
477 44 100       168 if ( $type eq "category" ) {
    50          
478 19         95 $html = $class->_make_dropdown_html(
479             %$criterion, guide => $guide );
480 19         13099 $got_cat = 1;
481             } elsif ( $type eq "locale" ) {
482 25         138 $html = $class->_make_dropdown_html(
483             %$criterion, guide => $guide );
484 25         18498 $got_loc = 1;
485             } else {
486 0         0 warn "Unknown or missing criterion type: $type";
487             }
488 44 50       147 if ( $html ) {
489 44         213 push @dropdowns, { type => $type, html => $html };
490             }
491             }
492 37 100       132 if ( !$got_cat ) {
493 18         70 push @dropdowns, { type => "category", html =>
494             $class->_make_dropdown_html( type => "category", guide => $guide )
495             };
496             }
497 37 100       10432 if ( !$got_loc ) {
498 12         56 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         7757 @dropdowns = sort { $a->{type} cmp $b->{type} } @dropdowns;
  37         126  
504 37         209 return @dropdowns;
505             }
506              
507             sub _make_dropdown_html {
508 74     74   261 my ( $class, %args ) = @_;
509 74         110 my ( $field_name, $any_label );
510              
511 74 100       225 if ( $args{type} eq "locale" ) {
512 37         87 $args{type} = "locales"; # hysterical raisins
513 37         74 $any_label = " -- anywhere -- ";
514 37         62 $field_name = "loc";
515             } else {
516 37         62 $any_label = " -- anything -- ";
517 37         65 $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         294 ignore_case => 1,
524             );
525 74         25193 @options = map { s/^Category //; s/^Locale //; $_ } @options;
  118         299  
  118         302  
  118         328  
526 74         165 my %labels = map { lc( $_ ) => $_ } @options;
  118         420  
527 74         335 my @values = sort keys %labels;
528 74   100     372 my $default = lc( $args{value} || "");
529              
530 74         424 my $q = CGI->new( "" );
531 74         14830 return $q->popup_menu( -name => $field_name,
532             -values => [ "", @values ],
533             -labels => { "" => $any_label, %labels },
534             -default => $default );
535             }
536              
537             =back
538              
539             =head1 AUTHOR
540              
541             The OpenGuides Project (openguides-dev@lists.openguides.org)
542              
543             =head1 COPYRIGHT
544              
545             Copyright (C) 2003-2013 The OpenGuides Project. All Rights Reserved.
546              
547             This module is free software; you can redistribute it and/or modify it
548             under the same terms as Perl itself.
549              
550             =cut
551              
552             1;
553