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