Add entries_cache_meta and recententries to MANIFEST.medium.
[matthijs/upstream/blosxom-plugins.git] / general / atomfeed
1 # Blosxom Plugin: atomfeed -*-cperl-*-
2 # Author(s): Original plugin: Rael Dornfest <rael@oreilly.com>
3 #            XML::Parser: Sam Ruby
4 #            UTC and <modified> fixes for 0.3: Frank Hecker
5 #            Enclosures support: Dave Slusher and Keith Irwin
6 #            Upgrade to Atom 1.0 spec: Sam Pearson
7 # Version: 2005-08-04
8 # Docs: Included below: type "perldoc atomfeed", or scroll down
9 # Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
10
11 package atomfeed;
12
13 # ----- Mandatory configurable variables -----
14
15 # For a basic atom feed, you only need set $default_author and $feed_yr.
16 # If you do not, the plugin will exit quietly.
17 # All other configuration is optional, and can be safely ignored.
18
19 # Who would you like your feed to credit as the default author of each entry? 
20 # Leave blank and the atomfeed plugin will attempt to use the whoami and
21 # fauxami plugins
22 $default_author = "";
23
24 # What year was your weblog started?  This will be used 
25 # to form part of your weblog's unique ID.
26 $feed_yr = "";
27
28 # ----- Optional configurable variables -----
29
30 # What is the default author's URL?
31 # Blank defaults to $blosxom::url
32 $author_uri = "";
33
34 # What is the default author's email address?
35 # Leave blank to exclude.
36 $author_email = '';
37
38 # Copyright statement:
39 # leave blank to exclude.
40 $copyright = "";
41
42 # What domain should Blosxom use in ID tags?
43 # Leave blank if you don't understand or for Blosxom to use the domain in $url.
44 $id_domain = "";
45
46 # Feed url
47 # Set the URL of the atom feed here.  Defaults to $blosxom::url/index.atom
48 $feed_url = "";
49
50 # Icon
51 # Put the URL for a site icon here (for example, your site's favicon).  Leave blank to exclude.
52 $icon_url = "";
53
54 # Logo
55 # Set to the URL for your site logo.  Leave blank to exclude.
56 $logo_url = "";
57
58 # What template placeholder in your flavour template should I replace with feed-level <updated>?
59 # If you are using the built-in templates, leave this alone.
60 my $template_placeholder = "{{{updated}}}";
61
62 # Generator that produced this feed
63 $generator_url = "http://blosxom.sourceforge.net/";
64
65 # Enclosures support
66 # ------------------
67
68 # You can add enclosures to your atom feed by linking to them in your post
69 # and giving the anchor tag a rel attribute of "enclosure".
70
71 # Set $use_full_enclosures to 1 if you wish to add length and content-type
72 # to your enclosures.  This function relies upon your webserver having 
73 # LWP modules installed.
74 $use_full_enclosures = '0';
75
76 # Name of a file to cache info about your enclosures:
77 $DataFile = "$blosxom::plugin_state_dir/enclosures.dat";
78
79 # Stylesheet support
80 # ------------------
81
82 # If you have a stylesheet to associate with your atom feed, place it's URL here.
83 $css_url = "";
84
85 # You can specify the type of stylesheet here:
86 $css_type = "text/css";
87
88 # ----- END OF CONFIGURABLE VARIABLES -----
89
90 # --- Plug-in package variables -----
91
92 $author = '';
93 $T = 'T';
94 $colon = ':';
95 $zerozero = '00';
96
97 # Try to glean the domain from $url
98 $id_domain or ($id_domain) = $blosxom::url =~ m#http://(?:www\.)?([^\/]+)#;
99
100 $utc_date = '';
101 $feed_utc_date = '';
102 use vars qw/$feed_utc_date/;
103 $category;
104 $links;
105 $summary;
106
107 # ----- plugin subroutines -----
108
109 sub start {
110
111   # Check for our two mandatory variables:
112   unless ( ( eval { whoami::start() or fauxami::start() } or $default_author ) and $feed_yr ) {
113     warn 'Blosxom plugin: atomfeed > Please set $default_author and $feed_yr.  Exiting.\n';
114     return 0;
115   }
116
117   # Check for the existence of already-loaded flavour templates or theme,
118   # loading templates if there's nothing:
119   # Note that it looks like this condition should *never* be met, so why
120   # did Rael put this code here?  Can't we just do _load_templates();
121
122   $blosxom::template{'atom'}{'head'} or _load_templates();
123
124   # changed to require from use to make plugin work for those
125   # without XML::Parser.  Consequence: entries will never be labelled
126   # type='xhtml', only 'text' or 'html'.  Thanks, S2!
127   eval { require XML::Parser; $parser = new XML::Parser; };
128
129   %escape = ('<'=>'&lt;', '>'=>'&gt;', '&'=>'&amp;', '"'=>'&quot;');
130   $escape_re  = join '|' => keys %escape;
131
132   foreach ( keys %escape ) { $unescape{$escape{$_}} = $_; }
133   $unescape_re  = join '|' => keys %unescape;
134
135   # If required, initialise the enclosures data cache:
136   $use_full_enclosures and _load_cache();
137
138   1;
139 }
140
141 sub head {
142
143   # Make adjustments to plugin variables here, so that users 
144   # can modify their defaults using the config and prefs plugins.
145   # Note that these plugins will have to run *before* atomfeed for this to work as intended.
146
147   $css_url and $css_url = "\n<?xml-stylesheet href=\"$css_url\" type=\"$css_type\"?>";
148
149   $feed_url or $feed_url = "$blosxom::url/index.atom";
150
151   $copyright and $copyright = "<rights>$copyright</rights>";
152
153   $author_uri or $author_uri = "$blosxom::url";
154   $author_uri = "<uri>$author_uri</uri>";
155
156   $author_email and $author_email = "\n      <email>$author_email</email>";
157   $icon_url and $icon_url = "<icon>$icon_url</icon>";
158   $logo_url and $logo_url = "<logo>$logo_url</logo>";
159
160   # Check and prepare a <title> and <subtitle>:
161
162   ($blog_title_type, $blog_title) = _parse_markup($blosxom::blog_title);
163   ($blog_description_type, $blog_description) = _parse_markup($blosxom::blog_description);
164
165   1;
166 }
167
168
169 sub story {
170   my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
171
172   use File::stat;
173
174   # set up <category>:
175   if ( $path ) {
176     $category = "<category term=\"$path\"/>";
177   }
178
179   # <published>: derive from %blosxom::files
180   my @published_utc = gmtime($blosxom::files{"$blosxom::datadir$path/$filename.$blosxom::file_extension"});
181   $published_utc_date = sprintf("%4d-%02d-%02dT%02d:%02d:00Z",
182                                 $published_utc[5]+1900,
183                                 $published_utc[4]+1,
184                                 $published_utc[3],
185                                 $published_utc[2],
186                                 $published_utc[1]);
187
188   # <updated>: derive by stat()ing the file for its mtime:
189   my @updated_utc = gmtime(stat("$blosxom::datadir$path/$filename.$blosxom::file_extension")->mtime);
190   $updated_utc_date = sprintf("%4d-%02d-%02dT%02d:%02d:00Z",
191                               $updated_utc[5]+1900,
192                               $updated_utc[4]+1,
193                               $updated_utc[3],
194                               $updated_utc[2],
195                               $updated_utc[1]);
196
197   # Date/time of most recently-modified story becomes date/time of the feed.
198   $feed_utc_date = $updated_utc_date if $updated_utc_date > $feed_utc_date;
199
200   # use %blosxom::files for the year component of feed-level <atom:id>
201   # in case the creation time is cached somewhere.
202   $utc_yr = $published_utc[5]+1900;
203
204   # Set authorship if available, falling back to $atomfeed::author
205   $author = $whoami::fullname || $fauxami::name || $default_author || '';
206
207   # Setup $summary.  Adapted from Rael's foreshortened plugin.
208   # For simplicities sake, we're going to provide plaint text summaries.
209   $summary = $$body_ref;
210   # first remove tags:
211   $summary =~ s/<.+?>//gs;
212   # then unescape any entities:
213   $summary =~ s/($unescape_re)/$unescape{$1}/g;
214   # truncate to what looks like first sentence:
215   $summary =~ s/[\.\!\?].+$/.../s;
216   # Remove newlines and carriage returns:
217   $summary =~ s/[\r\n]/ /g;
218   # Prepare for use in tempate:
219   $summary = "<summary type=\"text\">$summary</summary>";
220
221   # take look through $$body_ref for any enclosures or via/related links:
222   my @anchors = ( $$body_ref =~ /(<a [^>]+>)/gis );
223   $links = "\n";
224   foreach my $anchor ( @anchors ) {
225     if ( $anchor =~ /rel\s*=\s*"?\s*(via|enclosure|related)"?/is ) {
226       my( $type, $href );
227       $type = $1;
228       if ( $anchor =~ /href\s*=\s*"([^"]+)"/is ) {
229         $href = $1;
230       }
231       elsif ( $anchor =~ /href\s*=\s*([^\s]+)/is ) {
232         $href = $1;
233       }
234       if ( $href ){
235         $href =~ s/\s//g;
236         if ( $use_full_enclosures && ( $type eq "enclosure" ) ) {
237           my( $mime, $length );
238           # Check for presence of enclosure in $info:
239           unless ( $info->{$href} ) { _get_info($href); }
240           if ( $info->{$href} ) {
241             # Check again for data on enclosure in $info, just in case of problems getting it.
242             $mime = $info->{$href}->{type};
243             $length = $info->{$href}->{length};
244             $links .= "    <link rel=\"$type\" href=\"$href\" type=\"$mime\" length=\"$length\"/>\n";
245           }
246           else {
247             # Fall back on a basic link:
248             $links .= "    <link rel=\"$type\" href=\"$href\"/>\n";
249           }
250         }
251         else {
252           # Basic link:
253           $links .= "    <link rel=\"$type\" href=\"$href\"/>\n";
254         }
255       }
256     }
257   }
258
259   # Parse post title:
260   ($title_type, $title) = _parse_markup($$title_ref);
261
262   # Parse the post body:
263   ($body_type, $body) = _parse_markup($$body_ref);
264
265   1;
266 }
267
268 sub foot {
269     my($pkg, $currentdir, $foot_ref) = @_;
270     # Replace the placeholder with the feed-level <updated> element:
271     $feed_utc_date = "<updated>$feed_utc_date</updated>";
272     $blosxom::output =~ s/$template_placeholder/$feed_utc_date/m;
273     return 1;
274 }
275
276 # ----- private subroutines -----
277
278 sub _parse_markup {
279
280   # Pass in some test to parse, and I'll return a type and the text suitably configured.
281   my $text = shift;
282   my $type;
283
284   # First, check to see if $text appears to contain markup.
285   # This regex should match any tag-like string: opening, closing or orphan tags.
286   if ( $text =~ m!</?[a-zA-Z0-9]+ ?/?>! ) {
287     # OK, looks like markup in there.
288     # Now, check to see if it looks well-formed:
289     if ( eval{$parser->parse("<div>$text</div>")}) {
290       # Yes?  XHTML it is, then.  I hope.
291       $type = 'xhtml';
292       $text = "<div xmlns=\"http://www.w3.org/1999/xhtml\">$text</div>";
293     }
294     else {
295       # No?  Good old tag soup.
296       $type = 'html';
297       $text =~ s/($escape_re)/$escape{$1}/g;
298     }
299   }
300   else {
301     # We'll assume it's plaintext then.
302     $type = 'text';
303   }
304
305   # Out go the results:
306   return $type, $text;
307
308 }
309
310 sub _load_cache {
311   # Loads the data stored in $DataFile:
312   $info = {};
313   #open data file
314   local *FH;
315   if( -e "$DataFile") {
316     open FH, "$DataFile" or return $info;
317   }
318   flock(FH, 2);
319   while (<FH>) {
320     chomp ($_);
321     my ($url, $size, $type) = split (/ /, $_);
322     $info->{$url}->{length} = $size;
323     $info->{$url}->{type} = $type;
324   }
325   close (FH);
326   return $info;
327 }
328
329 sub _save_cache {
330   # Saves enclosure data structure in $info out to $DataFile
331   local *FH;
332   open FH, ">$DataFile" or return 0;
333   flock(FH, 2);
334   foreach $url (keys (%{$info})) { 
335     print FH $url." ".$info->{$url}->{length} ." ". $info->{$url}->{type}."\n"; 
336   }
337   close FH;
338   return 1;
339 }
340
341 sub _get_info {
342   # Uses LWP to get content-type and content-length data
343   # for a given URL, adds this to the $info data structure
344   # and then calls _save_cache to preserve $info
345   return 0 unless eval "require LWP::UserAgent";
346   my $url = shift;
347   my $ua = LWP::UserAgent->new;
348   $ua->agent('BlosxomAtomFeed/0.5');
349   my $req = HTTP::Request->new(HEAD => "$url");
350   my $res = $ua->request($req);
351   my( $ct, $cl );
352   if ( $res->is_success ){
353     $ct = $res->header('content-type');
354     $cl = $res->header('content-length');
355     $info->{$url}->{type} = $ct;
356     $info->{$url}->{length} = $cl;
357     _save_cache();
358     return 1;
359   }
360   return 0;
361 }
362
363 sub _load_templates {
364   $blosxom::template{'atom'}{'content_type'} = 'application/atom+xml';
365
366   $blosxom::template{'atom'}{'date'} = "\n";
367
368   $blosxom::template{'atom'}{'head'} =<<'HEAD';
369 <?xml version="1.0" encoding="utf-8"?>$atomfeed::css_url
370 <feed xmlns="http://www.w3.org/2005/Atom" xml:base="http://$atomfeed::id_domain">
371   <title type="$atomfeed::blog_title_type">$atomfeed::blog_title</title>
372   <subtitle type="$atomfeed::blog_description_type">$atomfeed::blog_description</subtitle>
373   <link rel="self" type="application/atom+xml" href="$atomfeed::feed_url"/>
374   <link rel="alternate" type="text/html" hreflang="$blosxom::blog_language" href="$blosxom::url" />
375   <id>tag$atomfeed::colon$atomfeed::id_domain,$atomfeed::feed_yr$atomfeed::colon/$blosxom::path_info</id>
376   <generator uri="$atomfeed::generator_url" version="$blosxom::version">Blosxom</generator>
377   $atomfeed::copyright
378   $atomfeed::icon_url
379   $atomfeed::logo_url
380   {{{updated}}}
381 HEAD
382
383   $blosxom::template{'atom'}{'story'} =<<'STORY';
384   <entry>
385     <id>tag$atomfeed::colon$atomfeed::id_domain,$atomfeed::utc_yr$atomfeed::colon$path/$fn</id>
386     <link rel="alternate" type="text/html" href="$blosxom::url$blosxom::path/$blosxom::fn.$blosxom::default_flavour" />$atomfeed::links
387     <title type="$atomfeed::title_type">$atomfeed::title</title>
388     <published>$atomfeed::published_utc_date</published>
389     <updated>$atomfeed::updated_utc_date</updated>
390     $atomfeed::category
391     <author>
392       <name>$atomfeed::author</name>
393       $atomfeed::author_uri$atomfeed::author_email
394     </author>
395     <content type="$atomfeed::body_type" xml:base="http://$atomfeed::id_domain" xml:lang="$blosxom::blog_language">
396 $atomfeed::body
397     </content>
398   </entry>
399
400 STORY
401
402   $blosxom::template{'atom'}{'foot'} =<<'FOOT';
403 </feed>
404 FOOT
405
406   1;
407 }
408
409 1;
410
411 __END__
412
413 =head1 NAME
414
415 Blosxom Plug-in: atomfeed
416
417 =head1 SYNOPSIS
418
419 Provides an Atom 1.0 feed of your weblog.
420
421 The plugin has all you need right on-board, including the appropriate
422 flavour template components and a few configuration directives.
423
424 It supports the majority of the Atom 1.0 spec excluding the <source>
425 element, which seems intended for use in feeds that contain items
426 aggregated from other feeds, and currently the <contributor> element,
427 which could be included using the meta plugin.
428
429 Point your browser/feed reader at http://yoururl/index.atom.
430
431 =head1 VERSION
432
433 2005-08-04
434
435 =head1 AUTHORS
436
437 Rael Dornfest <rael@oreilly.com>, http://www.raelity.org/
438   - wrote the original plugin based on the 0.3 spec
439
440 Sam Ruby <sam@intertwingly.net>, http://www.intertwingly.net/
441   - contributed the XML::Parser magic
442
443 Frank Hecker <hecker@hecker.org>, http://www.hecker.org/
444   - contributed patches for Atom 0.3 compliance, UTC date/time fix
445
446 Sam Pearson <sam@sgp.me.uk>, http://sgp.me.uk/
447   - Upgraded the plugin to handle Atom 1.0
448
449 Additional code was incorporated in the Atom 1.0 revision from the
450 enclosures plugin originally written by:
451
452 Dave Slusher, http://www.evilgeniuschronicles.org/wordpress/ and Keith
453 Irwin,  http://www.asyserver.com/~kirwin/.
454
455 This plugin is now maintained by the Blosxom Sourceforge Team,
456 <blosxom-devel@lists.sourceforge.net>.
457
458 =head1 QUICKSTART INSTALLATION
459
460 To get an Atom feed up and running in a jiffy, you need only set the
461 following variables and drop the plugin into your plugins directory:
462
463 B<$default_author> is where you specify who to credit as the default
464 author of each entry.  This can be overidden with the value provided
465 by the B<whoami> or B<fauxami> plugins.
466
467 B<$feed_yr> is where you specify the year your site began.  This is
468 important as atomfeed needs to create a unique, unchanging ID for
469 your weblog and it need this information to do so.
470
471 Everything else is optional.
472
473 =head1 FURTHER CONFIGURATION
474
475 There are a lot of variables available in the plugin you can use to
476 customise your Atom feed.  These are all listed under B<CONFIGURABLE
477 VARIABLES>, below, with some notes as to their intended usage.  Some
478 have defaults already specified, others will silently be excluded
479 until you set them.
480
481 As there are some variables generated entirely by the plugin, and as
482 some of the configurable variables are modified by the plugin, there
483 is also a complete list of all the variables available for use in
484 templates with notes on their form under B<TEMPLATE VARIABLES>.
485
486 If you wish to include enclosures or other types of <link> element in
487 your feed, see the section B<ENCLOSURES AND LINK ELEMENTS>, below.
488
489 Although you can use this plugin without anything other than blosxom
490 itself and a standard perl installation, it will perform better with
491 some optional extras available.  See B<PERL MODULES> and B<OTHER
492 PLUGINS> for more information, particularly if you intend to use the
493 B<config> or B<prefs> plugins, any plugin that modifies your posts'
494 actual content (particularly by introducing markup), or any plugin
495 that operates on Blosxom's variable interpolation, such as
496 B<interpolate_fancy>.
497
498 =head1 CONFIGURABLE VARIABLES
499
500 In addition to B<$default_author> and B<$feed_yr>, the plugin has the
501 following user-configurable variables.  Note that when setting
502 variables that are to be used at feed level and that contain URLs, any
503 relative URLs will be interpreted in relation to the value of the
504 variable B<$id_domain>.  This is also true of any URLs included in
505 your posts.
506
507 B<$author_uri> provides a URI for your default author.  If you
508 leave this blank, it defaults to B<$blosxom::url>.
509
510 B<$author_email> Set this if you wish to include an email address for
511 the author of each entry.  Leave it blank to exclude this element of
512 the feed.
513
514 B<$copyright> Set this variable to a statement of copyright for your
515 site.  Leave blank to exclude.
516
517 B<$id_domain> Atom associates unique ID tags with the feed itself and
518 individual entries.  By default it'll attempt to glean your domain
519 from the specified or calculated value of B<$blosxom::url>, but you can
520 override this by setting this variable.
521
522 B<$feed_url> Atom feeds contain pointers to themselves, so you can set
523 this variable to the location of your atom feed.  If you leave in
524 blank, it will use B<$blosxom::url/index.atom>, which in most cases will
525 be correct.
526
527 B<$icon_url> Set this variable to the URL of an icon to associate with
528 your site.  This should be a small image with a 1:1 aspect ratio -
529 favicons are ideal.  Leave blank to exclude.
530
531 B<$logo_url> Set this variable to the URL of a logo to associate with
532 your site.  This can be larger than the icon, and should have an
533 aspect ratio of 2:1.  Leave blank to exclude.
534
535 B<$template_placeholder> Set this varibale to the string used in your
536 head.atom flavour template to identify where you would like the
537 feed-level updated element to appear.  If you are using the built-in
538 templates, there is no need to change the default value.
539
540 B<$use_full_enclosures> If you are including enclosures in your Atom
541 feed, set this variable to 1 if you would like to include length and
542 type attributes.  This requires that you have the LWP modules
543 installed on your webserver to work.  See B<ENCLOSURES AND LINK
544 ELEMENTS>, below, for more information.
545
546 B<$DataFile> Set this variable to the name of a file where length and
547 type data on your enclosures is stored.
548
549 B<$css_url> Set this variable to the location of a stylesheet you
550 would like to have applied to your Atom feed.  Leave blank to exclude
551 altogether.
552
553 B<$css_type> Set this variable to the correct MIME type for the
554 stylesheet you are including in your feed.  Defaults to 'text/css'.
555
556 =head1 TEMPLATE VARIABLES
557
558 The following notes will be of use if you intend to create your own
559 atom flavour templates.
560
561 Note that some variables have the necessary markup included, while
562 others do not; it is stated clearly when a variable contains the
563 required markup.  This is so that they can be included in templates
564 without leaving empty elements when they are not required.
565
566 B<$atomfeed::author> contains the contents for the author section's
567 <name> element.
568
569 B<$atomfeed::author_email> contains any <email> element for the
570 author. Includes the required opening and closing tags.
571
572 B<$atomfeed::author_uri> contains any <uri> element for the author.
573 Includes the required opening and closing tags.
574
575 B<$atomfeed::blog_description> contains the contents for the
576 <subtitle> element.
577
578 B<$atomfeed::blog_description_type> contains the value for the type
579 attribute of the <subtitle> element of the feed.
580
581 B<$atomfeed::blog_title> contains the title of your blog, suitably
582 prepared for use as the content of the feed-level <title> element.
583
584 B<$atomfeed::blog_title_type> contains the value required for the type
585 attribute for the feed-level <title> element.
586
587 B<$atomfeed::body> contains the full text of the body of your weblog
588 post, suitably formatted for use as the contents of the <content>
589 element.
590
591 B<$atomfeed::body_type> contains the value for the type attribute of
592 the <content> element.
593
594 B<$atomfeed::category> contains a <category> for an entry, derived
595 from a story's path.  This variable contains the required opening and
596 closing tags.
597
598 B<$atomfeed::colon> simply contains a colon character, for use in the
599 <id> elements - helps avoid confusion with variable interpolation.
600
601 B<$atomfeed::copyright> contains any copyright statement.  This
602 variable includes the required opening and closing tags.
603
604 B<$atomfeed::css_url> contains everything you need to link to a
605 stylesheet, including the required opening and closing tags.  Note
606 that this element belongs before the opening <feed> tag, as it is a
607 generic XML element.
608
609 B<$atomfeed::feed_url> contains the value for the href attribute of a
610 feed-level <link rel="self"> element which points back at the feed
611 itself.
612
613 B<$atomfeed::feed_yr> contains the year your weblog started.
614
615 B<$atomfeed::icon_url> contains a complete <icon> element, including
616 the required opening and closing tags.
617
618 B<$atomfeed::id_domain> contains the root domain for your weblog.
619
620 B<$atomfeed::links> contains all the via, related and enclosure links
621 for an entry.  This variable contains all the required markup.
622
623 B<$atomfeed::logo_url> contains a complete <logo> element, including
624 the required opening and closing tags.
625
626 B<$atomfeed::published_utc_date> contains the timestamp for an entry
627 based on the value stored in the B<%blosxom::files> hash.
628
629 B<$atomfeed::summary> contains a trimmed <summary> element, including
630 the opening and closing tags.  Derived by truncating the entry down to
631 the first sentence, similar to the B<foreshortened> plugin.
632
633 B<$atomfeed::title> contains the contents for the story-level <title>
634 element.
635
636 B<$atomfeed::title_type> contains the value required for the type
637 attribute of the story-level <title> element.
638
639 B<$atomfeed::updated_utc_date> contains the timestamp for an entry
640 based on a direct stat on the story file itself.
641
642 B<$atomfeed::utc_yr> contains the year in which an entry was made,
643 based upon the value stored in the B<%blosxom::files> hash.
644
645 =head1 ENCLOSURES AND LINK ELEMENTS
646
647 Atom provides an elegant method for expressing relationships between
648 different resources using the rel attribute of its <link> element.
649 This includes the method Atom uses to support enclosures, used to
650 deliver additional content - often audio or video data - to the
651 receipient of the feed.
652
653 To take advantage of this, the plugin supports rel attribute values of
654 via, related and enclosure.  To have these included in your feed,
655 simply link the the resource in the body of your weblog post and make
656 sure that the anchor tag has an appropriate rel attribute of
657 enclosure, via or related, depending upon the kind of relationship you
658 are expressing.
659
660 Ideally, enclosures should also contain information on their length
661 (the size of the file) and MIME type.  The atomfeed plugin will try to
662 determine this information if you set the B<$use_full_enclosures>
663 variable to '1'.  To make sure this works correctly, you should link
664 to the anclosure using an absolute URL rather than a relative one -
665 "http://example.com/podcasts/july-05.mp3" instead of
666 "/podcasts/july-05.mp3" - even if the enclosure is hosted under the
667 same domain.
668
669 If you are unsure as to whether your server has this module installed,
670 you should be able to experiment by setting the variable anyway, as
671 the plugin should continue to function even if it is not present.
672
673 =head1 PERL MODULES
674
675 This plugin will work at its best if your server has B<XML::Parser>
676 and B<LWP> modules installed, although it will function adequately
677 without them.
678
679 =head1 OTHER PLUGINS
680
681 In order for the <published> and <updated> timestamps to make sense,
682 you should be running a plugin like B<entries_cache> that retains the
683 original timestamps of your entries and places them into the
684 B<%blosxom::files> hash.  If you are not, you should remove the
685 <published> element from the story template.
686
687 The atomfeed plugin assumes you're not running any fancy interpolation
688 plugin (e.g. B<interpolate_fancy>) which changes the way variables are
689 specified in a template (e.g. <$foo /> rather than $foo).  If you are
690 running B<interpolate_fancy> or the like use the B<config> plugin and
691 a config.atom file in your blosxom B<$datadir> consisting of:
692
693   $blosxom::plugins{"interpolate_fancy"} = 0;
694
695 Where "interpolate_fancy" is the name of the interpolation plugin
696 you're turning off _just for the atom feed_.
697
698 If you are planning on using the B<config> or B<prefs> plugins to alter
699 variables in the atomfeed namespace, you will need to ensure that
700 these plugins run B<before> the atomfeed plugin.  You can do this by
701 prefixing a number to the name of the relevant plugin, such as B<1config>
702 or B<1prefs>.
703
704 Similarly, if you are running any plugins that alter the content of
705 your posts - for example by escaping characters or adding markup -
706 these should also be set to run before atomfeed.  Essentially, you
707 want atomfeed to get each post as it would be sent to a normal web
708 browser for it to work as intended.
709
710 =head1 SEE ALSO
711
712 Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
713
714 Blosxom Plugin Docs: http://blosxom.sourceforge.net/documentation/users/plugins.html
715
716 1.0 Update Release Notes:
717 http://sgp.me.uk/sam/2005/08/04/atom-for-blosxom
718
719 Atom 1.0 Specification:
720 http://atompub.org/2005/07/11/draft-ietf-atompub-format-10.html
721
722 =head1 BUGS
723
724 None known; please send bug reports and feedback to the Blosxom
725 development mailing list <blosxom-devel@lists.sourceforge.net>.
726
727 =head1 LICENSE
728
729 Blosxom and this Blosxom Plug-in
730 Copyright 2003, Rael Dornfest 
731
732 Permission is hereby granted, free of charge, to any person obtaining
733 a copy of this software and associated documentation files (the
734 "Software"), to deal in the Software without restriction, including
735 without limitation the rights to use, copy, modify, merge, publish,
736 distribute, sublicense, and/or sell copies of the Software, and to
737 permit persons to whom the Software is furnished to do so, subject to
738 the following conditions:
739
740 The above copyright notice and this permission notice shall be
741 included in all copies or substantial portions of the Software.
742
743 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
744 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
745 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
746 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
747 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
748 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
749 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.