root/tags/rel0_57/lib/OpenGuides.pm

Revision 856, 56.3 kB (checked in by dom, 2 years ago)

Fix bugs introduced by me (readd moderate variable, fix up template to use correct variables

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1package OpenGuides;
2use strict;
3
4use Carp "croak";
5use CGI;
6use Wiki::Toolkit::Plugin::Diff;
7use Wiki::Toolkit::Plugin::Locator::Grid;
8use OpenGuides::CGI;
9use OpenGuides::Feed;
10use OpenGuides::Template;
11use OpenGuides::Utils;
12use Time::Piece;
13use URI::Escape;
14
15use vars qw( $VERSION );
16
17$VERSION = '0.57';
18
19=head1 NAME
20
21OpenGuides - A complete web application for managing a collaboratively-written guide to a city or town.
22
23=head1 DESCRIPTION
24
25The OpenGuides software provides the framework for a collaboratively-written
26city guide.  It is similar to a wiki but provides somewhat more structured
27data storage allowing you to annotate wiki pages with information such as
28category, location, and much more.  It provides searching facilities
29including "find me everything within a certain distance of this place".
30Every page includes a link to a machine-readable (RDF) version of the page.
31
32=head1 METHODS
33
34=over
35
36=item B<new>
37
38  my $config = OpenGuides::Config->new( file => "wiki.conf" );
39  my $guide = OpenGuides->new( config => $config );
40
41=cut
42
43sub new {
44    my ($class, %args) = @_;
45    my $self = {};
46    bless $self, $class;
47    my $wiki = OpenGuides::Utils->make_wiki_object( config => $args{config} );
48    $self->{wiki} = $wiki;
49    $self->{config} = $args{config};
50    my $geo_handler = $self->config->geo_handler;
51    my $locator;
52    if ( $geo_handler == 1 ) {
53        $locator = Wiki::Toolkit::Plugin::Locator::Grid->new(
54                                             x => "os_x",    y => "os_y" );
55    } elsif ( $geo_handler == 2 ) {
56        $locator = Wiki::Toolkit::Plugin::Locator::Grid->new(
57                                             x => "osie_x"y => "osie_y" );
58    } else {
59        $locator = Wiki::Toolkit::Plugin::Locator::Grid->new(
60                                             x => "easting", y => "northing" );
61    }
62    $wiki->register_plugin( plugin => $locator );
63    $self->{locator} = $locator;
64    my $differ = Wiki::Toolkit::Plugin::Diff->new;
65    $wiki->register_plugin( plugin => $differ );
66    $self->{differ} = $differ;
67    return $self;
68}
69
70=item B<wiki>
71
72An accessor, returns the underlying L<Wiki::Toolkit> object.
73
74=cut
75
76sub wiki {
77    my $self = shift;
78    return $self->{wiki};
79}
80
81=item B<config>
82
83An accessor, returns the underlying L<OpenGuides::Config> object.
84
85=cut
86
87sub config {
88    my $self = shift;
89    return $self->{config};
90}
91
92=item B<locator>
93
94An accessor, returns the underlying L<Wiki::Toolkit::Plugin::Locator::UK> object.
95
96=cut
97
98sub locator {
99    my $self = shift;
100    return $self->{locator};
101}
102
103=item B<differ>
104
105An accessor, returns the underlying L<Wiki::Toolkit::Plugin::Diff> object.
106
107=cut
108
109sub differ {
110    my $self = shift;
111    return $self->{differ};
112}
113
114=item B<display_node>
115
116  # Print node to STDOUT.
117  $guide->display_node(
118                          id      => "Calthorpe Arms",
119                          version => 2,
120                      );
121
122  # Or return output as a string (useful for writing tests).
123  $guide->display_node(
124                          id            => "Calthorpe Arms",
125                          return_output => 1,
126                      );
127
128  # Or return the hash of variables that will be passed to the template
129  # (not including those set additionally by OpenGuides::Template).
130  $guide->display_node(
131                          id             => "Calthorpe Arms",
132                          return_tt_vars => 1,
133                      );
134
135If C<version> is omitted then the latest version will be displayed.
136
137=cut
138
139sub display_node {
140    my ($self, %args) = @_;
141    my $return_output = $args{return_output} || 0;
142    my $version = $args{version};
143    my $id = $args{id} || $self->config->home_name;
144    my $wiki = $self->wiki;
145    my $config = $self->config;
146    my $oldid = $args{oldid} || '';
147    my $do_redirect = $args{redirect} || 1;
148
149    my %tt_vars;
150
151    if ( $id =~ /^(Category|Locale) (.*)$/ ) {
152        my $type = $1;
153        $tt_vars{is_indexable_node} = 1;
154        $tt_vars{index_type} = lc($type);
155        $tt_vars{index_value} = $2;
156        $tt_vars{"rss_".lc($type)."_url"} =
157                           $config->script_name . "?action=rc;format=rss;"
158                           . lc($type) . "=" . lc(CGI->escape($2));
159        $tt_vars{"atom_".lc($type)."_url"} =
160                           $config->script_name . "?action=rc;format=atom;"
161                           . lc($type) . "=" . lc(CGI->escape($2));
162    }
163
164    my %current_data = $wiki->retrieve_node( $id );
165    my $current_version = $current_data{version};
166    undef $version if ($version && $version == $current_version);
167    my %criteria = ( name => $id );
168    $criteria{version} = $version if $version; # retrieve_node default is current
169
170    my %node_data = $wiki->retrieve_node( %criteria );
171
172    # Fixes passing undefined values to Text::Wikiformat if node doesn't exist.
173    my $raw        = $node_data{content} || " ";
174    my $content    = $wiki->format($raw);
175    my $modified   = $node_data{last_modified};
176    my $moderated  = $node_data{moderated};
177    my %metadata   = %{$node_data{metadata}};
178
179    my ($wgs84_long, $wgs84_lat) = OpenGuides::Utils->get_wgs84_coords(
180                                        longitude => $metadata{longitude}[0],
181                                        latitude => $metadata{latitude}[0],
182                                        config => $config);
183    if ($args{format} && $args{format} eq 'raw') {
184      print "Content-Type: text/plain\n\n";
185      print $raw;
186      return 0;
187    }
188   
189    my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
190                            wiki     => $wiki,
191                            config   => $config,
192                            metadata => $node_data{metadata}
193                        );
194
195    %tt_vars = (
196                   %tt_vars,
197                   %metadata_vars,
198                   content       => $content,
199                   last_modified => $modified,
200                   version       => $node_data{version},
201                   node          => $id,
202                   language      => $config->default_language,
203                   moderated     => $moderated,
204                   oldid         => $oldid,
205                   enable_gmaps  => 1,
206                   display_google_maps => $self->get_cookie("display_google_maps"),
207                   wgs84_long    => $wgs84_long,
208                   wgs84_lat     => $wgs84_lat
209               );
210
211    # Should we include a standard list of categories or locales?
212    if ($config->enable_common_categories || $config->enable_common_locales) {
213        $tt_vars{common_catloc} = 1;
214        $tt_vars{common_categories} = $config->enable_common_categories;
215        $tt_vars{common_locales} = $config->enable_common_locales;
216        $tt_vars{catloc_link} = $config->script_name . "?id=";
217    }
218
219    if ( $raw =~ /^#REDIRECT\s+(.+?)\s*$/ ) {
220        my $redirect = $1;
221        # Strip off enclosing [[ ]] in case this is an extended link.
222        $redirect =~ s/^\[\[//;
223        $redirect =~ s/\]\]\s*$//;
224
225        # Don't redirect if the parameter "redirect" is given as 0.
226        if ($do_redirect == 0) {
227            return %tt_vars if $args{return_tt_vars};
228            $tt_vars{current} = 1;
229            my $output = $self->process_template(
230                                                  id            => $id,
231                                                  template      => "node.tt",
232                                                  tt_vars       => \%tt_vars,
233                                                );
234            return $output if $return_output;
235            print $output;
236        } elsif ( $wiki->node_exists($redirect) && $redirect ne $id && $redirect ne $oldid ) {
237            # Avoid loops by not generating redirects to the same node or the previous node.
238            my $output = $self->redirect_to_node($redirect, $id);
239            return $output if $return_output;
240            print $output;
241            return 0;
242        }
243    }
244
245    # We've undef'ed $version above if this is the current version.
246    $tt_vars{current} = 1 unless $version;
247
248    if ($id eq "RecentChanges") {
249        $self->display_recent_changes(%args);
250    } elsif ( $id eq $self->config->home_name ) {
251        my @recent = $wiki->list_recent_changes(
252            last_n_changes => 10,
253            metadata_was   => { edit_type => "Normal edit" },
254        );
255        @recent = map {
256                          {
257                              name          => CGI->escapeHTML($_->{name}),
258                              last_modified => CGI->escapeHTML($_->{last_modified}),
259                              version       => CGI->escapeHTML($_->{version}),
260                              comment       => CGI->escapeHTML($_->{metadata}{comment}[0]),
261                              username      => CGI->escapeHTML($_->{metadata}{username}[0]),
262                              url           => $config->script_name . "?"
263                                               . CGI->escape($wiki->formatter->node_name_to_node_param($_->{name}))
264                          }
265                      } @recent;
266        $tt_vars{recent_changes} = \@recent;
267        return %tt_vars if $args{return_tt_vars};
268        my $output = $self->process_template(
269                                                id            => $id,
270                                                template      => "home_node.tt",
271                                                tt_vars       => \%tt_vars,
272                                            );
273        return $output if $return_output;
274        print $output;
275    } else {
276        return %tt_vars if $args{return_tt_vars};
277        my $output = $self->process_template(
278                                                id            => $id,
279                                                template      => "node.tt",
280                                                tt_vars       => \%tt_vars,
281                                            );
282        return $output if $return_output;
283        print $output;
284    }
285}
286
287=item B<display_recent_changes> 
288
289  $guide->display_recent_changes;
290
291As with other methods, the C<return_output> parameter can be used to
292return the output instead of printing it to STDOUT.
293
294=cut
295
296sub display_recent_changes {
297    my ($self, %args) = @_;
298    my $config = $self->config;
299    my $wiki = $self->wiki;
300    my $minor_edits = $self->get_cookie( "show_minor_edits_in_rc" );
301    my $id = $args{id} || $self->config->home_name;
302    my $return_output = $args{return_output} || 0;
303    my (%tt_vars, %recent_changes);
304    my $q = CGI->new;
305    my $since = $q->param("since");
306    if ( $since ) {
307        $tt_vars{since} = $since;
308        my $t = localtime($since); # overloaded by Time::Piece
309        $tt_vars{since_string} = $t->strftime;
310        my %criteria = ( since => $since );   
311        $criteria{metadata_was} = { edit_type => "Normal edit" }
312          unless $minor_edits;
313        my @rc = $self->{wiki}->list_recent_changes( %criteria );
314 
315        @rc = map {
316            {
317              name        => CGI->escapeHTML($_->{name}),
318              last_modified => CGI->escapeHTML($_->{last_modified}),
319              version     => CGI->escapeHTML($_->{version}),
320              comment     => CGI->escapeHTML($_->{metadata}{comment}[0]),
321              username    => CGI->escapeHTML($_->{metadata}{username}[0]),
322              host        => CGI->escapeHTML($_->{metadata}{host}[0]),
323              username_param => CGI->escape($_->{metadata}{username}[0]),
324              edit_type   => CGI->escapeHTML($_->{metadata}{edit_type}[0]),
325              url         => $config->script_name . "?"
326      . CGI->escape($wiki->formatter->node_name_to_node_param($_->{name})),
327        }
328                   } @rc;
329        if ( scalar @rc ) {
330            $recent_changes{since} = \@rc;
331        }
332    } else {
333        for my $days ( [0, 1], [1, 7], [7, 14], [14, 30] ) {
334            my %criteria = ( between_days => $days );
335            $criteria{metadata_was} = { edit_type => "Normal edit" }
336              unless $minor_edits;
337            my @rc = $self->{wiki}->list_recent_changes( %criteria );
338
339            @rc = map {
340            {
341              name        => CGI->escapeHTML($_->{name}),
342              last_modified => CGI->escapeHTML($_->{last_modified}),
343              version     => CGI->escapeHTML($_->{version}),
344              comment     => CGI->escapeHTML($_->{metadata}{comment}[0]),
345              username    => CGI->escapeHTML($_->{metadata}{username}[0]),
346              host        => CGI->escapeHTML($_->{metadata}{host}[0]),
347              username_param => CGI->escape($_->{metadata}{username}[0]),
348              edit_type   => CGI->escapeHTML($_->{metadata}{edit_type}[0]),
349              url         => $config->script_name . "?"
350      . CGI->escape($wiki->formatter->node_name_to_node_param($_->{name})),
351        }
352                       } @rc;
353            if ( scalar @rc ) {
354                $recent_changes{$days->[1]} = \@rc;
355        }
356        }
357    }
358    $tt_vars{recent_changes} = \%recent_changes;
359    my %processing_args = (
360                            id            => $id,
361                            template      => "recent_changes.tt",
362                            tt_vars       => \%tt_vars,
363                           );
364    if ( !$since && $self->get_cookie("track_recent_changes_views") ) {
365    my $cookie =
366           OpenGuides::CGI->make_recent_changes_cookie(config => $config );
367        $processing_args{cookies} = $cookie;
368        $tt_vars{last_viewed} = OpenGuides::CGI->get_last_recent_changes_visit_from_cookie( config => $config );
369    }
370    return %tt_vars if $args{return_tt_vars};
371    my $output = $self->process_template( %processing_args );
372    return $output if $return_output;
373    print $output;
374}
375
376=item B<display_diffs>
377
378  $guide->display_diffs(
379                           id            => "Home Page",
380                           version       => 6,
381                           other_version => 5,
382                       );
383
384  # Or return output as a string (useful for writing tests).
385  my $output = $guide->display_diffs(
386                                        id            => "Home Page",
387                                        version       => 6,
388                                        other_version => 5,
389                                        return_output => 1,
390                                    );
391
392  # Or return the hash of variables that will be passed to the template
393  # (not including those set additionally by OpenGuides::Template).
394  my %vars = $guide->display_diffs(
395                                      id             => "Home Page",
396                                      version        => 6,
397                                      other_version  => 5,
398                                      return_tt_vars => 1,
399                                  );
400
401=cut
402
403sub display_diffs {
404    my ($self, %args) = @_;
405    my %diff_vars = $self->differ->differences(
406                                                  node          => $args{id},
407                                                  left_version  => $args{version},
408                                                  right_version => $args{other_version},
409                                              );
410    $diff_vars{not_deletable} = 1;
411    $diff_vars{not_editable}  = 1;
412    $diff_vars{deter_robots}  = 1;
413    return %diff_vars if $args{return_tt_vars};
414    my $output = $self->process_template(
415                                            id       => $args{id},
416                                            template => "differences.tt",
417                                            tt_vars  => \%diff_vars
418                                        );
419    return $output if $args{return_output};
420    print $output;
421}
422
423=item B<find_within_distance>
424
425  $guide->find_within_distance(
426                                  id => $node,
427                                  metres => $q->param("distance_in_metres")
428                              );
429
430=cut
431
432sub find_within_distance {
433    my ($self, %args) = @_;
434    my $node = $args{id};
435    my $metres = $args{metres};
436    my %data = $self->wiki->retrieve_node( $node );
437    my $lat = $data{metadata}{latitude}[0];
438    my $long = $data{metadata}{longitude}[0];
439    my $script_url = $self->config->script_url;
440    my $q = CGI->new;
441    print $q->redirect( $script_url . "search.cgi?lat=$lat;long=$long;distance_in_metres=$metres" );
442}
443
444=item B<show_backlinks>
445
446  $guide->show_backlinks( id => "Calthorpe Arms" );
447
448As with other methods, parameters C<return_tt_vars> and
449C<return_output> can be used to return these things instead of
450printing the output to STDOUT.
451
452=cut
453
454sub show_backlinks {
455    my ($self, %args) = @_;
456    my $wiki = $self->wiki;
457    my $formatter = $wiki->formatter;
458
459    my @backlinks = $wiki->list_backlinks( node => $args{id} );
460    my @results = map {
461                          {
462                              url   => CGI->escape($formatter->node_name_to_node_param($_)),
463                              title => CGI->escapeHTML($_)
464                          }
465                      } sort @backlinks;
466    my %tt_vars = ( results       => \@results,
467                    num_results   => scalar @results,
468                    not_deletable => 1,
469                    deter_robots  => 1,
470                    not_editable  => 1 );
471    return %tt_vars if $args{return_tt_vars};
472    my $output = OpenGuides::Template->output(
473                                                 node    => $args{id},
474                                                 wiki    => $wiki,
475                                                 config  => $self->config,
476                                                 template=>"backlink_results.tt",
477                                                 vars    => \%tt_vars,
478                                             );
479    return $output if $args{return_output};
480    print $output;
481}
482
483=item B<show_index>
484
485  $guide->show_index(
486                        type   => "category",
487                        value  => "pubs",
488                    );
489
490  # RDF version.
491  $guide->show_index(
492                        type   => "locale",
493                        value  => "Holborn",
494                        format => "rdf",
495                    );
496
497  # RSS / Atom version (recent changes style).
498  $guide->show_index(
499                        type   => "locale",
500                        value  => "Holborn",
501                        format => "rss",
502                    );
503
504  # Or return output as a string (useful for writing tests).
505  $guide->show_index(
506                        type          => "category",
507                        value         => "pubs",
508                        return_output => 1,
509                    );
510
511=cut
512
513sub show_index {
514    my ($self, %args) = @_;
515    my $wiki = $self->wiki;
516    my $formatter = $wiki->formatter;
517    my %tt_vars;
518    my @selnodes;
519
520    if ( $args{type} and $args{value} ) {
521        if ( $args{type} eq "fuzzy_title_match" ) {
522            my %finds = $wiki->fuzzy_title_match( $args{value} );
523            @selnodes = sort { $finds{$a} <=> $finds{$b} } keys %finds;
524            $tt_vars{criterion} = {
525                type  => $args{type},  # for RDF version
526                value => $args{value}, # for RDF version
527                name  => CGI->escapeHTML("Fuzzy Title Match on '$args{value}'")
528            };
529            $tt_vars{not_editable} = 1;
530        } else {
531            @selnodes = $wiki->list_nodes_by_metadata(
532                metadata_type  => $args{type},
533                metadata_value => $args{value},
534                ignore_case    => 1
535            );
536            my $name = ucfirst($args{type}) . " $args{value}";
537            my $url = $self->config->script_name
538                      . "?"
539                      . ucfirst( $args{type} )
540                      . "_"
541                      . uri_escape(
542                                      $formatter->node_name_to_node_param($args{value})
543                                  );
544            $tt_vars{criterion} = {
545                type  => $args{type},
546                value => $args{value}, # for RDF version
547                name  => CGI->escapeHTML( $name ),
548                url   => $url
549            };
550            $tt_vars{not_editable} = 1;
551        }
552    } else {
553        @selnodes = $wiki->list_all_nodes();
554    }
555
556    my @nodes = map {
557                        {
558                            name      => $_,
559                            node_data => { $wiki->retrieve_node( name => $_ ) },
560                            param     => $formatter->node_name_to_node_param($_) }
561                        } sort @selnodes;
562
563    $tt_vars{nodes} = \@nodes;
564
565    my ($template, %conf);
566
567    if ( $args{format} ) {
568        if ( $args{format} eq "rdf" ) {
569            $template = "rdf_index.tt";
570            $conf{content_type} = "application/rdf+xml";
571        }
572        elsif ( $args{format} eq "plain" ) {
573            $template = "plain_index.tt";
574            $conf{content_type} = "text/plain";
575        } elsif ( $args{format} eq "map" ) {
576            my $q = CGI->new;
577            $tt_vars{zoom} = $q->param('zoom') || '';
578            $tt_vars{lat} = $q->param('lat') || '';
579            $tt_vars{long} = $q->param('long') || '';
580            $tt_vars{centre_long} = $self->config->centre_long;
581            $tt_vars{centre_lat} = $self->config->centre_lat;
582            $tt_vars{default_gmaps_zoom} = $self->config->default_gmaps_zoom;
583            $tt_vars{enable_gmaps} = 1;
584            $tt_vars{display_google_maps} = 1; # override for this page
585            $template = "map_index.tt";
586           
587        } elsif( $args{format} eq "rss" || $args{format} eq "atom") {
588            # They really wanted a recent changes style rss/atom feed
589            my $feed_type = $args{format};
590            my ($feed,$content_type) = $self->get_feed_and_content_type($feed_type);
591            $feed->set_feed_name_and_url_params(
592                        "Index of $args{type} $args{value}",
593                        "action=index;index_type=$args{type};index_value=$args{value}"
594            );
595
596            # Grab the actual node data out of @nodes
597            my @node_data;
598            foreach my $node (@nodes) {
599                $node->{node_data}->{name} = $node->{name};
600                push @node_data, $node->{node_data};
601            }
602
603            my $output = "Content-Type: ".$content_type."\n";
604            $output .= $feed->build_feed_for_nodes($feed_type, @node_data);
605
606            return $output if $args{return_output};
607            print $output;
608            return;
609        }
610    } else {
611        $template = "site_index.tt";
612    }
613
614    %conf = (
615                %conf,
616                node        => "$args{type} index", # KLUDGE
617                template    => $template,
618                tt_vars     => \%tt_vars,
619            );
620
621    my $output = $self->process_template( %conf );
622    return $output if $args{return_output};
623    print $output;
624}
625
626=item B<list_all_versions>
627
628  $guide->list_all_versions ( id => "Home Page" );
629
630  # Or return output as a string (useful for writing tests).
631  $guide->list_all_versions (
632                                id            => "Home Page",
633                                return_output => 1,
634                            );
635
636  # Or return the hash of variables that will be passed to the template
637  # (not including those set additionally by OpenGuides::Template).
638  $guide->list_all_versions (
639                                id             => "Home Page",
640                                return_tt_vars => 1,
641                            );
642
643=cut
644
645sub list_all_versions {
646    my ($self, %args) = @_;
647    my $return_output = $args{return_output} || 0;
648    my $node = $args{id};
649    my %curr_data = $self->wiki->retrieve_node($node);
650    my $curr_version = $curr_data{version};
651    my @history;
652    for my $version ( 1 .. $curr_version ) {
653        my %node_data = $self->wiki->retrieve_node( name    => $node,
654                                                    version => $version );
655        # $node_data{version} will be zero if this version was deleted.
656        push @history, {
657            version  => CGI->escapeHTML( $version ),
658            modified => CGI->escapeHTML( $node_data{last_modified} ),
659            username => CGI->escapeHTML( $node_data{metadata}{username}[0] ),
660            comment  => CGI->escapeHTML( $node_data{metadata}{comment}[0] ),
661                       } if $node_data{version};
662    }
663    @history = reverse @history;
664    my %tt_vars = (
665                      node          => $node,
666                      version       => $curr_version,
667                      not_deletable => 1,
668                      not_editable  => 1,
669                      deter_robots  => 1,
670                      history       => \@history
671                  );
672    return %tt_vars if $args{return_tt_vars};
673    my $output = $self->process_template(
674                                            id       => $node,
675                                            template => "node_history.tt",
676                                            tt_vars  => \%tt_vars,
677                                        );
678    return $output if $return_output;
679    print $output;
680}
681
682=item B<get_feed_and_content_type>
683
684Fetch the OpenGuides feed object, and the output content type, for the
685supplied feed type.
686
687Handles all the setup for the OpenGuides feed object.
688=cut
689sub get_feed_and_content_type {
690    my ($self, $feed_type) = @_;
691
692    my $feed = OpenGuides::Feed->new(
693                                        wiki       => $self->wiki,
694                                        config     => $self->config,
695                                        og_version => $VERSION,
696                                    );
697
698    my $content_type = $feed->default_content_type($feed_type);
699
700    return ($feed, $content_type);
701}
702
703=item B<display_feed>
704
705  # Last ten non-minor edits to Hammersmith pages in RSS 1.0 format
706  $guide->display_feed(
707                         feed_type          => 'rss',
708                         feed_listing       => 'recent_changes',
709                         items              => 10,
710                         ignore_minor_edits => 1,
711                         locale             => "Hammersmith",
712                     );
713
714  # All edits bob has made to pub pages in the last week in Atom format
715  $guide->display_feed(
716                         feed_type    => 'atom',
717                         feed_listing => 'recent_changes',
718                         days         => 7,
719                         username     => "bob",
720                         category     => "Pubs",
721                     );
722
723C<feed_type> is a mandatory parameter. Supported values at present are
724"rss" and "atom".
725
726C<feed_listing> is a mandatory parameter. Supported values at present
727are "recent_changes". (More values are coming soon though!)
728
729As with other methods, the C<return_output> parameter can be used to
730return the output instead of printing it to STDOUT.
731
732=cut
733
734sub display_feed {
735    my ($self, %args) = @_;
736
737    my $feed_type = $args{feed_type};
738    croak "No feed type given" unless $feed_type;
739
740    my $feed_listing = $args{feed_listing};
741    croak "No feed listing given" unless $feed_listing;
742   
743    my $return_output = $args{return_output} ? 1 : 0;
744
745    # Basic criteria, whatever the feed listing type is
746    my %criteria = (
747                       feed_type             => $feed_type,
748                       feed_listing          => $feed_listing,
749                       also_return_timestamp => 1,
750                   );
751
752    # Feed listing specific criteria
753    if($feed_listing eq "recent_changes") {
754        $criteria{items} = $args{items} || "";
755        $criteria{days}  = $args{days}  || "";
756        $criteria{ignore_minor_edits} = $args{ignore_minor_edits} ? 1 : 0;
757
758        my $username = $args{username} || "";
759        my $category = $args{category} || "";
760        my $locale   = $args{locale}   || "";
761
762        my %filter;
763        $filter{username} = $username if $username;
764        $filter{category} = $category if $category;
765        $filter{locale}   = $locale   if $locale;
766        if ( scalar keys %filter ) {
767            $criteria{filter_on_metadata} = \%filter;
768        }
769    }
770    elsif($feed_listing eq "node_all_versions") {
771        $criteria{name} = $args{name};
772    }
773
774
775    # Get the feed object, and the content type
776    my ($feed,$content_type) = $self->get_feed_and_content_type($feed_type);
777
778    my $output = "Content-Type: ".$content_type."\n";
779   
780    # Get the feed, and the timestamp, in one go
781    my ($feed_output, $feed_timestamp) =
782        $feed->make_feed( %criteria );
783
784    $output .= "Last-Modified: " . $feed_timestamp . "\n\n";
785    $output .= $feed_output;
786
787    return $output if $return_output;
788    print $output;
789}
790
791sub display_about {
792    my ($self, %args) = @_;
793
794    my $output;
795
796    if ($args{format} && $args{format} =~ /^rdf$/i) {
797        $output = qq{Content-Type: application/rdf+xml
798
799<?xml version="1.0" encoding="UTF-8"?>
800<rdf:RDF xmlns      = "http://usefulinc.com/ns/doap#"
801         xmlns:rdf  = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
802         xmlns:foaf = "http://xmlns.com/foaf/0.1/">
803<Project rdf:ID="OpenGuides">
804  <name>OpenGuides</name>
805
806  <created>2003-04-29</created>
807 
808  <shortdesc xml:lang="en">
809    A wiki engine for collaborative description of places with specialised
810    geodata metadata features.
811  </shortdesc>
812
813  <description xml:lang="en">
814    OpenGuides is a collaborative wiki environment, written in Perl, for 
815    building guides and sharing information, as both human-readable text
816    and RDF. The engine contains a number of geodata-specific metadata
817    mechanisms such as locale search, node classification and integration
818    with Google Maps.
819  </description>
820
821  <homepage rdf:resource="http://openguides.org/" />
822  <mailing-list rdf:resource="http://openguides.org/mm/listinfo/openguides-dev/" />
823  <mailing-list rdf:resource="http://urchin.earth.li/mailman/listinfo/openguides-commits/" />
824
825  <maintainer>
826    <foaf:Person rdf:ID="OpenGuidesMaintainer">
827      <foaf:name>Dominic Hargreaves</foaf:name>
828      <foaf:homepage rdf:resource="http://www.larted.org.uk/~dom/" />
829    </foaf:Person>
830  </maintainer>
831
832  <repository>
833    <SVNRepository rdf:ID="OpenGuidesSVN">
834      <location rdf:resource="https://urchin.earth.li/svn/openguides/" />
835      <browse rdf:resource="http://dev.openguides.org/browser" />
836    </SVNRepository>
837  </repository>
838
839  <release>
840    <Version rdf:ID="OpenGuidesVersion">
841      <revision>$VERSION</revision>
842    </Version>
843  </release>
844
845  <download-page rdf:resource="http://search.cpan.org/dist/OpenGuides/" />
846 
847  <!-- Freshmeat category: Internet :: WWW/HTTP :: Dynamic Content -->
848  <category rdf:resource="http://freshmeat.net/browse/92/" />
849 
850  <license rdf:resource="http://www.opensource.org/licenses/gpl-license.php" />
851  <license rdf:resource="http://www.opensource.org/licenses/artistic-license.php" />
852
853</Project>
854
855</rdf:RDF>};
856    }
857    else {
858        my $site_name  = $self->config->{site_name};
859        my $script_name = $self->config->{script_name};
860        $output = qq{Content-Type: text/html; charset=utf-8
861
862<html>
863<head>
864  <title>About $site_name</title>
865<style type="text/css">
866body        { margin: 0px; }
867#content    { padding: 50px; margin: auto; width: 50%; }
868h1          { margin-bottom: 0px; font-style: italic; }
869h2          { margin-top: 0px; }
870#logo       { text-align: center; }
871#about      { margin: 0em 0em 1em 0em; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }
872#meta       { font-size: small; text-align: center;}
873</style>
874<link rel="alternate"
875  type="application/rdf+xml"
876  title="DOAP (Description Of A Project) profile for this site's software" 
877  href="$script_name?action=about;format=rdf" />
878</head>
879<body>
880<div id="content">
881<div id="logo">
882<a href="http://openguides.org/"><img
883src="http://openguides.org/img/logo.png" alt="OpenGuides"></a>
884<h1><a href="$script_name">$site_name</a></h1>
885<h2>is powered by <a href="http://openguides.org/">OpenGuides</a> -<br>
886the guides made by you.</h2>
887<h3>version <a href="http://search.cpan.org/~dom/OpenGuides-$VERSION">$VERSION</a></h3>
888</div>
889<div id="about">
890<p>
891<a href="http://www.w3.org/RDF/"><img
892src="http://openguides.org/img/rdf_icon.png" width="44" height="48"
893style="float: right; margin-left: 10px; border: 0px"></a> OpenGuides is a
894web-based collaborative <a href="http://wiki.org/wiki.cgi?WhatIsWiki">wiki</a>
895environment for building guides and sharing information, as both
896human-readable text and <a href="http://www.w3.org/RDF/"><acronym
897title="Resource Description Framework">RDF</acronym></a>. The engine contains
898a number of geodata-specific metadata mechanisms such as locale search, node
899classification and integration with <a href="http://maps.google.com/">Google
900Maps</a>.
901</p>
902<p>
903OpenGuides is written in <a href="http://www.perl.org/">Perl</a>, and is
904made available under the same license as Perl itself (dual <a
905href="http://dev.perl.org/licenses/artistic.html" title='The "Artistic Licence"'>Artistic</a> and <a
906href="http://www.opensource.org/licenses/gpl-license.php"><acronym
907title="GNU Public Licence">GPL</acronym></a>). Developer information for the
908project is available from the <a href="http://dev.openguides.org/">OpenGuides
909development site</a>.
910</p>
911<p>
912Copyright &copy;2003-2006, <a href="http://openguides.org/">The OpenGuides
913Project</a>. "OpenGuides", "[The] Open Guide To..." and "The guides made by
914you" are trademarks of The OpenGuides Project. Any uses on this site are made
915with permission.
916</p>
917</div>
918<div id="meta">
919<a href="$script_name?action=about;format=rdf"><acronym
920title="Description Of A Project">DOAP</acronym> RDF version of this
921information</a>
922</div>
923</div>
924</body>
925</html>};
926    }
927   
928    return $output if $args{return_output};
929    print $output;
930}
931
932=item B<commit_node>
933
934  $guide->commit_node(
935                         id      => $node,
936                         cgi_obj => $q,
937                     );
938
939As with other methods, parameters C<return_tt_vars> and
940C<return_output> can be used to return these things instead of
941printing the output to STDOUT.
942
943The geographical data that you should provide in the L<CGI> object
944depends on the handler you chose in C<wiki.conf>.
945
946=over
947
948=item *
949
950B<British National Grid> - provide either C<os_x> and C<os_y> or
951C<latitude> and C<longitude>; whichever set of data you give, it will
952be converted to the other and both sets will be stored.
953
954=item *
955
956B<Irish National Grid> - provide either C<osie_x> and C<osie_y> or
957C<latitude> and C<longitude>; whichever set of data you give, it will
958be converted to the other and both sets will be stored.
959
960=item *
961
962B<UTM ellipsoid> - provide C<latitude> and C<longitude>; these will be
963converted to easting and northing and both sets of data will be stored.
964
965=back
966
967=cut
968
969sub commit_node {
970    my ($self, %args) = @_;
971    my $node = $args{id};
972    my $q = $args{cgi_obj};
973    my $return_output = $args{return_output};
974    my $wiki = $self->wiki;
975    my $config = $self->config;
976
977    my $content  = $q->param("content");
978    $content =~ s/\r\n/\n/gs;
979    my $checksum = $q->param("checksum");
980
981    my %metadata = OpenGuides::Template->extract_metadata_vars(
982        wiki    => $wiki,
983        config  => $config,
984        cgi_obj => $q
985    );
986
987    delete $metadata{website} if $metadata{website} eq 'http://';
988
989    $metadata{opening_hours_text} = $q->param("hours_text") || "";
990
991    # Pick out the unmunged versions of lat/long if they're set.
992    # (If they're not, it means they weren't munged in the first place.)
993    $metadata{latitude} = delete $metadata{latitude_unmunged}
994<