package ModBlosxom; ############################################################################## # ModBlosxom - OO module for blosxom.cgi # # The original code is from Rael Dornfest 's Blosxom v.2.0. # # Author : Makamaka Hannyaharamitu # http://www.donzoko.net/ # Version : 0.1 # License : same as Blosxom (http://www.blosxom.com/license.html) # See end of this file. # ############################################################################## # Blosxom # Author: Rael Dornfest # Version: 2.0 # Home/Docs/Licensing: http://www.raelity.org/apps/blosxom/ ############################################################################## #use warnings; use strict; use CGI; use Time::localtime; use vars qw($VERSION); $VERSION = 0.22; package ModBlosxom::AccessDB; package ModBlosxom::Entries; package ModBlosxom::Entry; package ModBlosxom::NullEntry; package ModBlosxom; my $month2num = { nil=>'00', Jan=>'01', Feb=>'02', Mar=>'03', Apr=>'04', May=>'05', Jun=>'06', Jul=>'07', Aug=>'08', Sep=>'09', Oct=>'10', Nov=>'11', Dec=>'12', }; my $num2month = [ sort { $month2num->{$a} <=> $month2num->{$b} } keys %{ $month2num } ]; ############################################################################## sub new { my $class = shift; my $self = bless {}, $class; $self->_init(@_); } sub _init { my $self = shift; $self->{CGI} = new CGI; $self->{plugins} = { order => [], name => {} }; $self->{templates} = {}; $self->connect_db(); while () { last if /^(__END__)?$/; my($ct, $comp, $txt) = /^(\S+)\s(\S+)\s(.*)$/; $txt =~ s/\\n/\n/mg; $self->{templates}->{$ct}->{$comp} = $txt; } $self->settings(@_); # Default finding entries $self->{find_entries_routine} = sub { my $self = shift; my ($datadir,$depth,$file_extension,$show_future_entries) = @{ $self->{settings} }{qw/datadir depth file_extension show_future_entries/}; my $entries = $self->access_db()->find_entries({ root => $datadir, depth => $depth, file_ext => $file_extension, show_future => $show_future_entries, }); return ($entries->files, $entries->indexes, $entries->others); }; # Define default template subroutine $self->{template_routine} = sub { my $self = shift; my ($path, $chunk, $flavour) = map{ (defined $_ ? $_ : '') } @_; my %template = %{ $self->{templates} }; my ($datadir) = $self->{settings}->{datadir}; do { my $template = $self->access_db() ->get_template({datadir => $datadir, id=> "/$path/$chunk.$flavour"}); return $template if($template); } while ($path =~ s/(\/*[^\/]*)$// and $1); return join '', ($template{$flavour}{$chunk} || $template{error}{$chunk} || ''); }; # Define default interpolation subroutine $self->{interpolate_routine} = sub{ my $self = shift; my $template = shift; $template =~ s{\$((\w+(?:::)?)+\w*)}{ defined $1 ? (defined $self->{_param}->{$1} ? $self->{_param}->{$1} : '') : ''; }ge; return $template; }; $self; } sub settings { my $self = shift; return $self->{settings} if(!defined $_[0]); if(ref($_[0]) eq 'HASH'){ return $self->{settings} = $_[0]; } elsif(ref($_[0]) eq 'ARRAY'){ my @results; for my $name (@{ $_[0] }){ push @results,$self->{settings}->{$name}; } return @results; } elsif(@_ > 1){ my %hash = @_; while( my($name,$value) = each %hash ){ $self->{settings}->{$name} = $value; } } else{ return $self->{settings}->{$_[0]}; } } sub param { my $self = shift; my @args = @_; my @result; return keys %{ $self->{_param} } if(@args == 0); if(ref($_[0]) eq 'HASH'){ my %hash = %{$_[0]}; while( my($name,$value) = each %hash ){ $self->{_param}->{$name} = $value; } } elsif(ref($_[0]) eq 'ARRAY'){ my @results; for my $name (@{ $_[0] }){ push @results,$self->{_param}->{$name}; } return @results; } elsif(@args > 1){ my %hash = @args; while( my($name,$value) = each %hash ){ $self->{_param}->{$name} = $value; } } else{ return $self->{_param}->{$_[0]}; } } sub settings2param { my $self = shift; my @param = (qw[ blog_title blog_description url default_flavour path_info flavour ]); for my $name ( @param ){ $self->param( $name => $self->settings($name) ); } } sub run { my $self = shift; $self->get_request(); $self->settings2param(); $self->get_plugins(); $self->plugins_template(); $self->plugins_entries(); $self->find_entries(); $self->sort_entries(); $self->plugins_filter(); $self->generate_content_type(); $self->handle_entry(); $self->output(); $self->plugins_end(); } sub get_request { my $self = shift; $self->_get_request_url(); $self->_get_request_path(); $self->_get_request_misc(); $self->_get_request_currentdir(); $self; } sub _get_request_url { $_[0]->{settings}->{url} ||= $_[0]->cgi->url(); $_[0]->{settings}->{url} =~ s/^included:/http:/; # Fix for SSI } sub _split_path_info { split m{/}, $_[0]->cgi->path_info() || $_[0]->cgi->param('path') || ''; } sub _get_request_path { my $self = shift; my @path_info = $self->_split_path_info(); shift @path_info; $self->{settings}->{path_info} = ''; while ($path_info[0] and $path_info[0] =~ /^[a-zA-Z].*$/ and $path_info[0] !~ /(.*)\.(.*)/){ $self->{settings}->{path_info} .= '/' . shift @path_info; } if( defined $path_info[$#path_info] and $path_info[$#path_info] =~ /(.+)\.(.+)$/ ){ $self->{settings}->{fn} = $1; $self->{settings}->{flavour} = $2; if($1 ne 'index'){ $self->{settings}->{path_info} .= "/$1.$2"; } pop @path_info; } else{ $self->{settings}->{flavour} = $self->cgi->param('flav') || $self->{settings}->{default_flavour} || ''; } # Strip spurious slashes $self->{settings}->{path_info} =~ s!(^/*)|(/*$)!!g; # Date fiddling @{ $self->{settings} }{qw/path_info_yr path_info_mo path_info_da/} = @path_info; @{ $self->{settings} }{qw/path_info_yr path_info_mo path_info_da/} = map{ defined $_ ? $_ : '' } @{$self->{settings} }{qw/path_info_yr path_info_mo path_info_da/}; $self->{settings}->{path_info_mo_num} = $self->{settings}->{path_info_mo} ? ($self->{settings}->{path_info_mo} =~ /\d{2}/ ? $self->{settings}->{path_info_mo} : ($month2num->{ucfirst(lc $self->{settings}->{path_info_mo})} || '') ) : ''; $self; } sub _get_request_misc { # Drop ending any / from dir settings and url for my $name (qw/url datadir plugin_dir/){$_[0]->{settings}->{$name} =~ s{/$}{};} # Fix depth to take into account datadir's path $_[0]->{settings}->{depth} += ($_[0]->{settings}->{datadir} =~ tr[/][]) - 1 if($_[0]->{settings}->{depth}); $_[0]->{settings}->{flavour} = '' if(!defined $_[0]->{settings}->{flavour}); $_[0]; } sub _get_request_currentdir { my $currentdir = $_[0]->{settings}->{path_info}; my $file_extension = $_[0]->{settings}->{file_extension}; ( $currentdir =~ /(.*?)([^\/]+)\.(.+)$/ and defined $2 and $2 ne 'index' ) ? $currentdir = "$1$2.$file_extension" : $currentdir =~ s!/index\..+$!!; $_[0]->{settings}->{currentdir} = $currentdir; } sub plugins_order { my ($self,@args) = @_; if(@args and ref($args[0]) eq 'ARRAY'){ $self->{plugins_order} = $args[0]; } elsif(@args){ $self->{plugins_order} = [@args]; } return ($self->{plugins_order}) ? @{ $self->{plugins_order} } : (); } sub get_plugins { my $self = shift; my $plugin_dir = $self->{settings}->{plugin_dir} || return; ($plugin_dir) = $plugin_dir =~ /^([-:.\/\w]+)$/; my @plugins = $self->plugins_order(); if(! @plugins and $plugin_dir){ opendir(PLUGINS, $plugin_dir) or die "$! $plugin_dir"; @plugins = sort{ $a cmp $b } map { /^(\w+)\.pm$/; $1 } grep{ -f "$plugin_dir/$_" and /^\w+\.pm$/ } readdir(PLUGINS); closedir PLUGINS; } for my $plugin_name ( @plugins ){ last if($plugin_name eq '-'); (my $pluginfile = $plugin_name) =~ s/::/\//g; eval qq{ require "ModBlosxom/plugin/$pluginfile.pm" }; if($@){ warn "Can't load plugin '$plugin_name'."; next; } $plugin_name =~ s/^\d+//; my $plugin = ('ModBlosxom::plugin::' . $plugin_name)->new(); if( $plugin->start($self) ){ $self->{plugins}->{name}->{$plugin_name}->{on_off} = 1; $self->{plugins}->{name}->{$plugin_name}->{object} = $plugin; push @{ $self->{plugins}->{order} }, $plugin_name; } } return $self->{plugins}->{order}; } sub find_entries { my $self = shift; my ($files, $indexes, $others) = $self->{find_entries_routine}->($self); my ($datadir,$currentdir,$file_extension) = @{$self->{settings}}{qw/datadir path_info file_extension/}; my $found_files; $self->{all_entries_files} = $files; if( $currentdir =~ /(.*?)([^\/]+)\.(.+)$/ and $files->{"$datadir/$1$2.$file_extension"} ){ $found_files = {"$datadir/$1$2.$file_extension" => $files->{"$datadir/$1$2.$file_extension"}}; } else{ $found_files = $files; } $self->{entries} = { all_entries => $files, files => $found_files, indexes => $indexes, others => $others }; } sub sort_entries { my $self = shift; my $sort = $self->plugins_sort() || sub { my($files_ref) = @_; return sort { $files_ref->{$b} <=> $files_ref->{$a} } keys %$files_ref; }; $self->{sorted_entries} = [ $sort->($self->{entries}->{files}, $self->{entries}->{others}) ]; } sub get_template { my $self = shift; my ($path, $chunk, $flavour) = map{ (defined $_ ? $_ : '') } @_; $self->{template_routine}->($self, $path, $chunk, $flavour); } sub interpolate { my $self = shift; my $data = shift; $self->{interpolate_routine}->($self,$data); } sub generate_content_type { my $self = shift; my $content_type = $self->get_template( $self->{settings}->{path_info},'content_type',$self->{settings}->{flavour} ); $content_type =~ s!\n.*!!s; $self->{header}->{'-type'} = $content_type; } sub handle_entry { my $self = shift; return if( $self->plugins_skip() ); $self->plugins_interpolate(); $self->generate_head(); $self->generate_story(); $self->generate_foot(); $self->plugins_last(); } sub generate_head { my $self = shift; my $head = $self->get_template( $self->{settings}->{path_info},'head',$self->{settings}->{flavour} ); $self->plugins_head($self->{settings}->{path_info},\$head); $head = $self->interpolate($head); $self->{rendered}->{head} = $head; } sub generate_story { my $self = shift; my ($flavour) = @{ $self->{settings} }{qw/flavour/}; $self->init_handling_story(); for my $path_file (@{ $self->{sorted_entries} }){ $self->handling_story_is_continued() or last; $self->filter_by_path($path_file) or next; $self->filter_by_date() or next; $self->generate_date(); my $entry = $self->read_entry_data() or next; my ($title, $body) = ($entry->title, $entry->body); my $story = $self->get_template($self->settings('path'), 'story', $flavour); $self->plugins_story($self->settings([qw/path fn/]), \$story, \$title, \$body); $self->handle_by_content_type(\$title,\$body); $self->param(title => $title, body => $body); $self->{rendered}->{story} .= $self->interpolate($story); } } sub init_handling_story { my $self = shift; $self->{handle_story}->{count} = $self->{settings}->{num_entries}; $self->{handle_story}->{stop} = 0; $self->{handle_story}->{currentdir} = $self->{settings}->{currentdir}; $self->{handle_story}->{currentdate} = ''; $self->{handle_story}->{date} = join('/', @{$self->{settings}}{qw/path_info_yr path_info_mo_num path_info_da/}); } sub handling_story_is_continued { my $self = shift; return 0 if($self->{handle_story}->{stop} == 1); return 0 if($self->{handle_story}->{count}-- <= 0 and $self->{handle_story}->{date} !~ /\d/ ); return 1 } sub handle_by_content_type { my ($self, $titleref, $bodyref) = @_; # Escape <, >, and &, and to produce valid RSS if($self->{header}->{'-type'} =~ m{\Wxml$}){ my %escape = ('<'=>'<', '>'=>'>', '&'=>'&', '"'=>'"'); my $escape_re = join '|' => keys %escape; $$titleref =~ s/($escape_re)/$escape{$1}/g; $$bodyref =~ s/($escape_re)/$escape{$1}/g; } $self; } sub filter_by_path { my $self = shift; my $path_file = shift; # absolute path to entry data files my ($datadir,$file_extension) = @{$self->{settings}}{qw/datadir file_extension/}; my $currentdir = $self->{handle_story}->{currentdir}; my ($path,$fn) = $path_file =~ m!^$datadir/(?:(.*)/)?(.*)\.$file_extension!; if(!defined $path){ $path = ''; } return if($path !~ /^$currentdir/ and $path_file ne "$datadir/$currentdir"); $path &&= "/$path"; ($self->{settings}->{path},$self->{settings}->{fn}) = ($path,$fn); $self->param(path => $path, fn => $fn); $self->{handle_story}->{path} = $path; $self->{handle_story}->{path_file} = $path_file; $self->{handle_story}->{entry_id} = "$path/$fn.$file_extension"; return ($path,$fn); } sub filter_by_date { my $self = shift; my $date = $self->{handle_story}->{date}; my $path_file = $self->{handle_story}->{path_file}; my @num2month = @{ $num2month }; my ($dw,$mo,$mo_num,$da,$ti,$yr) = $self->nice_date($self->{entries}->{files}->{$path_file}); my ($hr,$min) = split /:/, $ti; my ($hr12, $ampm) = $hr >= 12 ? ($hr - 12,'pm') : ($hr, 'am'); $hr12 =~ s/^0//; if($hr12 == 0){ $hr12 = 12; } my($path_info_yr,$path_info_mo_num, $path_info_da) = split /\//, $date; return if($path_info_yr and $yr != $path_info_yr); $self->{handle_story}->{stop} = 1, return if($path_info_yr and $yr < $path_info_yr); return if($path_info_mo_num and $mo ne $num2month[$path_info_mo_num]); return if($path_info_da and $da != $path_info_da); $self->{handle_story}->{stop} = 1, return if($path_info_da and $da < $path_info_da); @{ $self->{settings} }{qw/dw mo mo_num da ti yr hr min hr12 ampm/} = ($dw,$mo,$mo_num,$da,$ti,$yr,$hr,$min,$hr12,$ampm); @{ $self->{_param} }{qw/dw mo mo_num da ti yr hr min hr12 ampm/} = ($dw,$mo,$mo_num,$da,$ti,$yr,$hr,$min,$hr12,$ampm); $self->{handle_story}->{storydate} = "$yr/$mo_num/$da"; return 1; } sub generate_date { my $self = shift; my $path = $self->{handle_story}->{path}; my $flavour = $self->{settings}->{flavour}; return if($self->{handle_story}->{currentdate} eq $self->{handle_story}->{storydate}); my $date = $self->get_template($path, 'date', $flavour); my $currentdir = $self->{handle_story}->{currentdir}; my $path_file = $self->{handle_story}->{path_file}; $self->plugins_date( $currentdir, \$date, $path_file, @{ $self->{settings} }{qw/dw mo mo_num da ti yr/}, ); $date = $self->interpolate($date); $self->_insert_date($date); $self->{handle_story}->{currentdate} = $self->{handle_story}->{storydate}; } sub _insert_date { $_[0]->{rendered}->{story} .= $_[1]; } sub read_entry_data { my $self = shift; my $path_file = $self->{handle_story}->{path_file}; my $datadir = $self->settings('datadir'); my $id = $self->{handle_story}->{entry_id}; my $entry = $self->access_db()->get_entry( {datadir => $datadir, id => $id} ); $self->{settings}->{raw} = $entry->title . "\n" . $entry->body; return $entry->exists ? $entry : undef; } sub generate_foot { my $self = shift; my $foot = $self->get_template( $self->{settings}->{path_info},'foot',$self->{settings}->{flavour} ); $self->plugins_foot($self->{settings}->{path_info},\$foot); $foot = $self->interpolate($foot); $self->{rendered}->{foot} = $foot; } sub output { my $self = shift; my $header = $self->{header}; print $self->{CGI}->header(%$header); print $self->{rendered}->{head} if(defined $self->{rendered}->{head}); print $self->{rendered}->{story} if(defined $self->{rendered}->{story}); print $self->{rendered}->{foot} if(defined $self->{rendered}->{foot}); } # Handle plugins sub plugins_template { my $self = shift; my $sub = $self->_get_coderef_from_plugins('template'); $self->{template_routine} = $sub if($sub); } sub plugins_entries { my $self = shift; my $sub = $self->_get_coderef_from_plugins('entries'); $self->{find_entries_routine} = $sub if($sub); } sub plugins_sort { $_[0]->_get_coderef_from_plugins('sort'); } sub plugins_filter { my $self = shift; my $files = shift || $self->{entries}->{all_entries}; my $others = shift || $self->{entries}->{others}; return $self->{handle_story}->{plugin_status} = $self->_iterate_plugins('filter', [$files,$others]); } sub plugins_skip { return $_[0]->_iterate_plugins('skip'); } sub plugins_interpolate { my $self = shift; my $sub = $self->_get_coderef_from_plugins('interpolate'); $self->{interpolate_routine} = $sub if($sub); } sub plugins_head { my ($self, $currentdir, $headref) = @_; return $self->{handle_story}->{plugin_status} = $self->_iterate_plugins('head', [$currentdir,$headref]); } sub plugins_date { my ($self,$currentdir, $dateref, $path_file, @date_bits) = @_; my $mtime = $self->{entries}->{files}->{$path_file}; return $self->{handle_story}->{plugin_status} = $self->_iterate_plugins('date', [$currentdir,$dateref,$mtime,@date_bits]); } sub plugins_story { my ($self, $path, $fn, $storyref, $titleref, $bodyref) = @_; return $self->{handle_story}->{plugin_status} = $self->_iterate_plugins('story', [$path,$fn,$storyref,$titleref,$bodyref]); } sub plugins_foot { my ($self, $currentdir, $footref) = @_; return $self->{handle_story}->{plugin_status} = $self->_iterate_plugins('foot', [$currentdir,$footref]); } sub plugins_last { return $_[0]->_iterate_plugins('last', undef, sub{ $_[1]->last($_[0]) }); } sub plugins_end { return $_[0]->_iterate_plugins('end', undef, sub{ $_[1]->end($_[0]) }); } sub _get_coderef_from_plugins { my $self = shift; my ($name) = @_; # hook name $self->_iterate_plugins($name, undef, sub{ my ($self,$plugin, $name) = @_; my $sub = $plugin->$name($self); return $sub if(defined $sub and ref($sub) eq 'CODE'); }, 1); } sub _iterate_plugins { # $hook_name, $args_ref, $code_ref, $stop_option my ($self, $name, $args, $sub, $stop) = @_; my $result; if(!defined $sub or ref($sub) ne 'CODE'){ # Default iterator $sub = sub{ my ($self,$plugin,$name,$args) = @_; $args ||= []; return $plugin->$name($self, @$args) # if( $plugin->can($name) ); }; } for my $plugin_name ( @{ $self->{plugins}->{order} } ){ next if($self->{plugins}->{name}->{$plugin_name}->{on_off} <= 0); my $plugin = $self->{plugins}->{name}->{$plugin_name}->{object}; $result = $sub->($self,$plugin,$name,$args) if( $plugin->can($name) ); last if($stop and defined $result); } $result; } # Handle date sub nice_date { my $self = shift if(ref($_[0])); my($unixtime) = @_; my $c_time = ctime($unixtime); my($dw,$mo,$da,$ti,$yr) = ( $c_time =~ /(\w{3}) +(\w{3}) +(\d{1,2}) +(\d{2}:\d{2}):\d{2} +(\d{4})$/ ); $da = sprintf("%02d", $da); my $mo_num = $month2num->{$mo}; return ($dw,$mo,$mo_num,$da,$ti,$yr); } # Handle DataBase sub connect_db { my $self = shift; $self->{db_obj} = ModBlosxom::AccessDB->new(@_); } sub access_db { $_[0]->{db_obj}; } # Miscellaneous sub cgi { $_[0]->{CGI}; } sub add_cookie { push @{ $_[0]->{header}->{'-cookie'} }, $_[1]; } #sub DESTROY { print STDERR "$_[0] is destroyed.\n" } ############################################################################## package ModBlosxom::AccessDB; use strict; use FileHandle; use File::Find; use File::stat; sub new { bless { fh => new FileHandle }, shift; } sub get_template { my $fh = $_[0]->{fh}; my $hash = $_[1]; my $datadir = $hash->{datadir}; my $file = $hash->{id}; $fh->open("< $datadir$file") or return ''; return join '', <$fh>; } sub find_entries { my $self = shift; my $hashref = shift; my ($datadir,$depth,$file_extension,$show_future_entries) = @{ $hashref }{qw/root depth file_ext show_future/}; my (%files, %indexes, %others); find( sub { my $d; my $curr_depth = $File::Find::dir =~ tr[/][]; return if $depth and $curr_depth > $depth; # a match not an index, .file, and is readable if( $File::Find::name =~ m!^$datadir/(?:(.*)/)?(.+)\.$file_extension$! and $2 ne 'index' and $2 !~ /^\./ and (-r $File::Find::name) ){ # to show or not to show future entries( ($show_future_entries or stat($File::Find::name)->mtime < time ) # add the file and its associated mtime to the list of files and $files{$File::Find::name} = stat($File::Find::name)->mtime and $indexes{(defined $1 ? $1 : '')} = 1 and $d = join('/', (ModBlosxom::nice_date($files{$File::Find::name}))[5,2,3]) and $indexes{$d} = $d and $indexes{ ($1 ? "$1/" : '') . "$2.$file_extension" } = 1 } else { !-d $File::Find::name and -r $File::Find::name and $others{$File::Find::name} = stat($File::Find::name)->mtime } }, $datadir ); return ModBlosxom::Entries->new({files => \%files, indexes => \%indexes, others => \%others}); } sub retrieve_entry { } # get one or more entries with more detailed information. # retrieve_entry takes hash ref (ex. { id => 'hoge' }). sub get_entry { # easy version of retrieve_entry. my $fh = $_[0]->{fh}; my $hash = $_[1]; my ($title,$body); my $datadir = $hash->{datadir}; my $file = $hash->{id}; $fh->open("< $datadir$file") or return new ModBlosxom::NullEntry; chomp($title = <$fh>); chomp($body = join '', <$fh>); $fh->close; return ModBlosxom::Entry->new({title => $title, body => $body}); } ############################################################################## package ModBlosxom::Entries; sub new { bless $_[1], $_[0]; } sub files { $_[0]->{files}; } sub indexes { $_[0]->{indexes}; } sub others { $_[0]->{others}; } package ModBlosxom::Entry; sub new { bless $_[1], $_[0]; } sub exists{ 1; } sub title { $_[0]->{title} = $_[1] if(defined $_[1]); $_[0]->{title}; } sub body { $_[0]->{body} = $_[1] if(defined $_[1]); $_[0]->{body}; } package ModBlosxom::NullEntry; sub new { bless {}, $_[0]; } sub exists{ 0; } sub title { ''; } sub body { ''; } package ModBlosxom; ############################################################################## 1; ############################################################################## # Default HTML and RSS template bits __DATA__ html content_type text/html html head \n\n$blog_title\n\n\n\n

$blog_title

\n

$blog_description

\n html story

$title

\n$body\n

Posted at: $ti | Path: $path | Permanent link to this entry

\n html date

$yr/$mo_num/$da

\n html foot
\n\n\n rss content_type text/xml rss head \n\n\n\n\n$blog_title\n$url\n$blog_description\n$blog_language\n rss story \n$title\n$url$path$fn.$default_flavour\n$body\n\n rss date \n rss foot \n\n error content_type text/html error head \n\n$blog_title\n\n\n

$blog_title

\n

$blog_description

\n

Error: I'm afraid this is the first I've heard of a "$flavour" flavoured Blosxom. Try dropping the "/+$flavour" bit from the end of the URL.

\n error story

$title

\n$body\n

Posted at: $ti | Path: $path | Permanent link to this entry

\n error date

$yr/$mo_num/$da

\n error foot
\n\n\n __END__ =head1 NAME ModBlosxom - OO module for Blosxom 2.0 =head1 SYNOPSIS use ModBlosxom; my $blosxom = new ModBlosxom; my $basedir = '/your/blosxom/path'; $blosxom->settings({ # most basical settings blog_title => 'Blosxom!!', blog_description => 'hoge hoge', basedir => "$basedir", datadir => "$basedir/entries", url => '', depth => 0, num_entries => 15, file_extension => 'txt', default_flavour => 'html', show_future_entries => 0, }); $blosxom->run(); =head1 DESCRIPTION ModBlosxom.pm doesn't support static rendering function. =head1 ABOUT PLUGIN ModBlosxom's plugins are not compatible with blosxom2's but its modifications are relatively easy (I hope so). This package has 'ModBlosxom::plugin::_blosxom' which can use blosxom2's plugin. For ModBlosxom::plugin::_blosxom, you specify 'blosxom_plugin_dir' with settings methods. =head1 METHODs =head2 Basic mthods =over 4 =item new return a ModBlosxom object. =item settings =item run =item get_template =item interpolate =item param =item plugins_order =back =head2 Flow mthods =over 4 =item get_request =item settings2param =item get_plugins =item find_entries =item sort_entries =item generate_content_type =item handle_entry =item output =back =head2 Entries mthods =over 4 =item generate_head =item generate_story =item init_handling_story =item handling_story_is_continued =item filter_by_path =item filter_by_date =item generate_date =item read_entry_data =item generate_foot =item handle_by_content_type =back =head2 Plugin Methods =over 4 =item plugins_template =item plugins_entries =item plugins_sort =item plugins_filter =item plugins_skip =item plugins_interpolate =item plugins_head =item plugins_story =item plugins_foot =item plugins_last =item plugins_end =back =head2 Other Methods =over 4 =item connect_db =item access_db =item nice_date =item cgi =back =head1 INTERNAL VARIABLE =over 4 =item find_entries_routine =item template_routine =item interpolate_routine =item templates =item header =item rendered =back =head1 AUTHOR makamaka[at]donzoko.net =head1 LICENSE ModBlosxom's license is same as Blosxom. 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 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. =cut