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

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

Fix a typo, and support the node image for editing (but not yet display)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 20.1 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<Wiki::Toolkit> OR the query
241parameters in a L<CGI> object, and packages them nicely for passing to
242templates or storing in L<Wiki::Toolkit> 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    my $node_image = $args{metadata} ? $metadata{node_image}[0]
324                                     : $q->param("node_image");
325    if ($config->enable_node_image && $node_image) {
326        $vars{node_image} = $node_image;
327    }
328
329    if (exists $metadata{source}) {
330        ($vars{source_site}) = $metadata{source}[0] =~ /^(.*?)(?:\?|$)/;
331    }
332   
333    if ( $args{metadata} ) {
334        foreach my $var ( qw( phone fax address postcode os_x os_y osie_x
335                              osie_y latitude longitude map_link website
336                              summary) ) {
337            $vars{$var} = $metadata{$var}[0];
338        }
339        # Data for the distance search forms on the node display.
340        my $geo_handler = $config->geo_handler;
341        if ( $geo_handler == 1 ) {
342            %vars = (
343                        %vars,
344                        coord_field_1       => "os_x",
345                        coord_field_2       => "os_y",
346                        dist_field          => "os_dist",
347                        coord_field_1_name  => "OS X coordinate",
348                        coord_field_2_name  => "OS Y coordinate",
349                        coord_field_1_value => $metadata{os_x}[0],
350                        coord_field_2_value => $metadata{os_y}[0],
351                    );
352        } elsif ( $geo_handler == 2 ) {
353            %vars = (
354                        %vars,
355                        coord_field_1       => "osie_x",
356                        coord_field_2       => "osie_y",
357                        dist_field          => "osie_dist",
358                        coord_field_1_name  =>"Irish National Grid X coordinate",
359                        coord_field_2_name  =>"Irish National Grid Y coordinate",
360                        coord_field_1_value => $metadata{osie_x}[0],
361                        coord_field_2_value => $metadata{osie_y}[0],
362                    );
363        } else {
364            %vars = (
365                        %vars,
366                        coord_field_1       => "latitude",
367                        coord_field_2       => "longitude",
368                        dist_field          => "latlong_dist",
369                        coord_field_1_name  => "Latitude (decimal)",
370                        coord_field_2_name  => "Longitude (decimal)",
371                        coord_field_1_value => $metadata{latitude}[0],
372                        coord_field_2_value => $metadata{longitude}[0],
373                    );
374        }
375    } else {
376        foreach my $var ( qw( phone fax address postcode map_link website summary) ) {
377            $vars{$var} = $q->param($var);
378        }
379
380        my $geo_handler = $config->geo_handler;
381        if ( $geo_handler == 1 ) {
382            require Geography::NationalGrid::GB;
383            my $os_x   = $q->param("os_x");
384            my $os_y   = $q->param("os_y");
385            my $lat    = $q->param("latitude");
386            my $long   = $q->param("longitude");
387
388            # Trim whitespace - trailing whitespace buggers up the
389            # integerification by postgres and it's an easy mistake to
390            # make when typing into a form.
391            $os_x =~ s/\s+//g;
392            $os_y =~ s/\s+//g;
393
394            # If we were sent x and y, work out lat/long; and vice versa.
395            if ( $os_x && $os_y ) {
396                my $point = Geography::NationalGrid::GB->new( Easting =>$os_x,
397                                     Northing=>$os_y);
398                $lat  = sprintf("%.6f", $point->latitude);
399                $long = sprintf("%.6f", $point->longitude);
400            } elsif ( $lat && $long ) {
401                my $point = Geography::NationalGrid::GB->new(Latitude  => $lat,
402                                                             Longitude => $long);
403                $os_x = $point->easting;
404                $os_y = $point->northing;
405            }
406           
407            if ( $os_x && $os_y ) {
408                %vars = (
409                            %vars,
410                            latitude  => $lat,
411                            longitude => $long,
412                            os_x      => $os_x,
413                            os_y      => $os_y,
414                        );
415            }
416            if ( $args{set_coord_field_vars} ) {
417                %vars = (
418                            %vars,
419                            coord_field_1       => "os_x",
420                            coord_field_2       => "os_y",
421                            dist_field          => "os_dist",
422                            coord_field_1_name  => "OS X coordinate",
423                            coord_field_2_name  => "OS Y coordinate",
424                            coord_field_1_value => $os_x,
425                            coord_field_2_value => $os_y,
426                        );
427            }
428        } elsif ( $geo_handler == 2 ) {
429            require Geography::NationalGrid::IE;
430            my $osie_x = $q->param("osie_x");
431            my $osie_y = $q->param("osie_y");
432            my $lat    = $q->param("latitude");
433            my $long   = $q->param("longitude");
434
435            # Trim whitespace.
436            $osie_x =~ s/\s+//g;
437            $osie_y =~ s/\s+//g;
438
439            # If we were sent x and y, work out lat/long; and vice versa.
440            if ( $osie_x && $osie_y ) {
441                my $point = Geography::NationalGrid::IE->new(Easting=>$osie_x,
442                                   Northing=>$osie_y);
443                $lat = sprintf("%.6f", $point->latitude);
444                $long = sprintf("%.6f", $point->longitude);
445            } elsif ( $lat && $long ) {
446                my $point = Geography::NationalGrid::GB->new(Latitude  => $lat,
447                                                             Longitude => $long);
448                $osie_x = $point->easting;
449                $osie_y = $point->northing;
450            }
451            if ( $osie_x && $osie_y ) {
452                %vars = (
453                            %vars,
454                            latitude  => $lat,
455                            longitude => $long,
456                            osie_x    => $osie_x,
457                            osie_y    => $osie_y,
458                        );
459            }
460            if ( $args{set_coord_field_vars} ) {
461                %vars = (
462                            %vars,
463                            coord_field_1       => "osie_x",
464                            coord_field_2       => "osie_y",
465                            dist_field          => "osie_dist",
466                            coord_field_1_name  => "Irish National Grid X coordinate",
467                            coord_field_2_name  => "Irish National Grid Y coordinate",
468                            coord_field_1_value => $osie_x,
469                            coord_field_2_value => $osie_y,
470                        );
471            }
472        } elsif ( $geo_handler == 3 ) {
473            require Geo::Coordinates::UTM;
474            my $lat    = $q->param("latitude");
475            my $long   = $q->param("longitude");
476           
477            if ( $lat && $long ) {
478                # Trim whitespace.
479                $lat =~ s/\s+//g;
480                $long =~ s/\s+//g;
481                my ($zone, $easting, $northing) =
482                 Geo::Coordinates::UTM::latlon_to_utm( $config->ellipsoid,
483                                                       $lat, $long );
484                $easting  =~ s/\..*//; # chop off decimal places
485                $northing =~ s/\..*//; # - metre accuracy enough
486                %vars = (
487                            %vars,
488                            latitude  => $lat,
489                            longitude => $long,
490                            easting   => $easting,
491                            northing  => $northing,
492                        );
493             }
494             if ( $args{set_coord_field_vars} ) {
495                %vars = (
496                            %vars,
497                            coord_field_1       => "latitude",
498                            coord_field_2       => "longitude",
499                            dist_field          => "latlong_dist",
500                            coord_field_1_name  => "Latitude (decimal)",
501                            coord_field_2_name  => "Longitude (decimal)",
502                            coord_field_1_value => $lat,
503                            coord_field_2_value => $long,
504                        );
505             }
506        }
507    }
508
509    # Check whether we need to munge lat and long.
510    # Store them unmunged as well so commit_node can get hold of them.
511    my %prefs = OpenGuides::CGI->get_prefs_from_cookie( config => $config );
512    if ( $prefs{latlong_traditional} ) {
513        foreach my $var ( qw( latitude longitude ) ) {
514            next unless defined $vars{$var};
515            $vars{$var."_unmunged"} = $vars{$var};
516            $vars{$var} = Geography::NationalGrid->deg2string($vars{$var});
517        }
518    }
519
520    return %vars;
521}
522
523sub format_website_text {
524    my ($class, %args) = @_;
525    my ($formatter, $text) = @args{ qw( formatter text ) };
526    my $formatted = $formatter->format($text);
527
528    # Strip out paragraph markers put in by formatter since we want this
529    # to be a single string to put in a <ul>.
530    $formatted =~ s/<p>//g;
531    $formatted =~ s/<\/p>//g;
532
533    return $formatted;
534}
535
536
537=back
538
539=head1 AUTHOR
540
541The OpenGuides Project (openguides-dev@openguides.org)
542
543=head1 COPYRIGHT
544
545  Copyright (C) 2003-2005 The OpenGuides Project.  All Rights Reserved.
546
547This module is free software; you can redistribute it and/or modify it
548under the same terms as Perl itself.
549
550=cut
551
5521;
Note: See TracBrowser for help on using the repository browser.