package AxKit::XSP::MyBlogger;

$VERSION = "0.01";

# taglib stuff
use AxKit 1.4;
use Apache;
use Apache::AxKit::Language::XSP;
use Apache::File;
use XML::LibXML 1.31;
use Time::Piece; # overrides localtime

use vars qw/@ISA $NS $VERSION/;
        

$NS = 'http://axkit.org/NS/xsp/myblogger/v1';

@ISA = qw(Apache::AxKit::Language::XSP);

use strict;

my $base = "/var/www/blog/";
my $my_name = "";




## Parser subs
        
sub parse_char {
    my ($e, $text) = @_;
    $text =~ s/^\s*//;
    $text =~ s/\s*$//;

    return '' unless $text;

    $text =~ s/\|/\\\|/g;
    if ($e->current_element() =~ /^(name)$/) {
        $my_name = $text;
        return "\$_name = q|$text|;";
    }
    return ''; # nothing else in util: should have text (?)
}

sub parse_start {
    my ($e, $tag, %attribs) = @_; 
#    warn "Checking: $tag\n";


    if ($tag eq 'item' or $tag eq 'list' or $tag eq 'block') {

        $my_name = "";
        my $code = "{# start item\nmy (\$_name);\n";
        if ($attribs{name}) {
            $code .= '$_name = q|' . $attribs{name} . '|;';
        } else {
	  $code .= "\$_name = q||\n";
	}
        return $code;
    }
    elsif ($tag eq 'items' or $tag eq 'lists' or $tag eq 'blocks' or $tag eq 'catch') {
        $my_name = "";
        return "{# start of list etc \n";
    }
    elsif ($tag eq 'name') {
        $my_name = "";
        return "# Start of name \n";
    }
    else {
        die "Unknown util tag: $tag";
    }
}

sub parse_end {
    my ($e, $tag) = @_;

    if ($tag eq 'item') {
      if ( $my_name ){
        return ";\nAxKit::XSP::MyBlogger::item(\n" .
        "\$document, \$parent, qq|$my_name|" .
        ");}\n";
      } else {
        return ";\nAxKit::XSP::MyBlogger::item(\n" .
        '$document, $parent, $_name' .
        ");}\n";
      }
    }
    elsif ($tag eq 'list') {
      if ( $my_name ){
        return ";\nAxKit::XSP::MyBlogger::list(\n" .
        "\$document, \$parent, qq|$my_name|" .
        ");}\n";
      } else {
        return ";\nAxKit::XSP::MyBlogger::list(\n" .
        '$document, $parent, $_name' .
        ");}\n";
      }
    }
    elsif ($tag eq 'block') {
        return ";\nAxKit::XSP::MyBlogger::block(\n" .
        '$document, $parent, $_name' .
        ");}\n";
    }
    elsif ($tag eq 'items') {
        return "AxKit::XSP::MyBlogger::items(\n" .
        '$document, $parent' .
        ");}\n";
    }
    elsif ($tag eq 'lists') {
        return "AxKit::XSP::MyBlogger::lists(\n" .
        '$document, $parent' .
        ");}\n";
    }
    elsif ($tag eq 'blocks') {
        return "AxKit::XSP::MyBlogger::blocks(\n" .
        '$document, $parent' .
        ");}\n";
    }
    elsif ($tag eq 'catch') {
        return "AxKit::XSP::MyBlogger::catch(\n" .
        '$document, $parent' .
        ");}\n";
    }
    elsif ($tag eq 'name') {
        return "\n# End of Name \n";
    }
    return ";";
}


sub items {
  warn "\n\n\n\n\n I am running items: @_\n";
  my ($document, $parent) = @_;
  my @files = glob($base."*.txt");
  my $items = {};
  foreach my $file ( @files ){
     my ( $name ) = $file =~ /^.*\/(.*?)\.txt$/;
     next unless $name;
     $items->{$name} = { 'content' => _get_item($name), 'date' => _get_file_time($file) };
  }

  my $content = "<articles>";
  foreach my $item ( reverse 
                       sort { $items->{$a}->{'date'} <=> $items->{$b}->{'date'} } 
		         keys %{$items} ){
    $content .= $items->{$item}->{'content'};
  }
  $content .= "</articles>";

  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}


sub item {
  warn "\n\n\n\n\n I am running item: @_\n";
  my ($document, $parent, $name) = @_;
  my $content = _get_item($name);
  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}

sub _get_item{
  my $name = shift;
  warn "\n Going for item: $name \n";
  my $filename = $base.$name.".txt";
  my ( $content, $time ) = _get_file($filename);
  my @rows = grep(!/^$/,split(/\n/, $content ));
  my $hdr = shift @rows;
  my $link = $rows[0] =~ /^(http:|\/)/ ? shift @rows : "/article.xml?$name";
  $content = "<item name='$name'><time>$time</time><title>$hdr</title><link>$link</link><body><![CDATA[".
  join("\n",@rows)."]]></body></item>";
  return $content;
}


sub lists {
  warn "\n\n\n\n\n I am running lists: @_\n";
  my ($document, $parent) = @_;
  my @files = grep(!/\~$/, glob($base."*.list*"));
  my $lists = {};
  foreach my $file ( @files ){
     my ( $name, undef, $pos ) = $file =~ /^.*\/(.*?)\.list(\.(\d+))?$/;
     next unless $name;
     $lists->{$name} = { 'content' => _get_list($name), 'date' => _get_file_time($file) };
     $lists->{$name}->{'position'} = $pos if $pos;
  }

  my $content = "<lists>";
  foreach my $item (
         ( sort { $lists->{$a}->{'position'} <=> $lists->{$b}->{'position'} } 
           grep ( { exists $lists->{$_}->{'position'} } keys %{$lists} ) ),
 
         ( reverse 
           sort { $lists->{$a}->{'date'} <=> $lists->{$b}->{'date'} } 
           grep ( { ! exists $lists->{$_}->{'position'} } keys %{$lists} ) )
         ){
    $content .= $lists->{$item}->{'content'};
  }
  $content .= "</lists>";

  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}


sub list {
  warn "\n\n\n\n\n I am running list: @_ \n";
  my ($document, $parent, $name) = @_;
  my $content = _get_list($name);
  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}

sub _get_list{
  my $name = shift;
  my $filename = $base.$name.".list";
  my ( $file ) = grep(!/\~$/, glob($filename."*"));
  my ( $pos ) = $file =~ /\.list\.(\d+)$/;
  $filename .= ".$pos" if $pos;
  my ( $content, $time ) = _get_file($filename);
  my @rows = grep(!/^$/,split(/\n/, $content ));
  my $hdr = shift @rows;
  $content = "<list name='$name'><time>$time</time><title><![CDATA[$hdr]]></title><body><![CDATA[".
  join("\n",@rows)."]]></body>".($pos?"<position>$pos</position>":"")."</list>";
  return $content;
}


sub blocks {
  warn "\n\n\n\n\n I am running blocks: @_\n";
  my ($document, $parent) = @_;
  my @files = grep(!/\~$/, glob($base."*.block*"));
  my $blocks = {};
  foreach my $file ( @files ){
     my ( $name, undef, $pos ) = $file =~ /^.*\/(.*?)\.block(\.(\d+))?$/;
     next unless $name;
     $blocks->{$name} = { 'content' => _get_block($name), 'date' => _get_file_time($file) };
     $blocks->{$name}->{'position'} = $pos if $pos;
  }

  my $content = "<blocks>";
  foreach my $item (
         ( sort { $blocks->{$a}->{'position'} <=> $blocks->{$b}->{'position'} } 
           grep ( { exists $blocks->{$_}->{'position'} } keys %{$blocks} ) ),
 
         ( reverse 
           sort { $blocks->{$a}->{'date'} <=> $blocks->{$b}->{'date'} } 
           grep ( { ! exists $blocks->{$_}->{'position'} } keys %{$blocks} ) )
         ){
    $content .= $blocks->{$item}->{'content'};
  }
  $content .= "</blocks>";

  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}


sub block {
  warn "\n\n\n\n\n I am running block: @_\n";
  my ($document, $parent, $name) = @_;
  my $content = _get_block($name);
  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}

sub _get_block{
  my $name = shift;
  my $filename = $base.$name.".block";
  my ( $file ) = grep(!/\~$/, glob($filename."*"));
  my ( $pos ) = $file =~ /\.block\.(\d+)$/;
  $filename .= ".$pos" if $pos;
  my ( $content, $time ) = _get_file($filename);
  my @rows = grep(!/^$/,split(/\n/, $content ));
  my $hdr = shift @rows;
  $content = "<block name='$name'><time>$time</time><title><![CDATA[$hdr]]></title><body><![CDATA[".
  join("\n",@rows)."]]></body>".($pos?"<position>$pos</position>":"")."</block>";
  return $content;
}


sub _get_file_time{
  my $filename = shift;
  my $fh = Apache::File->new($filename) || 
  throw Apache::AxKit::Exception::IO( -text => "error opening $filename");
  my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
                        $atime,$mtime,$ctime,$blksize,$blocks)
			                          = stat($fh);
  $fh->close;
  return $mtime;
}


sub _get_file{
  my $filename = shift;
  my $fh = Apache::File->new($filename) || 
  throw Apache::AxKit::Exception::IO( -text => "error opening $filename");
  flock($fh, 1);
  my $content = "";
  my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
                        $atime,$mtime,$ctime,$blksize,$blocks)
			                          = stat($fh);
  {
    local $/;
    $content = <$fh>;
  }
  $fh->close;
  return ($content, scalar localtime $mtime);
}


sub catch {
  warn "\n\n\n\n\n I am running catch: @_\n";
  my ($document, $parent) = @_;
  my $file = $base."offthewall";
  my ( $otw, $time ) = _get_file($file);
  warn "The catch is: $otw \n\n\n";
  $otw =~ s/\n//g;
  $otw =~ s/^\s.*?(Quot.*?ry:)(.*?)$/\<a href=\'http:\/\/www.perl.com\'\>$1\<\/a\>$2/s;
  my $content = "<catch><![CDATA[$otw]]></catch>";
  warn "The catch content is: $otw \n\n\n";

  my $doc = XML::LibXML->new()->parse_string($content);
  if ($doc) {
    my $root = $doc->getDocumentElement();
    $root = $document->importNode($root, 1);
    $parent->appendChild($root);
  }
   else {
     die "XML::LibXML returned undef";
  }
}


# insert from a local file as plain text
sub get_file_contents {
    my ($filename) = @_;
    my $fh = Apache::File->new($filename) || 
    throw Apache::AxKit::Exception::IO( -text => "error opening $filename");
    flock($fh, 1);
    local $/;
    my $content = <$fh>;
    $fh->close;
     return $content;
}



1;

__END__

=head1 NAME

AxKit::XSP::MyBlogger - Utilities for building XSP pages from Rael Dornfests' Blosxom blogging format


=head1 SYNOPSIS

AxAddXSPTaglib AxKit::XSP::MyBlogger

=head1 DESCRIPTION

Rael Dornfests' blogging format Blosxom http://www.oreillynet.com/~rael/lang/perl/blosxom/, is a fast, and straight forward way of organising and creating your own blog.  This simple file based storage format has been taken and integrated with the AxKit http://axkit.org, XML delivery system, to provide yet another way of agregating, and presenting personal content.

Lives at: http://www.piersharding.com/download/MyBlogger.pm

=head2 How to use?

Add this directive to your AxKit Config:

 AxAddXSPTaglib AxKit::XSP::MyBLogger

Sample XSP Page:

 <?xml version="1.0"?>
 <?xml-stylesheet type="application/x-xsp" href="."?>
 <?xml-stylesheet type="application/x-xpathscript"
		href="/stylesheets/blosxom.xps"
		title="default"?>
 <xsp:page 
         xmlns:blog="http://axkit.org/NS/xsp/myblogger/v1"
         xmlns:util="http://apache.org/xsp/util/v1"
         xmlns:xsp="http://apache.org/xsp/core/v1"
         language="Perl">
 <page>
 <blog:items/>
 <blog:lists/>
 <blog:blocks/>
 </page>
 </xsp:page>

There are three basic ellements 

=over 4

=item items

Items are individual articles, and come in files ending in .txt.  These are delivered in reverse date order.

=item lists

Lists are lists of links.  These files end in .list, and are order by date, or by an additional suffix number in the form of .1 etc.

=item blocks

Blocks are chunks of HTML, that are just inserted into the document as CDATA.

=back

foreach of items, lists, and blocks, there is a corresponding singular (item, list, block).  Each of this enables you to access a single file by name ( omit and suffix off the filenames ).
The filename for the Blosxom source data1 can be either determined by a child
element <name/> or attribute name='' on the <item/> element.
<blog:item name='somefile'/>

Currently the base directory is hard coded as /var/www/blog 
(inline with the default Red Hat installation), and I just choose
to link file into this directory that I want to access.

=head1 AUTHOR

Piers Harding - piers@ompa.net

This module was based on AxKit::XSP::WebUtils


