source: trunk/lib/OpenGuides/Template.pm @ 782

Last change on this file since 782 was 782, checked in by Dominic Hargreaves, 15 years ago

Fix bug and release 0.54 (fixes #112)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.9 KB
Line 
1package OpenGuides::Template;
2
3use strict;
4use vars qw( $VERSION );
5$VERSION = '0.12';
6
7use Carp qw( croak );
8use CGI; # want to get rid of this and put the burden on the templates
9use Geography::NationalGrid;
10use Geography::NationalGrid::GB;
11use OpenGuides; # for $VERSION for template variable
12use OpenGuides::CGI;
13use Template;
14use URI::Escape;
15
16=head1 NAME
17
18OpenGuides::Template - Do Template Toolkit related stuff for OpenGuides applications.
19
20=head1 DESCRIPTION
21
22Does all the Template Toolkit stuff for OpenGuides.  Distributed and
23installed as part of the OpenGuides project, not intended for
24independent installation.  This documentation is probably only useful
25to OpenGuides developers.
26
27=head1 SYNOPSIS
28
29  use OpenGuides::Config;
30  use OpenGuides::Utils;
31  use OpenGuides::Template;
32
33  my $config = OpenGuides::Config->new( file => "wiki.conf" );
34  my $wiki = OpenGuides::Utils->make_wiki_object( config => $config );
35
36  print OpenGuides::Template->output( wiki     => $wiki,
37                                      config   => $config,
38                                      template => "node.tt",
39                                      vars     => { foo => "bar" }
40  );
41
42=head1 METHODS
43
44=over 4
45
46=item B<output>
47
48  print OpenGuides::Template->output( wiki         => $wiki,
49                                      config       => $config,
50                                      template     => "node.tt",
51                                      content_type => "text/html",
52                                      cookies      => $cookie,
53                                      vars         => {foo => "bar"}
54  );
55
56Returns everything you need to send to STDOUT, including the
57Content-Type: header. Croaks unless C<template> is supplied.
58
59The variables supplied in C<vars> are passed through to the template
60specified.  Additional Template Toolkit variables are automatically
61set and passed through as well, as described below.  B<Note:>
62variables set in C<vars> will over-ride any variables of the same name
63in the config object or the user cookies.
64
65=over
66
67=item * C<openguides_version>
68
69=item * C<site_name>
70
71=item * C<cgi_url>
72
73=item * C<full_cgi_url>
74
75=item * C<enable_page_deletion> (gets set to true or false - defaults to false)
76
77=item * C<contact_email>
78
79=item * C<stylesheet>
80
81=item * C<home_link>
82
83=item * C<formatting_rules_link> (unless C<omit_formatting_link> is set in user cookie)
84
85=item * C<navbar_on_home_page>
86
87=item * C<home_name>
88
89=item * C<gmaps_api_key>
90
91=item * C<licence_name>
92
93=item * C<licence_url>
94
95=item * C<licence_info_url>
96
97=back
98
99=over
100
101If C<node> is supplied:
102
103=item * C<node_name>
104
105=item * C<node_param> (the node name escaped for use in URLs)
106
107=back
108
109Content-Type: defaults to C<text/html> and is omitted if the
110C<content_type> arg is explicitly set to the blank string.
111
112=cut
113
114sub output {
115    my ($class, %args) = @_;
116    croak "No template supplied" unless $args{template};
117    my $config = $args{config} or croak "No config supplied";
118    my $template_path = $config->template_path;
119    my $custom_template_path = $config->custom_template_path || "";
120    my $tt = Template->new( { INCLUDE_PATH => "$custom_template_path:$template_path" } );
121
122    my $script_name  = $config->script_name;
123    my $script_url   = $config->script_url;
124    my $default_city = $config->default_city;
125   
126    # Check cookie to see if we need to set the formatting_rules_link.
127    my ($formatting_rules_link, $omit_help_links);
128    my $formatting_rules_node = $config->formatting_rules_node;
129    $formatting_rules_link = $config->formatting_rules_link;
130    my %cookie_data = OpenGuides::CGI->get_prefs_from_cookie(config=>$config);
131    if ( $cookie_data{omit_help_links} ) {
132        $omit_help_links = 1;
133    } else {
134        if (( $formatting_rules_node ) and !( $formatting_rules_link )){
135            $formatting_rules_link = $script_url . $script_name . "?"
136                                   . uri_escape($args{wiki}->formatter->node_name_to_node_param($formatting_rules_node));
137        }
138    }
139
140    my $enable_page_deletion = 0;
141    if ( $config->enable_page_deletion
142         and ( lc($config->enable_page_deletion) eq "y"
143               or $config->enable_page_deletion eq "1" )
144       ) {
145        $enable_page_deletion = 1;
146    }
147
148    my $tt_vars = {
149        site_name             => $config->site_name,
150        cgi_url               => $script_name,
151        full_cgi_url          => $script_url . $script_name,
152        contact_email         => $config->contact_email,
153        stylesheet            => $config->stylesheet_url,
154        home_link             => $script_url . $script_name,
155        home_name             => $config->home_name,
156        navbar_on_home_page   => $config->navbar_on_home_page,
157        omit_help_links       => $omit_help_links,
158        formatting_rules_link => $formatting_rules_link,
159        formatting_rules_node => $formatting_rules_node,
160        openguides_version    => $OpenGuides::VERSION,
161        enable_page_deletion  => $enable_page_deletion,
162        language              => $config->default_language,
163        default_city          => $default_city,
164        gmaps_api_key         => $config->gmaps_api_key,
165        licence_name          => $config->licence_name,
166        licence_url           => $config->licence_url,
167        licence_info_url      => $config->licence_info_url
168    };
169
170    if ($args{node}) {
171        $tt_vars->{node_name} = CGI->escapeHTML($args{node});
172        $tt_vars->{node_param} = CGI->escape($args{wiki}->formatter->node_name_to_node_param($args{node}));
173    }
174
175    # Now set further TT variables if explicitly supplied - do this last
176    # as these override auto-set ones.
177    $tt_vars = { %$tt_vars, %{ $args{vars} || {} } };
178
179    my $header = "";
180    unless ( defined $args{content_type} and $args{content_type} eq "" ) {
181        my $content_type;
182        if ($args{content_type}) {
183            $content_type = $args{content_type};
184        } else {
185            $content_type = "text/html";
186        }
187        $header = CGI::header( -type => $content_type, -cookie => $args{cookies} );
188    }
189
190    # vile hack
191    my %field_vars = OpenGuides::Template->extract_metadata_vars(
192                         wiki                 => $args{wiki},
193                         config               => $config,
194                         set_coord_field_vars => 1,
195                         metadata => {},
196                     );
197                     
198    $tt_vars = { %field_vars, %$tt_vars };
199
200    my $output;
201    $tt->process( $args{template}, $tt_vars, \$output );
202
203    $output ||= qq(<html><head><title>ERROR</title></head><body><p>
204                   Failed to process template: )
205              . $tt->error
206              . qq(</p></body></html>);
207
208    return $header . $output;
209}
210
211=item B<extract_metadata_vars>
212
213  my %node_data = $wiki->retrieve_node( "Home Page" );
214
215  my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
216                          wiki     => $wiki,
217                          config   => $config,
218                          metadata => $node_data{metadata}
219                      );
220
221  # -- or --
222
223  my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
224                          wiki     => $wiki,
225                          config   => $config,
226                          cgi_obj  => $q
227                      );
228
229  # -- then --
230
231  print OpenGuides::Template->output(
232            wiki     => $wiki,
233            config   => $config,
234            template => "node.tt",
235            vars     => { foo => "bar",
236                          %metadata_vars }
237        );
238
239Picks out things like categories, locales, phone number etc from
240EITHER the metadata hash returned by L<CGI::Wiki> OR the query
241parameters in a L<CGI> object, and packages them nicely for passing to
242templates or storing in L<CGI::Wiki> datastore.  If you supply both
243C<metadata> and C<cgi_obj> then C<metadata> will take precedence, but
244don't do that.
245
246The variables C<dist_field>, C<coord_field_1>, C<coord_field_1_name>,
247C<coord_field_1_value>, C<coord_field_2>, C<coord_field_2_name>, and
248C<coord_field_2_value>, which are used to create various forms, will
249only be set if I<either> C<metadata> is supplied I<or>
250C<set_coord_field_vars> is true, to prevent these values from being
251stored in the database on a node commit.
252
253=cut
254
255sub extract_metadata_vars {
256    my ($class, %args) = @_;
257    my %metadata = %{$args{metadata} || {} };
258    my $q = $args{cgi_obj};
259    my $formatter = $args{wiki}->formatter;
260    my $config = $args{config};
261    my $script_name = $config->script_name;
262
263    # Categories and locales are displayed as links in the page footer.
264    # We return these twice, as eg 'category' being a simple array of
265    # category names, but 'categories' being an array of hashrefs including
266    # a URL too.  This is ick.
267    my (@catlist, @loclist);
268    if ( $args{metadata} ) {
269        @catlist = @{ $metadata{category} || [] };
270        @loclist = @{ $metadata{locale}   || [] };
271    } else {
272        my $categories_text = $q->param('categories');
273        my $locales_text    = $q->param('locales');
274
275        # Basic sanity-checking. Probably lives elsewhere.
276        $categories_text =~ s/</&lt;/g;
277        $categories_text =~ s/>/&gt;/g;
278        $locales_text =~ s/</&lt;/g;
279        $locales_text =~ s/>/&gt;/g;
280
281        @catlist = sort grep { s/^\s+//; s/\s+$//; $_; } # trim lead/trail space
282                        split("\r\n", $categories_text);
283        @loclist = sort grep { s/^\s+//; s/\s+$//; $_; } # trim lead/trail space
284                        split("\r\n", $locales_text);
285    }
286
287    my @categories = map { { name => $_,
288                             url  => "$script_name?Category_"
289            . uri_escape($formatter->node_name_to_node_param($_)) } } @catlist;
290
291    my @locales    = map { { name => $_,
292                             url  => "$script_name?Locale_"
293            . uri_escape($formatter->node_name_to_node_param($_)) } } @loclist;
294
295    # The 'website' attribute might contain a URL so we wiki-format it here
296    # rather than just CGI::escapeHTMLing it all in the template.
297    my $website = $args{metadata} ? $metadata{website}[0]
298                                  : $q->param("website");
299    my $formatted_website_text = "";
300    if ( $website && $website ne "http://" ) {
301        $formatted_website_text = $class->format_website_text(
302            formatter => $formatter,
303            text      => $website
304        );
305    }
306
307    my $hours_text = $args{metadata} ? $metadata{opening_hours_text}[0]
308                                    : $q->param("hours_text");
309
310    my $summary = $args{metadata} ? $metadata{summary}[0]
311                                  : $q->param("summary");
312                                 
313    my %vars = (
314        categories             => \@categories,
315        locales                => \@locales,
316        category               => \@catlist,
317        locale                 => \@loclist,
318        formatted_website_text => $formatted_website_text,
319        hours_text             => $hours_text,
320        summary                => $summary,
321    );
322
323    if (exists $metadata{source}) {
324        ($vars{source_site}) = $metadata{source}[0] =~ /^(.*?)(?:\?|$)/;
325    }
326   
327    if ( $args{metadata} ) {
328        foreach my $var ( qw( phone fax address postcode os_x os_y osie_x
329                              osie_y latitude longitude map_link website
330                              summary) ) {
331            $vars{$var} = $metadata{$var}[0];
332        }
333        # Data for the distance search forms on the node display.
334        my $geo_handler = $config->geo_handler;
335        if ( $geo_handler == 1 ) {
336            %vars = (
337                        %vars,
338                        coord_field_1       => "os_x",
339                        coord_field_2       => "os_y",
340                        dist_field          => "os_dist",
341                        coord_field_1_name  => "OS X coordinate",
342                        coord_field_2_name  => "OS Y coordinate",
343                        coord_field_1_value => $metadata{os_x}[0],
344                        coord_field_2_value => $metadata{os_y}[0],
345                    );
346        } elsif ( $geo_handler == 2 ) {
347            %vars = (
348                        %vars,
349                        coord_field_1       => "osie_x",
350                        coord_field_2       => "osie_y",
351                        dist_field          => "osie_dist",
352                        coord_field_1_name  =>"Irish National Grid X coordinate",
353                        coord_field_2_name  =>"Irish National Grid Y coordinate",
354                        coord_field_1_value => $metadata{osie_x}[0],
355                        coord_field_2_value => $metadata{osie_y}[0],
356                    );
357        } else {
358            %vars = (
359                        %vars,
360                        coord_field_1       => "latitude",
361                        coord_field_2       => "longitude",
362                        dist_field          => "latlong_dist",
363                        coord_field_1_name  => "Latitude (decimal)",
364                        coord_field_2_name  => "Longitude (decimal)",
365                        coord_field_1_value => $metadata{latitude}[0],
366                        coord_field_2_value => $metadata{longitude}[0],
367                    );
368        }
369    } else {
370        foreach my $var ( qw( phone fax address postcode map_link website summary) ) {
371            $vars{$var} = $q->param($var);
372        }
373
374        my $geo_handler = $config->geo_handler;
375        if ( $geo_handler == 1 ) {
376            require Geography::NationalGrid::GB;
377            my $os_x   = $q->param("os_x");
378            my $os_y   = $q->param("os_y");
379            my $lat    = $q->param("latitude");
380            my $long   = $q->param("longitude");
381
382            # Trim whitespace - trailing whitespace buggers up the
383            # integerification by postgres and it's an easy mistake to
384            # make when typing into a form.
385            $os_x =~ s/\s+//g;
386            $os_y =~ s/\s+//g;
387
388            # If we were sent x and y, work out lat/long; and vice versa.
389            if ( $os_x && $os_y ) {
390                my $point = Geography::NationalGrid::GB->new( Easting =>$os_x,
391                                     Northing=>$os_y);
392                $lat  = sprintf("%.6f", $point->latitude);
393                $long = sprintf("%.6f", $point->longitude);
394            } elsif ( $lat && $long ) {
395                my $point = Geography::NationalGrid::GB->new(Latitude  => $lat,
396                                                             Longitude => $long);
397                $os_x = $point->easting;
398                $os_y = $point->northing;
399            }
400           
401            if ( $os_x && $os_y ) {
402                %vars = (
403                            %vars,
404                            latitude  => $lat,
405                            longitude => $long,
406                            os_x      => $os_x,
407                            os_y      => $os_y,
408                        );
409            }
410            if ( $args{set_coord_field_vars} ) {
411                %vars = (
412                            %vars,
413                            coord_field_1       => "os_x",
414                            coord_field_2       => "os_y",
415                            dist_field          => "os_dist",
416                            coord_field_1_name  => "OS X coordinate",
417                            coord_field_2_name  => "OS Y coordinate",
418                            coord_field_1_value => $os_x,
419                            coord_field_2_value => $os_y,
420                        );
421            }
422        } elsif ( $geo_handler == 2 ) {
423            require Geography::NationalGrid::IE;
424            my $osie_x = $q->param("osie_x");
425            my $osie_y = $q->param("osie_y");
426            my $lat    = $q->param("latitude");
427            my $long   = $q->param("longitude");
428
429            # Trim whitespace.
430            $osie_x =~ s/\s+//g;
431            $osie_y =~ s/\s+//g;
432
433            # If we were sent x and y, work out lat/long; and vice versa.
434            if ( $osie_x && $osie_y ) {
435                my $point = Geography::NationalGrid::IE->new(Easting=>$osie_x,
436                                   Northing=>$osie_y);
437                $lat = sprintf("%.6f", $point->latitude);
438                $long = sprintf("%.6f", $point->longitude);
439            } elsif ( $lat && $long ) {
440                my $point = Geography::NationalGrid::GB->new(Latitude  => $lat,
441                                                             Longitude => $long);
442                $osie_x = $point->easting;
443                $osie_y = $point->northing;
444            }
445            if ( $osie_x && $osie_y ) {
446                %vars = (
447                            %vars,
448                            latitude  => $lat,
449                            longitude => $long,
450                            osie_x    => $osie_x,
451                            osie_y    => $osie_y,
452                        );
453            }
454            if ( $args{set_coord_field_vars} ) {
455                %vars = (
456                            %vars,
457                            coord_field_1       => "osie_x",
458                            coord_field_2       => "osie_y",
459                            dist_field          => "osie_dist",
460                            coord_field_1_name  => "Irish National Grid X coordinate",
461                            coord_field_2_name  => "Irish National Grid Y coordinate",
462                            coord_field_1_value => $osie_x,
463                            coord_field_2_value => $osie_y,
464                        );
465            }
466        } elsif ( $geo_handler == 3 ) {
467            require Geo::Coordinates::UTM;
468            my $lat    = $q->param("latitude");
469            my $long   = $q->param("longitude");
470           
471            if ( $lat && $long ) {
472                # Trim whitespace.
473                $lat =~ s/\s+//g;
474                $long =~ s/\s+//g;
475                my ($zone, $easting, $northing) =
476                 Geo::Coordinates::UTM::latlon_to_utm( $config->ellipsoid,
477                                                       $lat, $long );
478                $easting  =~ s/\..*//; # chop off decimal places
479                $northing =~ s/\..*//; # - metre accuracy enough
480                %vars = (
481                            %vars,
482                            latitude  => $lat,
483                            longitude => $long,
484                            easting   => $easting,
485                            northing  => $northing,
486                        );
487             }
488             if ( $args{set_coord_field_vars} ) {
489                %vars = (
490                            %vars,
491                            coord_field_1       => "latitude",
492                            coord_field_2       => "longitude",
493                            dist_field          => "latlong_dist",
494                            coord_field_1_name  => "Latitude (decimal)",
495                            coord_field_2_name  => "Longitude (decimal)",
496                            coord_field_1_value => $lat,
497                            coord_field_2_value => $long,
498                        );
499             }
500        }
501    }
502
503    # Check whether we need to munge lat and long.
504    # Store them unmunged as well so commit_node can get hold of them.
505    my %prefs = OpenGuides::CGI->get_prefs_from_cookie( config => $config );
506    if ( $prefs{latlong_traditional} ) {
507        foreach my $var ( qw( latitude longitude ) ) {
508            next unless defined $vars{$var};
509            $vars{$var."_unmunged"} = $vars{$var};
510            $vars{$var} = Geography::NationalGrid->deg2string($vars{$var});
511        }
512    }
513
514    return %vars;
515}
516
517sub format_website_text {
518    my ($class, %args) = @_;
519    my ($formatter, $text) = @args{ qw( formatter text ) };
520    my $formatted = $formatter->format($text);
521
522    # Strip out paragraph markers put in by formatter since we want this
523    # to be a single string to put in a <ul>.
524    $formatted =~ s/<p>//g;
525    $formatted =~ s/<\/p>//g;
526
527    return $formatted;
528}
529
530
531=back
532
533=head1 AUTHOR
534
535The OpenGuides Project (openguides-dev@openguides.org)
536
537=head1 COPYRIGHT
538
539  Copyright (C) 2003-2005 The OpenGuides Project.  All Rights Reserved.
540
541This module is free software; you can redistribute it and/or modify it
542under the same terms as Perl itself.
543
544=cut
545
5461;
Note: See TracBrowser for help on using the repository browser.