Add initial geo plugin.
[blosxom-plugins.git] / gavinc / uf_hcard_meta
blob90b23339252c50931581fcaa1a147c6c58138fd0
1 # Blosxom Plugin: uf_hcard_meta
2 # Author(s): Gavin Carr <gavin@openfusion.com.au>
3 # Version: 0.001000
4 # Documentation: 'perldoc uf_hcard_meta'
6 package uf_hcard_meta;
8 use strict;
10 # Uncomment next line to enable debug output (don't uncomment debug() lines)
11 #use Blosxom::Debug debug_level => 2;
13 # --- Configuration defaults -----
15 my %config = ();
17 # Extra CSS classes to add to the microformat container e.g. to turn display off
18 $config{class} = '';
19 #$config{class} = 'nodisplay';
21 # Whether to automatically add microformat to story bodies. If not set, 
22 # you must explicitly add $uf_adr_meta::adr to a template somewhere.
23 $config{auto_append_to_body} = 1;
25 # What markup style to use for your adr, if auto-appending. 
26 # 3 styles are currently defined: 
27 # 'div-span' uses a 'div' elt for the container, and 'span' elements for the fields
28 # 'ul' uses a 'ul' list for the container, and 'li' elements for the fields
29 # 'dl' uses a 'dl' list for the container, 'dt' elements for field names, and 
30 #    'dd' elements for the fields themselves
31 #$config{style} = 'div-span';
32 #$config{style} = 'ul';
33 $config{style} = 'dl';
35 # --------------------------------
36 # __END_CONFIG__
38 use vars qw($hcard);
40 # Official hcard attributes
41 my @req_attr = qw(fn);
42 my %opt_attr = (
43     'adr'         => [ qw(address-type post-office-box extended-address street-address
44                           locality region postal-code country-name) ],
45     'agent'       => 1,
46     'bday'        => 1,
47     'email'       => 1,
48     'geo'         => [ qw(latitude longitude) ],
49     'key'         => 1,
50     'label'       => 1,
51     'logo'        => 1,
52     'mailer'      => 1,
53     'n'           => [ qw(honorific-prefix given-name additional-name family-name honorific-suffix) ],
54     'nickname'    => 1,
55     'note'        => 1,
56     'org'         => [ qw(organization-name organization-unit) ],
57     'photo'       => 1,
58     'rev'         => 1,
59     'role'        => 1,
60     'sort-string' => 1,
61     'sound'       => 1,
62     'tel'         => 1,
63     'tz'          => 1,
64     'uid'         => 1,
65     'url'         => 1,
67   
68 # Attribute aliases
69 my %alias = (
70     'fn'                    => [ 'name' ],
71     'email-value'           => [ 'email' ],
72     'tel'                   => [ qw(telephone phone) ],
73     'organization-name'     => [ qw(org organisation organization) ],
74     'post-office-box'       => [ 'pobox' ],
75     'street-address'        => [ 'street' ],
76     'locality'              => [ 'suburb', 'city' ],
77     'region'                => [ 'state' ],
78     'postal-code'           => [ 'postcode' ],
79     'country-name'          => [ 'country' ],
81 my %attr_types = map { $_ => 1 } qw(tel);
82 my %attr_types_standalone = (
83     tel                     => { map { $_ => 1 } qw(fax cell mobile) },
85 my %alias_types = (
86     mobile                  => 'cell',
89 $config{style} = 'div-span' unless $config{style} eq 'ul' or $config{style} eq 'dl';
91 sub start { 1 }
93 # Return the first existing metadata item key and value given a list of keys
94 sub _get_meta {
95     for my $attr ( @_ ) {
96         my $meta_attr = $attr;
97         $meta_attr =~ s/-/_/g;
98         my $value = $blosxom::meta{$meta_attr};
99         $value = eval "\$meta::$attr" unless defined $value;
100         return wantarray ? ( $attr, $value ) : $value if defined $value;
101     }
102     return wantarray ? () : undef;
105 sub _add_attr {
106     my ($hcard, $attr, $value, $style, $parent_attr, $parent_started, $type) = @_;
108     # Start parent if set and not started
109     if ($parent_attr && (! defined $parent_started || ! $$parent_started)) {
110         $$hcard .= qq(<span class="$parent_attr">\n);
111         $$parent_started = 1 if defined $parent_started;
112     }
114     # Append hcard output
115     my $label = '';
116     if ($type) {
117         $label = join ', ', map { qq(<span class="type">$_</span>) } split /[_\W]+/, $type;
118     }
119     elsif ($attr =~ m/^(\w+)-(value)$/) {
120         $label = $1;
121         $attr = $2;
122     } 
123     elsif ($style eq 'dl') {
124         $label = $attr;
125         $label =~ s/\s+/-/g;
126     }
127     my $type_string;
128     if ($style eq 'dl') {
129         $$hcard .= qq(<dt>$label</dt>);
130         $type_string = '';
131     }
132     elsif ($label) {
133         $type_string = "($label) ";
134     }
135     my $etag = $style eq 'div-span' ? 'span' : 
136                $style eq 'ul' ? 'li' : 'dd';
137     $$hcard .= qq(<$etag class="$attr">$type_string$value</$etag>\n);
139     # Close parent unless we have a $parent_started flag to set
140     $$hcard .= qq(</span>\n) if $parent_attr && ! defined $parent_started;
143 sub story {
144     my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
146     # Skip unless all required attributes are set
147     for (@req_attr) {
148         my $value = _get_meta($_, $alias{$_} ? @{$alias{$_}} : ());
149         unless ($value) {
150             # debug(2, "No name attribute found in $path/$filename - skipping post");
151             return 1;
152         }
153     }
155     my $story_style = _get_meta( 'hcard_style' ) || $config{style};
156     my $ctag = $story_style eq 'div-span' ? 'div' : $story_style;
158     $hcard = '';
159     my $container_classes = 'vcard';
160     if (my $meta_class = _get_meta('hcard_class')) {
161       $container_classes .= " $meta_class";
162     }
163     else {
164       $container_classes .= " $config{class}" if $config{class};
165     }
166     $hcard .= qq(<$ctag class="$container_classes">\n);
167     for my $attr ( @req_attr ) {
168         my $value = _get_meta( $attr, $alias{$attr} ? @{$alias{$attr}} : () );
169         _add_attr(\$hcard, $attr, $value, $story_style) if $value;
170     }
171     for my $attr ( sort keys %opt_attr ) {
172         # Allow nested attributes
173         if (ref $opt_attr{$attr}) {
174             my $parent_attr = $attr;
175             my $parent_started = 0;
176             for $attr ( @{ $opt_attr{$parent_attr} } ) {
177                 my $value = _get_meta( $attr, $alias{$attr} ? @{$alias{$attr}} : () );
178                 _add_attr(\$hcard, $attr, $value, $story_style, $parent_attr, \$parent_started)
179                     if $value;
180             }
181             if ($parent_started) {
182                 $hcard .= qq(</span>\n);
183                 next;
184             }
185         }
186     
187         # Allow bare attributes and aliases
188         my $value = _get_meta( $attr, $alias{$attr} ? @{$alias{$attr}} : () );
189         _add_attr(\$hcard, $attr, $value, $story_style) if $value;
191         # Allow typed attributes 
192         if ($attr_types{ $attr }) {
193             # TODO: need to support $meta package variables here too
194             for my $meta (sort keys %blosxom::meta) {
195                 # Search for all metadata beginning with $attr or aliases
196                 for my $a ( $attr, $alias{$attr} ? @{$alias{$attr}} : () ) {
197                     if (lc $meta =~ m/^$a[^a-z]+(\w+)$/) {
198                         _add_attr(\$hcard, 'value', $blosxom::meta{ $meta }, $story_style, $attr, undef, $1);
199                     }
200                 }
201             }
203             # Allow standalone types
204             for my $type ( sort keys %{ $attr_types_standalone{ $attr } } ) {
205                 if (my $value = _get_meta( $type )) {
206                     _add_attr(\$hcard, 'value', $value, $story_style, $attr, undef, 
207                               $alias_types{ $type } || $type);
208                 }
210             }
211         }
212     }
213     $hcard .= qq(</$ctag>\n);
214     # debug(2, "hcard $hcard\n");
216     my $autoappend = _get_meta( 'hcard_autoappend' );
217     $autoappend = $config{auto_append_to_body} unless defined $autoappend;
218     return 1 unless $autoappend;
220     $$body_ref .= "\n\n$hcard\n\n";
222     return 1;
227 __END__
229 =head1 NAME
231 uf_hcard_meta - plugin to create an 'hcard' microformat tag from post 
232 metadata
234 =head1 DESCRIPTION
236 uf_hcard_meta is a plugin to create an 'hcard' microformat tag from 
237 metadata in your post. The microformat tag is created in the 
238 $uf_hcard_meta::hcard story variable for use in templates or by other 
239 plugins, or if the 'auto_append_to_body' config variable is set (it is 
240 by default), uf_hcard_meta will append the tag to your story body 
241 automatically.
243 The following metadata items are supported. By and large the official
244 hcard attribute name is supported, and sometimes one or aliases 
245 (labelled alt below) may also be supported. See the hcard definition at
246 http://www.microformats.org/wiki/hcard for definitions and discussion
247 of attributes and usage.
249 =head2 REQUIRED METADATA ITEMS
251 =over 4
253 =item fn (alt: name) - full name
255 =back
257 =head2 OPTIONAL METADATA ITEMS
259 =over 4
261 =item address-type
263 =item post-office-box (alt: pobox)
265 =item extended-address 
267 =item street-address (alt: street)
269 =item locality (alt: suburb, city)
271 =item region (alt: state)
273 =item postal-code (alt: postcode)
275 =item country-name (alt: country)
277 =item agent
279 =item bday
281 =item cell (alt: mobile)
283 =item email
285 =item fax
287 =item key
289 =item label
291 =item latitude
293 =item longitude
295 =item logo
297 =item mailer
299 =item honorific-prefix
301 =item given-name
303 =item additional-name
305 =item family-name
307 =item honorific-suffix
309 =item nickname
311 =item note
313 =item organization-name (alt: org, organization, organisation)
315 =item organization-unit
317 =item photo
319 =item rev
321 =item role
323 =item sort-string
325 =item sound
327 =item tel (alt: telephone, phone)
329 And see also the Telephone Element Handling section below.
331 =item tz
333 =item uid
335 =item uri
337 =back
339 =head2 Telephone Element Handling
341 hcard telephone numbers may have type attributes, and because most people have
342 multiple telephone numbers, uf_hcard_meta also supports decorating the 'tel' 
343 element (or its aliases) with one or more hcard telephone types, just suffixed 
344 on the end of the element and separated by hyphens or underscores. For instance, 
345 all of the following are valid telephone number entries for uf_hcard_meta:
347     Tel: 02 8669 0001
348     Phone: 8669 0002
349     Fax: +612 8669 0003
350     Cell: 02 8669 0004
351     Mobile: 0401 8669 0005
352     Tel-Work: 123 456 7890
353     Phone-Work-Direct-Pref-Msg: +1 232 868 7123
354     Telephone-Home: 02 8669 0006
356 =head2 Config Elements
358 uf_hcard_meta also supports a couple of config elements that can be used to
359 override plugin config data on a per-story basis:
361 =over 4
363 =item HCard-Class (metamail) / hcard_class (meta)
365 This class (or list of classes) is appended to the class list applied to the
366 top-level hcard element in the rendered hcard i.e. it overrides the 
367 'class' config variable. 
369 =item HCard-Autoappend (metamail) / hcard_autoappend (meta)
371 This is a flag (0 or 1) indicating whether the rendered hcard should be 
372 automatically appended to the story body. It overrides the 'auto_append_to_body'
373 config variable.
375 =item HCard-Style (metamail) / hcard_style (meta)
377 One of the following styles: 'div-span', 'ul', 'dl', used to render the
378 hcard. It overrides the 'style' config variable.
380 =back
382 =head1 EXAMPLES
384 An simple example hcard for me:
386     Name:     Gavin Carr
387     Org:      Open Fusion
388     Role:     Chief Geek
389     Email:    gavin@openfusion.com.au
390     URL:      http://www.openfusion.net/
391     POBox:    PO Box 1222
392     Suburb:   Wahroonga
393     State:    NSW
394     Postcode: 2076
395     Country:  Australia
397 =head1 USAGE
399 uf_hcard_meta should be loaded after the meta plugins (meta
400 itself, or the metaclear/metamail/metadir/metafile family).
402 =head1 SEE ALSO
404 Microformats.org: http://www.microformats.org/, http://microformats.org/wiki/hcard.
406 Blosxom: http://blosxom.sourceforge.net/
408 =head1 BUGS
410 Probably, since hcards can be pretty horrendously complicated - please 
411 report to the author. Also, this plugin is pretty alpha - I'm not sure 
412 of quite a few of the interface elements, and would welcome input on how
413 attributes etc. should be represented. 
415 I also make no guarantees about backwards compatibility - future releases
416 may break existing hcards, so use at your own risk.
418 =head1 AUTHOR
420 Gavin Carr <gavin@openfusion.com.au>, http://www.openfusion.net/
422 =head1 LICENSE
424 Copyright 2007, Gavin Carr.
426 This plugin is licensed under the same terms as blosxom itself i.e.
428 Permission is hereby granted, free of charge, to any person obtaining a
429 copy of this software and associated documentation files (the "Software"),
430 to deal in the Software without restriction, including without limitation
431 the rights to use, copy, modify, merge, publish, distribute, sublicense,
432 and/or sell copies of the Software, and to permit persons to whom the
433 Software is furnished to do so, subject to the following conditions:
435 The above copyright notice and this permission notice shall be included
436 in all copies or substantial portions of the Software.
438 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
439 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
440 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
441 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
442 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
443 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
444 OTHER DEALINGS IN THE SOFTWARE.
446 =cut
448 # vim:ft=perl:sw=4