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

Last change on this file since 718 was 718, checked in by Earle Martin, 16 years ago

fixes #28

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