source: trunk/lib/OpenGuides.pm @ 872

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

Do the wgs84 convertion for the google maps page. Add tests for this too. References #89

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.2 KB
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{not_editable} = 1;
359    $tt_vars{recent_changes} = \%recent_changes;
360    my %processing_args = (
361                            id            => $id,
362                            template      => "recent_changes.tt",
363                            tt_vars       => \%tt_vars,
364                           );
365    if ( !$since && $self->get_cookie("track_recent_changes_views") ) {
366    my $cookie =
367           OpenGuides::CGI->make_recent_changes_cookie(config => $config );
368        $processing_args{cookies} = $cookie;
369        $tt_vars{last_viewed} = OpenGuides::CGI->get_last_recent_changes_visit_from_cookie( config => $config );
370    }
371    return %tt_vars if $args{return_tt_vars};
372    my $output = $self->process_template( %processing_args );
373    return $output if $return_output;
374    print $output;
375}
376
377=item B<display_diffs>
378
379  $guide->display_diffs(
380                           id            => "Home Page",
381                           version       => 6,
382                           other_version => 5,
383                       );
384
385  # Or return output as a string (useful for writing tests).
386  my $output = $guide->display_diffs(
387                                        id            => "Home Page",
388                                        version       => 6,
389                                        other_version => 5,
390                                        return_output => 1,
391                                    );
392
393  # Or return the hash of variables that will be passed to the template
394  # (not including those set additionally by OpenGuides::Template).
395  my %vars = $guide->display_diffs(
396                                      id             => "Home Page",
397                                      version        => 6,
398                                      other_version  => 5,
399                                      return_tt_vars => 1,
400                                  );
401
402=cut
403
404sub display_diffs {
405    my ($self, %args) = @_;
406    my %diff_vars = $self->differ->differences(
407                                                  node          => $args{id},
408                                                  left_version  => $args{version},
409                                                  right_version => $args{other_version},
410                                              );
411    $diff_vars{not_deletable} = 1;
412    $diff_vars{not_editable}  = 1;
413    $diff_vars{deter_robots}  = 1;
414    return %diff_vars if $args{return_tt_vars};
415    my $output = $self->process_template(
416                                            id       => $args{id},
417                                            template => "differences.tt",
418                                            tt_vars  => \%diff_vars
419                                        );
420    return $output if $args{return_output};
421    print $output;
422}
423
424=item B<find_within_distance>
425
426  $guide->find_within_distance(
427                                  id => $node,
428                                  metres => $q->param("distance_in_metres")
429                              );
430
431=cut
432
433sub find_within_distance {
434    my ($self, %args) = @_;
435    my $node = $args{id};
436    my $metres = $args{metres};
437    my %data = $self->wiki->retrieve_node( $node );
438    my $lat = $data{metadata}{latitude}[0];
439    my $long = $data{metadata}{longitude}[0];
440    my $script_url = $self->config->script_url;
441    my $q = CGI->new;
442    print $q->redirect( $script_url . "search.cgi?lat=$lat;long=$long;distance_in_metres=$metres" );
443}
444
445=item B<show_backlinks>
446
447  $guide->show_backlinks( id => "Calthorpe Arms" );
448
449As with other methods, parameters C<return_tt_vars> and
450C<return_output> can be used to return these things instead of
451printing the output to STDOUT.
452
453=cut
454
455sub show_backlinks {
456    my ($self, %args) = @_;
457    my $wiki = $self->wiki;
458    my $formatter = $wiki->formatter;
459
460    my @backlinks = $wiki->list_backlinks( node => $args{id} );
461    my @results = map {
462                          {
463                              url   => CGI->escape($formatter->node_name_to_node_param($_)),
464                              title => CGI->escapeHTML($_)
465                          }
466                      } sort @backlinks;
467    my %tt_vars = ( results       => \@results,
468                    num_results   => scalar @results,
469                    not_deletable => 1,
470                    deter_robots  => 1,
471                    not_editable  => 1 );
472    return %tt_vars if $args{return_tt_vars};
473    my $output = OpenGuides::Template->output(
474                                                 node    => $args{id},
475                                                 wiki    => $wiki,
476                                                 config  => $self->config,
477                                                 template=>"backlink_results.tt",
478                                                 vars    => \%tt_vars,
479                                             );
480    return $output if $args{return_output};
481    print $output;
482}
483
484=item B<show_index>
485
486  $guide->show_index(
487                        type   => "category",
488                        value  => "pubs",
489                    );
490
491  # RDF version.
492  $guide->show_index(
493                        type   => "locale",
494                        value  => "Holborn",
495                        format => "rdf",
496                    );
497
498  # RSS / Atom version (recent changes style).
499  $guide->show_index(
500                        type   => "locale",
501                        value  => "Holborn",
502                        format => "rss",
503                    );
504
505  # Or return output as a string (useful for writing tests).
506  $guide->show_index(
507                        type          => "category",
508                        value         => "pubs",
509                        return_output => 1,
510                    );
511
512=cut
513
514sub show_index {
515    my ($self, %args) = @_;
516    my $wiki = $self->wiki;
517    my $formatter = $wiki->formatter;
518    my %tt_vars;
519    my @selnodes;
520
521    if ( $args{type} and $args{value} ) {
522        if ( $args{type} eq "fuzzy_title_match" ) {
523            my %finds = $wiki->fuzzy_title_match( $args{value} );
524            @selnodes = sort { $finds{$a} <=> $finds{$b} } keys %finds;
525            $tt_vars{criterion} = {
526                type  => $args{type},  # for RDF version
527                value => $args{value}, # for RDF version
528                name  => CGI->escapeHTML("Fuzzy Title Match on '$args{value}'")
529            };
530            $tt_vars{not_editable} = 1;
531        } else {
532            @selnodes = $wiki->list_nodes_by_metadata(
533                metadata_type  => $args{type},
534                metadata_value => $args{value},
535                ignore_case    => 1
536            );
537            my $name = ucfirst($args{type}) . " $args{value}";
538            my $url = $self->config->script_name
539                      . "?"
540                      . ucfirst( $args{type} )
541                      . "_"
542                      . uri_escape(
543                                      $formatter->node_name_to_node_param($args{value})
544                                  );
545            $tt_vars{criterion} = {
546                type  => $args{type},
547                value => $args{value}, # for RDF version
548                name  => CGI->escapeHTML( $name ),
549                url   => $url
550            };
551            $tt_vars{not_editable} = 1;
552        }
553    } else {
554        @selnodes = $wiki->list_all_nodes();
555    }
556
557    my @nodes = map {
558                        {
559                            name      => $_,
560                            node_data => { $wiki->retrieve_node( name => $_ ) },
561                            param     => $formatter->node_name_to_node_param($_) }
562                        } sort @selnodes;
563
564    # Convert the lat+long to WGS84 as required
565    for(my $i=0; $i<scalar @nodes;$i++) {
566        my $node = $nodes[$i];
567        if($node) {
568            my %metadata = %{$node->{node_data}->{metadata}};
569            my ($wgs84_long, $wgs84_lat);
570            eval {
571                ($wgs84_long, $wgs84_lat) = OpenGuides::Utils->get_wgs84_coords(
572                                      longitude => $metadata{longitude}[0],
573                                      latitude => $metadata{latitude}[0],
574                                      config => $self->config);
575            };
576            warn $@." on ".$metadata{latitude}[0]." ".$metadata{longitude}[0] if $@;
577
578            push @{$nodes[$i]->{node_data}->{metadata}->{wgs84_long}}, $wgs84_long;
579            push @{$nodes[$i]->{node_data}->{metadata}->{wgs84_lat}},  $wgs84_lat;
580        }
581    }
582
583    $tt_vars{nodes} = \@nodes;
584
585    my ($template, %conf);
586
587    if ( $args{format} ) {
588        if ( $args{format} eq "rdf" ) {
589            $template = "rdf_index.tt";
590            $conf{content_type} = "application/rdf+xml";
591        }
592        elsif ( $args{format} eq "plain" ) {
593            $template = "plain_index.tt";
594            $conf{content_type} = "text/plain";
595        } elsif ( $args{format} eq "map" ) {
596            my $q = CGI->new;
597            $tt_vars{zoom} = $q->param('zoom') || '';
598            $tt_vars{lat} = $q->param('lat') || '';
599            $tt_vars{long} = $q->param('long') || '';
600            $tt_vars{centre_long} = $self->config->centre_long;
601            $tt_vars{centre_lat} = $self->config->centre_lat;
602            $tt_vars{default_gmaps_zoom} = $self->config->default_gmaps_zoom;
603            $tt_vars{enable_gmaps} = 1;
604            $tt_vars{display_google_maps} = 1; # override for this page
605            $template = "map_index.tt";
606           
607        } elsif( $args{format} eq "rss" || $args{format} eq "atom") {
608            # They really wanted a recent changes style rss/atom feed
609            my $feed_type = $args{format};
610            my ($feed,$content_type) = $self->get_feed_and_content_type($feed_type);
611            $feed->set_feed_name_and_url_params(
612                        "Index of $args{type} $args{value}",
613                        "action=index;index_type=$args{type};index_value=$args{value}"
614            );
615
616            # Grab the actual node data out of @nodes
617            my @node_data;
618            foreach my $node (@nodes) {
619                $node->{node_data}->{name} = $node->{name};
620                push @node_data, $node->{node_data};
621            }
622
623            my $output = "Content-Type: ".$content_type."\n";
624            $output .= $feed->build_feed_for_nodes($feed_type, @node_data);
625
626            return $output if $args{return_output};
627            print $output;
628            return;
629        }
630    } else {
631        $template = "site_index.tt";
632    }
633
634    %conf = (
635                %conf,
636                node        => "$args{type} index", # KLUDGE
637                template    => $template,
638                tt_vars     => \%tt_vars,
639            );
640
641    my $output = $self->process_template( %conf );
642    return $output if $args{return_output};
643    print $output;
644}
645
646=item B<list_all_versions>
647
648  $guide->list_all_versions ( id => "Home Page" );
649
650  # Or return output as a string (useful for writing tests).
651  $guide->list_all_versions (
652                                id            => "Home Page",
653                                return_output => 1,
654                            );
655
656  # Or return the hash of variables that will be passed to the template
657  # (not including those set additionally by OpenGuides::Template).
658  $guide->list_all_versions (
659                                id             => "Home Page",
660                                return_tt_vars => 1,
661                            );
662
663=cut
664
665sub list_all_versions {
666    my ($self, %args) = @_;
667    my $return_output = $args{return_output} || 0;
668    my $node = $args{id};
669    my %curr_data = $self->wiki->retrieve_node($node);
670    my $curr_version = $curr_data{version};
671    my @history;
672    for my $version ( 1 .. $curr_version ) {
673        my %node_data = $self->wiki->retrieve_node( name    => $node,
674                                                    version => $version );
675        # $node_data{version} will be zero if this version was deleted.
676        push @history, {
677            version  => CGI->escapeHTML( $version ),
678            modified => CGI->escapeHTML( $node_data{last_modified} ),
679            username => CGI->escapeHTML( $node_data{metadata}{username}[0] ),
680            comment  => CGI->escapeHTML( $node_data{metadata}{comment}[0] ),
681                       } if $node_data{version};
682    }
683    @history = reverse @history;
684    my %tt_vars = (
685                      node          => $node,
686                      version       => $curr_version,
687                      not_deletable => 1,
688                      not_editable  => 1,
689                      deter_robots  => 1,
690                      history       => \@history
691                  );
692    return %tt_vars if $args{return_tt_vars};
693    my $output = $self->process_template(
694                                            id       => $node,
695                                            template => "node_history.tt",
696                                            tt_vars  => \%tt_vars,
697                                        );
698    return $output if $return_output;
699    print $output;
700}
701
702=item B<get_feed_and_content_type>
703
704Fetch the OpenGuides feed object, and the output content type, for the
705supplied feed type.
706
707Handles all the setup for the OpenGuides feed object.
708=cut
709sub get_feed_and_content_type {
710    my ($self, $feed_type) = @_;
711
712    my $feed = OpenGuides::Feed->new(
713                                        wiki       => $self->wiki,
714                                        config     => $self->config,
715                                        og_version => $VERSION,
716                                    );
717
718    my $content_type = $feed->default_content_type($feed_type);
719
720    return ($feed, $content_type);
721}
722
723=item B<display_feed>
724
725  # Last ten non-minor edits to Hammersmith pages in RSS 1.0 format
726  $guide->display_feed(
727                         feed_type          => 'rss',
728                         feed_listing       => 'recent_changes',
729                         items              => 10,
730                         ignore_minor_edits => 1,
731                         locale             => "Hammersmith",
732                     );
733
734  # All edits bob has made to pub pages in the last week in Atom format
735  $guide->display_feed(
736                         feed_type    => 'atom',
737                         feed_listing => 'recent_changes',
738                         days         => 7,
739                         username     => "bob",
740                         category     => "Pubs",
741                     );
742
743C<feed_type> is a mandatory parameter. Supported values at present are
744"rss" and "atom".
745
746C<feed_listing> is a mandatory parameter. Supported values at present
747are "recent_changes". (More values are coming soon though!)
748
749As with other methods, the C<return_output> parameter can be used to
750return the output instead of printing it to STDOUT.
751
752=cut
753
754sub display_feed {
755    my ($self, %args) = @_;
756
757    my $feed_type = $args{feed_type};
758    croak "No feed type given" unless $feed_type;
759
760    my $feed_listing = $args{feed_listing};
761    croak "No feed listing given" unless $feed_listing;
762   
763    my $return_output = $args{return_output} ? 1 : 0;
764
765    # Basic criteria, whatever the feed listing type is
766    my %criteria = (
767                       feed_type             => $feed_type,
768                       feed_listing          => $feed_listing,
769                       also_return_timestamp => 1,
770                   );
771
772    # Feed listing specific criteria
773    if($feed_listing eq "recent_changes") {
774        $criteria{items} = $args{items} || "";
775        $criteria{days}  = $args{days}  || "";
776        $criteria{ignore_minor_edits} = $args{ignore_minor_edits} ? 1 : 0;
777
778        my $username = $args{username} || "";
779        my $category = $args{category} || "";
780        my $locale   = $args{locale}   || "";
781
782        my %filter;
783        $filter{username} = $username if $username;
784        $filter{category} = $category if $category;
785        $filter{locale}   = $locale   if $locale;
786        if ( scalar keys %filter ) {
787            $criteria{filter_on_metadata} = \%filter;
788        }
789    }
790    elsif($feed_listing eq "node_all_versions") {
791        $criteria{name} = $args{name};
792    }
793
794
795    # Get the feed object, and the content type
796    my ($feed,$content_type) = $self->get_feed_and_content_type($feed_type);
797
798    my $output = "Content-Type: ".$content_type."\n";
799   
800    # Get the feed, and the timestamp, in one go
801    my ($feed_output, $feed_timestamp) = 
802        $feed->make_feed( %criteria );
803
804    $output .= "Last-Modified: " . $feed_timestamp . "\n\n";
805    $output .= $feed_output;
806
807    return $output if $return_output;
808    print $output;
809}
810
811sub display_about {
812    my ($self, %args) = @_;
813
814    my $output;
815
816    if ($args{format} && $args{format} =~ /^rdf$/i) {
817        $output = qq{Content-Type: application/rdf+xml
818
819<?xml version="1.0" encoding="UTF-8"?>
820<rdf:RDF xmlns      = "http://usefulinc.com/ns/doap#"
821         xmlns:rdf  = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
822         xmlns:foaf = "http://xmlns.com/foaf/0.1/">
823<Project rdf:ID="OpenGuides">
824  <name>OpenGuides</name>
825
826  <created>2003-04-29</created>
827 
828  <shortdesc xml:lang="en">
829    A wiki engine for collaborative description of places with specialised
830    geodata metadata features.
831  </shortdesc>
832
833  <description xml:lang="en">
834    OpenGuides is a collaborative wiki environment, written in Perl, for
835    building guides and sharing information, as both human-readable text
836    and RDF. The engine contains a number of geodata-specific metadata
837    mechanisms such as locale search, node classification and integration
838    with Google Maps.
839  </description>
840
841  <homepage rdf:resource="http://openguides.org/" />
842  <mailing-list rdf:resource="http://openguides.org/mm/listinfo/openguides-dev/" />
843  <mailing-list rdf:resource="http://urchin.earth.li/mailman/listinfo/openguides-commits/" />
844
845  <maintainer>
846    <foaf:Person rdf:ID="OpenGuidesMaintainer">
847      <foaf:name>Dominic Hargreaves</foaf:name>
848      <foaf:homepage rdf:resource="http://www.larted.org.uk/~dom/" />
849    </foaf:Person>
850  </maintainer>
851
852  <repository>
853    <SVNRepository rdf:ID="OpenGuidesSVN">
854      <location rdf:resource="https://urchin.earth.li/svn/openguides/" />
855      <browse rdf:resource="http://dev.openguides.org/browser" />
856    </SVNRepository>
857  </repository>
858
859  <release>
860    <Version rdf:ID="OpenGuidesVersion">
861      <revision>$VERSION</revision>
862    </Version>
863  </release>
864
865  <download-page rdf:resource="http://search.cpan.org/dist/OpenGuides/" />
866 
867  <!-- Freshmeat category: Internet :: WWW/HTTP :: Dynamic Content -->
868  <category rdf:resource="http://freshmeat.net/browse/92/" />
869 
870  <license rdf:resource="http://www.opensource.org/licenses/gpl-license.php" />
871  <license rdf:resource="http://www.opensource.org/licenses/artistic-license.php" />
872
873</Project>
874
875</rdf:RDF>};
876    }
877    else {
878        my $site_name  = $self->config->{site_name};
879        my $script_name = $self->config->{script_name};
880        $output = qq{Content-Type: text/html; charset=utf-8
881
882<html>
883<head>
884  <title>About $site_name</title>
885<style type="text/css">
886body        { margin: 0px; }
887#content    { padding: 50px; margin: auto; width: 50%; }
888h1          { margin-bottom: 0px; font-style: italic; }
889h2          { margin-top: 0px; }
890#logo       { text-align: center; }
891#about      { margin: 0em 0em 1em 0em; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }
892#meta       { font-size: small; text-align: center;}
893</style>
894<link rel="alternate"
895  type="application/rdf+xml"
896  title="DOAP (Description Of A Project) profile for this site's software"
897  href="$script_name?action=about;format=rdf" />
898</head>
899<body>
900<div id="content">
901<div id="logo">
902<a href="http://openguides.org/"><img
903src="http://openguides.org/img/logo.png" alt="OpenGuides"></a>
904<h1><a href="$script_name">$site_name</a></h1>
905<h2>is powered by <a href="http://openguides.org/">OpenGuides</a> -<br>
906the guides made by you.</h2>
907<h3>version <a href="http://search.cpan.org/~dom/OpenGuides-$VERSION">$VERSION</a></h3>
908</div>
909<div id="about">
910<p>
911<a href="http://www.w3.org/RDF/"><img
912src="http://openguides.org/img/rdf_icon.png" width="44" height="48"
913style="float: right; margin-left: 10px; border: 0px"></a> OpenGuides is a
914web-based collaborative <a href="http://wiki.org/wiki.cgi?WhatIsWiki">wiki</a>
915environment for building guides and sharing information, as both
916human-readable text and <a href="http://www.w3.org/RDF/"><acronym
917title="Resource Description Framework">RDF</acronym></a>. The engine contains
918a number of geodata-specific metadata mechanisms such as locale search, node
919classification and integration with <a href="http://maps.google.com/">Google
920Maps</a>.
921</p>
922<p>
923OpenGuides is written in <a href="http://www.perl.org/">Perl</a>, and is
924made available under the same license as Perl itself (dual <a
925href="http://dev.perl.org/licenses/artistic.html" title='The "Artistic Licence"'>Artistic</a> and <a
926href="http://www.opensource.org/licenses/gpl-license.php"><acronym
927title="GNU Public Licence">GPL</acronym></a>). Developer information for the
928project is available from the <a href="http://dev.openguides.org/">OpenGuides
929development site</a>.
930</p>
931<p>
932Copyright &copy;2003-2006, <a href="http://openguides.org/">The OpenGuides
933Project</a>. "OpenGuides", "[The] Open Guide To..." and "The guides made by
934you" are trademarks of The OpenGuides Project. Any uses on this site are made
935with permission.
936</p>
937</div>
938<div id="meta">
939<a href="$script_name?action=about;format=rdf"><acronym
940title="Description Of A Project">DOAP</acronym> RDF version of this
941information</a>
942</div>
943</div>
944</body>
945</html>};
946    }
947   
948    return $output if $args{return_output};
949    print $output;
950}
951
952=item B<commit_node>
953
954  $guide->commit_node(
955                         id      => $node,
956                         cgi_obj => $q,
957                     );
958
959As with other methods, parameters C<return_tt_vars> and
960C<return_output> can be used to return these things instead of
961printing the output to STDOUT.
962
963The geographical data that you should provide in the L<CGI> object
964depends on the handler you chose in C<wiki.conf>.
965
966=over
967
968=item *
969
970B<British National Grid> - provide either C<os_x> and C<os_y> or
971C<latitude> and C<longitude>; whichever set of data you give, it will
972be converted to the other and both sets will be stored.
973
974=item *
975
976B<Irish National Grid> - provide either C<osie_x> and C<osie_y> or
977C<latitude> and C<longitude>; whichever set of data you give, it will
978be converted to the other and both sets will be stored.
979
980=item *
981
982B<UTM ellipsoid> - provide C<latitude> and C<longitude>; these will be
983converted to easting and northing and both sets of data will be stored.
984
985=back
986
987=cut
988
989sub commit_node {
990    my ($self, %args) = @_;
991    my $node = $args{id};
992    my $q = $args{cgi_obj};
993    my $return_output = $args{return_output};
994    my $wiki = $self->wiki;
995    my $config = $self->config;
996
997    my $content  = $q->param("content");
998    $content =~ s/\r\n/\n/gs;
999    my $checksum = $q->param("checksum");
1000
1001    my %metadata = OpenGuides::Template->extract_metadata_vars(
1002        wiki    => $wiki,
1003        config  => $config,
1004        cgi_obj => $q
1005    );
1006
1007    delete $metadata{website} if $metadata{website} eq 'http://';
1008
1009    $metadata{opening_hours_text} = $q->param("hours_text") || "";
1010
1011    # Pick out the unmunged versions of lat/long if they're set.
1012    # (If they're not, it means they weren't munged in the first place.)
1013    $metadata{latitude} = delete $metadata{latitude_unmunged}
1014        if $metadata{latitude_unmunged};
1015    $metadata{longitude} = delete $metadata{longitude_unmunged}
1016        if $metadata{longitude_unmunged};
1017
1018    # Check to make sure all the indexable nodes are created
1019    # Skip this for nodes needing moderation - this occurs for them once
1020    #  they've been moderated
1021    unless($wiki->node_required_moderation($node)) {
1022        $self->_autoCreateCategoryLocale(
1023                                          id       => $node,
1024                                          metadata => \%metadata
1025        );
1026    }
1027   
1028    foreach my $var ( qw( summary username comment edit_type ) ) {
1029        $metadata{$var} = $q->param($var) || "";
1030    }
1031    $metadata{host} = $ENV{REMOTE_ADDR};
1032
1033    # Wiki::Toolkit::Plugin::RSS::ModWiki wants "major_change" to be set.
1034    $metadata{major_change} = ( $metadata{edit_type} eq "Normal edit" )
1035                            ? 1
1036                            : 0;
1037
1038    my $written = $wiki->write_node($node, $content, $checksum, \%metadata );
1039
1040    if ($written) {
1041        my $output = $self->redirect_to_node($node);
1042        return $output if $return_output;
1043        print $output;
1044    } else {
1045        my %node_data = $wiki->retrieve_node($node);
1046        my %tt_vars = ( checksum       => $node_data{checksum},
1047                        new_content    => $content,
1048                        stored_content => $node_data{content} );
1049        foreach my $mdvar ( keys %metadata ) {
1050            if ($mdvar eq "locales") {
1051                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{locale};
1052                $tt_vars{"new_$mdvar"}    = $metadata{locale};
1053            } elsif ($mdvar eq "categories") {
1054                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{category};
1055                $tt_vars{"new_$mdvar"}    = $metadata{category};
1056            } elsif ($mdvar eq "username" or $mdvar eq "comment"
1057                      or $mdvar eq "edit_type" ) {
1058                $tt_vars{$mdvar} = $metadata{$mdvar};
1059            } else {
1060                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{$mdvar}[0];
1061                $tt_vars{"new_$mdvar"}    = $metadata{$mdvar};
1062            }
1063        }
1064        return %tt_vars if $args{return_tt_vars};
1065        my $output = $self->process_template(
1066                                              id       => $node,
1067                                              template => "edit_conflict.tt",
1068                                              tt_vars  => \%tt_vars,
1069                                            );
1070        return $output if $args{return_output};
1071        print $output;
1072    }
1073}
1074
1075=item B<_autoCreateCategoryLocale>
1076
1077  $guide->_autoCreateCategoryLocale(
1078                         id       => "FAQ",
1079                         metadata => \%metadata,
1080                     );
1081
1082When a new node is added, or a previously un-moderated node is moderated,
1083identifies if any of its Categories or Locales are missing, and creates them.
1084
1085For nodes not requiring moderation, should be called on writing the node
1086For nodes requiring moderation, should only be called on moderation
1087=cut
1088sub _autoCreateCategoryLocale {
1089    my ($self, %args) = @_;
1090
1091    my $wiki = $self->wiki;
1092    my $id = $args{'id'};
1093    my %metadata = %{$args{'metadata'}};
1094
1095    # Check to make sure all the indexable nodes are created
1096    foreach my $type (qw(Category Locale)) {
1097        my $lctype = lc($type);
1098        foreach my $index (@{$metadata{$lctype}}) {
1099            $index =~ s/(.*)/\u$1/;
1100            my $node = $type . " " . $index;
1101            # Uppercase the node name before checking for existence
1102            $node =~ s/ (\S+)/ \u$1/g;
1103            unless ( $wiki->node_exists($node) ) {
1104                my $category = $type eq "Category" ? "Category" : "Locales";
1105                $wiki->write_node(
1106                                     $node,
1107                                     "\@INDEX_LINK [[$node]]",
1108                                     undef,
1109                                     {
1110                                         username => "Auto Create",
1111                                         comment  => "Auto created $lctype stub page",
1112                                         category => $category
1113                                     }
1114                );
1115            }
1116        }
1117    }
1118}
1119
1120
1121=item B<delete_node>
1122
1123  $guide->delete_node(
1124                         id       => "FAQ",
1125                         version  => 15,
1126                         password => "beer",
1127                     );
1128
1129C<version> is optional - if it isn't supplied then all versions of the
1130node will be deleted; in other words the node will be entirely
1131removed.
1132
1133If C<password> is not supplied then a form for entering the password
1134will be displayed.
1135
1136As with other methods, parameters C<return_tt_vars> and
1137C<return_output> can be used to return these things instead of
1138printing the output to STDOUT.
1139
1140=cut
1141
1142sub delete_node {
1143    my ($self, %args) = @_;
1144    my $node = $args{id} or croak "No node ID supplied for deletion";
1145    my $return_tt_vars = $args{return_tt_vars} || 0;
1146    my $return_output = $args{return_output} || 0;
1147
1148    my %tt_vars = (
1149                      not_editable  => 1,
1150                      not_deletable => 1,
1151                      deter_robots  => 1,
1152                  );
1153    $tt_vars{delete_version} = $args{version} || "";
1154
1155    my $password = $args{password};
1156
1157    if ($password) {
1158        if ($password ne $self->config->admin_pass) {
1159            return %tt_vars if $return_tt_vars;
1160            my $output = $self->process_template(
1161                                                    id       => $node,
1162                                                    template => "delete_password_wrong.tt",
1163                                                    tt_vars  => \%tt_vars,
1164                                                );
1165            return $output if $return_output;
1166            print $output;
1167        } else {
1168            $self->wiki->delete_node(
1169                                        name    => $node,
1170                                        version => $args{version},
1171                                    );
1172            # Check whether any versions of this node remain.
1173            my %check = $self->wiki->retrieve_node( name => $node );
1174            $tt_vars{other_versions_remain} = 1 if $check{version};
1175            return %tt_vars if $return_tt_vars;
1176            my $output = $self->process_template(
1177                                                    id       => $node,
1178                                                    template => "delete_done.tt",
1179                                                    tt_vars  => \%tt_vars,
1180                                                );
1181            return $output if $return_output;
1182            print $output;
1183        }
1184    } else {
1185        return %tt_vars if $return_tt_vars;
1186        my $output = $self->process_template(
1187                                                id       => $node,
1188                                                template => "delete_confirm.tt",
1189                                                tt_vars  => \%tt_vars,
1190                                            );
1191        return $output if $return_output;
1192        print $output;
1193    }
1194}
1195
1196=item B<set_node_moderation>
1197
1198  $guide->set_node_moderation(
1199                         id       => "FAQ",
1200                         password => "beer",
1201                         moderation_flag => 1,
1202                     );
1203
1204Sets the moderation needed flag on a node, either on or off.
1205
1206If C<password> is not supplied then a form for entering the password
1207will be displayed.
1208=cut
1209sub set_node_moderation {
1210    my ($self, %args) = @_;
1211    my $node = $args{id} or croak "No node ID supplied for node moderation";
1212    my $return_tt_vars = $args{return_tt_vars} || 0;
1213    my $return_output = $args{return_output} || 0;
1214
1215    # Get the moderation flag into something sane
1216    if($args{moderation_flag} eq "1" || $args{moderation_flag} eq "yes" ||
1217       $args{moderation_flag} eq "on" || $args{moderation_flag} eq "true") {
1218        $args{moderation_flag} = 1;
1219    } else {
1220        $args{moderation_flag} = 0;
1221    }
1222
1223    # Set up the TT variables
1224    my %tt_vars = (
1225                      not_editable  => 1,
1226                      not_deletable => 1,
1227                      deter_robots  => 1,
1228                      moderation_action => 'set_moderation',
1229                      moderation_flag   => $args{moderation_flag},
1230                      moderation_url_args => 'action=set_moderation;moderation_flag='.$args{moderation_flag},
1231                  );
1232
1233    my $password = $args{password};
1234
1235    if ($password) {
1236        if ($password ne $self->config->admin_pass) {
1237            return %tt_vars if $return_tt_vars;
1238            my $output = $self->process_template(
1239                                                    id       => $node,
1240                                                    template => "moderate_password_wrong.tt",
1241                                                    tt_vars  => \%tt_vars,
1242                                                );
1243            return $output if $return_output;
1244            print $output;
1245        } else {
1246            $self->wiki->set_node_moderation(
1247                                        name    => $node,
1248                                        required => $args{moderation_flag},
1249                                    );
1250
1251            # Send back to the admin interface
1252            my $script_url = $self->config->script_url;
1253            my $script_name = $self->config->script_name;
1254            my $q = CGI->new;
1255            my $output = $q->redirect( $script_url.$script_name."?action=admin;moderation=changed" );
1256            return $output if $return_output;
1257            print $output;
1258        }
1259    } else {
1260        return %tt_vars if $return_tt_vars;
1261        my $output = $self->process_template(
1262                                                id       => $node,
1263                                                template => "moderate_confirm.tt",
1264                                                tt_vars  => \%tt_vars,
1265                                            );
1266        return $output if $return_output;
1267        print $output;
1268    }
1269}
1270
1271=item B<moderate_node>
1272
1273  $guide->moderate_node(
1274                         id       => "FAQ",
1275                         version  => 12,
1276                         password => "beer",
1277                     );
1278
1279Marks a version of a node as moderated. Will also auto-create and Locales
1280and Categories for the newly moderated version.
1281
1282If C<password> is not supplied then a form for entering the password
1283will be displayed.
1284=cut
1285sub moderate_node {
1286    my ($self, %args) = @_;
1287    my $node = $args{id} or croak "No node ID supplied for node moderation";
1288    my $version = $args{version} or croak "No node version supplied for node moderation";
1289    my $return_tt_vars = $args{return_tt_vars} || 0;
1290    my $return_output = $args{return_output} || 0;
1291
1292    # Set up the TT variables
1293    my %tt_vars = (
1294                      not_editable  => 1,
1295                      not_deletable => 1,
1296                      deter_robots  => 1,
1297                      version       => $version,
1298                      moderation_action => 'moderate',
1299                      moderation_url_args => 'action=moderate;version='.$version
1300                  );
1301
1302    my $password = $args{password};
1303    unless($self->config->moderation_requires_password) {
1304        $password = $self->config->admin_pass;
1305    }
1306
1307    if ($password) {
1308        if ($password ne $self->config->admin_pass) {
1309            return %tt_vars if $return_tt_vars;
1310            my $output = $self->process_template(
1311                                                    id       => $node,
1312                                                    template => "moderate_password_wrong.tt",
1313                                                    tt_vars  => \%tt_vars,
1314                                                );
1315            return $output if $return_output;
1316            print $output;
1317        } else {
1318            $self->wiki->moderate_node(
1319                                        name    => $node,
1320                                        version => $version
1321                                    );
1322
1323            # Create any categories or locales for it
1324            my %details = $self->wiki->retrieve_node(
1325                                        name    => $node,
1326                                        version => $version
1327                                    );
1328            $self->_autoCreateCategoryLocale(
1329                                          id       => $node,
1330                                          metadata => $details{'metadata'}
1331            );
1332
1333            # Send back to the admin interface
1334            my $script_url = $self->config->script_url;
1335            my $script_name = $self->config->script_name;
1336            my $q = CGI->new;
1337            my $output = $q->redirect( $script_url.$script_name."?action=admin;moderation=moderated" );
1338            return $output if $return_output;
1339            print $output;
1340        }
1341    } else {
1342        return %tt_vars if $return_tt_vars;
1343        my $output = $self->process_template(
1344                                                id       => $node,
1345                                                template => "moderate_confirm.tt",
1346                                                tt_vars  => \%tt_vars,
1347                                            );
1348        return $output if $return_output;
1349        print $output;
1350    }
1351}
1352
1353=item B<show_missing_metadata>
1354Search for nodes which don't have a certain kind of metadata. Optionally
1355also excludes Locales and Categories
1356=cut
1357sub show_missing_metadata {
1358    my ($self, %args) = @_;
1359    my $return_tt_vars = $args{return_tt_vars} || 0;
1360    my $return_output = $args{return_output} || 0;
1361
1362    my $wiki = $self->wiki;
1363    my $formatter = $self->wiki->formatter;
1364    my $script_name = $self->config->script_name;
1365
1366    my ($metadata_type, $metadata_value, $exclude_locales, $exclude_categories)
1367        = @args{ qw( metadata_type metadata_value exclude_locales exclude_categories ) };
1368
1369    my @nodes;
1370    my $done_search = 0;
1371
1372    # Only search if they supplied at least a metadata type
1373    if($metadata_type) {
1374        $done_search = 1;
1375        @nodes = $wiki->list_nodes_by_missing_metadata(
1376                            metadata_type => $metadata_type,
1377                            metadata_value => $metadata_value,
1378                            ignore_case    => 1,
1379        );
1380
1381        # Do we need to filter some nodes out?
1382        if($exclude_locales || $exclude_categories) {
1383            my @all_nodes = @nodes;
1384            @nodes = ();
1385
1386            foreach my $node (@all_nodes) {
1387                if($exclude_locales && $node =~ /^Locale /) { next; }
1388                if($exclude_categories && $node =~ /^Category /) { next; }
1389                push @nodes, $node;
1390            }
1391        }
1392    }
1393
1394    # Build nice edit etc links for our nodes
1395    my @tt_nodes;
1396    for my $node (@nodes) {
1397        my %n;
1398
1399        # Make the URLs
1400        my $node_param = uri_escape( $formatter->node_name_to_node_param( $node ) );
1401
1402        # Save into the hash
1403        $n{'name'} = $node;
1404        $n{'view_url'} = $script_name . "?id=" . $node_param;
1405        $n{'edit_url'} = $script_name . "?id=" . $node_param . ";action=edit";
1406        push @tt_nodes, \%n;
1407    }
1408
1409    # Set up our TT variables, including the search parameters
1410    my %tt_vars = (
1411                      not_editable  => 1,
1412                      not_deletable => 1,
1413                      deter_robots  => 1,
1414
1415                      nodes => \@tt_nodes,
1416                      done_search    => $done_search,
1417                      metadata_type  => $metadata_type,
1418                      metadata_value => $metadata_value,
1419                      exclude_locales => $exclude_locales,
1420                      exclude_categories => $exclude_categories
1421                  );
1422    return %tt_vars if $return_tt_vars;
1423
1424    # Render to the page
1425    my $output = $self->process_template(
1426                                           id       => "",
1427                                           template => "missing_metadata.tt",
1428                                           tt_vars  => \%tt_vars,
1429                                        );
1430    return $output if $return_output;
1431    print $output;
1432}
1433
1434=item B<display_admin_interface>
1435Fetch everything we need to display the admin interface, and passes it off
1436 to the template
1437=cut
1438sub display_admin_interface {
1439    my ($self, %args) = @_;
1440    my $return_tt_vars = $args{return_tt_vars} || 0;
1441    my $return_output = $args{return_output} || 0;
1442
1443    my $wiki = $self->wiki;
1444    my $formatter = $self->wiki->formatter;
1445    my $script_name = $self->config->script_name;
1446
1447    # Grab all the recent nodes
1448    my @all_nodes = $wiki->list_recent_changes(last_n_changes => 100);
1449
1450    # Split into nodes, Locales and Categories
1451    my @nodes;
1452    my @categories;
1453    my @locales;
1454    for my $node (@all_nodes) {
1455        # Add moderation status
1456        $node->{'moderate'} = $wiki->node_required_moderation($node->{'name'});
1457
1458        # Make the URLs
1459        my $node_param = uri_escape( $formatter->node_name_to_node_param( $node->{'name'} ) );
1460        $node->{'view_url'} = $script_name . "?id=" . $node_param;
1461        $node->{'versions_url'} = $script_name .
1462                        "?action=list_all_versions;id=" . $node_param;
1463        $node->{'moderation_url'} = $script_name .
1464                        "?action=set_moderation;id=" . $node_param;
1465
1466        # Filter
1467        if($node->{'name'} =~ /^Category /) {
1468            $node->{'page_name'} = $node->{'name'};
1469            $node->{'name'} =~ s/^Category //;
1470            push @categories, $node;
1471        } elsif($node->{'name'} =~ /^Locale /) {
1472            $node->{'page_name'} = $node->{'name'};
1473            $node->{'name'} =~ s/^Locale //;
1474            push @locales, $node;
1475        } else {
1476            push @nodes, $node;
1477        }
1478    }
1479
1480    # Handle completed notice for actions
1481    my $completed_action = "";
1482    if($args{moderation_completed}) {
1483        if($args{moderation_completed} eq "moderation") {
1484            $completed_action = "Version moderated";
1485        }
1486        if($args{moderation_completed} eq "changed") {
1487            $completed_action = "Node moderation flag changed";
1488        }
1489    }
1490
1491    # Render in a template
1492    my %tt_vars = (
1493                      not_editable  => 1,
1494                      not_deletable => 1,
1495                      deter_robots  => 1,
1496                      nodes      => \@nodes,
1497                      categories => \@categories,
1498                      locales    => \@locales,
1499                      completed_action => $completed_action
1500                  );
1501    return %tt_vars if $return_tt_vars;
1502    my $output = $self->process_template(
1503                                           id       => "",
1504                                           template => "admin_home.tt",
1505                                           tt_vars  => \%tt_vars,
1506                                        );
1507    return $output if $return_output;
1508    print $output;
1509}
1510
1511sub process_template {
1512    my ($self, %args) = @_;
1513    my %output_conf = (
1514                          wiki     => $self->wiki,
1515                          config   => $self->config,
1516                          node     => $args{id},
1517                          template => $args{template},
1518                          vars     => $args{tt_vars},
1519                          cookies  => $args{cookies},
1520                      );
1521    if ( $args{content_type} ) {
1522        $output_conf{content_type} = $args{content_type};
1523    }
1524    return OpenGuides::Template->output( %output_conf );
1525}
1526
1527sub redirect_to_node {
1528    my ($self, $node, $redirected_from) = @_;
1529   
1530    my $script_url = $self->config->script_url;
1531    my $script_name = $self->config->script_name;
1532    my $formatter = $self->wiki->formatter;
1533
1534    my $id = $formatter->node_name_to_node_param( $node );
1535    my $oldid;
1536    $oldid = $formatter->node_name_to_node_param( $redirected_from ) if $redirected_from;
1537
1538    my $redir_param = "$script_url$script_name?";
1539    $redir_param .= 'id=' if $oldid;
1540    $redir_param .= $id;
1541    $redir_param .= ";oldid=$oldid" if $oldid;
1542   
1543    my $q = CGI->new;
1544    return $q->redirect( $redir_param );
1545}
1546
1547sub get_cookie {
1548    my $self = shift;
1549    my $config = $self->config;
1550    my $pref_name = shift or return "";
1551    my %cookie_data = OpenGuides::CGI->get_prefs_from_cookie(config=>$config);
1552    return $cookie_data{$pref_name};
1553}
1554
1555
1556=head1 BUGS AND CAVEATS
1557
1558UTF8 data are currently not handled correctly throughout.
1559
1560Other bugs are documented at
1561L<http://dev.openguides.org/>
1562
1563=head1 SEE ALSO
1564
1565=over 4
1566
1567=item * L<http://london.openguides.org/|The Open Guide to London>, the first and biggest OpenGuides site.
1568
1569=item * L<http://openguides.org/|The OpenGuides website>, with a list of all live OpenGuides installs.
1570
1571=item * L<Wiki::Toolkit>, the Wiki toolkit which does the heavy lifting for OpenGuides
1572
1573=back
1574
1575=head1 FEEDBACK
1576
1577If you have a question, a bug report, or a patch, or you're interested
1578in joining the development team, please contact openguides-dev@openguides.org
1579(moderated mailing list, will reach all current developers but you'll have
1580to wait for your post to be approved) or file a bug report at
1581L<http://dev.openguides.org/>
1582
1583=head1 AUTHOR
1584
1585The OpenGuides Project (openguides-dev@openguides.org)
1586
1587=head1 COPYRIGHT
1588
1589     Copyright (C) 2003-2006 The OpenGuides Project.  All Rights Reserved.
1590
1591The OpenGuides distribution is free software; you can redistribute it
1592and/or modify it under the same terms as Perl itself.
1593
1594=head1 CREDITS
1595
1596Programming by Dominic Hargreaves, Earle Martin, Kake Pugh, and Ivor
1597Williams.  Testing and bug reporting by Billy Abbott, Jody Belka,
1598Kerry Bosworth, Simon Cozens, Cal Henderson, Steve Jolly, and Bob
1599Walker (among others).  Much of the Module::Build stuff copied from
1600the Siesta project L<http://siesta.unixbeard.net/>
1601
1602=cut
1603
16041;
Note: See TracBrowser for help on using the repository browser.