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

Last change on this file since 587 was 587, checked in by kake, 17 years ago

Encapsulate config data in OpenGuides::Config.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.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=back
90
91=over
92
93If C<node> is supplied:
94
95=item * C<node_name>
96
97=item * C<node_param> (the node name escaped for use in URLs)
98
99=back
100
101Content-Type: defaults to C<text/html> and is omitted if the
102C<content_type> arg is explicitly set to the blank string.
103
104=cut
105
106sub output {
107    my ($class, %args) = @_;
108    croak "No template supplied" unless $args{template};
109    my $config = $args{config} or croak "No config supplied";
110    my $template_path = $config->template_path;
111    my $custom_template_path = $config->custom_template_path || "";
112    my $tt = Template->new( { INCLUDE_PATH => "$custom_template_path:$template_path" } );
113
114    my $script_name = $config->script_name;
115    my $script_url  = $config->script_url;
116
117    # Check cookie to see if we need to set the formatting_rules_link.
118    my ($formatting_rules_link, $omit_help_links);
119    my $formatting_rules_node = $config->formatting_rules_node ||"";
120    my %cookie_data = OpenGuides::CGI->get_prefs_from_cookie(config=>$config);
121    if ( $cookie_data{omit_help_links} ) {
122        $omit_help_links = 1;
123    } else {
124        if ( $formatting_rules_node ) {
125            $formatting_rules_link = $script_url . $script_name . "?"
126                                   . uri_escape($args{wiki}->formatter->node_name_to_node_param($formatting_rules_node));
127        }
128    }
129
130    my $enable_page_deletion = 0;
131    if ( $config->enable_page_deletion
132         and ( lc($config->enable_page_deletion) eq "y"
133               or $config->enable_page_deletion eq "1" )
134       ) {
135        $enable_page_deletion = 1;
136    }
137
138    my $tt_vars = { site_name             => $config->site_name,
139                    cgi_url               => $script_name,
140                    full_cgi_url          => $script_url . $script_name,
141                    contact_email         => $config->contact_email,
142                    stylesheet            => $config->stylesheet_url,
143                    home_link             => $script_url . $script_name,
144                    home_name             => $config->home_name,
145                    navbar_on_home_page   => $config->navbar_on_home_page,
146                    omit_help_links       => $omit_help_links,
147                    formatting_rules_link => $formatting_rules_link,
148                    formatting_rules_node => $formatting_rules_node,
149                    openguides_version    => $OpenGuides::VERSION,
150                    enable_page_deletion  => $enable_page_deletion,
151                    language              => $config->default_language,
152    };
153
154    if ($args{node}) {
155        $tt_vars->{node_name} = CGI->escapeHTML($args{node});
156        $tt_vars->{node_param} = CGI->escape($args{wiki}->formatter->node_name_to_node_param($args{node}));
157    }
158
159    # Now set further TT variables if explicitly supplied - do this last
160    # as these override auto-set ones.
161    $tt_vars = { %$tt_vars, %{ $args{vars} || {} } };
162
163    my $header = "";
164    unless ( defined $args{content_type} and $args{content_type} eq "" ) {
165        $header = CGI::header( -cookie => $args{cookies} );
166    }
167
168    # vile hack
169    my %field_vars = OpenGuides::Template->extract_metadata_vars(
170                                        wiki                 => $args{wiki},
171                                        config               => $config,
172                                        set_coord_field_vars => 1,
173                                        metadata => {},
174                                                           );
175    $tt_vars = { %field_vars, %$tt_vars };
176
177    my $output;
178    $tt->process( $args{template}, $tt_vars, \$output );
179
180    $output ||= qq(<html><head><title>ERROR</title></head><body><p>
181                   Failed to process template: )
182              . $tt->error
183              . qq(</p></body></html>);
184
185    return $header . $output;
186}
187
188=item B<extract_metadata_vars>
189
190  my %node_data = $wiki->retrieve_node( "Home Page" );
191
192  my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
193                                        wiki     => $wiki,
194                                        config   => $config,
195                                        metadata => $node_data{metadata} );
196
197  # -- or --
198
199  my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
200                                        wiki     => $wiki,
201                                        config   => $config,
202                                        cgi_obj  => $q );
203
204  # -- then --
205
206  print OpenGuides::Template->output( wiki     => $wiki,
207                                      config   => $config,
208                                      template => "node.tt",
209                                      vars     => { foo => "bar",
210                                                    %metadata_vars }
211                                     );
212
213Picks out things like categories, locales, phone number etc from
214EITHER the metadata hash returned by L<CGI::Wiki> OR the query
215parameters in a L<CGI> object, and packages them nicely for passing to
216templates or storing in L<CGI::Wiki> datastore.  If you supply both
217C<metadata> and C<cgi_obj> then C<metadata> will take precedence, but
218don't do that.
219
220The variables C<dist_field>, C<coord_field_1>, C<coord_field_1_name>,
221C<coord_field_1_value>, C<coord_field_2>, C<coord_field_2_name>, and
222C<coord_field_2_value>, which are used to create various forms, will
223only be set if I<either> C<metadata> is supplied I<or>
224C<set_coord_field_vars> is true, to prevent these values from being
225stored in the database on a node commit.
226
227=cut
228
229sub extract_metadata_vars {
230    my ($class, %args) = @_;
231    my %metadata = %{$args{metadata} || {} };
232    my $q = $args{cgi_obj};
233    my $formatter = $args{wiki}->formatter;
234    my $config = $args{config};
235    my $script_name = $config->script_name;
236
237    # Categories and locales are displayed as links in the page footer.
238    # We return these twice, as eg 'category' being a simple array of
239    # category names, but 'categories' being an array of hashrefs including
240    # a URL too.  This is ick.
241    my (@catlist, @loclist);
242    if ( $args{metadata} ) {
243        @catlist = @{ $metadata{category} || [] };
244        @loclist = @{ $metadata{locale}   || [] };
245    } else {
246        my $categories_text = $q->param('categories');
247        my $locales_text    = $q->param('locales');
248        @catlist = sort split("\r\n", $categories_text);
249        @loclist = sort split("\r\n", $locales_text);
250    }
251
252    my @categories = map { { name => $_,
253                             url  => "$script_name?Category_"
254            . uri_escape($formatter->node_name_to_node_param($_)) } } @catlist;
255
256    my @locales    = map { { name => $_,
257                             url  => "$script_name?Locale_"
258            . uri_escape($formatter->node_name_to_node_param($_)) } } @loclist;
259
260    # The 'website' attribute might contain a URL so we wiki-format it here
261    # rather than just CGI::escapeHTMLing it all in the template.
262    my $website = $args{metadata} ? $metadata{website}[0]
263                                  : $q->param("website");
264    my $formatted_website_text = "";
265    if ( $website ) {
266        $formatted_website_text = $class->format_website_text(
267            formatter => $formatter,
268            text      => $website );
269    }
270
271    my $hours_text = $args{metadata} ? $metadata{opening_hours_text}[0]
272                                     : $q->param("hours_text");
273    my %vars = (
274        categories             => \@categories,
275        locales                => \@locales,
276        category               => \@catlist,
277        locale                 => \@loclist,
278        formatted_website_text => $formatted_website_text,
279        hours_text             => $hours_text
280    );
281
282    if ( $args{metadata} ) {
283        foreach my $var ( qw( phone fax address postcode os_x os_y osie_x
284                              osie_y latitude longitude map_link website) ) {
285            $vars{$var} = $metadata{$var}[0];
286        }
287        # Data for the distance search forms on the node display.
288        my $geo_handler = $config->geo_handler;
289        if ( $geo_handler == 1 ) {
290            %vars = (
291                      %vars,
292                      coord_field_1       => "os_x",
293                      coord_field_2       => "os_y",
294                      dist_field          => "os_dist",
295                      coord_field_1_name  => "OS X coordinate",
296                      coord_field_2_name  => "OS Y coordinate",
297                      coord_field_1_value => $metadata{os_x}[0],
298                      coord_field_2_value => $metadata{os_y}[0],
299                    );
300        } elsif ( $geo_handler == 2 ) {
301            %vars = (
302                      %vars,
303                      coord_field_1       => "osie_x",
304                      coord_field_2       => "osie_y",
305                      dist_field          => "osie_dist",
306                      coord_field_1_name  =>"Irish National Grid X coordinate",
307                      coord_field_2_name  =>"Irish National Grid Y coordinate",
308                      coord_field_1_value => $metadata{osie_x}[0],
309                      coord_field_2_value => $metadata{osie_y}[0],
310                    );
311        } else {
312            %vars = (
313                      %vars,
314                      coord_field_1       => "latitude",
315                      coord_field_2       => "longitude",
316                      dist_field          => "latlong_dist",
317                      coord_field_1_name  => "Latitude (decimal)",
318                      coord_field_2_name  => "Longitude (decimal)",
319                      coord_field_1_value => $metadata{latitude}[0],
320                      coord_field_2_value => $metadata{longitude}[0],
321                    );
322        }
323    } else {
324        foreach my $var ( qw( phone fax address postcode map_link website) ) {
325            $vars{$var} = $q->param($var);
326        }
327
328        my $geo_handler = $config->geo_handler;
329        if ( $geo_handler == 1 ) {
330            require Geography::NationalGrid::GB;
331            my $os_x   = $q->param("os_x");
332            my $os_y   = $q->param("os_y");
333            my $lat    = $q->param("latitude");
334            my $long   = $q->param("longitude");
335
336            # Trim whitespace - trailing whitespace buggers up the
337            # integerification by postgres and it's an easy mistake to
338            # make when typing into a form.
339            $os_x =~ s/\s+//;
340            $os_y =~ s/\s+//;
341
342            # If we were sent x and y, work out lat/long; and vice versa.
343            if ( $os_x && $os_y ) {
344                my $point = Geography::NationalGrid::GB->new( Easting =>$os_x,
345                                                              Northing=>$os_y);
346                $lat  = sprintf("%.6f", $point->latitude);
347                $long = sprintf("%.6f", $point->longitude);
348            } elsif ( $lat && $long ) {
349                my $point = Geography::NationalGrid::GB->new(Latitude =>$lat,
350                                                             Longitude=>$long);
351                $os_x = $point->easting;
352                $os_y = $point->northing;
353            }
354            if ( $os_x && $os_y ) {
355                %vars = (
356                          %vars,
357                          latitude  => $lat,
358                          longitude => $long,
359                          os_x      => $os_x,
360                          os_y      => $os_y,
361                        );
362            }
363            if ( $args{set_coord_field_vars} ) {
364                %vars = (
365                          %vars,
366                          coord_field_1       => "os_x",
367                          coord_field_2       => "os_y",
368                          dist_field          => "os_dist",
369                          coord_field_1_name  => "OS X coordinate",
370                          coord_field_2_name  => "OS Y coordinate",
371                          coord_field_1_value => $os_x,
372                          coord_field_2_value => $os_y,
373                        );
374            }
375        } elsif ( $geo_handler == 2 ) {
376            require Geography::NationalGrid::IE;
377            my $osie_x = $q->param("osie_x");
378            my $osie_y = $q->param("osie_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            $osie_x =~ s/\s+//;
386            $osie_y =~ s/\s+//;
387
388            # If we were sent x and y, work out lat/long; and vice versa.
389            if ( $osie_x && $osie_y ) {
390                my $point = Geography::NationalGrid::IE->new(Easting=>$osie_x,
391                                                            Northing=>$osie_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                $osie_x = $point->easting;
398                $osie_y = $point->northing;
399            }
400            if ( $osie_x && $osie_y ) {
401                %vars = (
402                          %vars,
403                          latitude  => $lat,
404                          longitude => $long,
405                          osie_x    => $osie_x,
406                          osie_y    => $osie_y,
407                        );
408            }
409            if ( $args{set_coord_field_vars} ) {
410                %vars = (
411                          %vars,
412                          coord_field_1       => "osie_x",
413                          coord_field_2       => "osie_y",
414                          dist_field          => "osie_dist",
415                     coord_field_1_name  => "Irish National Grid X coordinate",
416                     coord_field_2_name  => "Irish National Grid Y coordinate",
417                          coord_field_1_value => $osie_x,
418                          coord_field_2_value => $osie_y,
419                        );
420            }
421        } elsif ( $geo_handler == 3 ) {
422            require Geo::Coordinates::UTM;
423            my $lat    = $q->param("latitude");
424            my $long   = $q->param("longitude");
425            if ( $lat && $long ) {
426                my ($zone, $easting, $northing) =
427                 Geo::Coordinates::UTM::latlon_to_utm( $config->ellipsoid,
428                                                       $lat, $long );
429                $easting  =~ s/\..*//; # chop off decimal places
430                $northing =~ s/\..*//; # - metre accuracy enough
431                %vars = ( %vars,
432                          latitude  => $lat,
433                          longitude => $long,
434                          easting   => $easting,
435                          northing  => $northing,
436                        );
437            }
438            if ( $args{set_coord_field_vars} ) {
439                %vars = (
440                          %vars,
441                          coord_field_1       => "latitude",
442                          coord_field_2       => "longitude",
443                          dist_field          => "latlong_dist",
444                          coord_field_1_name  => "Latitude (decimal)",
445                          coord_field_2_name  => "Longitude (decimal)",
446                          coord_field_1_value => $lat,
447                          coord_field_2_value => $long,
448                        );
449            }
450        }
451    }
452
453    # Check whether we need to munge lat and long.
454    # Store them unmunged as well so commit_node can get hold of them.
455    my %prefs = OpenGuides::CGI->get_prefs_from_cookie( config => $config );
456    if ( $prefs{latlong_traditional} ) {
457        foreach my $var ( qw( latitude longitude ) ) {
458            next unless defined $vars{$var};
459            $vars{$var."_unmunged"} = $vars{$var};
460            $vars{$var} = Geography::NationalGrid->deg2string($vars{$var});
461        }
462    }
463
464    return %vars;
465}
466
467sub format_website_text {
468    my ($class, %args) = @_;
469    my ($formatter, $text) = @args{ qw( formatter text ) };
470    my $formatted = $formatter->format($text);
471
472    # Strip out paragraph markers put in by formatter since we want this
473    # to be a single string to put in a <ul>.
474    $formatted =~ s/<p>//g;
475    $formatted =~ s/<\/p>//g;
476
477    return $formatted;
478}
479
480
481=back
482
483=head1 AUTHOR
484
485The OpenGuides Project (openguides-dev@openguides.org)
486
487=head1 COPYRIGHT
488
489  Copyright (C) 2003-2004 The OpenGuides Project.  All Rights Reserved.
490
491This module is free software; you can redistribute it and/or modify it
492under the same terms as Perl itself.
493
494=cut
495
4961;
Note: See TracBrowser for help on using the repository browser.