| 1 | package OpenGuides::JSON; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | |
|---|
| 5 | use vars qw( $VERSION ); |
|---|
| 6 | $VERSION = '0.01'; |
|---|
| 7 | |
|---|
| 8 | use Wiki::Toolkit::Plugin::JSON; |
|---|
| 9 | use Time::Piece; |
|---|
| 10 | use URI::Escape; |
|---|
| 11 | use Carp 'croak'; |
|---|
| 12 | |
|---|
| 13 | sub new { |
|---|
| 14 | my ( $class, @args ) = @_; |
|---|
| 15 | my $self = {}; |
|---|
| 16 | bless $self, $class; |
|---|
| 17 | $self->_init(@args); |
|---|
| 18 | } |
|---|
| 19 | |
|---|
| 20 | sub _init { |
|---|
| 21 | my ( $self, %args ) = @_; |
|---|
| 22 | |
|---|
| 23 | my $wiki = $args{wiki}; |
|---|
| 24 | |
|---|
| 25 | unless ( $wiki && UNIVERSAL::isa( $wiki, "Wiki::Toolkit" ) ) { |
|---|
| 26 | croak "No Wiki::Toolkit object supplied."; |
|---|
| 27 | } |
|---|
| 28 | $self->{wiki} = $wiki; |
|---|
| 29 | |
|---|
| 30 | my $config = $args{config}; |
|---|
| 31 | |
|---|
| 32 | unless ( $config && UNIVERSAL::isa( $config, "OpenGuides::Config" ) ) { |
|---|
| 33 | croak "No OpenGuides::Config object supplied."; |
|---|
| 34 | } |
|---|
| 35 | $self->{config} = $config; |
|---|
| 36 | |
|---|
| 37 | $self->{make_node_url} = sub { |
|---|
| 38 | my ( $node_name, $version ) = @_; |
|---|
| 39 | |
|---|
| 40 | my $config = $self->{config}; |
|---|
| 41 | |
|---|
| 42 | my $node_url = $config->script_url; |
|---|
| 43 | if ( $config->script_name ) { |
|---|
| 44 | $node_url .= uri_escape( $config->script_name ) . '?'; |
|---|
| 45 | } |
|---|
| 46 | $node_url .= 'id=' if defined $version; |
|---|
| 47 | $node_url .= |
|---|
| 48 | uri_escape( |
|---|
| 49 | $self->{wiki}->formatter->node_name_to_node_param($node_name) ); |
|---|
| 50 | $node_url .= ';version=' . uri_escape($version) if defined $version; |
|---|
| 51 | |
|---|
| 52 | $node_url; |
|---|
| 53 | }; |
|---|
| 54 | $self->{site_name} = $config->site_name; |
|---|
| 55 | $self->{default_city} = $config->default_city || ""; |
|---|
| 56 | $self->{default_country} = $config->default_country || ""; |
|---|
| 57 | $self->{site_description} = $config->site_desc || ""; |
|---|
| 58 | $self->{og_version} = $args{og_version}; |
|---|
| 59 | |
|---|
| 60 | $self; |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | sub emit_json { |
|---|
| 64 | my ( $self, %args ) = @_; |
|---|
| 65 | |
|---|
| 66 | my $node_name = $args{node}; |
|---|
| 67 | my $wiki = $self->{wiki}; |
|---|
| 68 | |
|---|
| 69 | my %node_data = $wiki->retrieve_node($node_name); |
|---|
| 70 | my $data = {}; |
|---|
| 71 | $data->{phone} = $node_data{metadata}{phone}[0] || ''; |
|---|
| 72 | $data->{fax} = $node_data{metadata}{fax}[0] || ''; |
|---|
| 73 | $data->{website} = $node_data{metadata}{website}[0] || ''; |
|---|
| 74 | $data->{opening_hours_text} = $node_data{metadata}{opening_hours_text}[0] |
|---|
| 75 | || ''; |
|---|
| 76 | $data->{address} = $node_data{metadata}{address}[0] || ''; |
|---|
| 77 | $data->{postcode} = $node_data{metadata}{postcode}[0] || ''; |
|---|
| 78 | $data->{city} = $node_data{metadata}{city}[0] || $self->{default_city}; |
|---|
| 79 | $data->{country} = $node_data{metadata}{country}[0] |
|---|
| 80 | || $self->{default_country}; |
|---|
| 81 | $data->{latitude} = $node_data{metadata}{latitude}[0] || ''; |
|---|
| 82 | $data->{longitude} = $node_data{metadata}{longitude}[0] || ''; |
|---|
| 83 | $data->{version} = $node_data{version}; |
|---|
| 84 | $data->{username} = $node_data{metadata}{username}[0] || ''; |
|---|
| 85 | $data->{os_x} = $node_data{metadata}{os_x}[0] || ''; |
|---|
| 86 | $data->{os_y} = $node_data{metadata}{os_y}[0] || ''; |
|---|
| 87 | $data->{categories} = $node_data{metadata}{category} || []; |
|---|
| 88 | $data->{locales} = $node_data{metadata}{locale} || []; |
|---|
| 89 | $data->{summary} = $node_data{metadata}{summary}[0] || ''; |
|---|
| 90 | |
|---|
| 91 | $data->{timestamp} = $node_data{last_modified}; |
|---|
| 92 | |
|---|
| 93 | |
|---|
| 94 | my $timestamp_fmt = $Wiki::Toolkit; |
|---|
| 95 | |
|---|
| 96 | if ( $data->{timestamp} ) { |
|---|
| 97 | my $time = Time::Piece->strptime( $data->{timestamp}, $timestamp_fmt ); |
|---|
| 98 | $data->{timestamp} = $time->strftime("%Y-%m-%dT%H:%M:%S"); |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | $data->{url} = $self->{make_node_url}->( $node_name, $data->{version} ); |
|---|
| 102 | $data->{version_indpt_url} = $self->{make_node_url}->($node_name); |
|---|
| 103 | return $self->json_maker->make_json($data); |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | sub json_maker { |
|---|
| 107 | my $self = shift; |
|---|
| 108 | |
|---|
| 109 | |
|---|
| 110 | unless ( $self->{json_maker} ) { |
|---|
| 111 | $self->{json_maker} = Wiki::Toolkit::Plugin::JSON->new( |
|---|
| 112 | wiki => $self->{wiki}, |
|---|
| 113 | site_name => $self->{site_name}, |
|---|
| 114 | site_url => $self->{config}->script_url, |
|---|
| 115 | site_description => $self->{site_description}, |
|---|
| 116 | make_node_url => $self->{make_node_url}, |
|---|
| 117 | recent_changes_link => $self->{config}->script_url . '?action=rc', |
|---|
| 118 | software_name => 'OpenGuides', |
|---|
| 119 | software_homepage => 'http://openguides.org/', |
|---|
| 120 | software_version => $self->{og_version}, |
|---|
| 121 | ); |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | return $self->{json_maker}; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | sub make_recentchanges_rss { |
|---|
| 128 | my ( $self, %args ) = @_; |
|---|
| 129 | |
|---|
| 130 | $self->json_maker->recent_changes(%args); |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | sub rss_timestamp { |
|---|
| 134 | my ( $self, %args ) = @_; |
|---|
| 135 | |
|---|
| 136 | $self->json_maker->rss_timestamp(%args); |
|---|
| 137 | } |
|---|
| 138 | |
|---|
| 139 | =head1 NAME |
|---|
| 140 | |
|---|
| 141 | OpenGuides::JSON - An OpenGuides plugin to output JSON. |
|---|
| 142 | |
|---|
| 143 | =head1 DESCRIPTION |
|---|
| 144 | |
|---|
| 145 | Does all the JSON stuff for OpenGuides. Distributed and installed as |
|---|
| 146 | part of the OpenGuides project, not intended for independent |
|---|
| 147 | installation. This documentation is probably only useful to OpenGuides |
|---|
| 148 | developers. |
|---|
| 149 | |
|---|
| 150 | =head1 SYNOPSIS |
|---|
| 151 | |
|---|
| 152 | use Wiki::Toolkit; |
|---|
| 153 | use OpenGuides::Config; |
|---|
| 154 | use OpenGuides::JSON; |
|---|
| 155 | |
|---|
| 156 | my $wiki = Wiki::Toolkit->new( ... ); |
|---|
| 157 | my $config = OpenGuides::Config->new( file => "wiki.conf" ); |
|---|
| 158 | my $rdf_writer = OpenGuides::JSON->new( wiki => $wiki, |
|---|
| 159 | config => $config ); |
|---|
| 160 | |
|---|
| 161 | # JSON version of a node. |
|---|
| 162 | print "Content-Type: application/rdf+xml\n\n"; |
|---|
| 163 | print $rdf_writer->emit_rdfxml( node => "Masala Zone, N1 0NU" ); |
|---|
| 164 | |
|---|
| 165 | # Ten most recent changes. |
|---|
| 166 | print "Content-Type: application/rdf+xml\n"; |
|---|
| 167 | print "Last-Modified: " . $self->rss_timestamp( items => 10 ) . "\n\n"; |
|---|
| 168 | print $rdf_writer->make_recentchanges_rss( items => 10 ); |
|---|
| 169 | |
|---|
| 170 | =head1 METHODS |
|---|
| 171 | |
|---|
| 172 | =over 4 |
|---|
| 173 | |
|---|
| 174 | =item B<new> |
|---|
| 175 | |
|---|
| 176 | my $rdf_writer = OpenGuides::JSON->new( wiki => $wiki, |
|---|
| 177 | config => $config ); |
|---|
| 178 | |
|---|
| 179 | C<wiki> must be a L<Wiki::Toolkit> object and C<config> must be an |
|---|
| 180 | L<OpenGuides::Config> object. Both arguments mandatory. |
|---|
| 181 | |
|---|
| 182 | |
|---|
| 183 | =item B<emit_rdfxml> |
|---|
| 184 | |
|---|
| 185 | $wiki->write_node( "Masala Zone, N1 0NU", |
|---|
| 186 | "Quick and tasty Indian food", |
|---|
| 187 | $checksum, |
|---|
| 188 | { comment => "New page", |
|---|
| 189 | username => "Kake", |
|---|
| 190 | locale => "Islington" } |
|---|
| 191 | ); |
|---|
| 192 | |
|---|
| 193 | print "Content-Type: application/rdf+xml\n\n"; |
|---|
| 194 | print $rdf_writer->emit_rdfxml( node => "Masala Zone, N1 0NU" ); |
|---|
| 195 | |
|---|
| 196 | B<Note:> Some of the fields emitted by the JSON generator are taken |
|---|
| 197 | from the node metadata. The form of this metadata is I<not> mandated |
|---|
| 198 | by L<Wiki::Toolkit>. Your wiki application should make sure to store some or |
|---|
| 199 | all of the following metadata when calling C<write_node>: |
|---|
| 200 | |
|---|
| 201 | =over 4 |
|---|
| 202 | |
|---|
| 203 | =item B<postcode> - The postcode or zip code of the place discussed by the node. Defaults to the empty string. |
|---|
| 204 | |
|---|
| 205 | =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. |
|---|
| 206 | |
|---|
| 207 | =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. |
|---|
| 208 | |
|---|
| 209 | =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. |
|---|
| 210 | |
|---|
| 211 | =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. |
|---|
| 212 | |
|---|
| 213 | =item B<phone> - Only one number supported at the moment. No validation. |
|---|
| 214 | |
|---|
| 215 | =item B<website> - No validation. |
|---|
| 216 | |
|---|
| 217 | =item B<opening_hours_text> - A freeform text field. |
|---|
| 218 | |
|---|
| 219 | =back |
|---|
| 220 | |
|---|
| 221 | =item B<json_maker> |
|---|
| 222 | |
|---|
| 223 | Returns a raw L<Wiki::Toolkit::Plugin::JSON> object created with the values you |
|---|
| 224 | invoked this module with. |
|---|
| 225 | |
|---|
| 226 | =item B<make_recentchanges_rss> |
|---|
| 227 | |
|---|
| 228 | # Ten most recent changes. |
|---|
| 229 | print "Content-Type: application/rdf+xml\n"; |
|---|
| 230 | print "Last-Modified: " . $rdf_writer->rss_timestamp( items => 10 ) . "\n\n"; |
|---|
| 231 | print $rdf_writer->make_recentchanges_rss( items => 10 ); |
|---|
| 232 | |
|---|
| 233 | # All the changes made by bob in the past week, ignoring minor edits. |
|---|
| 234 | |
|---|
| 235 | my %args = ( |
|---|
| 236 | days => 7, |
|---|
| 237 | ignore_minor_edits => 1, |
|---|
| 238 | filter_on_metadata => { username => "bob" }, |
|---|
| 239 | ); |
|---|
| 240 | |
|---|
| 241 | print "Content-Type: application/rdf+xml\n"; |
|---|
| 242 | print "Last-Modified: " . $rdf_writer->rss_timestamp( %args ) . "\n\n"; |
|---|
| 243 | print $rdf_writer->make_recentchanges_rss( %args ); |
|---|
| 244 | |
|---|
| 245 | =item B<rss_timestamp> |
|---|
| 246 | |
|---|
| 247 | print "Last-Modified: " . $rdf_writer->rss_timestamp( %args ) . "\n\n"; |
|---|
| 248 | |
|---|
| 249 | Returns the timestamp of the RSS feed in POSIX::strftime style ("Tue, 29 Feb 2000 |
|---|
| 250 | 12:34:56 GMT"), which is equivalent to the timestamp of the most recent item |
|---|
| 251 | in the feed. Takes the same arguments as make_recentchanges_rss(). You will most |
|---|
| 252 | likely need this to print a Last-Modified HTTP header so user-agents can determine |
|---|
| 253 | whether they need to reload the feed or not. |
|---|
| 254 | |
|---|
| 255 | =back |
|---|
| 256 | |
|---|
| 257 | =head1 SEE ALSO |
|---|
| 258 | |
|---|
| 259 | =over 4 |
|---|
| 260 | |
|---|
| 261 | =item * L<Wiki::Toolkit> |
|---|
| 262 | |
|---|
| 263 | =item * L<http://openguides.org/> |
|---|
| 264 | |
|---|
| 265 | =item * L<http://chefmoz.org/> |
|---|
| 266 | |
|---|
| 267 | =back |
|---|
| 268 | |
|---|
| 269 | =head1 AUTHOR |
|---|
| 270 | |
|---|
| 271 | The OpenGuides Project (openguides-dev@openguides.org) |
|---|
| 272 | |
|---|
| 273 | =head1 COPYRIGHT |
|---|
| 274 | |
|---|
| 275 | Copyright (C) 2003-2005 The OpenGuides Project. All Rights Reserved. |
|---|
| 276 | |
|---|
| 277 | This module is free software; you can redistribute it and/or modify it |
|---|
| 278 | under the same terms as Perl itself. |
|---|
| 279 | |
|---|
| 280 | =head1 CREDITS |
|---|
| 281 | |
|---|
| 282 | Code in this module is mostly pirated from OpenGuides::RDF, those authors deserve all the credit. Chris Prather |
|---|
| 283 | did the pirating. |
|---|
| 284 | |
|---|
| 285 | =cut |
|---|
| 286 | |
|---|
| 287 | 1; |
|---|