source: trunk/lib/OpenGuides.pm @ 856

Last change on this file since 856 was 856, checked in by Dominic Hargreaves, 15 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
File size: 56.3 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{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        if $metadata{latitude_unmunged};
995    $metadata{longitude} = delete $metadata{longitude_unmunged}
996        if $metadata{longitude_unmunged};
997
998    # Check to make sure all the indexable nodes are created
999    # Skip this for nodes needing moderation - this occurs for them once
1000    #  they've been moderated
1001    unless($wiki->node_required_moderation($node)) {
1002        $self->_autoCreateCategoryLocale(
1003                                          id       => $node,
1004                                          metadata => \%metadata
1005        );
1006    }
1007   
1008    foreach my $var ( qw( summary username comment edit_type ) ) {
1009        $metadata{$var} = $q->param($var) || "";
1010    }
1011    $metadata{host} = $ENV{REMOTE_ADDR};
1012
1013    # Wiki::Toolkit::Plugin::RSS::ModWiki wants "major_change" to be set.
1014    $metadata{major_change} = ( $metadata{edit_type} eq "Normal edit" )
1015                            ? 1
1016                            : 0;
1017
1018    my $written = $wiki->write_node($node, $content, $checksum, \%metadata );
1019
1020    if ($written) {
1021        my $output = $self->redirect_to_node($node);
1022        return $output if $return_output;
1023        print $output;
1024    } else {
1025        my %node_data = $wiki->retrieve_node($node);
1026        my %tt_vars = ( checksum       => $node_data{checksum},
1027                        new_content    => $content,
1028                        stored_content => $node_data{content} );
1029        foreach my $mdvar ( keys %metadata ) {
1030            if ($mdvar eq "locales") {
1031                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{locale};
1032                $tt_vars{"new_$mdvar"}    = $metadata{locale};
1033            } elsif ($mdvar eq "categories") {
1034                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{category};
1035                $tt_vars{"new_$mdvar"}    = $metadata{category};
1036            } elsif ($mdvar eq "username" or $mdvar eq "comment"
1037                      or $mdvar eq "edit_type" ) {
1038                $tt_vars{$mdvar} = $metadata{$mdvar};
1039            } else {
1040                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{$mdvar}[0];
1041                $tt_vars{"new_$mdvar"}    = $metadata{$mdvar};
1042            }
1043        }
1044        return %tt_vars if $args{return_tt_vars};
1045        my $output = $self->process_template(
1046                                              id       => $node,
1047                                              template => "edit_conflict.tt",
1048                                              tt_vars  => \%tt_vars,
1049                                            );
1050        return $output if $args{return_output};
1051        print $output;
1052    }
1053}
1054
1055=item B<_autoCreateCategoryLocale>
1056
1057  $guide->_autoCreateCategoryLocale(
1058                         id       => "FAQ",
1059                         metadata => \%metadata,
1060                     );
1061
1062When a new node is added, or a previously un-moderated node is moderated,
1063identifies if any of its Categories or Locales are missing, and creates them.
1064
1065For nodes not requiring moderation, should be called on writing the node
1066For nodes requiring moderation, should only be called on moderation
1067=cut
1068sub _autoCreateCategoryLocale {
1069    my ($self, %args) = @_;
1070
1071    my $wiki = $self->wiki;
1072    my $id = $args{'id'};
1073    my %metadata = %{$args{'metadata'}};
1074
1075    # Check to make sure all the indexable nodes are created
1076    foreach my $type (qw(Category Locale)) {
1077        my $lctype = lc($type);
1078        foreach my $index (@{$metadata{$lctype}}) {
1079            $index =~ s/(.*)/\u$1/;
1080            my $node = $type . " " . $index;
1081            # Uppercase the node name before checking for existence
1082            $node =~ s/ (\S+)/ \u$1/g;
1083            unless ( $wiki->node_exists($node) ) {
1084                my $category = $type eq "Category" ? "Category" : "Locales";
1085                $wiki->write_node(
1086                                     $node,
1087                                     "\@INDEX_LINK [[$node]]",
1088                                     undef,
1089                                     {
1090                                         username => "Auto Create",
1091                                         comment  => "Auto created $lctype stub page",
1092                                         category => $category
1093                                     }
1094                );
1095            }
1096        }
1097    }
1098}
1099
1100
1101=item B<delete_node>
1102
1103  $guide->delete_node(
1104                         id       => "FAQ",
1105                         version  => 15,
1106                         password => "beer",
1107                     );
1108
1109C<version> is optional - if it isn't supplied then all versions of the
1110node will be deleted; in other words the node will be entirely
1111removed.
1112
1113If C<password> is not supplied then a form for entering the password
1114will be displayed.
1115
1116As with other methods, parameters C<return_tt_vars> and
1117C<return_output> can be used to return these things instead of
1118printing the output to STDOUT.
1119
1120=cut
1121
1122sub delete_node {
1123    my ($self, %args) = @_;
1124    my $node = $args{id} or croak "No node ID supplied for deletion";
1125    my $return_tt_vars = $args{return_tt_vars} || 0;
1126    my $return_output = $args{return_output} || 0;
1127
1128    my %tt_vars = (
1129                      not_editable  => 1,
1130                      not_deletable => 1,
1131                      deter_robots  => 1,
1132                  );
1133    $tt_vars{delete_version} = $args{version} || "";
1134
1135    my $password = $args{password};
1136
1137    if ($password) {
1138        if ($password ne $self->config->admin_pass) {
1139            return %tt_vars if $return_tt_vars;
1140            my $output = $self->process_template(
1141                                                    id       => $node,
1142                                                    template => "delete_password_wrong.tt",
1143                                                    tt_vars  => \%tt_vars,
1144                                                );
1145            return $output if $return_output;
1146            print $output;
1147        } else {
1148            $self->wiki->delete_node(
1149                                        name    => $node,
1150                                        version => $args{version},
1151                                    );
1152            # Check whether any versions of this node remain.
1153            my %check = $self->wiki->retrieve_node( name => $node );
1154            $tt_vars{other_versions_remain} = 1 if $check{version};
1155            return %tt_vars if $return_tt_vars;
1156            my $output = $self->process_template(
1157                                                    id       => $node,
1158                                                    template => "delete_done.tt",
1159                                                    tt_vars  => \%tt_vars,
1160                                                );
1161            return $output if $return_output;
1162            print $output;
1163        }
1164    } else {
1165        return %tt_vars if $return_tt_vars;
1166        my $output = $self->process_template(
1167                                                id       => $node,
1168                                                template => "delete_confirm.tt",
1169                                                tt_vars  => \%tt_vars,
1170                                            );
1171        return $output if $return_output;
1172        print $output;
1173    }
1174}
1175
1176=item B<set_node_moderation>
1177
1178  $guide->set_node_moderation(
1179                         id       => "FAQ",
1180                         password => "beer",
1181                         moderation_flag => 1,
1182                     );
1183
1184Sets the moderation needed flag on a node, either on or off.
1185
1186If C<password> is not supplied then a form for entering the password
1187will be displayed.
1188=cut
1189sub set_node_moderation {
1190    my ($self, %args) = @_;
1191    my $node = $args{id} or croak "No node ID supplied for node moderation";
1192    my $return_tt_vars = $args{return_tt_vars} || 0;
1193    my $return_output = $args{return_output} || 0;
1194
1195    # Get the moderation flag into something sane
1196    if($args{moderation_flag} eq "1" || $args{moderation_flag} eq "yes" ||
1197       $args{moderation_flag} eq "on" || $args{moderation_flag} eq "true") {
1198        $args{moderation_flag} = 1;
1199    } else {
1200        $args{moderation_flag} = 0;
1201    }
1202
1203    # Set up the TT variables
1204    my %tt_vars = (
1205                      not_editable  => 1,
1206                      not_deletable => 1,
1207                      deter_robots  => 1,
1208                      moderation_action => 'set_moderation',
1209                      moderation_flag   => $args{moderation_flag},
1210                      moderation_url_args => 'action=set_moderation;moderation_flag='.$args{moderation_flag},
1211                  );
1212
1213    my $password = $args{password};
1214
1215    if ($password) {
1216        if ($password ne $self->config->admin_pass) {
1217            return %tt_vars if $return_tt_vars;
1218            my $output = $self->process_template(
1219                                                    id       => $node,
1220                                                    template => "moderate_password_wrong.tt",
1221                                                    tt_vars  => \%tt_vars,
1222                                                );
1223            return $output if $return_output;
1224            print $output;
1225        } else {
1226            $self->wiki->set_node_moderation(
1227                                        name    => $node,
1228                                        required => $args{moderation_flag},
1229                                    );
1230
1231            # Send back to the admin interface
1232            my $script_url = $self->config->script_url;
1233            my $script_name = $self->config->script_name;
1234            my $q = CGI->new;
1235            my $output = $q->redirect( $script_url.$script_name."?action=admin&moderation=changed" );
1236            return $output if $return_output;
1237            print $output;
1238        }
1239    } else {
1240        return %tt_vars if $return_tt_vars;
1241        my $output = $self->process_template(
1242                                                id       => $node,
1243                                                template => "moderate_confirm.tt",
1244                                                tt_vars  => \%tt_vars,
1245                                            );
1246        return $output if $return_output;
1247        print $output;
1248    }
1249}
1250
1251=item B<moderate_node>
1252
1253  $guide->moderate_node(
1254                         id       => "FAQ",
1255                         version  => 12,
1256                         password => "beer",
1257                     );
1258
1259Marks a version of a node as moderated. Will also auto-create and Locales
1260and Categories for the newly moderated version.
1261
1262If C<password> is not supplied then a form for entering the password
1263will be displayed.
1264=cut
1265sub moderate_node {
1266    my ($self, %args) = @_;
1267    my $node = $args{id} or croak "No node ID supplied for node moderation";
1268    my $version = $args{version} or croak "No node version supplied for node moderation";
1269    my $return_tt_vars = $args{return_tt_vars} || 0;
1270    my $return_output = $args{return_output} || 0;
1271
1272    # Set up the TT variables
1273    my %tt_vars = (
1274                      not_editable  => 1,
1275                      not_deletable => 1,
1276                      deter_robots  => 1,
1277                      version       => $version,
1278                      moderation_action => 'moderate',
1279                      moderation_url_args => 'action=moderate&version='.$version
1280                  );
1281
1282    my $password = $args{password};
1283    unless($self->config->moderation_requires_password) {
1284        $password = $self->config->admin_pass;
1285    }
1286
1287    if ($password) {
1288        if ($password ne $self->config->admin_pass) {
1289            return %tt_vars if $return_tt_vars;
1290            my $output = $self->process_template(
1291                                                    id       => $node,
1292                                                    template => "moderate_password_wrong.tt",
1293                                                    tt_vars  => \%tt_vars,
1294                                                );
1295            return $output if $return_output;
1296            print $output;
1297        } else {
1298            $self->wiki->moderate_node(
1299                                        name    => $node,
1300                                        version => $version
1301                                    );
1302
1303            # Create any categories or locales for it
1304            my %details = $self->wiki->retrieve_node(
1305                                        name    => $node,
1306                                        version => $version
1307                                    );
1308            $self->_autoCreateCategoryLocale(
1309                                          id       => $node,
1310                                          metadata => $details{'metadata'}
1311            );
1312
1313            # Send back to the admin interface
1314            my $script_url = $self->config->script_url;
1315            my $script_name = $self->config->script_name;
1316            my $q = CGI->new;
1317            my $output = $q->redirect( $script_url.$script_name."?action=admin&moderation=moderated" );
1318            return $output if $return_output;
1319            print $output;
1320        }
1321    } else {
1322        return %tt_vars if $return_tt_vars;
1323        my $output = $self->process_template(
1324                                                id       => $node,
1325                                                template => "moderate_confirm.tt",
1326                                                tt_vars  => \%tt_vars,
1327                                            );
1328        return $output if $return_output;
1329        print $output;
1330    }
1331}
1332
1333=item B<show_missing_metadata>
1334Search for nodes which don't have a certain kind of metadata. Optionally
1335also excludes Locales and Categories
1336=cut
1337sub show_missing_metadata {
1338    my ($self, %args) = @_;
1339    my $return_tt_vars = $args{return_tt_vars} || 0;
1340    my $return_output = $args{return_output} || 0;
1341
1342    my $wiki = $self->wiki;
1343    my $formatter = $self->wiki->formatter;
1344    my $script_name = $self->config->script_name;
1345
1346    my ($metadata_type, $metadata_value, $exclude_locales, $exclude_categories)
1347        = @args{ qw( metadata_type metadata_value exclude_locales exclude_categories ) };
1348
1349    my @nodes;
1350    my $done_search = 0;
1351
1352    # Only search if they supplied at least a metadata type
1353    if($metadata_type) {
1354        $done_search = 1;
1355        @nodes = $wiki->list_nodes_by_missing_metadata(
1356                            metadata_type => $metadata_type,
1357                            metadata_value => $metadata_value,
1358                            ignore_case    => 1,
1359        );
1360
1361        # Do we need to filter some nodes out?
1362        if($exclude_locales || $exclude_categories) {
1363            my @all_nodes = @nodes;
1364            @nodes = ();
1365
1366            foreach my $node (@all_nodes) {
1367                if($exclude_locales && $node =~ /^Locale /) { next; }
1368                if($exclude_categories && $node =~ /^Category /) { next; }
1369                push @nodes, $node;
1370            }
1371        }
1372    }
1373
1374    # Build nice edit etc links for our nodes
1375    my @tt_nodes;
1376    for my $node (@nodes) {
1377        my %n;
1378
1379        # Make the URLs
1380        my $node_param = uri_escape( $formatter->node_name_to_node_param( $node ) );
1381
1382        # Save into the hash
1383        $n{'name'} = $node;
1384        $n{'view_url'} = $script_name . "?id=" . $node_param;
1385        $n{'edit_url'} = $script_name . "?id=" . $node_param . ";action=edit";
1386        push @tt_nodes, \%n;
1387    }
1388
1389    # Set up our TT variables, including the search parameters
1390    my %tt_vars = (
1391                      not_editable  => 1,
1392                      not_deletable => 1,
1393                      deter_robots  => 1,
1394
1395                      nodes => \@tt_nodes,
1396                      done_search    => $done_search,
1397                      metadata_type  => $metadata_type,
1398                      metadata_value => $metadata_value,
1399                      exclude_locales => $exclude_locales,
1400                      exclude_categories => $exclude_categories
1401                  );
1402    return %tt_vars if $return_tt_vars;
1403
1404    # Render to the page
1405    my $output = $self->process_template(
1406                                           id       => "",
1407                                           template => "missing_metadata.tt",
1408                                           tt_vars  => \%tt_vars,
1409                                        );
1410    return $output if $return_output;
1411    print $output;
1412}
1413
1414=item B<display_admin_interface>
1415Fetch everything we need to display the admin interface, and passes it off
1416 to the template
1417=cut
1418sub display_admin_interface {
1419    my ($self, %args) = @_;
1420    my $return_tt_vars = $args{return_tt_vars} || 0;
1421    my $return_output = $args{return_output} || 0;
1422
1423    my $wiki = $self->wiki;
1424    my $formatter = $self->wiki->formatter;
1425    my $script_name = $self->config->script_name;
1426
1427    # Grab all the recent nodes
1428    my @all_nodes = $wiki->list_recent_changes(last_n_changes => 100);
1429
1430    # Split into nodes, Locales and Categories
1431    my @nodes;
1432    my @categories;
1433    my @locales;
1434    for my $node (@all_nodes) {
1435        # Add moderation status
1436        $node->{'moderate'} = $wiki->node_required_moderation($node->{'name'});
1437
1438        # Make the URLs
1439        my $node_param = uri_escape( $formatter->node_name_to_node_param( $node->{'name'} ) );
1440        $node->{'view_url'} = $script_name . "?id=" . $node_param;
1441        $node->{'versions_url'} = $script_name .
1442                        "?action=list_all_versions;id=" . $node_param;
1443        $node->{'moderation_url'} = $script_name .
1444                        "?action=set_moderation;id=" . $node_param;
1445
1446        # Filter
1447        if($node->{'name'} =~ /^Category /) {
1448            $node->{'page_name'} = $node->{'name'};
1449            $node->{'name'} =~ s/^Category //;
1450            push @categories, $node;
1451        } elsif($node->{'name'} =~ /^Locale /) {
1452            $node->{'page_name'} = $node->{'name'};
1453            $node->{'name'} =~ s/^Locale //;
1454            push @locales, $node;
1455        } else {
1456            push @nodes, $node;
1457        }
1458    }
1459
1460    # Handle completed notice for actions
1461    my $completed_action = "";
1462    if($args{moderation_completed}) {
1463        if($args{moderation_completed} eq "moderation") {
1464            $completed_action = "Version moderated";
1465        }
1466        if($args{moderation_completed} eq "changed") {
1467            $completed_action = "Node moderation flag changed";
1468        }
1469    }
1470
1471    # Render in a template
1472    my %tt_vars = (
1473                      not_editable  => 1,
1474                      not_deletable => 1,
1475                      deter_robots  => 1,
1476                      nodes      => \@nodes,
1477                      categories => \@categories,
1478                      locales    => \@locales,
1479                      completed_action => $completed_action
1480                  );
1481    return %tt_vars if $return_tt_vars;
1482    my $output = $self->process_template(
1483                                           id       => "",
1484                                           template => "admin_home.tt",
1485                                           tt_vars  => \%tt_vars,
1486                                        );
1487    return $output if $return_output;
1488    print $output;
1489}
1490
1491sub process_template {
1492    my ($self, %args) = @_;
1493    my %output_conf = (
1494                          wiki     => $self->wiki,
1495                          config   => $self->config,
1496                          node     => $args{id},
1497                          template => $args{template},
1498                          vars     => $args{tt_vars},
1499                          cookies  => $args{cookies},
1500                      );
1501    if ( $args{content_type} ) {
1502        $output_conf{content_type} = $args{content_type};
1503    }
1504    return OpenGuides::Template->output( %output_conf );
1505}
1506
1507sub redirect_to_node {
1508    my ($self, $node, $redirected_from) = @_;
1509   
1510    my $script_url = $self->config->script_url;
1511    my $script_name = $self->config->script_name;
1512    my $formatter = $self->wiki->formatter;
1513
1514    my $id = $formatter->node_name_to_node_param( $node );
1515    my $oldid;
1516    $oldid = $formatter->node_name_to_node_param( $redirected_from ) if $redirected_from;
1517
1518    my $redir_param = "$script_url$script_name?";
1519    $redir_param .= 'id=' if $oldid;
1520    $redir_param .= $id;
1521    $redir_param .= ";oldid=$oldid" if $oldid;
1522   
1523    my $q = CGI->new;
1524    return $q->redirect( $redir_param );
1525}
1526
1527sub get_cookie {
1528    my $self = shift;
1529    my $config = $self->config;
1530    my $pref_name = shift or return "";
1531    my %cookie_data = OpenGuides::CGI->get_prefs_from_cookie(config=>$config);
1532    return $cookie_data{$pref_name};
1533}
1534
1535
1536=head1 BUGS AND CAVEATS
1537
1538UTF8 data are currently not handled correctly throughout.
1539
1540Other bugs are documented at
1541L<http://dev.openguides.org/>
1542
1543=head1 SEE ALSO
1544
1545=over 4
1546
1547=item * L<http://london.openguides.org/|The Open Guide to London>, the first and biggest OpenGuides site.
1548
1549=item * L<http://openguides.org/|The OpenGuides website>, with a list of all live OpenGuides installs.
1550
1551=item * L<Wiki::Toolkit>, the Wiki toolkit which does the heavy lifting for OpenGuides
1552
1553=back
1554
1555=head1 FEEDBACK
1556
1557If you have a question, a bug report, or a patch, or you're interested
1558in joining the development team, please contact openguides-dev@openguides.org
1559(moderated mailing list, will reach all current developers but you'll have
1560to wait for your post to be approved) or file a bug report at
1561L<http://dev.openguides.org/>
1562
1563=head1 AUTHOR
1564
1565The OpenGuides Project (openguides-dev@openguides.org)
1566
1567=head1 COPYRIGHT
1568
1569     Copyright (C) 2003-2006 The OpenGuides Project.  All Rights Reserved.
1570
1571The OpenGuides distribution is free software; you can redistribute it
1572and/or modify it under the same terms as Perl itself.
1573
1574=head1 CREDITS
1575
1576Programming by Dominic Hargreaves, Earle Martin, Kake Pugh, and Ivor
1577Williams.  Testing and bug reporting by Billy Abbott, Jody Belka,
1578Kerry Bosworth, Simon Cozens, Cal Henderson, Steve Jolly, and Bob
1579Walker (among others).  Much of the Module::Build stuff copied from
1580the Siesta project L<http://siesta.unixbeard.net/>
1581
1582=cut
1583
15841;
Note: See TracBrowser for help on using the repository browser.