source: trunk/lib/OpenGuides/RDF.pm @ 1016

Last change on this file since 1016 was 1016, checked in by kake, 14 years ago

Move node RDF generation from inline strings in OpenGuides::RDF to its own template; also make sure all weird characters are encoded.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.3 KB
Line 
1package OpenGuides::RDF;
2
3use strict;
4
5use vars qw( $VERSION );
6$VERSION = '0.09';
7
8use Time::Piece;
9use URI::Escape;
10use Carp 'croak';
11use HTML::Entities qw( encode_entities_numeric );
12use Template;
13
14sub new {
15    my ($class, @args) = @_;
16    my $self = {};
17    bless $self, $class;
18    $self->_init(@args);
19}
20
21sub _init {
22    my ($self, %args) = @_;
23
24    my $wiki = $args{wiki};
25   
26    unless ( $wiki && UNIVERSAL::isa( $wiki, "Wiki::Toolkit" ) ) {
27      croak "No Wiki::Toolkit object supplied.";
28    }
29    $self->{wiki} = $wiki;
30
31    my $config = $args{config};
32
33    unless ( $config && UNIVERSAL::isa( $config, "OpenGuides::Config" ) ) {
34        croak "No OpenGuides::Config object supplied.";
35    }
36    $self->{config} = $config;
37
38    $self->{make_node_url} = sub {
39        my ($node_name, $version) = @_;
40
41        my $config = $self->{config};
42   
43        my $node_url = $config->script_url . uri_escape($config->script_name) . '?';
44        $node_url .= 'id=' if defined $version;
45        $node_url .= uri_escape($self->{wiki}->formatter->node_name_to_node_param($node_name));
46        $node_url .= ';version=' . uri_escape($version) if defined $version;
47
48        $node_url;
49      }; 
50    $self->{site_name}        = $config->site_name;
51    $self->{default_city}     = $config->default_city     || "";
52    $self->{default_country}  = $config->default_country  || "";
53    $self->{site_description} = $config->site_desc        || "";
54    $self->{og_version}       = $args{og_version};
55
56    $self;
57}
58
59sub emit_rdfxml {
60    my ($self, %args) = @_;
61
62    my $node_name = $args{node};
63    my $config = $self->{config};
64    my $wiki = $self->{wiki};
65    my $formatter = $wiki->formatter;
66
67    my %node_data = $wiki->retrieve_node( $node_name );
68    my %metadata = %{ $node_data{metadata} };
69    my %tt_vars = (
70                    node_name  => $node_name,
71                    version    => $node_data{version},
72                    site_name  => $self->{site_name},
73                    site_desc  => $self->{site_description},
74                    og_version => $self->{og_version},
75                  );
76
77    my %defaults = (
78                     city => $self->{default_city},
79                     country => $self->{default_country},
80                   );
81
82    foreach my $var ( qw( phone fax website opening_hours_text address
83                          postcode city country latitude longitude username
84                          os_x os_y summary ) ) {
85        my $val = $metadata{$var}[0] || $defaults{$var} || "";
86        $tt_vars{$var} = $val;
87    }
88
89   
90    my @cats = @{ $metadata{category} || [] };
91    @cats = map { { name => $_ } } @cats;
92    $tt_vars{categories} = \@cats;
93
94    my @locs = @{ $metadata{locale} || [] };
95    @locs = map {
96                  {
97                    name => $_,
98                    id   => $formatter->node_name_to_node_param( $_ ),
99                  }
100                } @locs;
101    $tt_vars{locales} = \@locs;
102
103    # Check for geospatialness and define container object as appropriate.
104    my $is_geospatial;
105    foreach my $var ( qw( os_x os_y latitude longitude address postcode
106                          opening_hours_text ) ) {
107        $is_geospatial = 1 if $tt_vars{$var};
108    }
109
110    $is_geospatial = 1 if scalar @locs;
111
112    $tt_vars{obj_type} = $is_geospatial ? "geo:SpatialThing"
113                                        : "rdf:Description";
114    $tt_vars{is_geospatial} = $is_geospatial;
115
116    # Timestamp of last edited.
117    my $timestamp = $node_data{last_modified};
118    if ( $timestamp ) {
119        # Make a Time::Piece object in order to canonicalise time.  I think.
120        my $timestamp_fmt = $Wiki::Toolkit::Store::Database::timestamp_fmt;
121        my $time   = Time::Piece->strptime($timestamp, $timestamp_fmt);
122        $tt_vars{timestamp} = $time->strftime("%Y-%m-%dT%H:%M:%S");
123    }
124
125    $tt_vars{user_id} = $tt_vars{username};
126    $tt_vars{user_id} =~ s/\s/_/g;
127   
128    $tt_vars{node_uri} = $self->{make_node_url}->( $node_name );
129    $tt_vars{node_uri_with_version}
130                            = $self->{make_node_url}->( $node_name,
131                                                        $tt_vars{version} );
132
133    # Should probably be moved into OpenGuides::Utils.
134    if ($node_data{content} =~ /^\#REDIRECT \[\[(.*?)]\]$/) {
135        my $redirect = $1;
136        $tt_vars{redirect} = $config->script_url . $config->script_name
137                             . "?id="
138                             . $formatter->node_name_to_node_param( $redirect )
139                             . ";format=rdf#obj";
140    }
141
142    # Escape stuff!
143    foreach my $var ( keys %tt_vars ) {
144        if ( $tt_vars{$var} ) {
145            $tt_vars{$var} = encode_entities_numeric( $tt_vars{$var} );
146        }
147    }
148
149    # OK, we've set all our template variables; now process the template.
150    my $template_path = $config->template_path;
151    my $custom_template_path = $config->custom_template_path || "";
152    my $tt = Template->new( {
153                    INCLUDE_PATH => "$custom_template_path:$template_path" } );
154
155    my $rdf;
156    $tt->process( "node_rdf.tt", \%tt_vars, \$rdf );
157    $rdf ||= "ERROR: " . $tt->error;
158    return $rdf;
159}
160
161=head1 NAME
162
163OpenGuides::RDF - An OpenGuides plugin to output RDF/XML.
164
165=head1 DESCRIPTION
166
167Does all the RDF stuff for OpenGuides.  Distributed and installed as
168part of the OpenGuides project, not intended for independent
169installation.  This documentation is probably only useful to OpenGuides
170developers.
171
172=head1 SYNOPSIS
173
174    use Wiki::Toolkit;
175    use OpenGuides::Config;
176    use OpenGuides::RDF;
177
178    my $wiki = Wiki::Toolkit->new( ... );
179    my $config = OpenGuides::Config->new( file => "wiki.conf" );
180    my $rdf_writer = OpenGuides::RDF->new( wiki   => $wiki,
181                                         config => $config );
182
183    # RDF version of a node.
184    print "Content-Type: application/rdf+xml\n\n";
185    print $rdf_writer->emit_rdfxml( node => "Masala Zone, N1 0NU" );
186
187=head1 METHODS
188
189=over 4
190
191=item B<new>
192
193    my $rdf_writer = OpenGuides::RDF->new( wiki   => $wiki,
194                                           config => $config );
195
196C<wiki> must be a L<Wiki::Toolkit> object and C<config> must be an
197L<OpenGuides::Config> object.  Both arguments mandatory.
198
199
200=item B<emit_rdfxml>
201
202    $wiki->write_node( "Masala Zone, N1 0NU",
203                     "Quick and tasty Indian food",
204                     $checksum,
205                     { comment  => "New page",
206                       username => "Kake",
207                       locale   => "Islington" }
208    );
209
210    print "Content-Type: application/rdf+xml\n\n";
211    print $rdf_writer->emit_rdfxml( node => "Masala Zone, N1 0NU" );
212
213B<Note:> Some of the fields emitted by the RDF/XML generator are taken
214from the node metadata. The form of this metadata is I<not> mandated
215by L<Wiki::Toolkit>. Your wiki application should make sure to store some or
216all of the following metadata when calling C<write_node>:
217
218=over 4
219
220=item B<postcode> - The postcode or zip code of the place discussed by the node.  Defaults to the empty string.
221
222=item B<city> - The name of the city that the node is in.  If not supplied, then the value of C<default_city> in the config object supplied to C<new>, if available, otherwise the empty string.
223
224=item B<country> - The name of the country that the node is in.  If not supplied, then the value of C<default_country> in the config object supplied to C<new> will be used, if available, otherwise the empty string.
225
226=item B<username> - An identifier for the person who made the latest edit to the node.  This person will be listed as a contributor (Dublin Core).  Defaults to empty string.
227
228=item B<locale> - The value of this can be a scalar or an arrayref, since some places have a plausible claim to being in more than one locale.  Each of these is put in as a C<Neighbourhood> attribute.
229
230=item B<phone> - Only one number supported at the moment.  No validation.
231
232=item B<website> - No validation.
233
234=item B<opening_hours_text> - A freeform text field.
235
236=back
237
238=head1 SEE ALSO
239
240=over 4
241
242=item * L<Wiki::Toolkit>
243
244=item * L<http://openguides.org/>
245
246=item * L<http://chefmoz.org/>
247
248=back
249
250=head1 AUTHOR
251
252The OpenGuides Project (openguides-dev@lists.openguides.org)
253
254=head1 COPYRIGHT
255
256Copyright (C) 2003-2006 The OpenGuides Project.  All Rights Reserved.
257
258This module is free software; you can redistribute it and/or modify it
259under the same terms as Perl itself.
260
261=head1 CREDITS
262
263Code in this module written by Kake Pugh and Earle Martin.  Dan Brickley, Matt
264Biddulph and other inhabitants of #swig on irc.freenode.net gave useful feedback
265and advice.
266
267=cut
268
2691;
Note: See TracBrowser for help on using the repository browser.