Add Fletcher Penney plugins to general.
authorGavin Carr <gonzai@users.sourceforge.net>
Wed, 5 Sep 2007 12:12:44 +0000 (12:12 +0000)
committerGavin Carr <gonzai@users.sourceforge.net>
Wed, 5 Sep 2007 12:12:44 +0000 (12:12 +0000)
16 files changed:
general/antispam [new file with mode: 0644]
general/autolink [new file with mode: 0644]
general/blacklist [new file with mode: 0644]
general/emptymessage [new file with mode: 0755]
general/entriescache [new file with mode: 0644]
general/find [new file with mode: 0644]
general/hide [new file with mode: 0644]
general/iconset [new file with mode: 0644]
general/imagegallery [new file with mode: 0644]
general/karma [new file with mode: 0644]
general/login [new file with mode: 0644]
general/menu [new file with mode: 0644]
general/recentwritebacks [new file with mode: 0644]
general/redirect [new file with mode: 0644]
general/seewritebacks [new file with mode: 0644]
general/zlocaldepth [new file with mode: 0644]

diff --git a/general/antispam b/general/antispam
new file mode 100644 (file)
index 0000000..02de36f
--- /dev/null
@@ -0,0 +1,86 @@
+#Bloxsom Plugin:AntiSpam
+#Author: Fletcher T. Penney
+#Version: 0.1
+
+package antispam;
+
+# --- Configurable variables ----
+
+
+
+# -------------------------------
+
+$ignore = 0;
+$email = '[\w\.]+@\w+(\.\w+)+';
+
+sub start {
+       1;
+}
+
+sub story {
+       my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ($$story_ref =~ m/<!-- noantispam -->/gi || $$body_ref =~ m/<!-- 
+noantispam -->/gi) {
+               $ignore = 1;
+       }
+
+       if ($ignore == 0) {
+               while ($$body_ref =~ /($email)/ig) {
+                       $original=$1;
+                       $masked="";
+                       @decimal = unpack('C*', $original);
+                       foreach $i (@decimal) {
+                               $masked.="&#".$i.";";
+                       }
+                       $$body_ref =~ s/$original/$masked/ig;
+               }
+       }
+       1;
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: antispam
+
+=head1 DESCRIPTION
+
+This plugin converts any email addresses in your stories into ascii code.  It 
+has no visible or functional effect, other than to change the html source code.  
+So something like this:
+       fletcher@hidden.domain
+
+Looks like this:
+&#102;&#108;&#101;&#116;&#99;&#104;&#101;&#114;&#64;&#97;&#108;&#117;&#109;&#110;&#105;&#46;&#100;&#117;&#107;&#101;&#46;&#101;&#100;&#117;
+
+But when viewed with a web browser, it is translated back into the expected 
+human readable string.  The idea is that some of the spambots can't read this.  
+Though as this technique becomes more popular, I am sure that the spambots will 
+catch on.  But it can't hurt, right?
+
+
+I would like to figure out how to get this work with Writebacks as well to hide 
+email address that are submitted, but haven't gotten around to it.  If someone 
+wants to modify the code and let me know, I can distribute the modified version 
+and give you credit.
+
+Let me know of any bugs!!
+
+=head1 AUTHOR
+
+Fletcher T. Penney  http://fletcher.freeshell.org
+
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  
+If you like, a comment in your modified source attributing credit to myself for 
+my work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT 
+YOUR OWN RISK!
diff --git a/general/autolink b/general/autolink
new file mode 100644 (file)
index 0000000..88ea144
--- /dev/null
@@ -0,0 +1,72 @@
+# Bloxsom Plugin:AutoLink
+# Author: Fletcher T. Penney
+# Version: 0.3
+# modelled after dictionary by Gregory Bair
+
+package autolink;
+
+# --- Configurable variables ----
+
+# Where is the definition page
+$link_file = "$blosxom::datadir/autolinks";
+
+
+# -------------------------------
+
+$ignore = 0;
+
+sub start {
+       open (LINKS, $link_file);
+       @linkslist = <LINKS>;
+       close LINKS;
+       1;
+}
+
+sub story {
+       my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ($$story_ref =~ m/<!-- noautolink -->/gi || $$body_ref =~ m/<!-- noautolink -->/gi) {
+               $ignore = 1;
+       }
+
+       if ($ignore == 0) {
+               foreach $i (@linkslist) {
+                       if ($i =~ /(.*?)=(.*)/) {
+                               $word = $1;
+                               $link = $2;
+
+# By default, autolinks now changes only the first occurence of a given term
+# If you want to change all occurences, 
+# change the end of the following line to '\/a>/ig'
+                               $$body_ref =~ s/(?<=\s)($word)(?=([\s\,\!\;]|\.\s|\:\s|[\(\)]\s))/<a href="$link">$1<\/a>/i;
+                       }
+               }
+       }
+       1;
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: autolink
+
+=head1 DESCRIPTION
+
+Allows you to create a list of keywords that are automatically converted to a url whenever they are used in a story.  You can insert "<!-- noautolink -->" in a story to prevent any changes
+
+You can set your own location for the autolinks data file, but by default it lives in your data directory.
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit to myself and Gregory Bair for his original work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
diff --git a/general/blacklist b/general/blacklist
new file mode 100644 (file)
index 0000000..58273a7
--- /dev/null
@@ -0,0 +1,130 @@
+# Blosxom Plugin: blacklist
+# Author: Fletcher T. Penney (http://fletcher.freeshell.org)
+# Version: 0.1
+
+package blacklist;
+
+# --- Configurable variables ---
+
+# Where is the blacklist file?
+$blacklist_file = "$blosxom::plugin_state_dir/blacklist";
+
+# Where is the whitelist file?
+$whitelist_file = "$blosxom::plugin_state_dir/whitelist";
+
+# Both the black and white lists consist of one IP address per line
+# you may also use regexp's to match more than one address
+# Be careful that you don't accidentally block out the world!
+# Examples:
+# 192.168.1.102 - block this ip addy
+# 192.168.1.* - block the whole subnet
+# 192.* - blocks a whole bunch of addresses
+# .* - blocks the world
+# 192.168.1 - fails to match, as this is not a complete address
+#
+
+# What should I display for a page that isn't found
+$message = "404 Not Found\n";
+
+# ------------------------------
+my ( @blacklist, @whitelist) = ();
+
+sub start {
+       
+       if (open (BLACK, "< $blacklist_file")) {
+               @blacklist = <BLACK>;
+               close BLACK;
+               
+               if (open (WHITE, "< $whitelist_file")) {
+                       @whitelist = <WHITE>;
+                       close WHITE;
+               }
+               
+                       
+               $remoteIP = $ENV{'REMOTE_ADDR'};
+               foreach (@whitelist) {
+                       chomp $_;
+                       if (($_ !~ /^\s*$/) && ($remoteIP =~ /^$_$/)) {
+                               return 0 ;
+                               warn "IP address $_ is whitelisted\n";
+                       }
+               }
+               
+               foreach (@blacklist) {
+                       chomp $_;
+                       if (($_ !~ /^\s*$/) && ($remoteIP =~ /^$_$/)) {
+                               #$blosxom::output = "Status: 404", exit;
+                               warn "IP address $_ is blacklisted\n";
+                               
+                               # You may want to disable certain plugins,
+                               # such as writebacks here, as blosxom is still
+                               # functioning - it just won't return any results
+                               #$blosxom::plugins{'writebacks'} = 0;
+                               
+                               return 1;                               
+                       }
+               }
+               return 0;  # We're finished
+       }
+}
+
+
+sub last {
+       # We only end up here if we are blacklisting someone
+       
+       $blosxom::output = $message;
+       print "Status: 404\n";
+       
+       1;
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: blacklist
+
+=head1 SYNOPSIS
+
+Allows you to blacklist (or whitelist) certain IP addresses or IP ranges by using regexp's.  You create two files, consisting of one term per line.  The plugin compares the visitor's IP address to each term and quits when it finds a match.  Whitelists supercede blacklists.
+
+This plugin does not prevent blosxom from performing all of its steps.  Instead, if a visitor is blacklisted, it erases all of the HTML just prior to sending it to the visitor's browser, and instead sends a 404 code, making it appear as if your web pages are no longer present.
+
+=head1 VERSION
+
+Version 0.1
+
+=head2 VERSION HISTORY
+
+0.1    - initial public release
+
+=head1 AUTHOR
+
+Fletcher T. Penney, http://fletcher.freeshell.org
+
+=head1 LICENSE
+
+Blosxom Blacklist Plug-in
+Copyright 2003, by Fletcher T. Penney
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/general/emptymessage b/general/emptymessage
new file mode 100755 (executable)
index 0000000..2baff87
--- /dev/null
@@ -0,0 +1,77 @@
+# Blosxom Plugin: emptymessage
+# Author(s): Fletcher T. Penney http://fletcher.freeshell.org
+# Version: 0.3
+
+# Displays a message if there are no stories available in the 
+# current directory or below.  Useful for preventing the empty
+# page when someone enters an incorrect path
+
+# Additionally, it gives an error if a partial path is entered, preventing
+# something like /path/t matching /path/to/something
+
+package emptymessage;
+
+# --- Configurable variables -----
+
+$message = qq!<h3><span class="header">I'm Sorry</span></h3>
+<p>There are no stories to display here.  If you feel you have reached this page in error, please use the searchbox on the left to find what you are looking for.</p>
+<p>If you have come here from an old link using a /weblog at the beginning, you can delete the weblog portion of the url and try again, or you can use the searchbox.</p>
+<p>Thank You...</p>!;
+
+$send_404 = 1;         # Set to 1 to send a 404 error instead.
+
+$message_404 = "<!DOCTYPE HTML PUBLIC" . qq! "-//IETF//DTD HTML 2.0//EN">
+<html>
+       <head>
+               <title>404 Not Found</title>
+       </head>
+       <body>
+               <h1>Not Found</h1>
+               <p>The requested page does not exist... Please try another URL</p>
+       <p><a href="$blosxom::url">Return to home page</a>.</p>
+       </body>
+</html>
+!;
+
+# --------------------------------
+
+use CGI qw/:standard/;
+
+my $is_empty = 0;
+
+sub start {
+1;
+}
+
+sub head {
+       my ($pkg, $currentdir, $head_ref) = @_;
+       my $empty = 0;
+       
+       $currentdir = "$blosxom::datadir/$currentdir";
+       $currentdir =~ s/\..*?$//;
+       $currentdir =~ s/\/$//;
+       
+       foreach my $file (keys %blosxom::files) {
+               $empty = 1 if $file =~ /^$currentdir([\.\/].*)?$/;
+       }
+       
+       if ( $empty == 0 ) {
+               if (! param('find')) {
+                       $$head_ref.= $message;
+                       $is_empty = 1;
+               }
+       }
+1;
+}
+
+sub last {
+       if ( ( $send_404 ) && ( $is_empty ) ) {
+               if (! param('recent')) {
+                       $blosxom::output = $message_404;
+                       print "Status: 404\n";
+               }
+       }
+       1;
+}
+
+1;
\ No newline at end of file
diff --git a/general/entriescache b/general/entriescache
new file mode 100644 (file)
index 0000000..61bc45e
--- /dev/null
@@ -0,0 +1,515 @@
+# Blosxom Plugin: entries_cache -*- perl -*-
+# Author(s): Fletcher T. Penney <http://fletcher.freeshell.org/> 
+# Modified by Steve Schwarz <http://agilitynerd.com/>
+# Version: 0.92
+# Based on entries_index by Rael Dornfest
+# Documentation help contributed by Iain Cheyne
+
+package entriescache;
+
+# --- Configurable variables -----
+
+$delay = 0;            # How many minutes delay before entries are re-indexed?
+                       # Set to 0 to force reindexing everytime - this
+                       # will provide the same behavior as Rael's
+                       # entries_index.  Though, I am not sure why one 
+                       # would do this.  At least use a small cache time to
+                       # improve performance....
+                       # SAS - I set this to zero so comments and new files
+                       # are immediately visible.
+$indexname = "$blosxom::plugin_state_dir/.entries_cache.index";
+$others_indexname = "$blosxom::plugin_state_dir/.entries_cache.others";
+
+
+$use_date_tags = 1;    # Set to 1 to enable parsing meta- keywords
+                       # for date tags
+
+$use_UK_dates = 0;     # Default is mm/dd/yy (US)
+                       # Set to 1 to use dd/mm/yy (UK)
+
+$update_meta_date = 0;         # Add a meta_date tag if it doesn't exist
+       # This will require that perl has write access to your story files
+       # and that you are using the meta plugin.
+       
+       # NOTE: As of version 0.91, you do not NEED the meta plugin...
+
+       # Be sure to save your text files with UNIX line endings
+       # (any decent text editor should be able to do this) or use the blok
+       # plugin (http://www.enilnomi.net/download.html#blok).
+       # There is some logic in entries_cache to fix this, but do not rely
+       # on it.
+
+       # Lastly, make sure there is at least one blank line between the
+       # entry title and the body. Again, there is some logic in
+       # entries_cache to fix this, but do not rely on it.
+       
+       # The default meta-keywords are compatible with Eric Sherman's
+       # entries_index_tagged defaults:
+       
+       # http://primitiveworker.org/blo.g/development/blosxom/entries_index_tagged/
+
+$meta_timestamp = "meta-creation_timestamp:" unless defined $meta_timestamp;
+       # timestamp_tag is the non-human readable date stamp format
+       # used by entries_index, entries_cache, entries_index_tagged,
+       # and blosxom
+
+$meta_date = "meta-creation_date:" unless defined $meta_date;
+       # date_tag is a human readable version
+
+$strip_meta_dates = 1;         # Strip meta-tags from story so that 
+       # they are not displayed.  Unnecessary if you are running the 
+       # meta plugin.
+
+$debug = 0;    # Debugging flag
+
+# --------------------------------
+
+use File::stat;
+use File::Find;
+use CGI qw/:standard/;
+use Time::Local;
+
+my $time = time();
+my $reindex = 0;
+
+sub start {
+       # Force a reindex
+       $reindex = 1 if (CGI::param('reindex'));
+       return 1;
+}
+
+
+sub entries {
+       
+       return sub {
+               my(%files, %indexes, %others);
+       
+               # Read cached index
+               if ( open CACHE, $indexname) {
+                       while ($line = <CACHE>) {
+                               # Improved backwards compatibility with entries_index
+                               if ($line =~ /\s*'?(.*?)'?\s*=>\s*(\d*),?/) {
+                                       $files{$1} = $2;
+                               }
+                       }
+                       close CACHE;
+                       # See if it's time to reindex
+                       $reindex = 1 if ( stat($indexname)->mtime lt ($time - $delay*60) );
+               } else {
+                       # No index found, so we need to index
+                       $reindex = 1;
+               }
+
+               # Read cached others index
+               if ( open CACHE, $others_indexname) {
+                       while ($line = <CACHE>) {
+                               # Improved backwards compatibility with entries_index
+                               if ($line =~ /\s*'?(.*)'?\s*=>\s*(.*),?/) {
+                                       $others{$1} = $2;
+                               }
+                       }
+                       close CACHE;
+                       # See if it's time to reindex
+                       $reindex = 1 if ( stat($others_indexname)->mtime lt ($time - $delay*60) );
+               } else {
+                       # No index found, so we need to index
+                       $reindex = 1;
+               }
+
+
+               # Perform reindexing if necessary
+               # This code was originally copied from entries_index by Rael Dornfest
+               # Check to see if previously indexed files exist, and then rescan
+               # the datadir for any new files, while preserving the old times
+
+               # Static mode requires some of the code in this section, and the
+               # speed hit is unimportant for static blogs
+               
+               if ($blosxom::static_or_dynamic eq "static") {
+                       $reindex = 1;
+               }
+
+
+               if ($reindex eq 1) {
+                       
+                       # If any files not available, err on side of caution and reindex
+                       for my $file (keys %files) { -f $file or do { $reindex++; delete $files{$file} }; }
+                       for my $other (keys %others) { -f $other or do { $reindex++; delete $others{$other} }; }
+
+
+                       find(
+                               sub {
+                                       my $d; 
+                                       my $curr_depth = $File::Find::dir =~ tr[/][]; 
+
+                                       #       if ( $blosxom::depth and $curr_depth > $blosxom::depth ) {
+                                       #       # We are beyond depth, so remove files
+                                       #       $files{$File::Find::name} and delete $files{$File::Find::name};
+                                       #       $others{$File::Find::name} and delete $others{$File::Find::name};
+                                       #       return;
+                                       #}
+    
+                               # Adding support for %others
+                               if (
+                                       $File::Find::name =~ 
+                                               m!^$blosxom::datadir/(?:(.*)/)?(.+)\.$blosxom::file_extension$!
+                                               and $2 ne 'index' and $2 !~ /^\./ and (-r $File::Find::name)
+                                       ) {
+                                   # SAS modified so if meta-creation_date or file timestamp are in the future
+                                   # and we don't want future entries the file won't be included in the file list
+                                   # the index list
+                                   if (!$blosxom::show_future_entries and 
+                                       ((extract_date($File::Find::name, $files{$File::Find::name}) > time) or
+                                        (stat($File::Find::name)->mtime > time))) {
+                                       $files{$File::Find::name} and delete $files{$File::Find::name};
+                                       $others{$File::Find::name} and delete $others{$File::Find::name};
+                                   } else {
+                                               ( $files{$File::Find::name} || ++$reindex )
+                                               and ( $files{$File::Find::name} = 
+                                                       extract_date($File::Find::name,$files{$File::Find::name}) ||
+                                                       $files{$File::Find::name} ||
+                                                       stat($File::Find::name)->mtime )
+                                                
+                                               # Static
+                                               and (
+                                                       param('-all') 
+                                                       or !-f "$blosxom::static_dir/$1/index." . $blosxom::static_flavours[0]
+#                                                      or stat("$blosxom::static_dir/$1/index." . $blosxom::static_flavours[0])->mtime < stat($File::Find::name)->mtime
+# Trying to fix for static mode
+                                                       or stat("$blosxom::static_dir/$1/index." . $blosxom::static_flavours[0])->mtime < $files{$File::Find::name}
+                                               )
+                                               and $indexes{$1} = 1
+                                               and $d = join('/', (blosxom::nice_date($files{$File::Find::name}))[5,2,3])
+                                               and $indexes{$d} = $d
+                                               and $blosxom::static_entries and $indexes{ ($1 ? "$1/" : '') . "$2.$blosxom::file_extension" } = 1;
+                                           }
+                                       } else {
+                                               !-d $File::Find::name and -r $File::Find::name and $others{$File::Find::name} = stat($File::Find::name)->mtime
+                                       }
+                               }, $blosxom::datadir
+                       );
+
+
+                       if ( $reindex ) {
+                               # The index was recreated, so we should record the new version
+                               if ( open ENTRIES, "> $indexname" ) {
+                                       foreach (sort keys %files) {
+                                               print ENTRIES "$_=>$files{$_}\n";
+                                       }
+                                       close ENTRIES;
+                               } else {
+                                       warn "couldn't > $indexname: $!\n";
+                               }
+
+                               if ( open ENTRIES, "> $others_indexname" ) {
+                                       foreach (sort keys %others) {
+                                               print ENTRIES "$_=>$others{$_}\n";
+                                       }
+                                       close ENTRIES;
+                               } else {
+                                       warn "couldn't > $others_indexname: $!\n";
+                               }
+                       }
+               }
+       return (\%files, \%indexes, \%others);
+       }
+}
+sub extract_date {
+       my ($file, $indexed_date) = @_;
+       my $new_story = "";
+       my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
+       
+       warn "Entries_Cache: Checking $file for meta-tag\n\tComparing to $indexed_date\n" if ($debug == 1);
+       
+       # This is an attempt for compatibility with Eric Sherman's entries_index_tagged
+       # But it does not handle as many date formats, as there are too many additional
+       # necessary modules that I am not willing to require
+       
+       if ( $use_date_tags != 0) {
+       
+               open (FILE, $file);
+               $line = <FILE>;         # Read first line ( ie the title)
+               $new_story .= $line;
+               
+               # now, parse the story, and try to correct misformatted stories
+               while ($line = <FILE>) {
+                       if ($line =~ /^$meta_timestamp\s*(\d+)/) {
+                               # If present, this format is used
+                               close File;
+                               $result = $1;
+                               warn "Entries_Cache: Found meta_timestamp $result for $file\n" if ($debug == 1);
+                               return $result;
+                       }
+                       
+                       if ($line =~ /^$meta_date\s*(.*)/) {
+                               close File;
+                               $result = $1;
+                               warn "Entries_Cache: Found meta-date $result for $file\n" if ($debug == 1);
+                               return parsedate($result);
+                       }
+                       
+                       if ( $line !~ /^meta.*?:/i) {
+                               # line doesn't start with meta...  (ie story was not formatted
+                               # for meta-tags), or the meta-tags are finished
+                               
+                               if ($update_meta_date eq 1) {
+                                       # Don't mess with stories unless using UNIX line endings.
+                                       
+                                       if (($line =~ /\r/) || ($new_story =~ /\r/)) {
+                                               warn "Entries_Cache: File $file has non-UNIX line endings; cannot update metatags...\n";
+                                               return 0;
+                                       }
+                                       
+                                       warn "Entries_Cache: Updating meta-tag for $file\n" if ($debug == 1);
+                                       if ($indexed_date eq 0 || $indexed_date == "") {
+                                               $indexed_date = stat($file)->mtime;
+                                               warn "Entries_Cache: No date for $file, using $indexed_date\n" if ($debug == 1);
+                                       }
+                                       
+                                       ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($indexed_date);
+                                       $year += 1900;
+                                       $mon += 1;
+                                       $hour = sprintf("%02d",$hour);
+                                       $min = sprintf("%02d",$min);
+                                       $sec = sprintf("%02d",$sec);
+
+                                       warn "Entries_Cache: Adding meta-tag to $file, using $meta_date $mday/$mon/$year $hour:$min:$sec\n" if ($debug == 1);
+                               
+                                       if ($use_UK_dates eq 1) {
+                                               $new_story .= "$meta_date $mday/$mon/$year $hour:$min:$sec\n\n";
+                                       } else {
+                                               $new_story .= "$meta_date $mon/$mday/$year $hour:$min:$sec\n\n";
+                                       }
+                                       
+                                       if ( $line !~ /^\s*$/) {
+                                               # If this line wasn't empty, then add it to story
+                                               $new_story .= $line;
+                                       }
+                                       
+                                       while ($line = <FILE>) {
+                                               # read remainder of story
+                                               $new_story .= $line;
+                                       }
+                                       
+                                       close FILE;
+                                       open (FILE, "> $file") or warn "Unable to update date meta-tag on $file\n";
+                                       print FILE $new_story;
+                                       close FILE;
+                                       return 0;
+                               } else {
+                                       close File;
+                                       return 0;
+                               }
+                       }
+
+                       $new_story .= $line;
+               }
+       }
+       return 0;       
+}
+
+sub parsedate {
+       my ($datestring) = @_;
+       #warn "Parsing $datestring\n";
+       
+       # Possible formatting
+       # Month can be 3 letter abbreviation or full name (in English)
+       # Time must be hh:mm or hh:mm:ss  in 24 hour format
+       # Year must be yyyy
+       # The remaining 1 or 2 digits are treated as date
+       # ie: May 25 2003 18:40 
+       # order is not important as long as pieces are there
+               
+       # Convert the datestring to a time() format
+
+       # Find "Shorthand" Date
+       if ( $datestring =~ /\d\d?\/\d\d?\/\d\d\d?\d?/) {
+               if ( $use_UK_dates eq 0) {
+                       # Use US Formatting
+                       $datestring =~ s/(\d\d?)\/(\d\d?)\/(\d\d\d?\d?)//;
+                       $mon = $1 - 1;
+                       $day = $2;
+                       $year = $3;
+               } else {
+                       # Use UK Formatting
+                       $datestring =~ s/(\d\d?)\/(\d\d?)\/(\d\d\d?\d?)//;
+                       $mon = $2 - 1;
+                       $day = $1;
+                       $year = $3;
+               }
+               
+               # Now, clean up year if 2 digit
+               # You may change the 70 to whatever cutoff you like
+               $year += 2000 if ($year < 70 );
+               $year += 1900 if ($year < 100);
+       }
+       
+       # Find Month
+       $mon = 0 if ($datestring =~ s/(Jan|January)//i);
+       $mon = 1 if ($datestring =~ s/(Feb|February)//i);
+       $mon = 2 if ($datestring =~ s/(Mar|March)//i);
+       $mon = 3 if ($datestring =~ s/(Apr|April)//i);
+       $mon = 4 if ($datestring =~ s/(May)//i);
+       $mon = 5 if ($datestring =~ s/(Jun|June)//i);
+       $mon = 6 if ($datestring =~ s/(Jul|July)//i);
+       $mon = 7 if ($datestring =~ s/(Aug|August)//i);
+       $mon = 8 if ($datestring =~ s/(Sep|September)//i);
+       $mon = 9 if ($datestring =~ s/(Oct|October)//i);
+       $mon = 10 if ($datestring =~ s/(Nov|November)//i);
+       $mon = 11 if ($datestring =~ s/(Dec|December)//i);
+
+       # Find Time
+       if ($datestring =~ s/(\d\d?):(\d\d)(:\d\d)?//) {
+               $hour = $1;
+               $min = $2;
+               $sec = $3;
+       }
+       
+       if ($datestring =~ s/(\d\d\d\d)//) {
+               $year = $1;
+       }
+       
+       if ($datestring =~ s/(\d\d?)//) {
+               $day = $1;
+       }
+       
+       return timelocal($sec,$min,$hour,$day,$mon,$year);
+       
+}
+
+sub story {
+       return 1 if (! $strip_meta_dates);
+       # This code based on Rael's meta plugin
+       my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+       
+       # Strip date-based meta tags from body
+       $$body_ref =~ s/^$meta_timestamp.*//g;
+       $$body_ref =~ s/^$meta_date.*//g;
+       
+       
+       return 1;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: entries_cache
+
+=head1 SYNOPSIS
+
+Purpose: This plugin reserves original creation timestamp on weblog 
+entries, allowing for editing of entries without altering the original 
+creation time. It maintains an index ($blosxom::plugin_state_dir/
+.entries_cache.index) of filenames and their creation times. It also adds 
+new entries to the index the first time Blosxom come across them. In 
+addition, it is possible to rely on entries_cache inserting date-based meta 
+tags automatically into entries - this is enabled by the $use_date_tags 
+setting. This makes the entries more portable. Note that if this approach 
+is used, the meta plugin is required: http://www.blosxom.com/downloads/
+plugins/meta.  (NOTE: As of version 0.91 you can use this without the meta
+plugin.)
+
+Replaces the default $blosxom::entries subroutine
+
+The entries_cache plugin is a "souped-up" version of the entries_index 
+plugin.  It maintains file modification times in the same manner as the 
+original plugin, but goes one-step further. It uses the modification time 
+of the index file to determine whether to rescan the datadir.  If $delay 
+minutes have not passed, it relies on the cached information.
+
+You can force a manual scan by appending ?reindex=y to the end of your base 
+url.
+
+The reason for this change is that the original blosxom and the 
+entries_index plugin rescan the datadir each time a page is viewed. This 
+plugin allows you to cache the information to speed up processing times on 
+most page views.  According to several posts on the blosxom mailing list, 
+this is one of the big processor hogs. With a $delay setting of 60 minutes, 
+there will only be one page view each hour that has to wait for the full 
+directory scan. To be honest, I have not noticed much of a speed boost 
+during my testing yet, but I imagine it would only appear for sites with a 
+large number of files to be indexed.
+
+
+=head1 VERSION
+
+0.92
+
+=head1 VERSION HISTORY
+0.92    Checks if meta-creation_date is in the future and only displays
+        entry if $show_future_entries is set. Otherwise future articles
+        (by meta-creation_date or file modified time) are not displayed.
+        Don't think this breaks static rendering... haven't tested it.
+        
+0.91   Improved documentation courtesy of Iain Cheyne - THANKS!!!
+               Now, if you are not running the meta plugin, you will not see any meta tags in your stories
+
+0.9            Added parser that detects stories that are not properly formatted for meta-tags and reformats them so that they are.
+               Additionally, it will not update files that have improper line endings ( ie non-UNIX endings).
+0.8            Fixed typo that caused index to be rebuilt every time...  :)
+
+0.7            Major revisions - fixed the "Year 1900" bug and an issue with statically generated blogs misbehaving
+
+0.61   Fixed bug reading old styled cache files
+
+0.6            Added feature to automatically create meta-tags from indexed
+                               time/date
+                               
+0.52   Fixed a bug where a new index might not be written
+
+0.51   Added dd/mm/yy(yy) and mm/dd/yy(yy) date formatting
+
+0.5            Complete rewrite - add support for %others, meta- tags, added backwards compatibility to using the .entries_index.index file from rael's plugin
+
+0.2            Removed reliance on Data::Dumper, making it suitable for use on Earthlink
+
+=head1 AUTHOR
+
+Fletcher T. Penney
+Modified by Steve Schwarz
+based on original code by:
+Rael Dornfest  <rael@oreilly.com>, http://www.raelity.org/
+
+=head1 SEE ALSO
+
+Blosxom Home/Docs/Licensing: http://www.raelity.org/apps/blosxom/
+
+Blosxom Plugin Docs: http://www.raelity.org/apps/blosxom/plugin.shtml
+
+=head1 BUGS
+
+Address bug reports and comments to the Blosxom mailing list 
+[http://www.yahoogroups.com/group/blosxom].
+
+=head1 LICENSE
+
+entries_cache plugin
+Copyright 2003, Fletcher Penney
+except for portions copied from entries_index and entries_index_tagged
+
+Blosxom and original entries_index plugin
+Copyright 2003, Rael Dornfest 
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/general/find b/general/find
new file mode 100644 (file)
index 0000000..7fd1c37
--- /dev/null
@@ -0,0 +1,353 @@
+# Blosxom Plugin: Find
+# Author: Fletcher T. Penney
+#              advanced search concept and keywords code by Eric Sherman
+#              Recent Searches feature based on code by Marc Nozell
+# Version: 0.9
+package find;
+
+# --- Configurable variables -----
+# None yet - may add ability to change search behaviors, such as
+# always starting at the root level of your datadir while staying in a subdirectory
+
+$keywords_tag = "meta-keywords:" unless defined $keywords_tag;
+
+$do_local_search = 0;  # Perform search relative to the current page, 
+                                               # not the whole site.  If set to 1, this will
+                                               # override the advanced search option
+
+$show_advanced = 0;            # Set to 1 to always show the advanced form
+
+$show_debug = 0;               # display more info about search terms for debugging
+
+$default_to_and = 0;           # Set to 1 to always do "and" searches by default
+
+$match_whole_words = 0;                # Set to 1 to only match whole words by default
+
+$log_searches = 1;             # Log search queries to a file?
+$log_filename = "$blosxom::plugin_state_dir/queries";  # Where should I log?
+
+
+$max_previous_searches = 10;   # Maximum old queries to display
+
+$search_writebacks = 1;                # Should I also search writebacks?
+$writeback_dir = "$blosxom::plugin_state_dir/writeback";
+$writeback_ext = "wb";
+
+
+$search_filenames = 1;         # Should I also search filenames?
+
+# --------------------------------
+
+
+$results = "";
+$recentsearches = "";
+
+use CGI qw/:standard/;
+
+
+sub start {
+
+
+       # Figure out the current path and flavour for the form
+       $path_withflavour = $blosxom::path_info;
+       if ($path_withflavour !~ s/\.[^\.]*$//) {
+               $path_withflavour =~ s/\/$//;
+               $path_withflavour .= "\/index";
+               $path_withflavour =~ s/^([^\/])/$1/;
+       }
+       $path_withflavour =~ s/^\/*//;
+       $path_withflavour.="\.$blosxom::flavour";
+
+       # Insert this html code only if advanced form is indicated
+       $advancedform = qq!<br />Search:<br />
+<input checked type="radio" name="domain" value="all" />Entire Site
+<input type="radio" name="domain" value="topic" />This Topic Only
+<br />Match:<br />
+<input checked="checked" type="radio" name="type" value="any" />Any
+<input type="radio" name="type" value="all" />All
+<br />
+<input checked="checked" type="radio" name="match" value="any" />Partial
+<input type="radio" name="match" value="whole" />Whole Words only
+! 
+               if ((param('advanced_search')) || $show_advanced);
+
+       # This is the basic form
+
+$searchform = qq!<form method="get" action="$blosxom::url/index.$blosxom::flavour">
+       <div>
+               <input type="text" name="find" size="15" value=""/>
+               <input type="submit" value="Search" />
+               <input type="hidden" name="plugin" value="find"/>
+               <input type="hidden" name="path" value="$blosxom::path_info"/>
+               <br/>
+               <a href="$blosxom::url/$path_withflavour?advanced_search=1">Advanced Search</a>
+$advancedform
+</div></form>!;
+
+       1;
+}
+
+sub filter {
+       # Check that writebacks are working
+       #$search_writebacks = 0 if ( $writeback::writeback_dir eq "");
+
+       my ($pkg, $files_ref) = @_;
+       my @files_list = keys %$files_ref;
+       if (param('find')) {
+               my $terms = param('find');
+               $searchpath = "$blosxom::datadir/" . param('path');
+               $do_local_search = 1 if (param('domain') eq "topic");
+
+               $match_whole_words =1 if (param('match') eq 'whole');
+               $match_whole_words =0 if (param('match') eq 'any');
+
+               my $searchtype = param('type');
+               $default_to_and = 0 if ($searchtype eq 'any');
+
+               my @requiredterms, @forbiddenterms;
+               
+
+               $results = "These pages matched: $terms";
+
+               if ($log_searches eq 1) {
+                       if ( !-e $log_filename ) {
+                               open (LOG, ">>$log_filename");
+                               chmod (0666, "$log_filename");
+                       } else {
+                               open (LOG, ">>$log_filename") or warn "Error in find logging file."
+                       }
+                       print LOG "$terms\n";
+                       close (LOG);
+               }
+
+               $terms = " " . $terms;  # Add a space for pattern matching reasons
+
+
+               # Handle double quotations (exact phrases)
+               $terms =~ s/\"([^\"]+)\"/\[\{$1\}\]/g;
+               while ($terms =~ s/\[\{([^\}]*)\s+([^\}]*)/\[\{$1\\s\+$2/g) {
+               }
+               $terms =~ s/\[\{/(/g;
+               $terms =~ s/\}\]/)/g;
+               # Any left over quotes were "odd-numbered"
+               $terms =~ s/\"//g;
+               
+               # Handle parentheses
+               while ($terms =~ s/\(([^\)]*)\s+([^\)]*)\)/\($1\|$2\)/g) {
+               }
+
+               # Strip trailing spaces to prevent empty terms
+               # Don't strip leading spaces yet!
+               $terms =~ s/\s+$//;
+               
+               # Convert English to symbols
+               # The "OR"'s will wait til the end
+               # Handle "NOT"'s
+               $terms =~ s/\s+not\s+/ \-/ig;
+               # Handle "AND"'s and convert to "+", unless preceded by "-"
+               $terms =~ s/\s+(\([^\)]+\))\s+and\s+/ \+$1 \+/ig;
+               $terms =~ s/\-(\([^\)]+\))\s+and\s+/\-$1 \+/ig;
+               $terms =~ s/\s+([^\)]+)\s+and\s+/ \+$1 \+/ig;
+               $terms =~ s/\-([^\)]+)\s+and\s+/\-$1 \+/ig;
+               $terms =~ s/\+\-/\-/g;  # Fix if the second term already had "-"
+               
+               $results = "These pages matched: $terms" if ($show_debug eq 1); 
+       
+               # If doing "all" search, then every term is required
+               # Will not override terms already set to "NOT"
+               $terms =~ s/\s+\+?([\(\)\|\w]+)/ \+$1/g if (($searchtype eq "all") || ($default_to_and eq 1));
+
+               # Extract all required terms ("AND"  terms)
+               while ($terms =~ s/\s+\+([\(\)\|\\\+\w]+)//){
+                       $theterm = $1;
+                       $theterm = "\\b$theterm\\b" if ($match_whole_words eq 1);
+                       push(@requiredterms,$theterm);
+                       $results.="<br>Required Term: $theterm" if ($show_debug eq 1);
+               }
+
+               # Extract all "forbidden" terms ("NOT" terms)
+               while ($terms =~ s/\s+\-([\(\)\|\\\+\w]+)//){
+                       $theterm = $1;
+                       $theterm = "\\b$theterm\\b" if ($match_whole_words eq 1);
+                       push(@forbiddenterms,$theterm);
+                       $results.="<br>Forbidden Term: $theterm" if ($show_debug eq 1);
+               }
+
+               # Strip "OR"'s with only one term
+               while ($terms =~ s/^\s*or\s+//i) {}
+               while ($terms =~ s/\s+or\s*$//i) {}
+               
+
+               # Now cleanup for regexp's
+               $terms =~ s/^\s+//;     #Strip leading and trailing spaces
+               $terms =~ s/\s+$//;
+               # Finally, convert all the "OR" terms to a single regexp
+               $terms =~ s/\s+(or\s+)?/\|/ig;
+               $terms =~ s/(\s)\+/$1/g;        # Loose '+' will crash regexp
+
+               # Handle whole word matching on remainder
+               $terms = "\\b$terms\\b" if ($match_whole_words eq 1);
+
+               # Debugging Aids
+               $results.="<br>Remainder regexp: $terms<br>" if ($show_debug eq 1);
+               $results.="Search path: $searchpath <br>" if ($show_debug eq 1);
+
+               # Quit now if nothing to search for
+               if (($terms eq "") & (scalar(@requiredterms) eq 0) & (scalar(@forbiddenterms) eq 0)) {
+                       $results = "";
+                       return 0;
+               }
+
+               foreach $file (@files_list) {
+               #       next;           # Enable this line to debug terms parsing only
+                       if ($do_local_search eq 1) {
+                               # Limit search to the current path only
+                               if ($file !~ /^$searchpath/) {
+                                       delete $files_ref->{$file};
+                                       next;
+                               }
+                       }
+                       my $keep = 0;
+                       my $delete = 0;
+                       open (FILE, "<$file") or next;
+                       my $contents = "";
+                       my $pastHeader = 0;
+                       while ($line = <FILE>) {
+                               if (!$pastHeader) {
+                                       # include keywords
+                                       if ($line =~ /^$keywords_tag/i) {
+                                               $line =~ s/^$keywords_tag(.*)$/\1/;
+                                       }
+                                       # don't read other meta- tags
+                                       elsif ($line =~ /^meta-/i) {
+                                               next;
+                                       }
+                                       # if reached the header, say so
+                                       elsif ($line =~ /^\s.*$/) {
+                                               $pastHeader = 1;
+                                       }
+                               }
+                               $contents .= $line;
+                       }
+
+                       close (FILE);
+                       
+                       # Now scan writebacks for this story
+                       if ( $search_writebacks == 1) {
+                               my $writeback_file = $file;
+                               $writeback_file =~ s/$blosxom::datadir/$writeback_dir/;
+                               $writeback_file =~ s/$blosxom::file_extension$/$writeback_ext/;
+
+                               if (open (FILE, "<$writeback_file")) {
+                                       while ($line = <FILE>) {
+                                               # We'll just appened writebacks to the story
+                                               $contents .= $line;
+                                       }       
+                                       close (FILE);           
+                               }
+                       }
+                       
+                       # If searching filenames, append that to the story for 
+                       # searching as well
+                       
+                       if ($search_filenames == 1) {
+                               $contents.=$file;
+                       }
+                       
+                       # If we match any "OR" terms flag file for keeping
+                       $keep = 1 if ($contents =~ /$terms/si);
+                                               
+                       # If we match required terms, keep, else delete for sure
+                       foreach (@requiredterms) {
+                               if ($contents =~ /$_/si) {
+                                       $keep =1;
+                               } else {
+                                       $delete = 1;
+                               }
+                       }
+
+                       # If we match forbidden terms, then delete
+                       foreach (@forbiddenterms) {
+                               if ($contents =~ /$_/si) {
+                                       $delete =1;
+                               } 
+                       }
+
+                       # Remove file if marked for delete or not marked to keep
+                       delete $files_ref->{$file} if (($delete eq 1) or ($keep eq 0));
+               }
+       }
+
+       1;
+}
+
+sub getrecentsearches {
+       if ( open(LOG, "< $log_filename")) { 
+               my @searches = <LOG>; 
+               close(LOG); 
+               @searches = reverse(@searches); 
+               $recentsearches = "<ul>";
+               for ($count = 0; $count < $max_previous_searches; $count++) { 
+                       $recentsearches .= '<li>' . $searches[$count] . '</li>';
+               }
+               $recentsearches .= "</ul>";
+       } else {
+               warn "Couldn't open $log_filename: $!\n" if ($log_searches == 1);
+       }
+       1;
+}
+
+sub head {
+       getrecentsearches();
+       1;
+}
+
+
+1;
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: find
+
+=head1 DESCRIPTION
+
+Find searches through the available articles and filters out those that do not match the submitted search terms.  To use it, simply place $find::searchform in your template, and it will create a search box that automatically calls the search routine.  It performs a boolean "OR" search by default, or you can use regular expressions for more complicated search terms.
+
+This plugin is capable of handling the following search terms
+
+term1 term2; term1 or term2
+       These match any page with term1 OR term2
+       
+term1 and term2; +term1 +term2
+       These match any page with both term1 AND term2
+       
+term1 not term2; term1 -term2
+       This matches pages with term1 that DO NOT contain term2
+       
+term1 not (term2 term3)
+       This matches pages with term1 that DO NOT contain term2 OR term3
+
+"term1 term2 term3"
+       This matches the exact phrase, term1 term2 term3
+
+" pen "
+       This will match the word "pen", but not the word "pencil".
+       
+You can also use regular expressions within your search terms to further refine your searches, creating a very powerful search engine.
+
+Additionally, you can include the most recent search requests in your blog.  Add $find::recentsearches in your template.  By default, the last 10 searches will be shown in an unordered list.  You can change $max_previous_searches to alter the number displayed.
+
+=head1 AUTHORS
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+Eric Sherman           <enkidu@enkidu.bloggedup.com>
+Marc Nozell            <marc@nozell.com> http://www.nozell.com/blog
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit for my original work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
diff --git a/general/hide b/general/hide
new file mode 100644 (file)
index 0000000..cb7c3ea
--- /dev/null
@@ -0,0 +1,106 @@
+# Blosxom Plugin: hide
+# Author(s): Fletcher T. Penney http://fletcher.freeshell.org
+# Version: 0.1
+# Based on exclude plug-in by Breyten Ernsting
+
+# Modified version to only hide files when find plugin is not active.
+# In other words, allow files to be shown when searching, but not browsing...
+
+package hide;
+
+# --- Configurable variables -----
+
+$ign_file = 'hide';
+
+# --------------------------------
+use CGI qw/:standard/;
+
+sub start {
+  $ign_fp = ($blosxom::path_info) ? "$blosxom::datadir/$blosxom::path_info" : "$blosxom::datadir";
+  $ign_ext = (-e "$ign_fp/${ign_file}.${blosxom::flavour}") ? ".${blosxom::flavour}" : "";
+  $ign_fn = "$ign_fp/$ign_file$ign_ext";
+  @excludes = ();
+  open(EXCLUDE, "< $ign_fn") or 1;
+  while (<EXCLUDE>) {
+    chomp;
+    push(@excludes, "$ign_fp/$_") if $_;
+  }
+  close(EXCLUDE);
+  1;
+}
+
+sub filter {
+  my ($pkg, $files_ref) = @_;
+  my @files_list = keys %$files_ref;
+
+  return 0 if ((param('plugin') eq 'find') || (param('find')));
+
+  foreach $exclude (@excludes) {
+    foreach $ign_cf (@files_list) {
+      $ign_cf !~ m/^$exclude/ or delete $files_ref->{$ign_cf};
+    }
+  }
+
+  1;
+}
+
+1;
+
+
+
+=head1 NAME
+
+Blosxom Plug-in: hide
+
+=head1 SYNOPSIS
+
+Purpose: Allows you to hide stories and categories from view while browsing, but still allow them to appear in the results of a search via my find plugin
+
+=head1 VERSION
+
+0.1
+
+=head1 AUTHOR
+
+Fletcher T. Penney
+http://fletcher.freeshell.org
+
+
+=head1 CONFIGURATION
+
+C<$ign_file> name to use for hide files. Defaults to
+C<hide>.
+
+=head1 EXAMPLE
+
+Your C<hide> should look like this:
+
+apps/
+life/about.txt
+
+One entry per line. You should be able to use regexes.  Works just like exclude in this regard.
+
+=head1 LICENSE
+
+original exclude Blosxom Plug-in
+Copyright 2003, Breyten Ernsting
+
+(This license is the same as Blosxom's)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/general/iconset b/general/iconset
new file mode 100644 (file)
index 0000000..cabe9f4
--- /dev/null
@@ -0,0 +1,90 @@
+# Bloxsom Plugin:IconSet
+# Author: Fletcher T. Penney
+# Version: 0.2
+
+package iconset;
+
+# --- Configurable variables ----
+
+# Where are your icon image files?
+$logos_url = "/images";
+
+# Where is the icon definition page
+# Note that this is optional
+$icon_file = "$blosxom::datadir/iconlist";
+
+# Is the icons plugin enabled
+$use_icons_plugin = 1;
+
+# -------------------------------
+
+$iconset="";
+
+sub start {
+       open (ICONS, $icon_file);
+       @iconslist = <ICONS>;
+       close ICONS;
+1;
+}
+
+sub story {
+       my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       $iconset = "";
+       foreach $iconpair (@iconslist) {
+               if ($iconpair =~ /(.*)=(.*)/) {
+                       $categ=$1;
+                       $icon=$2;
+               $iconset= qq!<img src="$logos_url/$icon" alt="logo">! if ($path =~ /^$categ/i);
+               }
+       }
+
+       if ($$body_ref =~ /<!-- iconset\s*(\S*)\s*-->/i) {
+               $iconset= qq!<img src="$logos_url/$1" alt="logo">! if $1 ne"";
+       }
+
+       if ($use_icons_plugin eq 1) {
+               $icons::icons = $iconset if ($iconset ne "");
+       }
+
+       1;
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: iconset
+
+=head1 DESCRIPTION
+
+IconSet allows you to set an icon to be used within a particular story.  Set the $logos_url to the url for your icons.  You then use the <!-- iconset ### --> directive within a story to set an icon to be displayed for that story.  If a default icon is set by the icons plug-in, it will be overridden.
+
+If the icons plugin is present, then you may continue to use the $icons::icons variable.  Otherwise, you can use the $iconset::iconset variable.
+
+The $icon_file allows you to set an icon for any story in a category by using a definitions file.  This allows you to set an icon for any category, whether it is top level or not, without having a world readable datadir.  (The original icons plugin cannot do this.)
+
+The format for iconfile is:
+path=iconfile
+
+and uses regexp's for either expression. So, 
+/computers/blosxom=blosxom.gif will set the icon to blosxom.gif for any file in the computers/blosxom directory, or any of its subdirectories.
+
+If you later defined:
+/computers/blosxom/newstuff=cool.png, then anything in this one subdirectory will have a different icon.  The line that matches last is the one that "sticks."
+
+By using this technique, you can replace the icons plugin altogether by replacing $iconset::iconset in your templates wherever you used to use $icons::icons.  If you do this, then change the $use_icons_plugin variable.
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit to myself and Gregory Bair for his original work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
\ No newline at end of file
diff --git a/general/imagegallery b/general/imagegallery
new file mode 100644 (file)
index 0000000..aa4a71c
--- /dev/null
@@ -0,0 +1,169 @@
+# Bloxsom Plugin: ImageGallery
+# Author: Fletcher T. Penney
+# Version: 0.6
+
+package imagegallery;
+
+# --- Configurable variables ----
+
+# URL for images directory - will be added to beginning of images for the web browser to find them
+$images_url = "/images/galleries";
+
+# Filepath for images directory
+# This gets added to the gallery to find the files
+# NOTE: This is a change in behavior from version 0.1
+$images_path = "$blosxom::datadir/../images/galleries";
+
+
+# Default thumbnail sub-directory name
+$thumbdir="thumbnails";
+
+# Default image sub-directory name
+$imagesdir="images";
+
+# Default comments sub-directory name
+$commentsdir="comments";
+
+# Default number of columns
+$columns = 3;
+
+# HTML Code for gallery pieces
+$galleryhead = "";
+
+$galleryrowstart = "";
+
+$galleryrowend = "";
+
+$gallerycolstart = qq!<div class="image">\n!;
+
+$gallerycolend = "</div>\n";
+
+$galleryfoot = "";
+
+
+# Message to display in category view
+$gallerymessage = "<br/>You must click on the individual story to see the images.  Try the \"permanent link\" button.";
+
+# HTML Code for single image view
+
+$imagehead = qq!<div class="single-image">\n!;
+
+$imagefoot = qq!</div>\n!;
+
+
+# Extensions for valid image types to display
+$imagetypes = "jpg|gif|png";
+
+# -------------------------------
+$path_noflavour = "";
+$gallery="";
+$gallerypath="";
+$comments = "";
+
+use File::Find;
+use CGI;
+
+sub start {
+       $path_noflavour = $blosxom::path_info;
+       if ($path_noflavour !~ s/\.[^\.]*$//) {
+               $path_noflavour =~ s/\/$//;
+               $path_noflavour .= "\/index";
+               $path_noflavour =~ s/^([^\/])/$1/;
+       }
+       $path_noflavour =~ s/^\/*//;
+
+}
+
+
+sub story {
+       $gallery="";
+       $comments="";
+       my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ($$body_ref =~ /<!-- imagegallery\s*(\S*)\s*-->/i) {
+               $gallerypath= $1 . "\/" if $1 ne "";
+
+               $gallerypath =~ s/\/\//\//g;
+       } else {
+               return 1;
+       }
+
+       if (($path_noflavour =~ /index$/) || ($path_noflavour =~ /\/$/)) {
+               $gallery = "<br/> You must view this story individually to <a href=\"$blosxom::url$path/$filename.$blosxom::flavour\">see the images</a>.)";
+               
+               if ($blosxom::flavour eq "rss") {
+                       $gallery = "&lt;br&gt; You must view this story individually to &lt;a href=&quot;$blosxom::url$path/$filename.$blosxom::flavour&quot; see the images&lt/a&gt;.)";
+               }
+               return 1;
+       }
+
+       $gallery_url = "$images_url/$gallerypath";
+       $gallerypath = "$images_path/$gallerypath";
+
+       # If this is a request for a single photo, display it, and exit
+       if (CGI::param("image")) {
+               $imagefile = CGI::param("image");
+               $gallery = $imagehead;
+               $gallery.= "<img src=\"$gallery_url" . "$imagesdir/$imagefile\" alt=\"Imagegallery - $imagefile\" />";
+               $gallery .= $imagefoot;
+
+               # Try and read comments
+               $imagefile =~ s/($imagetypes)$/txt/i;
+               open (COMMENT, "<$gallerypath/$commentsdir/$imagefile");
+               $comments = join '', <COMMENT>;
+               close COMMENT;
+               return 1;
+       }
+
+       # Now, find out how many images there are, and where
+       my(@images);
+
+       find(sub {
+               $File::Find::name =~ /($imagetypes)$/i
+               and
+               push (@images, $File::Find::name)
+               ;}, $gallerypath . $imagesdir);
+       
+       # Sort them.
+               @images = sort @images;
+
+       # Now fill out gallery
+       if (scalar(@images) > 0) {
+               $gallery = $galleryhead . $galleryrowstart;
+               my $count = 0;
+               foreach $image (@images) {
+                       $count++;
+                       if ($count gt $columns) {
+                               $count = 1;
+                               $gallery.= $galleryrowend . $galleryrowstart;
+                       }
+                       $image =~ s/$images_path/$images_url/;
+                       $thumb = $image;
+                       $thumb =~ s/(.*)$imagesdir/$1$thumbdir/;
+
+                       $imagefile = $image;
+                       $imagefile =~ s/.*?([^\/]*)$/$1/;
+
+                       $gallery.= qq!$gallerycolstart
+<a href="$blosxom::url/$path_noflavour.$blosxom::flavour?image=$imagefile">
+<img src="$thumb" alt="Thumbnail"/><p>$imagefile</p></a>
+$gallerycolend!;
+
+               }
+               $gallery.= $galleryrowend . $galleryfoot;
+       
+       } else {
+               $gallery = "No images to display... <br/>";
+       }
+
+
+       1;
+}
+
+1;
+
+
+__END__
+
+
+Documentation is Pending!!
diff --git a/general/karma b/general/karma
new file mode 100644 (file)
index 0000000..ec6d321
--- /dev/null
@@ -0,0 +1,304 @@
+# Bloxsom Plugin: Karma
+# Author: Fletcher T. Penney < http://fletcher.freeshell.org/ >
+# Original Concept: Beau Lebens
+# Idea of using a form - Pasi Savolainen
+
+# Version: 0.6
+
+package karma;
+
+# --- Configurable variables ----
+
+# Where is the data kept?
+$datafile = "$blosxom::plugin_state_dir/karma.dat";
+
+# FYI, the data format is as follows
+# Visits Positives Negatives Interest_Index Karma_Index Controversy_Index /Path/To/Article
+# tabs are used as separators
+
+# List of ip addresses to ignore for visit counts.
+# Use '|' as a separator if you have more than one
+# ie. $ip_list = '192.168.1.1|192.168.1.2'
+
+$ip_list = '';
+
+# CGI variable names to use for specifying certain minimum scores
+$minkarma_var          = "minkarma";
+$mincontroversy_var    = "mincontroversy";
+$mininterest_var       = "mininterest";
+
+# For instance, by adding "?minkarma=5", stories with a karma of less than 5 would be filtered.
+# You can change the terminology above if you don't like the names.
+
+# -------------------------------
+
+use CGI;
+
+# Initialize variables
+$stats = "";           # This variable will contain the results
+$votelinks = "";       # This variable will contain the links to submit a vote
+
+my $vote = "";
+my $karmadata = "";
+my $do_increment = 0;
+my $matchfound = 0;
+my $visits = 0;
+my $pos = 0;
+my $neg = 0;
+my $visits = 0;
+my $interest_index = 0;
+my $karma_index = 0;
+my $controversy_index = 0;
+
+$minkarma = 0;
+$mincontroversy = 0;
+$mininterest = 0;
+
+$message = "";         # Use this to report messages to the visitor
+
+
+sub start {
+
+       $path_noflavour = $blosxom::path_info;
+       if ($path_noflavour !~ s/\.[^\.]*$//) {
+               $path_noflavour =~ s/\/$//;
+               $path_noflavour .= "\/index";
+               $path_noflavour =~ s/^([^\/])/$1/;
+       }
+       $path_noflavour =~ s/^\/*//;
+
+       $remoteIP = $ENV{'REMOTE_ADDR'};        # Get IP address for visitor
+       $ip_list = 'disable' if ($ip_list eq "");       # Prevent an empty match expression
+
+       # Check to see if we are viewing a single story.
+       # If so, we want to increment the visits, unless this is a vote submission
+       # This does create the problem of reading a story on a category page, and voting for it
+       # No visit will be recorded - still looking for a solution
+
+       if (($path_noflavour !~ /index$/) && ($path_noflavour !~ /\/$/)) {
+               $do_increment = 1 if ($remoteIP !~ /($ip_list)/);
+
+               # Check to see whether a vote has been set
+               if (CGI::param("setkarma")) {
+                       $vote = CGI::param("setkarma");
+                       # If this is a vote, we don't want to update visit count
+                       # They have already read the article and been counted
+                       $do_increment = 0;
+               }
+       }
+
+       # Now we want to read in the data file, and update if appropriate
+       if (($do_increment eq 1) || ($vote ne "")) {
+               # The file should be locked during this process in case of two simultaneous accesses
+               open (DATA, "<$datafile");
+               flock (DATA, 2);        # I think I am using this properly...
+               while ( $line = <DATA>) {
+                       if ($line =~ /(\d+)\t(\d+)\t(\d+)\t[\.\d]+\t[\.\d]+\t[\.\d]+\t\/$path_noflavour$/) {
+                               # This is the story currently being viewed, so read in info
+                               $visits = $1 + $do_increment;
+                               $pos = $2;
+                               $neg = $3;
+                               $pos ++ if ($vote =~ /^p/i);
+                               $neg ++ if ($vote =~ /^n/i);
+                               $total_votes = $pos + $neg;
+
+                               # Try to correct for uncounted visits
+                               $visits = $total_votes if ($total_votes > $visits);
+
+                               $interest_index = ($pos + $neg)/$visits if ($visits ne 0);
+                               $karma_index = $pos/($total_votes) if ( $total_votes ne 0);
+                               $controversy_index = 2*$interest_index *(0.5 - abs($karma_index - 0.5));
+
+                               $line = "$visits\t$pos\t$neg\t$interest_index\t$karma_index\t$controversy_index\t\/$path_noflavour\n";
+                               $matchfound = 1;
+                       }
+                       $karmadata .= $line;
+               }
+
+               close (DATA);
+               open (DATA, ">$datafile");
+               flock (DATA, 2);        # Lock again, if it was unlocked
+               print DATA $karmadata;
+
+               # If no match was found for the story, create a new record
+               if ($matchfound eq 0) {
+                       $visits = 1;
+                       $pos = $neg = 0;
+                       $pos ++ if ($vote =~ /^p/i);
+                       $neg ++ if ($vote =~ /^n/i);
+                       $total_votes = $pos + $neg;
+
+                       $interest_index = ($pos + $neg)/$visits if ($visits ne 0);
+                       $karma_index = $pos/($total_votes) if ( $total_votes ne 0);
+                       $controversy_index = 2*$interest_index *(0.5 - abs($karma_index - 0.5));
+
+                       $line = "$visits\t$pos\t$neg\t$interest_index\t$karma_index\t$controversy_index\t\/$path_noflavour\n";
+                       print DATA "$line";
+                       $karmadata .= "$line\n";
+               }
+               close (DATA);
+       } else {
+               # Read in the existing data only - we are not viewing a single story
+               open(DATA, "<$datafile");
+               while ( $line = <DATA>) {$karmadata.=$line;}
+               close (DATA);
+       }
+       flock(DATA, 8);         # Unlock file
+
+       # Here is where we can create list of top controversies, etc
+       # And we have info loaded for the sort routine if necessary
+       # Currently the karma plugin can filter for certain scores, but not sort
+
+       1;
+}
+
+
+sub filter {
+       my ($pkg, $files_ref) = @_;
+       my @files_list = keys %$files_ref;
+       my $dofilter = 0;
+       if (CGI::param($minkarma_var)) {
+               $minkarma = CGI::param($minkarma_var);
+               $dofilter = 1;
+       }
+
+       if (CGI::param($mincontroversy_var)) {
+               $mincontroversy = CGI::param($mincontroversy_var);
+               $dofilter = 1;
+       }
+
+       if (CGI::param($mininterest_var)) {
+               $mininterest = CGI::param($mininterest_var);
+               $dofilter = 1;
+       }
+
+
+       if ($dofilter eq 1) {
+       foreach $file (@files_list) {
+               $realfile = $file;
+               $file =~ s/^$blosxom::datadir//;
+               $file =~ s/\.$blosxom::file_extension//;
+               if ($karmadata =~ /(\d+)\t(\d+)\t(\d+)\t([\.\d]+)\t([\.\d]+)\t([\.\d]+)\t$file/) {
+                       if ($minkarma && ($5 < $minkarma/10)) {
+                               delete $files_ref->{$realfile};
+                       }
+                       if ($mincontroversy && ($6 < $mincontroversy/10)) {
+                               delete $files_ref->{$realfile};
+                       }
+                       if ($mininterest && ($4 < $mininterest/10)) {
+                               delete $files_ref->{$realfile};
+                       }
+               } else {
+                       delete $files_ref->{$realfile};
+               }
+       }
+       }
+       1;
+}
+
+sub story {
+       my $karma_index = 0;
+       my $interest_index = 0;
+       my $controversy_index = 0;
+       my $total_votes = 0;
+       $pos = $neg = $visits = 0;
+
+       my ($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ($karmadata =~ /(\d+)\t(\d+)\t(\d+)\t([\.\d]+)\t([\.\d]+)\t([\.\d]+)\t$path\/$filename/) {
+               $visits = $1;
+               $pos = $2;
+               $neg = $3;
+               $total_votes = $pos + $neg;
+               $visits = $total_votes if ($total_votes > $visits);
+               $interest_index = $4;
+               $karma_index = $5;
+               $controversy_index = $6;
+
+               # Clean up the values for display - we'll want to use the originals (or at least 
+               # more decimal places for comparisons)
+               # All values are integers on scale from 1-10
+               $karma_index = int($karma_index * 10);
+               $controversy_index = int($controversy_index * 10);
+               $interest_index = int($interest_index * 10);
+       }
+
+
+       # Here you can define the HTML code that gets used to display your results
+       $stats = "Visits: $visits | Positives: $pos | Negatives: $neg<br>";
+       $stats.= "Karma: $karma_index | Interest: $interest_index | Controversy: $controversy_index";
+
+       # And this is the HTML code for the two vote submission links
+       #$votelinks = "<a href=\"$blosxom::url$path/$filename.$blosxom::flavour?setkarma=p\">I liked this article</a> | <a href=\"$blosxom::url$path/$filename.$blosxom::flavour?setkarma=n\">I didn't like it</a>.";
+       
+       $votelinks = qq!<form method="POST" action="$blosxom::url$path/$filename.html">
+       <div><input type="submit" name="setkarma" value="p">I liked this article |
+       <input type="submit" name="setkarma" value="n">I didn't like it
+       </div></form>!;
+               
+       1;
+}
+
+1;
+
+
+__END__
+
+
+=head1 NAME
+
+Blosxom Plug-in: karma
+
+=head1 DESCRIPTION
+
+Karma allows you to have your visitors score articles in a positive/negative fashion.  It then calculates various scores that you can use to incorporate a "Top 10 Articles" section, or something similar, into your website.
+
+To install:
+
+1) place the karma plugin in your plugins directory, with proper file permissions
+
+2) ensure the configuration variables are properly set:
+       $datafile should be left alone, unless you have customized where plugins store information
+
+       $ip_list should include the IP addresses of any visitors you do NOT wish to be counted in scoring.  For instance, you should probably include your own IP address to prevent falsely biasing the numbers as you read and reread your site looking for errors
+
+       The $minkarma_var, $mincontroversy_var, and $mininterest_var variables can be used to customize the url's used to filter articles.
+
+3) You can modify the HTML code snippets at the bottom of the story subroutine ($karma::stats and $karma::votelinks) to alter the display of the scores, and the vote submission links as you see fit.
+
+
+Once installed, modify your story.flavour files to include the $karma::stats and $karma::votelinks variables.
+
+Now, when you visit your site, you should see the scoring and links displayed with each story.  Each time you view a story individually, its visit count is advanced one.  Each time you click on the "I liked..." or "I didn't..." links, the positive and negative vote scores will be modified appropriately.
+
+These numbers are used to calculate several variables:
+
+       $karma_index - this is a rating from 0 to 10 of how positively the story was viewed
+
+       $interest_index - this is a rating from 0 to 10 of what proportion of people who viewed the story bothered to vote on it.  This will be artificially low, as many people might view the story as part of a category, and their visit will NOT be counted.  I am open to ideas as to how to more accurately handle this, keeping in mind that many stories that are displayed are not actually read (ie, older stories at the bottom of a blog...)
+
+       $controversy_index - this is a rating that I came up with just for fun.  It is a measure of how many people voted, and how divergent their opinions were.  For instance, as the interest_index goes up, the controversy_index goes up.  As the votes are more evenly split between positives and negatives, the controversy_index goes up.  A story with 100% positive (or negative) votes, will have a 0 controversy index (everyone is in agreement, therefore there is no controversy...)  It's not a very scientific number at all, but sort of fun nonetheless.
+
+4) You can add links to your site that will display the articles with the highest interest, karma, or controversy by using the $minkarma_var variables discussed in #3 above.
+
+For instance you could add a url in your header file like this:
+<a href="/weblog/index.html?minkarma=5">Show The Most-Liked Articles</a>
+
+This would allow the display of only those articles with a karma above 5.  In the future, I hope to add a sort routine that would sort stories by the various indices, but currently the stories are unsorted.
+
+=head1 NOTE
+
+This plugin filters out articles only.  It does not locate any articles on its own.
+
+Also, it uses the filter routine.  Therefore, it will interact with any other plugins that make use of the filtered articles list.  You may need to add numbers to the beginning of plugin names (ie, "55karma") to control the load order if you seem to be having unintended results.  For instance, karma will prevent some topics from being added by my "menu" plugin, unless it loads after menu.
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+Concept by Beau Lebens
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit to myself and Beau Lebens would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
diff --git a/general/login b/general/login
new file mode 100644 (file)
index 0000000..452d2f9
--- /dev/null
@@ -0,0 +1,302 @@
+# Blosxom Plugin:Login
+# Author: Fletcher T. Penney
+# Version: 0.4
+
+# NOTE: a functional cookies plugin is required.  Apparently, it has to be named something like
+# 9999cookies to work properly.
+#
+# Also, this plugin works by modifying the filter subroutine.  You may need to rename your plugins to insure
+# the proper order, but it works properly with exclude, and menu to my testing.  I am sure there are possible
+# conflicts with other plugins though.
+
+
+package login;
+
+# --- Configurable variables -----
+
+# Where is the passwd file?  Should not be in a public directory nor group/world writeable
+# This file is created with the htpasswd command (Do a man htpasswd to figure it out - please don't ask
+# me to explain how to do this - there are plenty of references on the web.
+# I will work on updating this to allow a cgi program to maintain this file, but can't guarantee
+# when that will occur.  In the meantime, this provides for better security.
+#
+#
+
+my $passwd_file = "$blosxom::datadir/passwd";
+
+# Where is your excludefile - this controls which user is able to see which files
+# the excludefile is built using regular expressions in the format:
+# user=page
+# user is a regexp to match allowed user (.* matches all VALIDATED users)
+# page is a regexp to match pages or directories (.* matches all pages)
+#
+# Examples:
+# A blank or nonexistent file allows full access to anyone, logged in or not
+# myusername=.*
+#      Only someone logged in as myusername can access any files on the web site
+# (myuser1|myuser2)=private
+#      Only these two people can access a file or directory named private, private1, privatestuff, etc
+# .*=priv$
+#      Any VALIDATED user can view the directory named priv, but this doesn't affect one named private
+#
+# 
+my $excludefile = "$blosxom::datadir/requireuser";
+
+my $default_message = "Please log in.";
+
+
+# This section for gui creation of accounts
+
+my $pending_file = "$blosxom::plugin_state_dir/pendingaccounts";
+
+
+# --------------------------------
+
+# Ideas for improvement
+# Option to log all succesful logins
+# Ability to set a sort of exclude file to filter out entries by login name
+
+use CGI qw/:standard/;
+
+$username = "";                # users login name, if any - set only after password is validated
+$path_noflavour = "";
+
+
+$message = "";
+
+my $passphrase ="";
+my $validated = 0;
+
+sub validate {
+       my ($submituser, $submitpass) = @_;
+       if (open(PASSWD, $passwd_file)) {
+               $message = "Password file open.";
+               while (<PASSWD>) {
+                       chop;
+                       ($testuser, $testpass) = split(':',$_);
+                       if ($testuser eq $submituser) {
+                               if (crypt($submitpass,$testpass) eq $testpass) {
+                                       $message = "Welcome, $submituser.";
+                                       $username = $submituser;
+                                       $validated = 1;
+                                       $loginform = $logoutform;
+                               } else {
+                                       warn "blosxom : login plugin > Incorrect password for user $submituser.";
+                               }
+                       }
+               }
+               if ($validated eq 0) {
+                       $message = "Login for user $submituser failed.";
+                       warn "blosxom : login plugin > $message";
+                       $username = "";
+               } 
+               close PASSWD;
+       } else {
+               $message =  "Unable to access password file.\n";
+               warn "blosxom : login plugin > $message";
+               $username = "";
+       }
+
+return $validated;
+}
+
+
+
+sub start {
+       $path_noflavour = $blosxom::path_info;
+       if ($path_noflavour !~ s/\.[^\.]*$//) {
+               $path_noflavour =~ s/\/$//;
+               $path_noflavour .= "\/index";
+               $path_noflavour =~ s/^([^\/])/$1/;
+       }
+       $path_noflavour =~ s/^\/*//;
+
+       open (REQUIRE, $excludefile);
+       @requiredlist = <REQUIRE>;
+       close REQUIRE;
+
+# HTML code for the login form to be displayed
+# Converts to a logout button as well?
+
+$loginform = qq!<form method="POST" action="$blosxom::url/$path_noflavour.$blosxom::flavour">
+<table border=0 cellpadding=0 cellspacing=0>
+<tr>
+<td align=right>Login:</td><td><input name="user" size="10" value="" ><br></td>
+</tr><tr>
+<td align=right>Password:</td><td><input name="pass" size="10" value="" type="password"><br></td>
+</tr><tr>
+<td colspan=2 align=center><input type="submit" value="Login"></td>
+</tr></table>
+<input type="hidden" name="plugin" value="login">
+<input type="hidden" name="task" value="login">
+</form>!;
+
+$logoutform = qq!<form method="POST" action="$blosxom::url/$path_noflavour.$blosxom::flavour">
+<input type="submit" value="Logout">
+<input type="hidden" name="plugin" value="login">
+<input type="hidden" name="task" value="logout">
+</form>!;
+
+$signupform = qq!<form method="POST" action="$blosxom::url/$path_noflavour.$blosxom::flavour">
+<table border=0 cellpadding=0 cellspacing=0>
+<tr>
+<td align=right>Login(no spaces):</td><td><input name="user" size="10" value="" ><br></td>
+</tr><tr>
+<td align=right>Email:</td><td><input name="email" size="10" value=""><br></td>
+</tr><tr>
+<td align=right>Password:</td><td><input name="pass" size="10" value="" type="password"><br></td>
+</tr><tr>
+<td align=right>Verify:</td><td><input name="verifypass" size="10" value="" type="password"><br></td>
+</tr><tr>
+<td colspan=2 align=center><input type="submit" value="Sign Me Up"></td>
+</tr></table>
+<input type="hidden" name="plugin" value="login">
+<input type="hidden" name="task" value="signup">
+</form>!;
+
+       1;
+}
+
+
+sub filter {
+       my ($pkg, $files_ref) = @_;
+       my @files_list = keys %$files_ref;
+
+       # This handles login requests and creates a cookie, if valid
+       if ( request_method() eq 'POST' and (param('plugin') eq 'login')) {
+               if (param('task') eq 'logout') {
+                       &cookies::remove('login');      # These don't seem to work
+                       &cookies::clear('login');       # So I overwrite the cookie below
+                               &cookies::add(
+                                       cookie(
+                                               -name=>'login',
+                                               -value=>{ },
+                                               -domain=>$cookies::domain,
+                                               -expires=>'now'
+                                       )
+                               );
+                       $username="";
+                       $validated=-1;
+               }
+
+               if (param('task') eq 'login') {
+                       my $user = param('user');
+                       my $pass = param('pass');
+                       if ((validate($user,$pass)) and $blosxom::plugins{cookies} > 0) {
+                               # Create a cookie
+                               &cookies::add(
+                                       cookie(
+                                               -name=>'login',
+                                               -value=>{ 'user' => $user, 'pass' => $pass},
+                                               -domain=>$cookies::domain,
+                                               -expires=>$cookies::expires
+                                       )
+                               );
+                       }
+               }
+
+               if (param('task') eq 'signup') {
+                       my $user = param('user');
+                       my $pass = param('pass');
+                       my $verify = param('verifypass');
+                       my $email = param('email');
+                       $validated=-1;
+
+                       $message= "";
+
+                       if ($pass ne $verify) {
+                               $message = "Your passwords were different.  Please try again.";
+                       } 
+                       if (length($pass) < 5) {
+                               $message = "Come on.... Your password has to be at least 5 characters.";
+                       }
+                       if (length($email) <5) {
+                               $message = "That's not a real email address... Try again.";
+                       }
+
+                       if ($message eq "") {
+                               open(PENDING,">>$pending_file") || ($message = "Error submitting information.  Please try again.");
+                               if ($message eq "") {
+                                       my @salts = (a..z,A..Z,0..9,'.','/'); 
+                                       my $salt=$salts[rand @salts];
+                                               $salt.=$salts[rand @salts];
+
+                                               my $encrypted=crypt($pass,$salt);
+
+                                               my ($dw, $mo, $mo_num, $da, $ti, $yr) = &blosxom::nice_date(time());
+                                       print PENDING "$yr $mo $da $ti $user $email $encrypted\n";
+                                       close PENDING;
+                                       $message="Request submitted.  Don't call us... We'll call you...  ;)";
+                               }
+                       }
+               }
+       }
+
+       # Now, read cookies to see if user is logged in
+       if ($blosxom::plugins{cookies} > 0 and my $cookie = &cookies::get('login') and $validated eq 0) {
+               my $user = $cookie->{'user'};
+               my $pass = $cookie->{'pass'};
+
+               validate($user,$pass);
+       }
+
+       $message = $default_message if ($message eq "");
+
+       # Now filter the files
+       foreach $file (@files_list) {
+               $localfile = $file;
+               $localfile =~ s/\/\//\//g;              #An improperly formatted url such as:
+                                               # /my/document/dir/secret//files would slip through
+               foreach $required (@requiredlist) {
+                       if ($required =~ /(.*)=(.*)/) {
+                               $requser=$1;
+                               $reqfile=$2;
+                               if ($localfile =~ /^$blosxom::datadir(\/)?$reqfile/) {
+                                       delete $files_ref->{$file} if (($username !~ /$requser/) or $username eq "");
+                               }
+                       }
+               }
+       }
+       
+
+1;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: login
+
+=head1 DESCRIPTION
+
+Login allows you to create a passwd file that defines usernames and passwords for your web site.  Another file, $excludefile, defines which users can access certain parts of your site.  The format for the files is explained above.
+
+$login::username provides the users login, once it has been validated against the database.  It remains "" if someone is not logged in.
+
+$login::loginform provides the login/logout box.  Place it in your templates wherever you like.
+$login::message gives certain status messages.
+
+By default, login provides logging messages when certain errors occur, and when a failed login attempt happens.
+
+$login::signupform provides html code for a simple account sign up form.  Once a user submits the information, it is added to a text file defined by $pending_file.  If you want to add the account, simply copy the user and password hash to the defined password file in the format "user:hash" as defined by the htpasswd command.
+
+
+PLEASE NOTE:  I am not a security expert.  I can't guarantee that there isn't a huge loophole in this somewhere.  And note - any malicious plug-in could expose a whole by tampering with the $username variable - verify that other plugins do not attempt to manipulate $login::username or access the cookies.  But this is more a matter of being careful which plugins you install.
+
+Also, this plugin does not pretend to offer serious security.  Someone could read a restricted file if they have read access to the location that the file is stored.  This could occur if you datadir is in your web servers documentroot.  
+
+There are many issues to consider when securing a web site, and I don't pretend to address them all, nor do I guarantee that this plugin even works properly.  It is simply a means to provide some form of control without much effort on your part.  You get what you pay for.  But I will continue to work with the community to improve it as security holes are noted.
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit for my original work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
diff --git a/general/menu b/general/menu
new file mode 100644 (file)
index 0000000..088d669
--- /dev/null
@@ -0,0 +1,210 @@
+# Blosxom Plugin: menu
+# Author: Fletcher T. Penney
+# Version: 0.5
+# Exclude functions copied from exclude plugin
+
+package menu;
+
+# --- Configurable variables ----
+
+# HTML code to insert before the menu
+$menustart=qq%<span class="menu">%;
+
+# HTML code to insert between items
+$menuseparator=qq% :: %;
+
+# HTML code to insert after menu
+$menuend=qq%</span>%;
+
+# These serve the same purpose as above, but for the root menu
+$rootmenustart = $menustart;
+$rootmenuseparator=qq%<br>%;
+$rootmenuend=$menuend;
+
+$pretty=1;     # Enable "prettying" up category names
+
+# -------------------------------
+
+
+$menu = "";
+$rootmenu = "";
+$path_noflavour = "";
+$pretty_path = "";
+
+$ign_file = $exclude::ign_file;
+$hide_file = $hide::ign_file;
+
+sub start {
+       @excludes = ();
+
+       if ($ign_file) {
+               $ign_fp = "$blosxom::datadir";
+               $ign_fn = "$ign_fp/$ign_file";
+               if (open(EXCLUDE, "< $ign_fn")) {
+                       while (<EXCLUDE>) {
+                               chomp;
+                               push(@excludes, "$_") if $_;
+                       }
+                       close(EXCLUDE);
+               }
+       }
+       
+       # Now do the same for hide
+       if ($hide_file) {
+               $ign_fp = "$blosxom::datadir";
+               $ign_fn = "$ign_fp/$hide_file";
+               if (open(EXCLUDE, "< $ign_fn")) {
+                       while (<EXCLUDE>) {
+                               chomp;
+                               push(@excludes, "$_") if $_;
+                       }
+                       close(EXCLUDE);
+               }
+       }
+       
+       $path_noflavour = $blosxom::path_info;
+       if ($path_noflavour !~ s/\.[^\.]*$//) {
+               $path_noflavour =~ s/\/$//;
+               $path_noflavour .= "\/index";
+               $path_noflavour =~ s/^([^\/])/$1/;
+       }
+       $path_noflavour =~ s/^\/*//;
+       1;
+}
+
+sub filter {
+       my ($pkg, $files) = @_;
+
+       @files_list = sort keys %$files;
+       @rootlist = @files_list;
+
+       $myroot = $blosxom::datadir ."/" . $blosxom::path_info;
+       $myurl = $blosxom::url ."/" . $blosxom::path_info;
+       $myurl =~ s/\/$//;
+
+       @dirs = ();
+       @rootdirs = ();
+
+       $lastdir="";
+       $lastroot="";
+
+       foreach (@files_list) {
+               if ($_ =~ s/$myroot//) {
+                       # These are subdirectories of the current directory
+                       $_ =~ s/^\/?([^\/]+)\/.*/$1/;
+                       $_ =~ s/\/?[^\/]+\.txt//;
+                       if ($_ and ($lastdir ne $_)) {
+                               $lastdir=$_;
+                               push(@dirs,$_);
+                       }
+               } 
+       }
+
+       foreach (@rootlist) {
+               #These are not
+       #               $rootmenu.="$_ <br>";
+               if ($_ =~ s/$blosxom::datadir\///) {
+                       $_ =~ s/^\/?([^\/]+)\/.*/$1/;
+                       $_ =~ s/\/?[^\/]+\.txt//;
+                       if ($_ and ($lastroot ne $_)) {
+                               $lastroot=$_;
+                               push(@rootdirs,$_);
+                       }
+               }
+       }
+
+
+       @rootdirs = @dirs if ($blosxom::path_info eq "");
+
+       $menu = $menustart;
+       for ($i=0;$i<scalar(@dirs)-1;$i++) {
+               $pretty = $dirs[$i];
+               $pretty =~ s/^(.)/\u$1/;
+               $pretty =~ s/[_-](.)/ \u$1/g;
+               $pretty =~ s/ojh/OJH/gi;
+
+               $menu.= "<a href=\"$myurl/$dirs[$i]/\">" . $pretty . "</a>" .$menuseparator;
+       }
+
+       for (;$i<scalar(@dirs);$i++) {
+               $pretty = $dirs[$i];
+               $pretty =~ s/^(.)/\u$1/;
+               $pretty =~ s/[_-](.)/ \u$1/g;
+               $pretty =~ s/ojh/OJH/gi;
+               
+               $menu.= "<a href=\"$myurl/$dirs[$i]/\">" . $pretty . "</a>" ;
+       }
+       $menu.=$menuend;
+
+       $sepholder = "";
+
+       $rootmenu .= $rootmenustart;
+       for ($i=0;$i<scalar(@rootdirs);$i++) {
+               $use = 1;
+               foreach $exclude (@excludes) {
+                       foreach $ign_cf (@files_list) {
+                               $use = 0 if ($rootdirs[$i] =~ m/^$exclude/) ;
+                       }
+               }
+               $sepholder = $rootmenuseparator if ($rootmenu ne $rootmenustart);
+
+               $pretty = $rootdirs[$i];
+               $pretty =~ s/^(.)/\u$1/;
+               $pretty =~ s/[_-](.)/ \u$1/g;
+               $pretty =~ s/ojh/OJH/gi;
+
+               $rootmenu.= $sepholder . "<a href=\"$blosxom::url/$rootdirs[$i]/\">" . $pretty . "</a>" if $use;
+       }
+
+       $rootmenu.=$rootmenuend;
+       
+1;
+}
+
+sub story {
+       my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+       
+       $pretty_path = $path;
+       $pretty_path =~ s/^\/?(.)/\u$1/;
+       $pretty_path =~ s/[_-](.)/ \u$1/g;
+       $pretty_path =~ s/\/(.)/ :: \u$1/g;
+       1;
+}
+
+sub date {
+       my ($pkg, $date_ref, $mtime, $dw,$mo,$mo_num,$da,$ti,$yr) = @_;
+       
+       $pretty_path = $blosxom::path;
+       $pretty_path =~ s/^\/?(.)/\u$1/;
+       $pretty_path =~ s/[_-](.)/ \u$1/g;
+       $pretty_path =~ s/\/(.)/ :: \u$1/g;
+       1;
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: menu
+
+=head1 DESCRIPTION
+
+This creates a menu ($menu::menu)showing the available subdirectories from your current position.  A subdirectory, or one of its "descendents" must have a story in it in order to be shown in the menu (ie, it does not lead down "blind alleys").  Additionally, it abides by the excluded sections if the exclude plug-in is enabled.
+
+The default configuration settings at the begin lead to a sort of reversal of the breadcrumbs plug-in.
+
+Additionally, the $menu::rootmenu variable contains the top level directories, regardless of the current page.
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit to myself for my work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
\ No newline at end of file
diff --git a/general/recentwritebacks b/general/recentwritebacks
new file mode 100644 (file)
index 0000000..d6211ca
--- /dev/null
@@ -0,0 +1,183 @@
+# Blosxom Plugin: RecentWritebacks
+# Author: Fletcher T. Penney
+# Version: 0.2
+
+package recentwritebacks;
+
+# --- Configurable variables -----
+
+$writebacks_extension = "wb";
+
+$writebacksdir = "$blosxom::plugin_state_dir/writeback";
+
+$switch_word = "recent";       # This defines the term that needs to be added to the url
+                               # ie "http://some.host/weblog/?recent=5"
+                               # This finds posts with comments in last 5 days
+
+$use_datestamp = 1;                    # Use embedded datestamp, rather than file modification date
+
+$date_field = "date";          # What variable contains the date
+
+$use_UK_dates = 0;                     # Default is mm/dd/yy (US)
+                                                       # Set to 1 to use dd/mm/yy (UK)
+
+# --------------------------------
+
+
+
+use CGI qw/:standard/;
+use FileHandle;
+use File::stat;
+use Time::Local;
+
+my $time = time();
+
+my $fh = new FileHandle;
+
+sub start {
+       if (CGI::param($switch_word)) {
+               $time_window = CGI::param($switch_word);
+               $time =  $time - $time_window*60*60*24;
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+sub filter {
+       my ($pkg, $files_ref) = @_;
+       my @files_list = keys %$files_ref;
+
+       foreach $file (@files_list) {
+               $realfile = $file;
+               $file =~ s/$blosxom::datadir/$writebacksdir/;
+               $file =~ s/txt$/$writebacks_extension/;
+               if ($fh->open("$file")) {
+                       $keep =0;
+                       
+                       $stats = stat($file)->mtime;
+
+                       if ( $use_datestamp == 1 ) {
+                               while ( $line = <$fh> ) {
+                                       if ($line =~ /^$date_field\:\s*(.*)/) {
+                                               $datestamp = $1;
+                                               
+                                               $stat2 = parsedate($datestamp);
+                                               if ($stat2 gt $stats) {
+                                                       $stats = $stat2;
+                                               }                               
+                                       }
+                               }
+                       }
+
+                       if ($stats lt $time) {
+                               delete $files_ref->{$realfile};
+                       }                               
+
+               } else {
+                       # No writebacks available
+                       delete $files_ref->{$realfile};
+               }
+       }
+       1;
+}
+
+
+sub parsedate {
+       my ($datestring) = @_;
+       #warn "Parsing $datestring\n";
+       
+       # Possible formatting
+       # Month can be 3 letter abbreviation or full name (in English)
+       # Time must be hh:mm or hh:mm:ss  in 24 hour format
+       # Year must be yyyy
+       # The remaining 1 or 2 digits are treated as date
+       # ie: May 25 2003 18:40 
+       # order is not important as long as pieces are there
+               
+       # Convert the datestring to a time() format
+
+       # Find "Shorthand" Date
+       if ( $datestring =~ /\d\d?\/\d\d?\/\d\d\d?\d?/) {
+               if ( $use_UK_dates eq 0) {
+                       # Use US Formatting
+                       $datestring =~ s/(\d\d?)\/(\d\d?)\/(\d\d\d?\d?)//;
+                       $mon = $1 - 1;
+                       $day = $2;
+                       $year = $3;
+               } else {
+                       # Use UK Formatting
+                       $datestring =~ s/(\d\d?)\/(\d\d?)\/(\d\d\d?\d?)//;
+                       $mon = $2 - 1;
+                       $day = $1;
+                       $year = $3;
+               }
+               
+               # Now, clean up year if 2 digit
+               # You may change the 70 to whatever cutoff you like
+               $year += 2000 if ($year < 70 );
+               $year += 1900 if ($year < 100);
+       }
+       
+       # Find Month
+       $mon = 0 if ($datestring =~ s/(Jan|January)//i);
+       $mon = 1 if ($datestring =~ s/(Feb|February)//i);
+       $mon = 2 if ($datestring =~ s/(Mar|March)//i);
+       $mon = 3 if ($datestring =~ s/(Apr|April)//i);
+       $mon = 4 if ($datestring =~ s/(May)//i);
+       $mon = 5 if ($datestring =~ s/(Jun|June)//i);
+       $mon = 6 if ($datestring =~ s/(Jul|July)//i);
+       $mon = 7 if ($datestring =~ s/(Aug|August)//i);
+       $mon = 8 if ($datestring =~ s/(Sep|September)//i);
+       $mon = 9 if ($datestring =~ s/(Oct|October)//i);
+       $mon = 10 if ($datestring =~ s/(Nov|November)//i);
+       $mon = 11 if ($datestring =~ s/(Dec|December)//i);
+
+       # Find Time
+       if ($datestring =~ s/(\d\d?):(\d\d)(:\d\d)?//) {
+               $hour = $1;
+               $min = $2;
+               $sec = $3;
+       }
+       
+       if ($datestring =~ s/(\d\d\d\d)//) {
+               $year = $1;
+       }
+       
+       if ($datestring =~ s/(\d\d?)//) {
+               $day = $1;
+       }
+       
+       return timelocal($sec,$min,$hour,$day,$mon,$year);
+       
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: recentwritebacks
+
+=head1 DESCRIPTION
+
+RecentWritebacks locates stories with comments posted within a specified period of time. If you are like me, you find it hard to keep up with comments posted to older articles.  This plugin is designed to assist with that.  You can also use it to make it easier for visitors to your site to locate the "Hot Topics" where writebacks are being submitted.
+
+To use it, simply install the plugin and configure the $writebacks_extension and $writebacksdir variables.  They should match whatever you used in the writebacks plugin.
+
+You can then use a url similar to:
+       http://some.host/weblog/index.html?recent=5
+to locate posts with comments written in the last 5 days.  You can also create links on your site to make this easier for your visitors.
+
+You are also welcome to change the $switch_word variable to whatever you like to fit your site.
+
+=head1 AUTHORS
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit for my original work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
diff --git a/general/redirect b/general/redirect
new file mode 100644 (file)
index 0000000..b97ac88
--- /dev/null
@@ -0,0 +1,107 @@
+# Blosxom Plugin: redirect
+# Author: Fletcher T. Penney (http://fletcher.freeshell.org)
+# Version: 0.1
+
+package redirect;
+
+# --- Configurable variables ---
+
+# Where is the redirect file?
+$redirect_file = "$blosxom::plugin_state_dir/redirect";
+
+# ------------------------------
+
+$path = $blosxom::path_info;
+
+sub start {    
+warn "Flavour check $blosxom::flavour\n";
+       if (open (REDIRECT, "< $redirect_file")) {
+               while ( $line = <REDIRECT> ) {  
+                       if ($line =~ /(.*)=>(.*)/) {
+                               $old = $1;
+                               $new = $2;
+                               if ( $path =~ s/^$old/$new/ ) {
+                                       return 1;
+                               }
+                       }
+               }
+               
+               close REDIRECT;
+       }
+       
+       return 0;
+}
+
+
+sub last {
+       # We only end up here if we are redirecting this url
+
+       $message = qq!This page has been moved.  The new URL is <a href="$blosxom::url/$path">$blosxom::url/$path</a>.\n!;
+       
+       $blosxom::output = $message;
+       print "Status: 301\n";
+       print "Location: $blosxom::url/$path\n";
+       1;
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: redirect
+
+=head1 SYNOPSIS
+
+One problem with having a weblog is that you become somewhat committed to your URL structure.  If you decide to rearrange your site, you run the risk of breaking external links.  The autocorrect plugin helps, attempting to find a story that might have moved.
+
+The difficulty with this is that the url's don't really get fixed.
+
+I recently moved my site out  of the "/weblog" directory by improving my .htaccess file.  I like the url's better, but I have lots of links out there that still point to the "/weblog" hierarchy.  I came up with some ways of pointing this out to human viewers, but robots still travel the old hierarchy, and it's a pain for people to figure out where to go.
+
+This plugin allows you to set up a redirect file that consists of lines like the following:
+path/to/some/article=>new/path/to/article
+
+If someone tries to go to a non-existent directory, you can re-route them automatically to the new location, and the url is updated in their browser.  For instance, my redirect file looks like this:
+weblog/=>
+
+Basically it strips out the weblog directory, creating a URL that is now valid.
+
+
+=head1 VERSION
+
+Version 0.1
+
+=head2 VERSION HISTORY
+
+0.1    - initial public release
+
+=head1 AUTHOR
+
+Fletcher T. Penney, http://fletcher.freeshell.org
+
+=head1 LICENSE
+
+Blosxom Redirect Plug-in
+Copyright 2003, by Fletcher T. Penney
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/general/seewritebacks b/general/seewritebacks
new file mode 100644 (file)
index 0000000..9e4c9e6
--- /dev/null
@@ -0,0 +1,84 @@
+# Bloxsom Plugin:SeeWritebacks
+# Author: Fletcher T. Penney
+# Version: 0.3
+
+package seewritebacks;
+
+use CGI;
+
+
+$seewritebacks = 0;
+$writebacksform = "";
+
+sub start {
+       1;
+}
+
+sub story {
+       my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ((CGI::param("seewritebacks")) || (CGI::param("plugin") eq 'writeback') || 
+($blosxom::path_info =~ /\./)) {
+               $seewritebacks = 1;
+               $writebacksform = &$blosxom::template($path,'writebacksform',$blosxom::flavour) || &$blosxom::template($path,'writebacksform','general');
+               $writebacksform = &$blosxom::interpolate($writebacksform);
+       }
+
+       1;
+}
+
+1;
+
+
+__END__
+
+
+=head1 NAME
+
+Blosxom Plug-in: seewritebacks
+
+=head1 DESCRIPTION
+
+This plugin makes it easy to have a consistent writebacks interface to your site, that is compatible with multiple themes.  For example, you can create a blue flavor, and a red flavor for your site that change the appearance, but still have writebacks enabled.  With the default writebacks plugin, this gets complicated and requires a separate writebacks flavor for each primary flavor.
+
+Well, say goodbye to that!!
+
+       1) Drop this plugin into your plugins directory.
+       2) Copy the writebacksform.general file to your flavor directory or your datadir.
+       3) Make sure you still have the writeback.$flavour file of your choosing installed
+       4) Put "$seewritebacks::writebacksform" at the end of your story.$flavor file
+       5) Make the following changes to your story.$flavor file:
+Original:
+(<a href="$url$path/$fn.$flavour">$writeback::count Comments</a>)
+
+Modified:
+(<a href="$url$path/$fn.$flavour?seewritebacks=y">$writeback::count Comments</a>)
+
+Note that your story.flavor file may be slightly different.  You are adding the ?seewritebacks=y to the url, which causes this plugin to act.
+
+       6) Repeat steps 4-5 for each flavor that you wish to update.
+       7) If you choose, you can create a writebacksform.$flavor file to customize the appearance for each flavor, but this is unnecessary.
+
+       It sounds complicated, but it's not that bad.  If you have suggestions on how to improve these instructions, let me know.
+
+       The upshot is that you can now have multiple flavors for your site that change the appearance, and all of them can use writebacks, and plugins such as breadcrumbs don't give unexpected results.
+
+=head1 CHANGES
+
+0.3    Forced interpolation as interpolate_fancy broke the previous interpolation.  Thanks to Andy for this report.
+
+0.2    Added a patch to allow the writebacks plugin to set $seewritebacks to 1 to ensure that a comment is displayed as soon as it is posted
+
+0.1    Initial Release
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify it.  If you like, a comment in your modified source attributing credit to myself for my original work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT YOUR OWN RISK!
diff --git a/general/zlocaldepth b/general/zlocaldepth
new file mode 100644 (file)
index 0000000..79eb743
--- /dev/null
@@ -0,0 +1,68 @@
+# Blosxom Plugin: zlocaldepth
+# Author: Fletcher T. Penney
+# Version: 0.3
+
+package zlocaldepth;
+
+$localdepth = 0;
+
+sub start {
+       1;
+}
+
+sub filter {
+       my ($pkg, $files) = @_;
+
+       if ($localdepth ne 0) {
+               @files_list = sort keys %$files;
+
+               $myroot = $blosxom::datadir ."/" . $blosxom::path_info;
+               $myroot =~ s/\/$//;
+               $myroot =~ s/\/[^\/]+\.[^\/]+$//;
+               
+               foreach (@files_list) {
+                       if ($_ !~ /$myroot([^\/]*\/[^\/]*){0,$localdepth}$/) {
+                               delete $files->{$_};
+                       }
+               }
+       }
+       
+1;
+}
+
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Blosxom Plug-in: zlocaldepth
+
+=head1 DESCRIPTION
+
+This plugin restricts entries to those within the $zlocaldepth::localdepth variable, which works just like blosxom's $depth variable.  Except that it is always calculated relative to the current directory.  This is useful, because unlike the built-in depth variable, it can be appropriately set from a config file via the config plugin to easily allow different depths in different parts of your site.
+
+
+BTW, the name starts with a z so that it will follow other plugins that affect 
+filters and abide by their settings.
+
+=head1 AUTHOR
+
+Fletcher T. Penney - http://fletcher.freeshell.org
+
+=head1 VERSION HISTORY
+
+=item 0.2
+
+Corrected an issue with a depth of 1 and the root directory.
+
+=head1 LICENSE
+
+This source is submitted to the public domain.  Feel free to use and modify  
+it.  If you like, a comment in your modified source attributing credit to 
+myself for my work would be appreciated.
+
+THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT ANY WARRANTY OF ANY KIND.  USE AT 
+YOUR OWN RISK!