1 # Blosxom Plugin: entries_timestamp
2 # Author(s): Gavin Carr <gavin@openfusion.com.au>
4 # Documentation: See the bottom of this file or type: perldoc entries_timestamp
6 package entries_timestamp;
15 #use Blosxom::Debug debug_level => 1;
17 # --- Configurable variables -----
19 # Where should I store the entries_timestamp index file?
20 # IMO timestamps are metadata rather than state, but you may well not care.
21 my $meta_dir = "$blosxom::datadir/../meta";
22 #my $meta_dir = $blosxom::plugin_state_dir;
24 # What name should my entries_timestamp index file be called?
25 # If you want to migrate from entries_index, you can just use the original
26 # entries_index .entries_index.index file, or or just copy/rename it.
27 my $entries_index = 'entries_timestamp.index';
28 #my $entries_index = '.entries_index.index';
30 # Reindexing password. If entries_timestamp finds a '?reindex=$reindex_password'
31 # parameter it will check and resync machine timestamps to the human versions
32 my $reindex_password = 'abracad'; # CHANGEME!
34 # --------------------------------
38 use vars qw($VAR1 $VAR2 $VAR3);
44 my(%indexes, %files_ts, %files_ts_str, %files_symlinks);
47 if ( open ENTRIES, "$meta_dir/$entries_index" ) {
48 my $index = join '', <ENTRIES>;
50 if ( $index =~ m/\$VAR1 = \{/ ) {
53 warn "(entries_timestamp) eval of $entries_index failed: $@";
58 %files_ts_str = %$VAR2 if keys %$VAR2;
59 %files_symlinks = %$VAR3 if keys %$VAR3;
63 %files_ts_str = () unless defined %files_ts_str;
64 %files_symlinks = () unless defined %files_symlinks;
68 # Check for deleted files
69 for my $file (keys %files_ts) {
70 if ( ! -f $file || ( -l $file && ! -f readlink($file)) ) {
72 delete $files_ts{$file};
73 delete $files_ts_str{$file};
74 delete $files_symlinks{$file};
75 # debug(2, "deleting removed file '$file' from indexes");
83 my $curr_depth = $File::Find::dir =~ tr[/][];
84 if ( $blosxom::depth and $curr_depth > $blosxom::depth ) {
85 delete $files_ts{$File::Find::name};
86 delete $files_ts_str{$File::Find::name};
87 delete $files_symlinks{$File::Find::name};
91 # Return unless a match
92 return unless $File::Find::name =~
93 m! ^$blosxom::datadir/(?:(.*)/)?(.+)\.$blosxom::file_extension$ !x;
96 # Return if an index, a dotfile, or unreadable
97 if ( $filename eq 'index' or $filename =~ /^\./ or ! -r $File::Find::name ) {
98 # debug(1, "(entries_timetamp) '$path/$filename.$blosxom::file_extension' is an index, a dotfile, or is unreadable - skipping\n");
102 # Get modification time
103 my $mtime = stat($File::Find::name)->mtime or return;
105 # Ignore if future unless $show_future_entries is set
106 return unless $blosxom::show_future_entries or $mtime <= time;
108 my @nice_date = blosxom::nice_date( $mtime );
110 # If a new symlink, add to %files_symlinks
111 if ( -l $File::Find::name ) {
112 if ( ! exists $files_symlinks{ $File::Find::name } ) {
113 $files_symlinks{$File::Find::name} = 1;
115 # Backwards compatibility deletes
116 delete $files_ts{$File::Find::name};
117 delete $files_ts_str{$File::Find::name};
118 # debug(2, "new file_symlinks entry $File::Find::name, index_mods now $index_mods");
122 # If a new file, add to %files_ts and %files_ts_str
124 if ( ! exists $files_ts{$File::Find::name} ) {
125 $files_ts{$File::Find::name} = $mtime;
127 # debug(2, "new file entry $File::Find::name, index_mods now $index_mods");
129 if ( ! exists $files_ts_str{$File::Find::name} ) {
130 my $date = join('-', @nice_date[5,2,3]);
131 my $time = sprintf '%s:%02d', $nice_date[4], (localtime($mtime))[0];
132 $files_ts_str{$File::Find::name} = join(' ', $date, $time, $nice_date[6]);
134 # debug(2, "new file_ts_str entry $File::Find::name, index_mods now $index_mods");
137 # If asked to reindex, check and sync machine timestamps to the human ones
138 if ( my $reindex = $q->param('reindex') ) {
139 if ( $reindex eq $reindex_password ) {
140 if ( my $reindex_ts = parse_ts( $files_ts_str{$File::Find::name} )) {
141 if ($reindex_ts != $files_ts{$File::Find::name}) {
142 # debug(1, "reindex: updating timestamp on '$File::Find::name'\n");
143 # debug(2, "reindex_ts $reindex_ts, files_ts $files_ts{$File::Find::name}");
144 $files_ts{$File::Find::name} = $reindex_ts;
149 warn "(entries_timestamp) Warning: bad timestamp '$files_ts_str{$File::Find::name}' on file '$File::Find::name' - failed to parse (not %Y-%m-%d %T %z?)\n";
153 warn "(entries_timestamp) Warning: reindex requested with incorrect password\n";
159 if ($blosxom::static_entries) {
160 my $static_file = "$blosxom::static_dir/$path/index.$blosxom::static_flavours[0]";
161 if ( $q->param('-all')
163 or stat($static_file)->mtime < $mtime ) {
164 # debug(3, "static_file: $static_file");
166 $d = join('/', @nice_date[5,2,3]);
168 $path = $path ? "$path/" : '';
169 $indexes{ "$path$filename.$blosxom::file_extension" } = 1;
175 # If updates, save back to index
177 # debug(1, "index_mods $index_mods, saving \%files to $meta_dir/$entries_index");
178 if ( open ENTRIES, "> $meta_dir/$entries_index" ) {
179 print ENTRIES Dumper \%files_ts, \%files_ts_str, \%files_symlinks;
183 warn "(entries_timestamp) couldn't open $meta_dir/$entries_index for writing: $!\n";
187 # Generate blosxom %files from %files_ts and %files_symlinks
188 my %files = %files_ts;
189 for (keys %files_symlinks) {
190 # Add to %files with mtime of referenced file
191 my $target = readlink $_;
192 # Note that we only support symlinks pointing to other posts
193 $files{ $_ } = $files{ $target } if exists $files{ $target };
196 return (\%files, \%indexes);
200 # Helper function to parse human-friendly %Y-%m-%d %T %z timestamps
204 if ($ts_str =~ m/^(\d{4})-(\d{2})-(\d{2}) # %Y-%m-%d
205 \s+ (\d{2}):(\d{2})(?::(\d{2}))? # %H-%M-%S
206 (?:\s+ [+-]?(\d{2})(\d{2})?)? # %z
208 my ($yy, $mm, $dd, $hh, $mi, $ss, $zh, $zm) = ($1, $2, $3, $4, $5, $6, $7, $8);
210 # FIXME: just use localtime for now
211 if (my $mtime = timelocal($ss, $mi, $hh, $dd, $mm, $yy)) {
225 entries_timestamp: blosxom plugin to capture and preserve the original
226 creation timestamp on blosxom posts
230 entries_timestamp is a blosxom plugin for capturing and preserving the
231 original creation timestamp on blosxom posts. It is based on Rael
232 Dornfest's original L<entries_index> plugin, and works in the same way:
233 it maintains an index file (configurable, but 'entries_timestamp.index',
234 by default) maintaining creation timestamps for all blosxom posts, and
235 replaces the default $blosxom::entries subrouting with one returning a
236 file hash using that index.
238 It differs from Rael's L<entries_index> as follows:
242 =item User-friendly timestamps
244 The index file contains two timestamps for every file - the
245 machine-friendly system L<time/2> version, for use by blosxom, and a
246 human-friendly stringified timestamp, to allow timestamps to be reviewed
247 and or modified easily.
249 entries_timestamp ordinarily just assumes those timestamps are in sync,
250 and ignores the string version. If you update the string version and want
251 that to override the system time, you should pass a
252 ?reindex=<reindex_password> argument to blosxom to force the system
253 timestamps to be checked and updated.
255 =item Separate symlink handling
257 entries_timestamp uses separate indexes for posts that are files and
258 posts that are symlinks, and doesn't bother to cache timestamps for
259 the latter at all, deriving them instead from the post they point to.
260 (Note that this means entries_timestamp currently doesn't support
261 symlinks to non-post files at all - they're just ignored).
263 =item Configurable index file name and location
265 I consider post timestamps to be metadata rather than state, so I
266 tend to use a separate C<meta> directory alongside by posts for this,
267 rather than the traditional $plugin_state_dir. You may note care. ;-)
269 =item A complete rewrite
271 Completely rewritten code, since the original used evil evil and-chains
272 and was pretty difficult to understand (imho).
278 L<entries_index>, L<entries_cache>, L<entries_cache_meta>
280 Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
282 =head1 ACKNOWLEDGEMENTS
284 This plugin is largely based on Rael Dornfest's original
285 L<entries_index> plugin.
287 =head1 BUGS AND LIMITATIONS
289 entries_timestamp currently only supports symlinks to local post files,
290 not symlinks to arbitrary files outside your $datadir.
292 entries_timestamp doesn't currently do any kind caching, so it's not
293 directly equivalent to L<entries_cache> or L<entries_cache_meta>.
295 Please report bugs either directly to the author or to the blosxom
296 development mailing list: <blosxom-devel@lists.sourceforge.net>.
300 Gavin Carr <gavin@openfusion.com.au>, http://www.openfusion.net/
304 Copyright 2007, Gavin Carr.
306 This plugin is licensed under the same terms as blosxom itself i.e.
308 Permission is hereby granted, free of charge, to any person obtaining a
309 copy of this software and associated documentation files (the "Software"),
310 to deal in the Software without restriction, including without limitation
311 the rights to use, copy, modify, merge, publish, distribute, sublicense,
312 and/or sell copies of the Software, and to permit persons to whom the
313 Software is furnished to do so, subject to the following conditions:
315 The above copyright notice and this permission notice shall be included
316 in all copies or substantial portions of the Software.
318 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
319 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
320 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
321 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
322 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
323 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
324 OTHER DEALINGS IN THE SOFTWARE.