File Coverage

blib/lib/OpenGuides/RDF.pm
Criterion Covered Total %
statement 107 110 97.2
branch 23 26 88.4
condition 14 24 58.3
subroutine 12 12 100.0
pod 2 2 100.0
total 158 174 90.8


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