7a0f17bab15d36b2eb1e45600474b428a7686973
[matthijs/upstream/blosxom-plugins.git] / general / entriescache
1 # Blosxom Plugin: entries_cache -*- perl -*-
2 # Author(s): Fletcher T. Penney <http://fletcher.freeshell.org/> 
3 # Modified by Steve Schwarz <http://agilitynerd.com/>
4 # Version: 0.92
5 # Based on entries_index by Rael Dornfest
6 # Documentation help contributed by Iain Cheyne
7
8 package entriescache;
9
10 # --- Configurable variables -----
11
12 $delay = 0;             # How many minutes delay before entries are re-indexed?
13                         # Set to 0 to force reindexing everytime - this
14                         # will provide the same behavior as Rael's
15                         # entries_index.  Though, I am not sure why one 
16                         # would do this.  At least use a small cache time to
17                         # improve performance....
18                         # SAS - I set this to zero so comments and new files
19                         # are immediately visible.
20 $indexname = "$blosxom::plugin_state_dir/.entries_cache.index";
21 $others_indexname = "$blosxom::plugin_state_dir/.entries_cache.others";
22
23
24 $use_date_tags = 1;     # Set to 1 to enable parsing meta- keywords
25                         # for date tags
26
27 $use_UK_dates = 0;      # Default is mm/dd/yy (US)
28                         # Set to 1 to use dd/mm/yy (UK)
29
30 $update_meta_date = 0;          # Add a meta_date tag if it doesn't exist
31         # This will require that perl has write access to your story files
32         # and that you are using the meta plugin.
33         
34         # NOTE: As of version 0.91, you do not NEED the meta plugin...
35
36         # Be sure to save your text files with UNIX line endings
37         # (any decent text editor should be able to do this) or use the blok
38         # plugin (http://www.enilnomi.net/download.html#blok).
39         # There is some logic in entries_cache to fix this, but do not rely
40         # on it.
41
42         # Lastly, make sure there is at least one blank line between the
43         # entry title and the body. Again, there is some logic in
44         # entries_cache to fix this, but do not rely on it.
45         
46         # The default meta-keywords are compatible with Eric Sherman's
47         # entries_index_tagged defaults:
48         
49         # http://primitiveworker.org/blo.g/development/blosxom/entries_index_tagged/
50
51 $meta_timestamp = "meta-creation_timestamp:" unless defined $meta_timestamp;
52         # timestamp_tag is the non-human readable date stamp format
53         # used by entries_index, entries_cache, entries_index_tagged,
54         # and blosxom
55
56 $meta_date = "meta-creation_date:" unless defined $meta_date;
57         # date_tag is a human readable version
58
59 $strip_meta_dates = 1;          # Strip meta-tags from story so that 
60         # they are not displayed.  Unnecessary if you are running the 
61         # meta plugin.
62
63 $debug = 0;     # Debugging flag
64
65 # --------------------------------
66
67 use File::stat;
68 use File::Find;
69 use CGI qw/:standard/;
70 use Time::Local;
71
72 my $time = time();
73 my $reindex = 0;
74
75 sub start {
76         # Force a reindex
77         $reindex = 1 if (CGI::param('reindex'));
78         return 1;
79 }
80
81
82 sub entries {
83         
84         return sub {
85                 my(%files, %indexes, %others);
86         
87                 # Read cached index
88                 if ( open CACHE, $indexname) {
89                         while ($line = <CACHE>) {
90                                 # Improved backwards compatibility with entries_index
91                                 if ($line =~ /\s*'?(.*?)'?\s*=>\s*(\d*),?/) {
92                                         $files{$1} = $2;
93                                 }
94                         }
95                         close CACHE;
96                         # See if it's time to reindex
97                         $reindex = 1 if ( stat($indexname)->mtime lt ($time - $delay*60) );
98                 } else {
99                         # No index found, so we need to index
100                         $reindex = 1;
101                 }
102
103                 # Read cached others index
104                 if ( open CACHE, $others_indexname) {
105                         while ($line = <CACHE>) {
106                                 # Improved backwards compatibility with entries_index
107                                 if ($line =~ /\s*'?(.*)'?\s*=>\s*(.*),?/) {
108                                         $others{$1} = $2;
109                                 }
110                         }
111                         close CACHE;
112                         # See if it's time to reindex
113                         $reindex = 1 if ( stat($others_indexname)->mtime lt ($time - $delay*60) );
114                 } else {
115                         # No index found, so we need to index
116                         $reindex = 1;
117                 }
118
119
120                 # Perform reindexing if necessary
121                 # This code was originally copied from entries_index by Rael Dornfest
122                 # Check to see if previously indexed files exist, and then rescan
123                 # the datadir for any new files, while preserving the old times
124
125                 # Static mode requires some of the code in this section, and the
126                 # speed hit is unimportant for static blogs
127                 
128                 if ($blosxom::static_or_dynamic eq "static") {
129                         $reindex = 1;
130                 }
131
132
133                 if ($reindex eq 1) {
134                         
135                         # If any files not available, err on side of caution and reindex
136                         for my $file (keys %files) { -f $file or do { $reindex++; delete $files{$file} }; }
137                         for my $other (keys %others) { -f $other or do { $reindex++; delete $others{$other} }; }
138
139
140                         find(
141                                 sub {
142                                         my $d; 
143                                         my $curr_depth = $File::Find::dir =~ tr[/][]; 
144
145                                         #       if ( $blosxom::depth and $curr_depth > $blosxom::depth ) {
146                                         #       # We are beyond depth, so remove files
147                                         #       $files{$File::Find::name} and delete $files{$File::Find::name};
148                                         #       $others{$File::Find::name} and delete $others{$File::Find::name};
149                                         #       return;
150                                         #}
151     
152                                 # Adding support for %others
153                                 if (
154                                         $File::Find::name =~ 
155                                                 m!^$blosxom::datadir/(?:(.*)/)?(.+)\.$blosxom::file_extension$!
156                                                 and $2 ne 'index' and $2 !~ /^\./ and (-r $File::Find::name)
157                                         ) {
158                                     # SAS modified so if meta-creation_date or file timestamp are in the future
159                                     # and we don't want future entries the file won't be included in the file list
160                                     # the index list
161                                     if (!$blosxom::show_future_entries and 
162                                         ((extract_date($File::Find::name, $files{$File::Find::name}) > time) or
163                                          (stat($File::Find::name)->mtime > time))) {
164                                         $files{$File::Find::name} and delete $files{$File::Find::name};
165                                         $others{$File::Find::name} and delete $others{$File::Find::name};
166                                     } else {
167                                                 ( $files{$File::Find::name} || ++$reindex )
168                                                 and ( $files{$File::Find::name} = 
169                                                         extract_date($File::Find::name,$files{$File::Find::name}) ||
170                                                         $files{$File::Find::name} ||
171                                                         stat($File::Find::name)->mtime )
172                                                  
173                                                 # Static
174                                                 and (
175                                                         param('-all') 
176                                                         or !-f "$blosxom::static_dir/$1/index." . $blosxom::static_flavours[0]
177 #                                                       or stat("$blosxom::static_dir/$1/index." . $blosxom::static_flavours[0])->mtime < stat($File::Find::name)->mtime
178 # Trying to fix for static mode
179                                                         or stat("$blosxom::static_dir/$1/index." . $blosxom::static_flavours[0])->mtime < $files{$File::Find::name}
180                                                 )
181                                                 and $indexes{$1} = 1
182                                                 and $d = join('/', (blosxom::nice_date($files{$File::Find::name}))[5,2,3])
183                                                 and $indexes{$d} = $d
184                                                 and $blosxom::static_entries and $indexes{ ($1 ? "$1/" : '') . "$2.$blosxom::file_extension" } = 1;
185                                             }
186                                         } else {
187                                                 !-d $File::Find::name and -r $File::Find::name and $others{$File::Find::name} = stat($File::Find::name)->mtime
188                                         }
189                                 }, $blosxom::datadir
190                         );
191
192
193                         if ( $reindex ) {
194                                 # The index was recreated, so we should record the new version
195                                 if ( open ENTRIES, "> $indexname" ) {
196                                         foreach (sort keys %files) {
197                                                 print ENTRIES "$_=>$files{$_}\n";
198                                         }
199                                         close ENTRIES;
200                                 } else {
201                                         warn "couldn't > $indexname: $!\n";
202                                 }
203
204                                 if ( open ENTRIES, "> $others_indexname" ) {
205                                         foreach (sort keys %others) {
206                                                 print ENTRIES "$_=>$others{$_}\n";
207                                         }
208                                         close ENTRIES;
209                                 } else {
210                                         warn "couldn't > $others_indexname: $!\n";
211                                 }
212                         }
213                 }
214         return (\%files, \%indexes, \%others);
215         }
216 }
217  
218  
219 sub extract_date {
220         my ($file, $indexed_date) = @_;
221         my $new_story = "";
222         my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
223         
224         warn "Entries_Cache: Checking $file for meta-tag\n\tComparing to $indexed_date\n" if ($debug == 1);
225         
226         # This is an attempt for compatibility with Eric Sherman's entries_index_tagged
227         # But it does not handle as many date formats, as there are too many additional
228         # necessary modules that I am not willing to require
229         
230         if ( $use_date_tags != 0) {
231         
232                 open (FILE, $file);
233                 $line = <FILE>;         # Read first line ( ie the title)
234                 $new_story .= $line;
235                 
236                 # now, parse the story, and try to correct misformatted stories
237                 while ($line = <FILE>) {
238                         if ($line =~ /^$meta_timestamp\s*(\d+)/) {
239                                 # If present, this format is used
240                                 close File;
241                                 $result = $1;
242                                 warn "Entries_Cache: Found meta_timestamp $result for $file\n" if ($debug == 1);
243                                 return $result;
244                         }
245                         
246                         if ($line =~ /^$meta_date\s*(.*)/) {
247                                 close File;
248                                 $result = $1;
249                                 warn "Entries_Cache: Found meta-date $result for $file\n" if ($debug == 1);
250                                 return parsedate($result);
251                         }
252                         
253                         if ( $line !~ /^meta.*?:/i) {
254                                 # line doesn't start with meta...  (ie story was not formatted
255                                 # for meta-tags), or the meta-tags are finished
256                                 
257                                 if ($update_meta_date eq 1) {
258                                         # Don't mess with stories unless using UNIX line endings.
259                                         
260                                         if (($line =~ /\r/) || ($new_story =~ /\r/)) {
261                                                 warn "Entries_Cache: File $file has non-UNIX line endings; cannot update metatags...\n";
262                                                 return 0;
263                                         }
264                                         
265                                         warn "Entries_Cache: Updating meta-tag for $file\n" if ($debug == 1);
266                                         if ($indexed_date eq 0 || $indexed_date == "") {
267                                                 $indexed_date = stat($file)->mtime;
268                                                 warn "Entries_Cache: No date for $file, using $indexed_date\n" if ($debug == 1);
269                                         }
270                                         
271                                         ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($indexed_date);
272                                         $year += 1900;
273                                         $mon += 1;
274                                         $hour = sprintf("%02d",$hour);
275                                         $min = sprintf("%02d",$min);
276                                         $sec = sprintf("%02d",$sec);
277
278                                         warn "Entries_Cache: Adding meta-tag to $file, using $meta_date $mday/$mon/$year $hour:$min:$sec\n" if ($debug == 1);
279                                 
280                                         if ($use_UK_dates eq 1) {
281                                                 $new_story .= "$meta_date $mday/$mon/$year $hour:$min:$sec\n\n";
282                                         } else {
283                                                 $new_story .= "$meta_date $mon/$mday/$year $hour:$min:$sec\n\n";
284                                         }
285                                         
286                                         if ( $line !~ /^\s*$/) {
287                                                 # If this line wasn't empty, then add it to story
288                                                 $new_story .= $line;
289                                         }
290                                         
291                                         while ($line = <FILE>) {
292                                                 # read remainder of story
293                                                 $new_story .= $line;
294                                         }
295                                         
296                                         close FILE;
297                                         open (FILE, "> $file") or warn "Unable to update date meta-tag on $file\n";
298                                         print FILE $new_story;
299                                         close FILE;
300                                         return 0;
301                                 } else {
302                                         close File;
303                                         return 0;
304                                 }
305                         }
306
307                         $new_story .= $line;
308                 }
309         }
310         return 0;       
311 }
312
313 sub parsedate {
314         my ($datestring) = @_;
315         #warn "Parsing $datestring\n";
316         
317         # Possible formatting
318         # Month can be 3 letter abbreviation or full name (in English)
319         # Time must be hh:mm or hh:mm:ss  in 24 hour format
320         # Year must be yyyy
321         # The remaining 1 or 2 digits are treated as date
322         # ie: May 25 2003 18:40 
323         # order is not important as long as pieces are there
324                 
325         # Convert the datestring to a time() format
326
327         # Find "Shorthand" Date
328         if ( $datestring =~ /\d\d?\/\d\d?\/\d\d\d?\d?/) {
329                 if ( $use_UK_dates eq 0) {
330                         # Use US Formatting
331                         $datestring =~ s/(\d\d?)\/(\d\d?)\/(\d\d\d?\d?)//;
332                         $mon = $1 - 1;
333                         $day = $2;
334                         $year = $3;
335                 } else {
336                         # Use UK Formatting
337                         $datestring =~ s/(\d\d?)\/(\d\d?)\/(\d\d\d?\d?)//;
338                         $mon = $2 - 1;
339                         $day = $1;
340                         $year = $3;
341                 }
342                 
343                 # Now, clean up year if 2 digit
344                 # You may change the 70 to whatever cutoff you like
345                 $year += 2000 if ($year < 70 );
346                 $year += 1900 if ($year < 100);
347         }
348         
349         # Find Month
350         $mon = 0 if ($datestring =~ s/(Jan|January)//i);
351         $mon = 1 if ($datestring =~ s/(Feb|February)//i);
352         $mon = 2 if ($datestring =~ s/(Mar|March)//i);
353         $mon = 3 if ($datestring =~ s/(Apr|April)//i);
354         $mon = 4 if ($datestring =~ s/(May)//i);
355         $mon = 5 if ($datestring =~ s/(Jun|June)//i);
356         $mon = 6 if ($datestring =~ s/(Jul|July)//i);
357         $mon = 7 if ($datestring =~ s/(Aug|August)//i);
358         $mon = 8 if ($datestring =~ s/(Sep|September)//i);
359         $mon = 9 if ($datestring =~ s/(Oct|October)//i);
360         $mon = 10 if ($datestring =~ s/(Nov|November)//i);
361         $mon = 11 if ($datestring =~ s/(Dec|December)//i);
362
363         # Find Time
364         if ($datestring =~ s/(\d\d?):(\d\d)(:\d\d)?//) {
365                 $hour = $1;
366                 $min = $2;
367                 $sec = $3;
368         }
369         
370         if ($datestring =~ s/(\d\d\d\d)//) {
371                 $year = $1;
372         }
373         
374         if ($datestring =~ s/(\d\d?)//) {
375                 $day = $1;
376         }
377         
378         return timelocal($sec,$min,$hour,$day,$mon,$year);
379         
380 }
381
382 sub story {
383         return 1 if (! $strip_meta_dates);
384         # This code based on Rael's meta plugin
385         my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
386         
387         # Strip date-based meta tags from body
388         $$body_ref =~ s/^$meta_timestamp.*//g;
389         $$body_ref =~ s/^$meta_date.*//g;
390         
391         
392         return 1;
393 }
394
395 1;
396
397 __END__
398
399 =head1 NAME
400
401 Blosxom Plug-in: entries_cache
402
403 =head1 SYNOPSIS
404
405 Purpose: This plugin reserves original creation timestamp on weblog 
406 entries, allowing for editing of entries without altering the original 
407 creation time. It maintains an index ($blosxom::plugin_state_dir/
408 .entries_cache.index) of filenames and their creation times. It also adds 
409 new entries to the index the first time Blosxom come across them. In 
410 addition, it is possible to rely on entries_cache inserting date-based meta 
411 tags automatically into entries - this is enabled by the $use_date_tags 
412 setting. This makes the entries more portable. Note that if this approach 
413 is used, the meta plugin is required: http://www.blosxom.com/downloads/
414 plugins/meta.  (NOTE: As of version 0.91 you can use this without the meta
415 plugin.)
416
417 Replaces the default $blosxom::entries subroutine
418
419 The entries_cache plugin is a "souped-up" version of the entries_index 
420 plugin.  It maintains file modification times in the same manner as the 
421 original plugin, but goes one-step further. It uses the modification time 
422 of the index file to determine whether to rescan the datadir.  If $delay 
423 minutes have not passed, it relies on the cached information.
424
425 You can force a manual scan by appending ?reindex=y to the end of your base 
426 url.
427
428 The reason for this change is that the original blosxom and the 
429 entries_index plugin rescan the datadir each time a page is viewed. This 
430 plugin allows you to cache the information to speed up processing times on 
431 most page views.  According to several posts on the blosxom mailing list, 
432 this is one of the big processor hogs. With a $delay setting of 60 minutes, 
433 there will only be one page view each hour that has to wait for the full 
434 directory scan. To be honest, I have not noticed much of a speed boost 
435 during my testing yet, but I imagine it would only appear for sites with a 
436 large number of files to be indexed.
437
438
439 =head1 VERSION
440
441 0.92
442
443 =head1 VERSION HISTORY
444 0.92    Checks if meta-creation_date is in the future and only displays
445         entry if $show_future_entries is set. Otherwise future articles
446         (by meta-creation_date or file modified time) are not displayed.
447         Don't think this breaks static rendering... haven't tested it.
448         
449 0.91    Improved documentation courtesy of Iain Cheyne - THANKS!!!
450                 Now, if you are not running the meta plugin, you will not see any meta tags in your stories
451
452 0.9             Added parser that detects stories that are not properly formatted for meta-tags and reformats them so that they are.
453                 Additionally, it will not update files that have improper line endings ( ie non-UNIX endings).
454  
455 0.8             Fixed typo that caused index to be rebuilt every time...  :)
456
457 0.7             Major revisions - fixed the "Year 1900" bug and an issue with statically generated blogs misbehaving
458
459 0.61    Fixed bug reading old styled cache files
460
461 0.6             Added feature to automatically create meta-tags from indexed
462                                 time/date
463                                 
464 0.52    Fixed a bug where a new index might not be written
465
466 0.51    Added dd/mm/yy(yy) and mm/dd/yy(yy) date formatting
467
468 0.5             Complete rewrite - add support for %others, meta- tags, added backwards compatibility to using the .entries_index.index file from rael's plugin
469
470 0.2             Removed reliance on Data::Dumper, making it suitable for use on Earthlink
471
472 =head1 AUTHOR
473
474 Fletcher T. Penney
475 Modified by Steve Schwarz
476 based on original code by:
477 Rael Dornfest  <rael@oreilly.com>, http://www.raelity.org/
478
479 This plugin is now maintained by the Blosxom Sourceforge Team,
480 <blosxom-devel@lists.sourceforge.net>.
481
482 =head1 SEE ALSO
483
484 Blosxom Home/Docs/Licensing: http://blosxom.sourceforge.net/
485
486 Blosxom Plugin Docs: http://blosxom.sourceforge.net/documentation/users/plugins.html
487
488 =head1 BUGS
489
490 None known; please send bug reports and feedback to the Blosxom
491 development mailing list <blosxom-devel@lists.sourceforge.net>.
492
493 =head1 LICENSE
494
495 entries_cache plugin
496 Copyright 2003, Fletcher Penney
497 except for portions copied from entries_index and entries_index_tagged
498
499 Blosxom and original entries_index plugin
500 Copyright 2003, Rael Dornfest 
501
502 Permission is hereby granted, free of charge, to any person obtaining a
503 copy of this software and associated documentation files (the "Software"),
504 to deal in the Software without restriction, including without limitation
505 the rights to use, copy, modify, merge, publish, distribute, sublicense,
506 and/or sell copies of the Software, and to permit persons to whom the
507 Software is furnished to do so, subject to the following conditions:
508
509 The above copyright notice and this permission notice shall be included
510 in all copies or substantial portions of the Software.
511
512 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
513 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
514 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
515 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
516 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
517 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
518 OTHER DEALINGS IN THE SOFTWARE.