package Blosxom::Include;

use strict;
use Filter::Simple;
use FileHandle;

use vars qw($VERSION);

$VERSION = 0.002000;

my $include_pattern = qr/^#?\s*(?:IncludeConfig|__END_CONFIG__).*$/m;

FILTER {
  my ($self, $plugin_name) = @_;
  return unless m/$include_pattern/;

  # Try and figure out $config_dir in the same way as blosxom itself
  my $config_dir = '';
  if ($ENV{BLOSXOM_CONFIG_FILE}) {
    ($config_dir = $ENV{BLOSXOM_CONFIG_FILE}) =~ s! / [^/]* $ !!x;
  }
  unless (-d $config_dir) {
    for my $blosxom_config_dir ($ENV{BLOSXOM_CONFIG_DIR}, '/etc/blosxom', '/etc') {
      if (-d $blosxom_config_dir) {
        $config_dir = $blosxom_config_dir;
        last;
      }
    }
  }

  # Load plugin config data
  if ($config_dir  && -d $config_dir && 
      $plugin_name && -f "$config_dir/$plugin_name" && 
                      -r "$config_dir/$plugin_name") {
    if (my $fh = FileHandle->new( "$config_dir/$plugin_name", 'r' )) {
      local $/ = undef;
      my $plugin_config = <$fh>;
      if (defined $plugin_config) {
        my ($before, $after) = split $include_pattern;

        # Munge the line count to include $plugin_config
        my $line_count = ($before =~ tr/\n//);
        $line_count += ($plugin_config =~ tr/\n//);
        $line_count++;

        # Insert config and revised line count
        $_ = $before . $plugin_config . "# line $line_count\n" . $after;
      }
      close $fh;
    }
  }
};


1;

__END__

=head1 NAME

Blosxom::Include - a perl source filter to allow external configuration 
settings to be included within blosxom plugins with minimal code

=head1 SYNOPSIS

    # In a random blosxom plugin ...

    # Add as the first code statement in your plugin (before the package stmt)
    use Blosxom::Include qw(my_plugin_name);

    # Add a commented-out include directive after last plugin config item:
    # IncludeConfig();
    

    # Then create your external configuration file in your $blosxom::config_dir
    # directory with the name you used in the 'use' statement above - typically
    # the name of your plugin. Usually you can just copy the entire 
    # configuration section, which is typically the bits between the:

    # --- Configuration variables ----- 

    # --------------------------------- 

    # sections. 


=head1 DESCRIPTION

Blosxom::Include is a perl source filter to allow external configuration 
settings to be included within blosxom plugins with a minimum of fuss.

It works by injecting an external configuration file directly into the 
plugin at the point of the IncludeConfig() directive, which is typically
immediately after the configuration section. This allows you to override 
and redefine any configuration item you choose, including lexical variables
(my $foo = 'bar'), which you can't modify externally any other way.

To use, you add the following to the very top of your plugin (before the 
package statement):

    use Blosxom::Include qw(my_plugin_name);

e.g. for atomfeed, you would use:

    use Blosxom::Include qw(atomfeed);

Then immediately after the configuration section, add an include marker in 
a comment:

    # IncludeConfig();

This gets replaced by the external config when the source filter runs, before
the script is passed to the interpreter.

If you are distributing your plugin, I suggest you also comment out the use 
directive, since not everyone will have this module installed. It's reasonably 
straightforward to uncomment it for an installed set of plugins after 
installation e.g.

    perl -i -pe 's/^# *(use Blosxom::Include)/$1/' file1 file2 ...


=head2 ALTERNATIVES

I tried initially to do this within blosxom.cgi itself by just doing a simple 
require on the external config file after the initial plugin itself had been
loaded (into the same package namespace, of course). This works great for
package-scoped (global) configuration variables, but doesn't work with lexical
variables (my $config_item), which are inherently file-scoped.


=head1 AUTHOR

Gavin Carr <gavin@openfusion.com.au>


=head1 LICENCE

Copyright 2007 Gavin Carr.

This program is free software; you can redistribute and/or modify it under the 
same terms as perl itself.

=cut