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