source: trunk/lib/OpenGuides.pm @ 765

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

Generate URIs (rdf:IDs) for categories and contributors in RDF output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.1 KB
Line 
1package OpenGuides;
2use strict;
3
4use Carp "croak";
5use CGI;
6use CGI::Wiki::Plugin::Diff;
7use CGI::Wiki::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.53-svn';
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 = CGI::Wiki::Plugin::Locator::Grid->new(
54                                             x => "os_x",    y => "os_y" );
55    } elsif ( $geo_handler == 2 ) {
56        $locator = CGI::Wiki::Plugin::Locator::Grid->new(
57                                             x => "osie_x",  y => "osie_y" );
58    } else {
59        $locator = CGI::Wiki::Plugin::Locator::Grid->new(
60                                             x => "easting", y => "northing" );
61    }
62    $wiki->register_plugin( plugin => $locator );
63    $self->{locator} = $locator;
64    my $differ = CGI::Wiki::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<CGI::Wiki> 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<CGI::Wiki::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<CGI::Wiki::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 %metadata   = %{$node_data{metadata}};
177
178    my ($wgs84_long, $wgs84_lat) = OpenGuides::Utils->get_wgs84_coords(
179                                        longitude => $metadata{longitude}[0],
180                                        latitude => $metadata{latitude}[0],
181                                        config => $config);
182    if ($args{format} && $args{format} eq 'raw') {
183      print "Content-Type: text/plain\n\n";
184      print $raw;
185      return 0;
186    }
187   
188    my %metadata_vars = OpenGuides::Template->extract_metadata_vars(
189                            wiki     => $wiki,
190                            config   => $config,
191                            metadata => $node_data{metadata}
192                        );
193
194    %tt_vars = (
195                   %tt_vars,
196                   %metadata_vars,
197                   content       => $content,
198                   last_modified => $modified,
199                   version       => $node_data{version},
200                   node          => $id,
201                   language      => $config->default_language,
202                   oldid         => $oldid,
203                   enable_gmaps  => 1,
204                   display_google_maps => $self->get_cookie("display_google_maps"),
205                   wgs84_long    => $wgs84_long,
206                   wgs84_lat     => $wgs84_lat
207               );
208
209    if ( $raw =~ /^#REDIRECT\s+(.+?)\s*$/ ) {
210        my $redirect = $1;
211        # Strip off enclosing [[ ]] in case this is an extended link.
212        $redirect =~ s/^\[\[//;
213        $redirect =~ s/\]\]\s*$//;
214
215        # Don't redirect if the parameter "redirect" is given as 0.
216        if ($do_redirect == 0) {
217            return %tt_vars if $args{return_tt_vars};
218            $tt_vars{current} = 1;
219            my $output = $self->process_template(
220                                                  id            => $id,
221                                                  template      => "node.tt",
222                                                  tt_vars       => \%tt_vars,
223                                                );
224            return $output if $return_output;
225            print $output;
226        } elsif ( $wiki->node_exists($redirect) && $redirect ne $id && $redirect ne $oldid ) {
227            # Avoid loops by not generating redirects to the same node or the previous node.
228            my $output = $self->redirect_to_node($redirect, $id);
229            return $output if $return_output;
230            print $output;
231            return 0;
232        }
233    }
234
235    # We've undef'ed $version above if this is the current version.
236    $tt_vars{current} = 1 unless $version;
237
238    if ($id eq "RecentChanges") {
239        $self->display_recent_changes(%args);
240    } elsif ( $id eq $self->config->home_name ) {
241        my @recent = $wiki->list_recent_changes(
242            last_n_changes => 10,
243            metadata_was   => { edit_type => "Normal edit" },
244        );
245        @recent = map {
246                          {
247                              name          => CGI->escapeHTML($_->{name}),
248                              last_modified => CGI->escapeHTML($_->{last_modified}),
249                              version       => CGI->escapeHTML($_->{version}),
250                              comment       => CGI->escapeHTML($_->{metadata}{comment}[0]),
251                              username      => CGI->escapeHTML($_->{metadata}{username}[0]),
252                              url           => $config->script_name . "?"
253                                               . CGI->escape($wiki->formatter->node_name_to_node_param($_->{name}))
254                          }
255                      } @recent;
256        $tt_vars{recent_changes} = \@recent;
257        return %tt_vars if $args{return_tt_vars};
258        my $output = $self->process_template(
259                                                id            => $id,
260                                                template      => "home_node.tt",
261                                                tt_vars       => \%tt_vars,
262                                            );
263        return $output if $return_output;
264        print $output;
265    } else {
266        return %tt_vars if $args{return_tt_vars};
267        my $output = $self->process_template(
268                                                id            => $id,
269                                                template      => "node.tt",
270                                                tt_vars       => \%tt_vars,
271                                            );
272        return $output if $return_output;
273        print $output;
274    }
275}
276
277=item B<display_recent_changes> 
278
279  $guide->display_recent_changes;
280
281As with other methods, the C<return_output> parameter can be used to
282return the output instead of printing it to STDOUT.
283
284=cut
285
286sub display_recent_changes {
287    my ($self, %args) = @_;
288    my $config = $self->config;
289    my $wiki = $self->wiki;
290    my $minor_edits = $self->get_cookie( "show_minor_edits_in_rc" );
291    my $id = $args{id} || $self->config->home_name;
292    my $return_output = $args{return_output} || 0;
293    my (%tt_vars, %recent_changes);
294    my $q = CGI->new;
295    my $since = $q->param("since");
296    if ( $since ) {
297        $tt_vars{since} = $since;
298        my $t = localtime($since); # overloaded by Time::Piece
299        $tt_vars{since_string} = $t->strftime;
300        my %criteria = ( since => $since );   
301        $criteria{metadata_was} = { edit_type => "Normal edit" }
302          unless $minor_edits;
303        my @rc = $self->{wiki}->list_recent_changes( %criteria );
304 
305        @rc = map {
306            {
307              name        => CGI->escapeHTML($_->{name}),
308              last_modified => CGI->escapeHTML($_->{last_modified}),
309              version     => CGI->escapeHTML($_->{version}),
310              comment     => CGI->escapeHTML($_->{metadata}{comment}[0]),
311              username    => CGI->escapeHTML($_->{metadata}{username}[0]),
312              host        => CGI->escapeHTML($_->{metadata}{host}[0]),
313              username_param => CGI->escape($_->{metadata}{username}[0]),
314              edit_type   => CGI->escapeHTML($_->{metadata}{edit_type}[0]),
315              url         => $config->script_name . "?"
316      . CGI->escape($wiki->formatter->node_name_to_node_param($_->{name})),
317        }
318                   } @rc;
319        if ( scalar @rc ) {
320            $recent_changes{since} = \@rc; 
321        }
322    } else {
323        for my $days ( [0, 1], [1, 7], [7, 14], [14, 30] ) {
324            my %criteria = ( between_days => $days );
325            $criteria{metadata_was} = { edit_type => "Normal edit" }
326              unless $minor_edits;
327            my @rc = $self->{wiki}->list_recent_changes( %criteria );
328
329            @rc = map {
330            {
331              name        => CGI->escapeHTML($_->{name}),
332              last_modified => CGI->escapeHTML($_->{last_modified}),
333              version     => CGI->escapeHTML($_->{version}),
334              comment     => CGI->escapeHTML($_->{metadata}{comment}[0]),
335              username    => CGI->escapeHTML($_->{metadata}{username}[0]),
336              host        => CGI->escapeHTML($_->{metadata}{host}[0]),
337              username_param => CGI->escape($_->{metadata}{username}[0]),
338              edit_type   => CGI->escapeHTML($_->{metadata}{edit_type}[0]),
339              url         => $config->script_name . "?"
340      . CGI->escape($wiki->formatter->node_name_to_node_param($_->{name})),
341        }
342                       } @rc;
343            if ( scalar @rc ) {
344                $recent_changes{$days->[1]} = \@rc;
345        }
346        }
347    }
348    $tt_vars{recent_changes} = \%recent_changes;
349    my %processing_args = (
350                            id            => $id,
351                            template      => "recent_changes.tt",
352                            tt_vars       => \%tt_vars,
353                           );
354    if ( !$since && $self->get_cookie("track_recent_changes_views") ) {
355    my $cookie =
356           OpenGuides::CGI->make_recent_changes_cookie(config => $config );
357        $processing_args{cookies} = $cookie;
358        $tt_vars{last_viewed} = OpenGuides::CGI->get_last_recent_changes_visit_from_cookie( config => $config );
359    }
360    return %tt_vars if $args{return_tt_vars};
361    my $output = $self->process_template( %processing_args );
362    return $output if $return_output;
363    print $output;
364}
365
366=item B<display_diffs>
367
368  $guide->display_diffs(
369                           id            => "Home Page",
370                           version       => 6,
371                           other_version => 5,
372                       );
373
374  # Or return output as a string (useful for writing tests).
375  my $output = $guide->display_diffs(
376                                        id            => "Home Page",
377                                        version       => 6,
378                                        other_version => 5,
379                                        return_output => 1,
380                                    );
381
382  # Or return the hash of variables that will be passed to the template
383  # (not including those set additionally by OpenGuides::Template).
384  my %vars = $guide->display_diffs(
385                                      id             => "Home Page",
386                                      version        => 6,
387                                      other_version  => 5,
388                                      return_tt_vars => 1,
389                                  );
390
391=cut
392
393sub display_diffs {
394    my ($self, %args) = @_;
395    my %diff_vars = $self->differ->differences(
396                                                  node          => $args{id},
397                                                  left_version  => $args{version},
398                                                  right_version => $args{other_version},
399                                              );
400    $diff_vars{not_deletable} = 1;
401    $diff_vars{not_editable}  = 1;
402    $diff_vars{deter_robots}  = 1;
403    return %diff_vars if $args{return_tt_vars};
404    my $output = $self->process_template(
405                                            id       => $args{id},
406                                            template => "differences.tt",
407                                            tt_vars  => \%diff_vars
408                                        );
409    return $output if $args{return_output};
410    print $output;
411}
412
413=item B<find_within_distance>
414
415  $guide->find_within_distance(
416                                  id => $node,
417                                  metres => $q->param("distance_in_metres")
418                              );
419
420=cut
421
422sub find_within_distance {
423    my ($self, %args) = @_;
424    my $node = $args{id};
425    my $metres = $args{metres};
426    my %data = $self->wiki->retrieve_node( $node );
427    my $lat = $data{metadata}{latitude}[0];
428    my $long = $data{metadata}{longitude}[0];
429    my $script_url = $self->config->script_url;
430    my $q = CGI->new;
431    print $q->redirect( $script_url . "search.cgi?lat=$lat;long=$long;distance_in_metres=$metres" );
432}
433
434=item B<show_backlinks>
435
436  $guide->show_backlinks( id => "Calthorpe Arms" );
437
438As with other methods, parameters C<return_tt_vars> and
439C<return_output> can be used to return these things instead of
440printing the output to STDOUT.
441
442=cut
443
444sub show_backlinks {
445    my ($self, %args) = @_;
446    my $wiki = $self->wiki;
447    my $formatter = $wiki->formatter;
448
449    my @backlinks = $wiki->list_backlinks( node => $args{id} );
450    my @results = map {
451                          {
452                              url   => CGI->escape($formatter->node_name_to_node_param($_)),
453                              title => CGI->escapeHTML($_)
454                          }
455                      } sort @backlinks;
456    my %tt_vars = ( results       => \@results,
457                    num_results   => scalar @results,
458                    not_deletable => 1,
459                    deter_robots  => 1,
460                    not_editable  => 1 );
461    return %tt_vars if $args{return_tt_vars};
462    my $output = OpenGuides::Template->output(
463                                                 node    => $args{id},
464                                                 wiki    => $wiki,
465                                                 config  => $self->config,
466                                                 template=>"backlink_results.tt",
467                                                 vars    => \%tt_vars,
468                                             );
469    return $output if $args{return_output};
470    print $output;
471}
472
473=item B<show_index>
474
475  $guide->show_index(
476                        type   => "category",
477                        value  => "pubs",
478                    );
479
480  # RDF version.
481  $guide->show_index(
482                        type   => "locale",
483                        value  => "Holborn",
484                        format => "rdf",
485                    );
486
487  # Or return output as a string (useful for writing tests).
488  $guide->show_index(
489                        type          => "category",
490                        value         => "pubs",
491                        return_output => 1,
492                    );
493
494=cut
495
496sub show_index {
497    my ($self, %args) = @_;
498    my $wiki = $self->wiki;
499    my $formatter = $wiki->formatter;
500    my %tt_vars;
501    my @selnodes;
502
503    if ( $args{type} and $args{value} ) {
504        if ( $args{type} eq "fuzzy_title_match" ) {
505            my %finds = $wiki->fuzzy_title_match( $args{value} );
506            @selnodes = sort { $finds{$a} <=> $finds{$b} } keys %finds;
507            $tt_vars{criterion} = {
508                type  => $args{type},  # for RDF version
509                value => $args{value}, # for RDF version
510                name  => CGI->escapeHTML("Fuzzy Title Match on '$args{value}'")
511            };
512            $tt_vars{not_editable} = 1;
513        } else {
514            @selnodes = $wiki->list_nodes_by_metadata(
515                metadata_type  => $args{type},
516                metadata_value => $args{value},
517                ignore_case    => 1
518            );
519            my $name = ucfirst($args{type}) . " $args{value}";
520            my $url = $self->config->script_name
521                      . "?"
522                      . ucfirst( $args{type} )
523                      . "_"
524                      . uri_escape(
525                                      $formatter->node_name_to_node_param($args{value})
526                                  );
527            $tt_vars{criterion} = {
528                type  => $args{type},
529                value => $args{value}, # for RDF version
530                name  => CGI->escapeHTML( $name ),
531                url   => $url
532            };
533            $tt_vars{not_editable} = 1;
534        }
535    } else {
536        @selnodes = $wiki->list_all_nodes();
537    }
538
539    my @nodes = map {
540                        {
541                            name      => $_,
542                            node_data => { $wiki->retrieve_node( name => $_ ) },
543                            param     => $formatter->node_name_to_node_param($_) }
544                        } sort @selnodes;
545
546    $tt_vars{nodes} = \@nodes;
547
548    my ($template, %conf);
549
550    if ( $args{format} ) {
551        if ( $args{format} eq "rdf" ) {
552            $template = "rdf_index.tt";
553            $conf{content_type} = "application/rdf+xml";
554        }
555        elsif ( $args{format} eq "plain" ) {
556            $template = "plain_index.tt";
557            $conf{content_type} = "text/plain";
558        } elsif ( $args{format} eq "map" ) {
559            my $q = CGI->new;
560            $tt_vars{zoom} = $q->param('zoom') || '';
561            $tt_vars{lat} = $q->param('lat') || '';
562            $tt_vars{long} = $q->param('long') || '';
563            $tt_vars{centre_long} = $self->config->centre_long;
564            $tt_vars{centre_lat} = $self->config->centre_lat;
565            $tt_vars{default_gmaps_zoom} = $self->config->default_gmaps_zoom;
566            $tt_vars{enable_gmaps} = 1;
567            $tt_vars{display_google_maps} = 1; # override for this page
568            $template = "map_index.tt";
569           
570        }
571    } else {
572        $template = "site_index.tt";
573    }
574
575    %conf = (
576                %conf,
577                node        => "$args{type} index", # KLUDGE
578                template    => $template,
579                tt_vars     => \%tt_vars,
580            );
581
582    my $output = $self->process_template( %conf );
583    return $output if $args{return_output};
584    print $output;
585}
586
587=item B<list_all_versions>
588
589  $guide->list_all_versions ( id => "Home Page" );
590
591  # Or return output as a string (useful for writing tests).
592  $guide->list_all_versions (
593                                id            => "Home Page",
594                                return_output => 1,
595                            );
596
597  # Or return the hash of variables that will be passed to the template
598  # (not including those set additionally by OpenGuides::Template).
599  $guide->list_all_versions (
600                                id             => "Home Page",
601                                return_tt_vars => 1,
602                            );
603
604=cut
605
606sub list_all_versions {
607    my ($self, %args) = @_;
608    my $return_output = $args{return_output} || 0;
609    my $node = $args{id};
610    my %curr_data = $self->wiki->retrieve_node($node);
611    my $curr_version = $curr_data{version};
612    my @history;
613    for my $version ( 1 .. $curr_version ) {
614        my %node_data = $self->wiki->retrieve_node( name    => $node,
615                                                    version => $version );
616        # $node_data{version} will be zero if this version was deleted.
617        push @history, {
618            version  => CGI->escapeHTML( $version ),
619            modified => CGI->escapeHTML( $node_data{last_modified} ),
620            username => CGI->escapeHTML( $node_data{metadata}{username}[0] ),
621            comment  => CGI->escapeHTML( $node_data{metadata}{comment}[0] ),
622                       } if $node_data{version};
623    }
624    @history = reverse @history;
625    my %tt_vars = (
626                      node          => $node,
627                      version       => $curr_version,
628                      not_deletable => 1,
629                      not_editable  => 1,
630                      deter_robots  => 1,
631                      history       => \@history
632                  );
633    return %tt_vars if $args{return_tt_vars};
634    my $output = $self->process_template(
635                                            id       => $node,
636                                            template => "node_history.tt",
637                                            tt_vars  => \%tt_vars,
638                                        );
639    return $output if $return_output;
640    print $output;
641}
642
643=item B<display_feed>
644
645  # Last ten non-minor edits to Hammersmith pages in RSS 1.0 format
646  $guide->display_feed(
647                         feed_type          => 'rss',
648                         items              => 10,
649                         ignore_minor_edits => 1,
650                         locale             => "Hammersmith",
651                     );
652
653  # All edits bob has made to pub pages in the last week in Atom format
654  $guide->display_feed(
655                         feed_type => 'atom',
656                         days      => 7,
657                         username  => "bob",
658                         category  => "Pubs",
659                     );
660
661C<feed_type> is a mandatory parameter. Supported values at present are
662"rss" and "atom".
663
664As with other methods, the C<return_output> parameter can be used to
665return the output instead of printing it to STDOUT.
666
667=cut
668
669sub display_feed {
670    my ($self, %args) = @_;
671
672    my $feed_type = $args{feed_type};
673    croak "No feed type given" unless $feed_type;
674   
675    my $return_output = $args{return_output} ? 1 : 0;
676
677    my $items = $args{items} || "";
678    my $days  = $args{days}  || "";
679    my $ignore_minor_edits = $args{ignore_minor_edits} ? 1 : 0;
680    my $username = $args{username} || "";
681    my $category = $args{category} || "";
682    my $locale   = $args{locale}   || "";
683    my %criteria = (
684                       items              => $items,
685                       days               => $days,
686                       ignore_minor_edits => $ignore_minor_edits,
687                       feed_type          => $feed_type,
688                   );
689    my %filter;
690    $filter{username} = $username if $username;
691    $filter{category} = $category if $category;
692    $filter{locale}   = $locale   if $locale;
693    if ( scalar keys %filter ) {
694        $criteria{filter_on_metadata} = \%filter;
695    }
696
697    my $feed = OpenGuides::Feed->new(
698                                        wiki       => $self->wiki,
699                                        config     => $self->config,
700                                        og_version => $VERSION,
701                                    );
702
703    my $output;
704   
705    if ($feed_type eq 'rss') {
706        $output = "Content-Type: application/rdf+xml\n";
707    }
708    elsif ($feed_type eq 'atom') {
709        $output = "Content-Type: application/atom+xml\n";
710    }
711    else {
712        croak "Unknown feed type given: $feed_type";
713    }
714   
715    $output .= "Last-Modified: " . $feed->feed_timestamp( %criteria ) . "\n\n";
716
717    $output .= $feed->make_feed( %criteria );
718
719    return $output if $return_output;
720    print $output;
721}
722
723sub display_about {
724    my ($self, %args) = @_;
725
726    my $output;
727
728    if ($args{format} && $args{format} =~ /^rdf$/i) {
729        $output = qq{Content-Type: application/rdf+xml
730
731<?xml version="1.0" encoding="UTF-8"?>
732<rdf:RDF xmlns      = "http://usefulinc.com/ns/doap#"
733         xmlns:rdf  = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
734         xmlns:foaf = "http://xmlns.com/foaf/0.1/">
735<Project rdf:ID="OpenGuides">
736  <name>OpenGuides</name>
737
738  <created>2003-04-29</created>
739 
740  <shortdesc xml:lang="en">
741    A wiki engine for collaborative description of places with specialised
742    geodata metadata features.
743  </shortdesc>
744
745  <description xml:lang="en">
746    OpenGuides is a collaborative wiki environment, written in Perl, for
747    building guides and sharing information, as both human-readable text
748    and RDF. The engine contains a number of geodata-specific metadata
749    mechanisms such as locale search, node classification and integration
750    with Google Maps.
751  </description>
752
753  <homepage rdf:resource="http://openguides.org/" />
754  <mailing-list rdf:resource="http://openguides.org/mm/listinfo/openguides-dev/" />
755  <mailing-list rdf:resource="http://urchin.earth.li/mailman/listinfo/openguides-commits/" />
756
757  <maintainer>
758    <foaf:Person rdf:ID="OpenGuidesMaintainer">
759      <foaf:name>Dominic Hargreaves</foaf:name>
760      <foaf:homepage rdf:resource="http://www.larted.org.uk/~dom/" />
761    </foaf:Person>
762  </maintainer>
763
764  <repository>
765    <SVNRepository rdf:ID="OpenGuidesSVN">
766      <location rdf:resource="https://urchin.earth.li/svn/openguides/" />
767      <browse rdf:resource="http://dev.openguides.org/browser" />
768    </SVNRepository>
769  </repository>
770
771  <release>
772    <Version rdf:ID="OpenGuidesVersion">
773      <revision>$VERSION</revision>
774    </Version>
775  </release>
776
777  <download-page rdf:resource="http://search.cpan.org/dist/OpenGuides/" />
778 
779  <!-- Freshmeat category: Internet :: WWW/HTTP :: Dynamic Content -->
780  <category rdf:resource="http://freshmeat.net/browse/92/" />
781 
782  <license rdf:resource="http://www.opensource.org/licenses/gpl-license.php" />
783  <license rdf:resource="http://www.opensource.org/licenses/artistic-license.php" />
784
785</Project>
786
787</rdf:RDF>};
788    }
789    else {
790        my $site_name  = $self->config->{site_name};
791        my $script_url = $self->config->{script_url};
792        $output = qq{Content-Type: text/html; charset=utf-8
793
794<html>
795<head>
796  <title>About $site_name</title>
797<style type="text/css">
798body        { margin: 0px; }
799#content    { padding: 50px; margin: auto; width: 50%; }
800h1          { margin-bottom: 0px; font-style: italic; }
801h2          { margin-top: 0px; }
802#logo       { text-align: center; }
803#logo a img { border: 1px solid #000; }
804#about      { margin: 0em 0em 1em 0em; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }
805#meta       { font-size: small; text-align: center;}
806</style>
807<link rel="alternate"
808  type="application/rdf+xml"
809  title="DOAP (Description Of A Project) profile for this site's software"
810  href="$script_url?action=about;format=rdf" />
811</head>
812<body>
813<div id="content">
814<div id="logo">
815<a href="http://openguides.org/"><img
816src="http://openguides.org/img/logo.jpg" alt="OpenGuides.org"></a>
817<h1><a href="$script_url">$site_name</a></h1>
818<h2>is powered by <a href="http://openguides.org/">OpenGuides</a> -<br>
819the guides built by you.</h2>
820<h3>version <a href="http://search.cpan.org/~dom/OpenGuides-$VERSION">$VERSION</a></h3>
821</div>
822<div id="about">
823<p>
824<a href="http://www.w3.org/RDF/"><img
825src="http://openguides.org/img/rdf_icon.png" width="44" height="48"
826style="float: right; margin-left: 10px; border: 0px"></a> OpenGuides is a
827web-based collaborative <a href="http://wiki.org/wiki.cgi?WhatIsWiki">wiki</a>
828environment for building guides and sharing information, as both
829human-readable text and <a href="http://www.w3.org/RDF/"><acronym
830title="Resource Description Framework">RDF</acronym></a>. The engine contains
831a number of geodata-specific metadata mechanisms such as locale search, node
832classification and integration with <a href="http://maps.google.com/">Google
833Maps</a>.
834</p>
835<p>
836OpenGuides is written in <a href="http://www.perl.org/">Perl</a>, and is
837made available under the same license as Perl itself (dual <a
838href="http://dev.perl.org/licenses/artistic.html" title='The "Artistic Licence"'>Artistic</a> and <a
839href="http://www.opensource.org/licenses/gpl-license.php"><acronym
840title="GNU Public Licence">GPL</acronym></a>). Developer information for the
841project is available from the <a href="http://dev.openguides.org/">OpenGuides
842development site</a>.
843</p>
844<p>
845Copyright &copy;2003-2006, <a href="http://openguides.org/">The OpenGuides
846Project</a>. "OpenGuides", "[The] Open Guide To..." and "The guides built by
847you" are trademarks of The OpenGuides Project. Any uses on this site are made
848with permission.
849</p>
850</div>
851<div id="meta">
852<a href="$script_url?action=about;format=rdf"><acronym
853title="Description Of A Project">DOAP</acronym> RDF version of this
854information</a>
855</div>
856</div>
857</body>
858</html>};
859    }
860   
861    return $output if $args{return_output};
862    print $output;
863}
864
865=item B<commit_node>
866
867  $guide->commit_node(
868                         id      => $node,
869                         cgi_obj => $q,
870                     );
871
872As with other methods, parameters C<return_tt_vars> and
873C<return_output> can be used to return these things instead of
874printing the output to STDOUT.
875
876The geographical data that you should provide in the L<CGI> object
877depends on the handler you chose in C<wiki.conf>.
878
879=over
880
881=item *
882
883B<British National Grid> - provide either C<os_x> and C<os_y> or
884C<latitude> and C<longitude>; whichever set of data you give, it will
885be converted to the other and both sets will be stored.
886
887=item *
888
889B<Irish National Grid> - provide either C<osie_x> and C<osie_y> or
890C<latitude> and C<longitude>; whichever set of data you give, it will
891be converted to the other and both sets will be stored.
892
893=item *
894
895B<UTM ellipsoid> - provide C<latitude> and C<longitude>; these will be
896converted to easting and northing and both sets of data will be stored.
897
898=back
899
900=cut
901
902sub commit_node {
903    my ($self, %args) = @_;
904    my $node = $args{id};
905    my $q = $args{cgi_obj};
906    my $return_output = $args{return_output};
907    my $wiki = $self->wiki;
908    my $config = $self->config;
909
910    my $content  = $q->param("content");
911    $content =~ s/\r\n/\n/gs;
912    my $checksum = $q->param("checksum");
913
914    my %metadata = OpenGuides::Template->extract_metadata_vars(
915        wiki    => $wiki,
916        config  => $config,
917    cgi_obj => $q
918    );
919
920    delete $metadata{website} if $metadata{website} eq 'http://';
921
922    $metadata{opening_hours_text} = $q->param("hours_text") || "";
923
924    # Pick out the unmunged versions of lat/long if they're set.
925    # (If they're not, it means they weren't munged in the first place.)
926    $metadata{latitude} = delete $metadata{latitude_unmunged}
927        if $metadata{latitude_unmunged};
928    $metadata{longitude} = delete $metadata{longitude_unmunged}
929        if $metadata{longitude_unmunged};
930
931    # Check to make sure all the indexable nodes are created
932    foreach my $type (qw(Category Locale)) {
933        my $lctype = lc($type);
934        foreach my $index (@{$metadata{$lctype}}) {
935            $index =~ s/(.*)/\u$1/;
936            my $node = $type . " " . $index;
937            # Uppercase the node name before checking for existence
938            $node =~ s/ (\S+)/ \u$1/g;
939            unless ( $wiki->node_exists($node) ) {
940                my $category = $type eq "Category" ? "Category" : "Locales";
941                $wiki->write_node(
942                                     $node,
943                                     "\@INDEX_LINK [[$node]]",
944                                     undef,
945                                     {
946                                         username => "Auto Create",
947                                         comment  => "Auto created $lctype stub page",
948                                         category => $category
949                                     }
950                                 );
951            }
952        }
953    }
954   
955    foreach my $var ( qw( summary username comment edit_type ) ) {
956        $metadata{$var} = $q->param($var) || "";
957    }
958    $metadata{host} = $ENV{REMOTE_ADDR};
959
960    # CGI::Wiki::Plugin::RSS::ModWiki wants "major_change" to be set.
961    $metadata{major_change} = ( $metadata{edit_type} eq "Normal edit" )
962                            ? 1
963                            : 0;
964
965    my $written = $wiki->write_node($node, $content, $checksum, \%metadata );
966
967    if ($written) {
968        my $output = $self->redirect_to_node($node);
969        return $output if $return_output;
970        print $output;
971    } else {
972        my %node_data = $wiki->retrieve_node($node);
973        my %tt_vars = ( checksum       => $node_data{checksum},
974                        new_content    => $content,
975                        stored_content => $node_data{content} );
976        foreach my $mdvar ( keys %metadata ) {
977            if ($mdvar eq "locales") {
978                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{locale};
979                $tt_vars{"new_$mdvar"}    = $metadata{locale};
980            } elsif ($mdvar eq "categories") {
981                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{category};
982                $tt_vars{"new_$mdvar"}    = $metadata{category};
983            } elsif ($mdvar eq "username" or $mdvar eq "comment"
984                      or $mdvar eq "edit_type" ) {
985                $tt_vars{$mdvar} = $metadata{$mdvar};
986            } else {
987                $tt_vars{"stored_$mdvar"} = $node_data{metadata}{$mdvar}[0];
988                $tt_vars{"new_$mdvar"}    = $metadata{$mdvar};
989            }
990        }
991        return %tt_vars if $args{return_tt_vars};
992        my $output = $self->process_template(
993                                              id       => $node,
994                                              template => "edit_conflict.tt",
995                                              tt_vars  => \%tt_vars,
996                                            );
997        return $output if $args{return_output};
998        print $output;
999    }
1000}
1001
1002
1003=item B<delete_node>
1004
1005  $guide->delete_node(
1006                         id       => "FAQ",
1007                         version  => 15,
1008                         password => "beer",
1009                     );
1010
1011C<version> is optional - if it isn't supplied then all versions of the
1012node will be deleted; in other words the node will be entirely
1013removed.
1014
1015If C<password> is not supplied then a form for entering the password
1016will be displayed.
1017
1018As with other methods, parameters C<return_tt_vars> and
1019C<return_output> can be used to return these things instead of
1020printing the output to STDOUT.
1021
1022=cut
1023
1024sub delete_node {
1025    my ($self, %args) = @_;
1026    my $node = $args{id} or croak "No node ID supplied for deletion";
1027    my $return_tt_vars = $args{return_tt_vars} || 0;
1028    my $return_output = $args{return_output} || 0;
1029
1030    my %tt_vars = (
1031                      not_editable  => 1,
1032                      not_deletable => 1,
1033                      deter_robots  => 1,
1034                  );
1035    $tt_vars{delete_version} = $args{version} || "";
1036
1037    my $password = $args{password};
1038
1039    if ($password) {
1040        if ($password ne $self->config->admin_pass) {
1041            return %tt_vars if $return_tt_vars;
1042            my $output = $self->process_template(
1043                                                    id       => $node,
1044                                                    template => "delete_password_wrong.tt",
1045                                                    tt_vars  => \%tt_vars,
1046                                                );
1047            return $output if $return_output;
1048            print $output;
1049        } else {
1050            $self->wiki->delete_node(
1051                                        name    => $node,
1052                                        version => $args{version},
1053                                    );
1054            # Check whether any versions of this node remain.
1055            my %check = $self->wiki->retrieve_node( name => $node );
1056            $tt_vars{other_versions_remain} = 1 if $check{version};
1057            return %tt_vars if $return_tt_vars;
1058            my $output = $self->process_template(
1059                                                    id       => $node,
1060                                                    template => "delete_done.tt",
1061                                                    tt_vars  => \%tt_vars,
1062                                                );
1063            return $output if $return_output;
1064            print $output;
1065        }
1066    } else {
1067        return %tt_vars if $return_tt_vars;
1068        my $output = $self->process_template(
1069                                                id       => $node,
1070                                                template => "delete_confirm.tt",
1071                                                tt_vars  => \%tt_vars,
1072                                            );
1073        return $output if $return_output;
1074        print $output;
1075    }
1076}
1077
1078sub process_template {
1079    my ($self, %args) = @_;
1080    my %output_conf = (
1081                          wiki     => $self->wiki,
1082                          config   => $self->config,
1083                          node     => $args{id},
1084                          template => $args{template},
1085                          vars     => $args{tt_vars},
1086                          cookies  => $args{cookies},
1087                      );
1088    if ( $args{content_type} ) {
1089        $output_conf{content_type} = $args{content_type};
1090    }
1091    return OpenGuides::Template->output( %output_conf );
1092}
1093
1094sub redirect_to_node {
1095    my ($self, $node, $redirected_from) = @_;
1096   
1097    my $script_url = $self->config->script_url;
1098    my $script_name = $self->config->script_name;
1099    my $formatter = $self->wiki->formatter;
1100
1101    my $id = $formatter->node_name_to_node_param( $node );
1102    my $oldid;
1103    $oldid = $formatter->node_name_to_node_param( $redirected_from ) if $redirected_from;
1104
1105    my $redir_param = "$script_url$script_name?";
1106    $redir_param .= 'id=' if $oldid;
1107    $redir_param .= $id;
1108    $redir_param .= ";oldid=$oldid" if $oldid;
1109   
1110    my $q = CGI->new;
1111    return $q->redirect( $redir_param );
1112}
1113
1114sub get_cookie {
1115    my $self = shift;
1116    my $config = $self->config;
1117    my $pref_name = shift or return "";
1118    my %cookie_data = OpenGuides::CGI->get_prefs_from_cookie(config=>$config);
1119    return $cookie_data{$pref_name};
1120}
1121
1122
1123=head1 BUGS AND CAVEATS
1124
1125UTF8 data are currently not handled correctly throughout.
1126
1127Other bugs are documented at
1128L<http://dev.openguides.org/>
1129
1130=head1 SEE ALSO
1131
1132=over 4
1133
1134=item * L<http://london.openguides.org/|The Open Guide to London>, the first and biggest OpenGuides site.
1135
1136=item * L<http://openguides.org/|The OpenGuides website>, with a list of all live OpenGuides installs.
1137
1138=item * L<CGI::Wiki>, the Wiki toolkit which does the heavy lifting for OpenGuides
1139
1140=back
1141
1142=head1 FEEDBACK
1143
1144If you have a question, a bug report, or a patch, or you're interested
1145in joining the development team, please contact openguides-dev@openguides.org
1146(moderated mailing list, will reach all current developers but you'll have
1147to wait for your post to be approved) or file a bug report at
1148L<http://dev.openguides.org/>
1149
1150=head1 AUTHOR
1151
1152The OpenGuides Project (openguides-dev@openguides.org)
1153
1154=head1 COPYRIGHT
1155
1156     Copyright (C) 2003-2006 The OpenGuides Project.  All Rights Reserved.
1157
1158The OpenGuides distribution is free software; you can redistribute it
1159and/or modify it under the same terms as Perl itself.
1160
1161=head1 CREDITS
1162
1163Programming by Dominic Hargreaves, Earle Martin, Kake Pugh, and Ivor
1164Williams.  Testing and bug reporting by Billy Abbott, Jody Belka,
1165Kerry Bosworth, Simon Cozens, Cal Henderson, Steve Jolly, and Bob
1166Walker (among others).  Much of the Module::Build stuff copied from
1167the Siesta project L<http://siesta.unixbeard.net/>
1168
1169=cut
1170
11711;
Note: See TracBrowser for help on using the repository browser.