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

Last change on this file since 868 was 868, checked in by nick, 15 years ago

Support the config flag http_charset, which sets a http charset header on content type. (Closes #141)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.3 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        http_charset          => $config->http_charset,
164        default_city          => $default_city,
165        gmaps_api_key         => $config->gmaps_api_key,
166        licence_name          => $config->licence_name,
167        licence_url           => $config->licence_url,
168        licence_info_url      => $config->licence_info_url
169    };
170
171    if ($args{node}) {
172        $tt_vars->{node_name} = CGI->escapeHTML($args{node});
173        $tt_vars->{node_param} = CGI->escape($args{wiki}->formatter->node_name_to_node_param($args{node}));
174    }
175
176    # Now set further TT variables if explicitly supplied - do this last
177    # as these override auto-set ones.
178    $tt_vars = { %$tt_vars, %{ $args{vars} || {} } };
179
180    my $header = "";
181    unless ( defined $args{content_type} and $args{content_type} eq "" ) {
182        my $content_type;
183        if ($args{content_type}) {
184            $content_type = $args{content_type};
185        } else {
186            $content_type = "text/html";
187        }
188        if ($tt_vars->{http_charset}) {
189            $content_type .= "; charset=".$tt_vars->{http_charset};
190        }
191        $header = CGI::header( -type => $content_type, -cookie => $args{cookies} );
192    }
193
194    # vile hack
195    my %field_vars = OpenGuides::Template->extract_metadata_vars(
196                         wiki                 => $args{wiki},
197                         config               => $config,
198                         set_coord_field_vars => 1,
199                         metadata => {},
200                     );
201                     
202    $tt_vars = { %field_vars, %$tt_vars };
203
204    my $output;
205    $tt->process( $args{template}, $tt_vars, \$output );
206
207    $output ||= qq(<html><head><title>ERROR</title></head><body><p>
208                   Failed to process template: )
209              . $tt->error
210              . qq(</p></body></html>);
211
212    return $header . $output;
213}
214
215=item B<extract_metadata_vars>
216
217  my %node_data = $wiki->retrieve_node( "Home Page" );
218
219  my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
220                          wiki     => $wiki,
221                          config   => $config,
222                          metadata => $node_data{metadata}
223                      );
224
225  # -- or --
226
227  my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
228                          wiki     => $wiki,
229                          config   => $config,
230                          cgi_obj  => $q
231                      );
232
233  # -- then --
234
235  print OpenGuides::Template->output(
236            wiki     => $wiki,
237            config   => $config,
238            template => "node.tt",
239            vars     => { foo => "bar",
240                          %metadata_vars }
241        );
242
243Picks out things like categories, locales, phone number etc from
244EITHER the metadata hash returned by L<Wiki::Toolkit> OR the query
245parameters in a L<CGI> object, and packages them nicely for passing to
246templates or storing in L<Wiki::Toolkit> datastore.  If you supply both
247C<metadata> and C<cgi_obj> then C<metadata> will take precedence, but
248don't do that.
249
250The variables C<dist_field>, C<coord_field_1>, C<coord_field_1_name>,
251C<coord_field_1_value>, C<coord_field_2>, C<coord_field_2_name>, and
252C<coord_field_2_value>, which are used to create various forms, will
253only be set if I<either> C<metadata> is supplied I<or>
254C<set_coord_field_vars> is true, to prevent these values from being
255stored in the database on a node commit.
256
257=cut
258
259sub extract_metadata_vars {
260    my ($class, %args) = @_;
261    my %metadata = %{$args{metadata} || {} };
262    my $q = $args{cgi_obj};
263    my $formatter = $args{wiki}->formatter;
264    my $config = $args{config};
265    my $script_name = $config->script_name;
266
267    # Categories and locales are displayed as links in the page footer.
268    # We return these twice, as eg 'category' being a simple array of
269    # category names, but 'categories' being an array of hashrefs including
270    # a URL too.  This is ick.
271    my (@catlist, @loclist);
272    if ( $args{metadata} ) {
273        @catlist = @{ $metadata{category} || [] };
274        @loclist = @{ $metadata{locale}   || [] };
275    } else {
276        my $categories_text = $q->param('categories');
277        my $locales_text    = $q->param('locales');
278
279        # Basic sanity-checking. Probably lives elsewhere.
280        $categories_text =~ s/</&lt;/g;
281        $categories_text =~ s/>/&gt;/g;
282        $locales_text =~ s/</&lt;/g;
283        $locales_text =~ s/>/&gt;/g;
284
285        @catlist = sort grep { s/^\s+//; s/\s+$//; $_; } # trim lead/trail space
286                        split("\r\n", $categories_text);
287        @loclist = sort grep { s/^\s+//; s/\s+$//; $_; } # trim lead/trail space
288                        split("\r\n", $locales_text);
289    }
290
291    my @categories = map { { name => $_,
292                             url  => "$script_name?Category_"
293            . uri_escape($formatter->node_name_to_node_param($_)) } } @catlist;
294
295    my @locales    = map { { name => $_,
296                             url  => "$script_name?Locale_"
297            . uri_escape($formatter->node_name_to_node_param($_)) } } @loclist;
298
299    # The 'website' attribute might contain a URL so we wiki-format it here
300    # rather than just CGI::escapeHTMLing it all in the template.
301    my $website = $args{metadata} ? $metadata{website}[0]
302                                  : $q->param("website");
303    my $formatted_website_text = "";
304    if ( $website && $website ne "http://" ) {
305        $formatted_website_text = $class->format_website_text(
306            formatter => $formatter,
307            text      => $website
308        );
309    }
310
311    my $hours_text = $args{metadata} ? $metadata{opening_hours_text}[0]
312                                    : $q->param("hours_text");
313
314    my $summary = $args{metadata} ? $metadata{summary}[0]
315                                  : $q->param("summary");
316                                 
317    my %vars = (
318        categories             => \@categories,
319        locales                => \@locales,
320        category               => \@catlist,
321        locale                 => \@loclist,
322        formatted_website_text => $formatted_website_text,
323        hours_text             => $hours_text,
324        summary                => $summary,
325    );
326
327    my $node_image = $args{metadata} ? $metadata{node_image}[0]
328                                     : $q->param("node_image");
329    if ($config->enable_node_image && $node_image) {
330        $vars{node_image} = $node_image;
331    }
332
333    if (exists $metadata{source}) {
334        ($vars{source_site}) = $metadata{source}[0] =~ /^(.*?)(?:\?|$)/;
335    }
336   
337    if ( $args{metadata} ) {
338        foreach my $var ( qw( phone fax address postcode os_x os_y osie_x
339                              osie_y latitude longitude map_link website
340                              summary) ) {
341            $vars{$var} = $metadata{$var}[0];
342        }
343        # Data for the distance search forms on the node display.
344        my $geo_handler = $config->geo_handler;
345        if ( $geo_handler == 1 ) {
346            %vars = (
347                        %vars,
348                        coord_field_1       => "os_x",
349                        coord_field_2       => "os_y",
350                        dist_field          => "os_dist",
351                        coord_field_1_name  => "OS X coordinate",
352                        coord_field_2_name  => "OS Y coordinate",
353                        coord_field_1_value => $metadata{os_x}[0],
354                        coord_field_2_value => $metadata{os_y}[0],
355                    );
356        } elsif ( $geo_handler == 2 ) {
357            %vars = (
358                        %vars,
359                        coord_field_1       => "osie_x",
360                        coord_field_2       => "osie_y",
361                        dist_field          => "osie_dist",
362                        coord_field_1_name  =>"Irish National Grid X coordinate",
363                        coord_field_2_name  =>"Irish National Grid Y coordinate",
364                        coord_field_1_value => $metadata{osie_x}[0],
365                        coord_field_2_value => $metadata{osie_y}[0],
366                    );
367        } else {
368            %vars = (
369                        %vars,
370                        coord_field_1       => "latitude",
371                        coord_field_2       => "longitude",
372                        dist_field          => "latlong_dist",
373                        coord_field_1_name  => "Latitude (decimal)",
374                        coord_field_2_name  => "Longitude (decimal)",
375                        coord_field_1_value => $metadata{latitude}[0],
376                        coord_field_2_value => $metadata{longitude}[0],
377                    );
378        }
379    } else {
380        foreach my $var ( qw( phone fax address postcode map_link website summary) ) {
381            $vars{$var} = $q->param($var);
382        }
383
384        my $geo_handler = $config->geo_handler;
385        if ( $geo_handler == 1 ) {
386            require Geography::NationalGrid::GB;
387            my $os_x   = $q->param("os_x");
388            my $os_y   = $q->param("os_y");
389            my $lat    = $q->param("latitude");
390            my $long   = $q->param("longitude");
391
392            # Trim whitespace - trailing whitespace buggers up the
393            # integerification by postgres and it's an easy mistake to
394            # make when typing into a form.
395            $os_x =~ s/\s+//g;
396            $os_y =~ s/\s+//g;
397
398            # If we were sent x and y, work out lat/long; and vice versa.
399            if ( $os_x && $os_y ) {
400                my $point = Geography::NationalGrid::GB->new( Easting =>$os_x,
401                                     Northing=>$os_y);
402                $lat  = sprintf("%.6f", $point->latitude);
403                $long = sprintf("%.6f", $point->longitude);
404            } elsif ( $lat && $long ) {
405                my $point = Geography::NationalGrid::GB->new(Latitude  => $lat,
406                                                             Longitude => $long);
407                $os_x = $point->easting;
408                $os_y = $point->northing;
409            }
410           
411            if ( $os_x && $os_y ) {
412                %vars = (
413                            %vars,
414                            latitude  => $lat,
415                            longitude => $long,
416                            os_x      => $os_x,
417                            os_y      => $os_y,
418                        );
419            }
420            if ( $args{set_coord_field_vars} ) {
421                %vars = (
422                            %vars,
423                            coord_field_1       => "os_x",
424                            coord_field_2       => "os_y",
425                            dist_field          => "os_dist",
426                            coord_field_1_name  => "OS X coordinate",
427                            coord_field_2_name  => "OS Y coordinate",
428                            coord_field_1_value => $os_x,
429                            coord_field_2_value => $os_y,
430                        );
431            }
432        } elsif ( $geo_handler == 2 ) {
433            require Geography::NationalGrid::IE;
434            my $osie_x = $q->param("osie_x");
435            my $osie_y = $q->param("osie_y");
436            my $lat    = $q->param("latitude");
437            my $long   = $q->param("longitude");
438
439            # Trim whitespace.
440            $osie_x =~ s/\s+//g;
441            $osie_y =~ s/\s+//g;
442
443            # If we were sent x and y, work out lat/long; and vice versa.
444            if ( $osie_x && $osie_y ) {
445                my $point = Geography::NationalGrid::IE->new(Easting=>$osie_x,
446                                   Northing=>$osie_y);
447                $lat = sprintf("%.6f", $point->latitude);
448                $long = sprintf("%.6f", $point->longitude);
449            } elsif ( $lat && $long ) {
450                my $point = Geography::NationalGrid::GB->new(Latitude  => $lat,
451                                                             Longitude => $long);
452                $osie_x = $point->easting;
453                $osie_y = $point->northing;
454            }
455            if ( $osie_x && $osie_y ) {
456                %vars = (
457                            %vars,
458                            latitude  => $lat,
459                            longitude => $long,
460                            osie_x    => $osie_x,
461                            osie_y    => $osie_y,
462                        );
463            }
464            if ( $args{set_coord_field_vars} ) {
465                %vars = (
466                            %vars,
467                            coord_field_1       => "osie_x",
468                            coord_field_2       => "osie_y",
469                            dist_field          => "osie_dist",
470                            coord_field_1_name  => "Irish National Grid X coordinate",
471                            coord_field_2_name  => "Irish National Grid Y coordinate",
472                            coord_field_1_value => $osie_x,
473                            coord_field_2_value => $osie_y,
474                        );
475            }
476        } elsif ( $geo_handler == 3 ) {
477            require Geo::Coordinates::UTM;
478            my $lat    = $q->param("latitude");
479            my $long   = $q->param("longitude");
480           
481            if ( $lat && $long ) {
482                # Trim whitespace.
483                $lat =~ s/\s+//g;
484                $long =~ s/\s+//g;
485                my ($zone, $easting, $northing) =
486                 Geo::Coordinates::UTM::latlon_to_utm( $config->ellipsoid,
487                                                       $lat, $long );
488                $easting  =~ s/\..*//; # chop off decimal places
489                $northing =~ s/\..*//; # - metre accuracy enough
490                %vars = (
491                            %vars,
492                            latitude  => $lat,
493                            longitude => $long,
494                            easting   => $easting,
495                            northing  => $northing,
496                        );
497             }
498             if ( $args{set_coord_field_vars} ) {
499                %vars = (
500                            %vars,
501                            coord_field_1       => "latitude",
502                            coord_field_2       => "longitude",
503                            dist_field          => "latlong_dist",
504                            coord_field_1_name  => "Latitude (decimal)",
505                            coord_field_2_name  => "Longitude (decimal)",
506                            coord_field_1_value => $lat,
507                            coord_field_2_value => $long,
508                        );
509             }
510        }
511    }
512
513    # Check whether we need to munge lat and long.
514    # Store them unmunged as well so commit_node can get hold of them.
515    my %prefs = OpenGuides::CGI->get_prefs_from_cookie( config => $config );
516    if ( $prefs{latlong_traditional} ) {
517        foreach my $var ( qw( latitude longitude ) ) {
518            next unless defined $vars{$var};
519            $vars{$var."_unmunged"} = $vars{$var};
520            $vars{$var} = Geography::NationalGrid->deg2string($vars{$var});
521        }
522    }
523
524    return %vars;
525}
526
527sub format_website_text {
528    my ($class, %args) = @_;
529    my ($formatter, $text) = @args{ qw( formatter text ) };
530    my $formatted = $formatter->format($text);
531
532    # Strip out paragraph markers put in by formatter since we want this
533    # to be a single string to put in a <ul>.
534    $formatted =~ s/<p>//g;
535    $formatted =~ s/<\/p>//g;
536
537    return $formatted;
538}
539
540
541=back
542
543=head1 AUTHOR
544
545The OpenGuides Project (openguides-dev@openguides.org)
546
547=head1 COPYRIGHT
548
549  Copyright (C) 2003-2005 The OpenGuides Project.  All Rights Reserved.
550
551This module is free software; you can redistribute it and/or modify it
552under the same terms as Perl itself.
553
554=cut
555
5561;
Note: See TracBrowser for help on using the repository browser.