Convert remaining gavinc plugins to %config versions.
[blosxom-plugins.git] / gavinc / storydate
blobf8c724689f5d847fe7983d4d82002a1e9cceb5bf
1 # Blosxom Plugin: storydate
2 # Author(s): Gavin Carr <gavin@openfusion.com.au>
3 # (based on work by Frank Hecker <hecker@hecker.org>
4 #  and Bob Schumaker <cobblers@pobox.com>)
5 # Version: 0.003000
6 # Documentation: See the bottom of this file or type: perldoc storydate
8 package storydate;
10 use strict;
12 use vars qw(%config);
13 use File::stat;
15 # Uncomment next line to enable debug output (don't uncomment debug() lines)
16 #use Blosxom::Debug debug_level => 1;
18 # --- Configuration variables -----
20 %config = ();
22 # Perl modules to use for strftime, in search order
23 $config{strftime_modules} = [ qw(Time::Piece Date::Format POSIX) ];
25 # ---------------------------------
26 # __END_CONFIG__
28 # Package variables
29 use vars qw(
30   $story_rfc822
31   $story_iso8601
32   $story_mtime_rfc822
33   $story_mtime_iso8601
34   $now_rfc822
35   $now_iso8601
36   $latest_rfc822
37   $latest_iso8601
38   %strftime_formats
41 %strftime_formats = (
42     rfc822      => "%a, %d %b %Y %T %z",
43     # ISO 8601 format (localtime, including time zone offset)
44     # Format is YYYY-MM-DDThh:mm:ssTZD per http://www.w3.org/TR/NOTE-datetime
45     iso8601     => "%Y-%m-%dT%T%z",
48 sub start { 
49     my $now = time();
50     $now_rfc822  = format_date('rfc822', $now);
51     $now_iso8601 = format_date('iso8601', $now);
52     # debug(1, "now_rfc822: $now_rfc822");
53     # debug(1, "now_iso8601: $now_iso8601");
54     1;
57 sub filter {
58     my ($pkg, $files, $others) = @_;
60     # Find the latest publish date in our stories
61     my $latest_ts = 0;
62     for (values %$files) {
63           $latest_ts = $_ if $_ > $latest_ts;
64     }
66     # Format the 'latest' variables
67     $latest_rfc822  = format_date('rfc822', $latest_ts);
68     $latest_iso8601 = format_date('iso8601', $latest_ts);
70     return 1;
73 sub story {
74     my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
76     ($story_rfc822, $story_mtime_rfc822) 
77         = get_story_dates('rfc822', $path, $filename);
78     ($story_iso8601, $story_mtime_iso8601) 
79         = get_story_dates('iso8601', $path, $filename);
81     return 1;
84 # Get the 'file' (publish/official) and 'mtime' formatted dates for the given story file
85 sub get_story_dates {
86     my ($format, $path, $filename) = @_; 
87     my ($file_dt, $mtime_dt) = ('', '');
88     $path ||= '';
90     my $story_file = "$blosxom::datadir$path/$filename.$blosxom::file_extension";
91     my $timestamp = $blosxom::files{ $story_file };
92     # debug(2, "$path/$filename timestamp: $timestamp");
94     if (my $stat = stat( $story_file )) {
95         if (my $mtime = $stat->mtime) {
96             # debug(2, "$path/$filename mtime: = $mtime");
98             $mtime_dt = format_date($format, $mtime);
100             $timestamp ||= $mtime;
101         }
102         else {
103             warn "storydate: cannot get mtime on story file '$story_file'\n";
104         }
105     }
106     else {
107         warn "storydate: cannot stat story file '$story_file'\n";
108     }
110     $file_dt = format_date($format, $timestamp) if $timestamp;
112     return wantarray ? ( $file_dt, $mtime_dt ) : $file_dt;
115 sub format_date {
116     my ($format, $time, $use_gmtime) = @_;
117     return unless $format;
119     my $date = '';
120     if ($format =~ m/%/) {
121         $date = strftime($format, $time, $use_gmtime);
122     }
123     elsif ($strftime_formats{ $format }) {
124         $date = strftime($strftime_formats{ $format }, $time, $use_gmtime);
125     }
126     # Hack to handle the fact that ISO8601 dates require a : in the timezone, which strftime doesn't support
127     if ($format eq 'iso8601') {
128         $date =~ s/(\d)(\d{2})$/$1:$2/;
129     }
131     return $date;
134 # strftime wrapper, using the first strftime() it finds in 'strftime_modules'
135 my %cache = ();
136 my $strftime_module = '';
137 sub strftime {
138     my ($format, $time, $use_gmtime) = @_;
139     return unless $format && $time;
140     $use_gmtime = 0 unless defined $use_gmtime;
141   
142     # Check cache
143     my $cache_key = "$time:$use_gmtime:$format";
144     return $cache{ $cache_key } if exists $cache{ $cache_key };
146     # For testing
147     $strftime_module = $ENV{BLOSXOM_STORYDATE_STRFTIME_MODULE};
149     # Search for a strftime module, and load
150     if (! $strftime_module) {
151         for my $module (@{$config{strftime_modules}}) {
152             if (eval "require $module") {
153                 $strftime_module = $module;
154                 # debug(2, "strftime_module: $strftime_module");
155                 last;
156             }
157         }
158         if (! $strftime_module) {
159             warn "storydate: cannot find any suitable strftime module\n";
160             return '';
161         }
162     }
164     my $datetime = '';
165     if ($strftime_module eq 'Time::Piece') {
166         my $t = $use_gmtime ? Time::Piece->gmtime($time) : Time::Piece->localtime($time);
168         # Seems to be a bug in Time::Piece %z handling - workaround
169         my $tz_offset = sprintf("%+03d%02d", int($t->tzoffset / 3600), ($t->tzoffset % 3600) / 60);
170         $format =~ s/%z/$tz_offset/g;
172         $datetime = $t->strftime( $format );
173     }
175     elsif ($strftime_module eq 'Date::Format') {
176         my @t = $use_gmtime ? gmtime($time) : localtime($time);
177         $datetime = Date::Format::strftime($format, \@t);
178     }
180     elsif ($strftime_module eq 'POSIX') {
181         my @t = $use_gmtime ? gmtime($time) : localtime($time);
182         $datetime = POSIX::strftime($format, @t);
183     }
185     $cache{ $cache_key } = $datetime if $datetime;
186     return $datetime;
192 __END__
194 =head1 NAME
196 storydate - blosxom plugin to provide story dates in various formats 
197 for use by other plugins
200 =head1 DESCRIPTION
202 storydate is a blosxom plugin that provides story dates in various 
203 formats for use by other plugins. It defines the following variables:
205 =over 4
207 =item $story_rfc822
209 The official/publication date of the story in RFC822 format.
211 =item $story_iso8601
213 The official/publication date of the story in ISO8601 format.
215 =item $story_mtime_rfc822
217 The last modification time of the story in RFC822 format.
219 =item $story_mtime_iso8601
221 The last modification time of the story in ISO8601 format.
223 =item $now_rfc822
225 The current time in RFC822 format.
227 =item $now_iso8601
229 The current time in ISO8601 format.
231 =item $latest_rfc822
233 The latest story publication date, in RFC822 format.
235 =item $latest_iso8601
237 The latest story publication date, in ISO8601 format.
239 =back
241 In addition, storydate defines a few subroutines that might be useful
242 to other plugins:
244 =over 4
246 =item strftime($format, $time)
248 Returns $time, formatted according to the L<strftime(3)> format 
249 $format. Requires one of the following perl modules be available to 
250 provide a strftime() function: Time::Piece, Date::Format, or POSIX.
252 =item get_story_dates($format, $path, $filename)
254 Returns the publication date and the last modified date of the story 
255 at "$path/$filename.$blosxom::file_extension", formatted according
256 to the L<strftime(3)> format $format.
258 =back
261 =head1 USAGE
263 storydate should be loaded early, before any story plugins that want
264 to make use of it.
267 =head1 SEE ALSO
269 lastmodified2
271 Blosxom: http://blosxom.sourceforge.net/
274 =head1 ACKNOWLEDGEMENTS
276 storydate is based on Frank Hecker's lastmodified2 plugin, which is 
277 itself based on Bob Schumaker's lastmodified plugin.
279 The problem with lastmodified2 is that its HTTP header functionality 
280 requires that it runs very late, which makes it useless for providing
281 dates to story plugins. storydate is basically just all the parts of
282 lastmodified2 that can be run early pulled out into a separate plugin.
285 =head1 AUTHOR
287 Gavin Carr <gavin@openfusion.com.au>, http://www.openfusion.net/
290 =head1 LICENSE
292 Copyright 2007, Gavin Carr.
294 This plugin is licensed under the same terms as blosxom itself i.e.
296 Permission is hereby granted, free of charge, to any person obtaining a
297 copy of this software and associated documentation files (the "Software"),
298 to deal in the Software without restriction, including without limitation
299 the rights to use, copy, modify, merge, publish, distribute, sublicense,
300 and/or sell copies of the Software, and to permit persons to whom the
301 Software is furnished to do so, subject to the following conditions:
303 The above copyright notice and this permission notice shall be included
304 in all copies or substantial portions of the Software.
306 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
307 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
308 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
309 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
310 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
311 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
312 OTHER DEALINGS IN THE SOFTWARE.
314 =cut
316 # vim:ft=perl:sw=4