Ticket #154: JSON.pm

File JSON.pm, 9.0 kB (added by perigrin, 2 years ago)
Line 
1package OpenGuides::JSON;
2
3use strict;
4
5use vars qw( $VERSION );
6$VERSION = '0.01';
7
8use Wiki::Toolkit::Plugin::JSON;
9use Time::Piece;
10use URI::Escape;
11use Carp 'croak';
12
13sub new {
14    my ( $class, @args ) = @_;
15    my $self = {};
16    bless $self, $class;
17    $self->_init(@args);
18}
19
20sub _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
63sub 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    # Make a Time::Piece object.
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
106sub json_maker {
107    my $self = shift;
108
109    # OAOO, please.
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
127sub make_recentchanges_rss {
128    my ( $self, %args ) = @_;
129
130    $self->json_maker->recent_changes(%args);
131}
132
133sub rss_timestamp {
134    my ( $self, %args ) = @_;
135
136    $self->json_maker->rss_timestamp(%args);
137}
138
139=head1 NAME
140
141OpenGuides::JSON - An OpenGuides plugin to output JSON.
142
143=head1 DESCRIPTION
144
145Does all the JSON stuff for OpenGuides.  Distributed and installed as
146part of the OpenGuides project, not intended for independent
147installation.  This documentation is probably only useful to OpenGuides
148developers.
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
179C<wiki> must be a L<Wiki::Toolkit> object and C<config> must be an
180L<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
196B<Note:> Some of the fields emitted by the JSON generator are taken
197from the node metadata. The form of this metadata is I<not> mandated
198by L<Wiki::Toolkit>. Your wiki application should make sure to store some or
199all 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
223Returns a raw L<Wiki::Toolkit::Plugin::JSON> object created with the values you
224invoked 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
249Returns the timestamp of the RSS feed in POSIX::strftime style ("Tue, 29 Feb 2000
25012:34:56 GMT"), which is equivalent to the timestamp of the most recent item
251in the feed. Takes the same arguments as make_recentchanges_rss(). You will most
252likely need this to print a Last-Modified HTTP header so user-agents can determine
253whether 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
271The OpenGuides Project (openguides-dev@openguides.org)
272
273=head1 COPYRIGHT
274
275Copyright (C) 2003-2005 The OpenGuides Project.  All Rights Reserved.
276
277This module is free software; you can redistribute it and/or modify it
278under the same terms as Perl itself.
279
280=head1 CREDITS
281
282Code in this module is mostly pirated from OpenGuides::RDF, those authors deserve all the credit. Chris Prather
283did the pirating.
284
285=cut
286
2871;