package Kwiki::Atom;
use strict;
use warnings;
use Kwiki::Plugin '-Base';
use Kwiki::Display;
use mixin 'Kwiki::Installer';
our $VERSION = '0.10';

BEGIN { unshift @INC, sub {
    die if $_[1] eq 'XML/LibXML.pm'
        or $_[1] eq 'XML/LibXSLT.pm';
} }

use XML::Atom;
use XML::Atom::Feed;
use XML::Atom::Link;
use XML::Atom::Entry;
use XML::Atom::Content;

use DateTime;
use XML::XPath;
use Kwiki::Atom::Server;

use constant ATOM_TYPE => "application/atom+xml";
#use constant ATOM_TYPE => "text/xml";

const class_id => 'atom';
const class_title => 'Atom';
#const css_file => 'atom.css';
const config_file => 'atom.yaml';
const cgi_class => 'Kwiki::Atom::CGI';
const server_class => 'Kwiki::Atom::Server';
field depth => 0;
field 'headers';
field 'server';

sub process {
}

sub register {
    my $registry = shift;
    $registry->add(action => 'atom_edit');
    $registry->add(action => 'atom_feed');
    $registry->add(action => 'atom_post');
    $registry->add(toolbar => 'recent_changes_atom_button', 
                   template => 'recent_changes_atom_button.html',
                   show_for => ['recent_changes'],
                  );
    $registry->add(toolbar => 'edit_atom_button', 
                   template => 'edit_atom_button.html',
                   show_for => ['display'],
                   params_class => $self->class_id,
                  );
}

sub fill_links {
    my $name = eval { $self->hub->cgi->page_name };
    my $url = CGI->new->url;
    push @{ $self->hub->{links}{all} }, ($name ? {
        rel => 'alternate',
        type => ATOM_TYPE,
        href => "$url?action=atom_edit;page_name=". $self->hub->cgi->page_name,
    } : ()), {
        rel => 'service.feed',
        type => ATOM_TYPE,
        href => "$url?action=atom_feed",
    }, {
        rel => 'service.post',
        type => ATOM_TYPE,
        href => "$url?action=atom_post",
    };
    return;
}

sub toolbar_params {
#    require YAML;
#    open X, '>>/tmp/post.log';
#    print X "POSTDATA:\n", $self->cgi->POSTDATA, "\n";
#    print X "HEADERS:\n", YAML::Dump(\%ENV), $/;
#    close X;
    return () unless $ENV{CONTENT_TYPE} and
                    ($ENV{CONTENT_TYPE} eq ATOM_TYPE
                  or $ENV{CONTENT_TYPE} =~ m{^\w+/xml}); # XXX ecto XXX

    $self->atom_post;

    my %header = &Spoon::Cookie::content_type;
    print CGI::header(%header);
    print $self->server->print;
    exit;
}

sub fill_header {
    $self->wrap_header if !$self->headers;
    $self->headers( [ @{$self->headers||[]}, @_ ] );
}

sub wrap_header {
    my $server = $self->server($self->server_class->new);
    $server->response_content_type(ATOM_TYPE);
    $server->client($self);

    no warnings 'redefine';
    require Spoon::Cookie;
    my $ref = Spoon::Cookie->can('content_type');
    *Spoon::Cookie::content_type = sub {
        *Spoon::Cookie::content_type = $ref;
        return( -type => ATOM_TYPE, @{$self->{headers}});
    };
}

sub make_entry {
    my ($page, $depth) = @_;
    my $url = $self->server->uri;

    my $author = XML::Atom::Person->new;
    $author->name($page->metadata->edit_by);

    my $link_html = XML::Atom::Link->new;
    $link_html->type('text/html');
    $link_html->rel('alternate');
    $link_html->href("$url?".$page->uri);
    $link_html->title($page->id);

    my $link_edit = XML::Atom::Link->new;
    $link_edit->type(ATOM_TYPE);
    $link_edit->rel('service.edit');
    $link_edit->href("$url?action=atom_edit;page_name=".$page->id);
    $link_edit->title($page->id);

    my $entry = XML::Atom::Entry->new;
    $entry->title($page->id);

    my $content = XML::Atom::Content->new(Type => 'text/plain');
    my $elem = $content->elem;
    my $text = ($content->LIBXML) ? 'XML::LibXML::Text'
                                    : 'XML::XPath::Node::Text';

    $elem->appendChild($text->new($page->content));
    $elem->setAttribute('mode', 'escaped');
    $entry->content($content);

    $entry->summary('');
    $entry->issued( DateTime->from_epoch( epoch => $page->io->ctime )->iso8601 . 'Z' );
    $entry->modified( DateTime->from_epoch( epoch => $page->io->mtime )->iso8601 . 'Z' );
    $entry->id("$url?".$page->uri);

    $entry->author($author);
    $entry->add_link($link_html);
    $entry->add_link($link_edit);

    return $entry;
}

sub update_page {
    my $page = shift;
    my $method = $self->server->request_method;
    my $entry = eval { $self->server->atom_body };

    if (!$entry) {
        print "Status: 400\n\n";
        $self->fill_header( -status => 400 );
        return;
    }

    if (!$page) {
        my $title = $entry->title;
        if ($entry->content->type =~ /\bx?html\b/i) {
            require HTML::Entities;
            HTML::Entities::decode_entities($title);
        }
        $page = $self->pages->new_page($title);

        if ($page->exists and $method eq 'POST') {
            $self->server->response_code(409);
            $self->server->{_error} = 'This page already exists';
            $self->fill_header(
                -status => 409,
                -type => 'text/plain',
                -warning => 'This page already exists',
            );
            return undef;
        }
    }

    $self->hub->users->current->name(
        eval { $self->server->get_auth_info->{Username} }
            || $self->hub->config->user_default_name
    );
    my $body = $entry->content->body;
    if ($entry->content->type =~ /\bx?html\b/i) {
        $body =~ s/<[^>]+>//g;
        require HTML::Entities;
        HTML::Entities::decode_entities($body);
    }
    $page->content($body);
    $page->update->store;

    return $page;
}

sub atom_list {
    my $url = $self->server->uri;

    my $link_feed = XML::Atom::Link->new;
    $link_feed->type(ATOM_TYPE);
    $link_feed->rel('service.feed');
    $link_feed->title($self->config->site_title);
    $link_feed->href("$url?action=atom_feed");

    my $link_post = XML::Atom::Link->new;
    $link_post->type(ATOM_TYPE);
    $link_post->rel('service.post');
    $link_post->title($self->config->site_title);
    $link_post->href("$url?action=atom_post");

    my $feed = XML::Atom::Feed->new;
    $feed->title($self->config->site_title);
    $feed->info($self->config->site_title);
    $feed->add_link($link_feed);
    $feed->add_link($link_post);
    $feed->modified(DateTime->now->iso8601 . 'Z');

    $self->munge($feed->as_xml);
}

sub atom_post {
    $self->fill_header;
    return $self->atom_list if $self->server->request_method eq 'GET';

    $self->server->{request_content} = $self->cgi->POSTDATA
        if $self->server->request_method eq 'POST';

    $self->server->run;
    $self->server->print;
}

sub atom_edit {
    $self->fill_header;
    $self->server->run;
    $self->server->print;
}

sub atom_feed {
    $self->fill_header;

    my $depth = $self->cgi->depth;
    my $pages = [
        sort {
            $b->modified_time <=> $a->modified_time 
        } ($depth ? $self->pages->recent_by_count($depth) : $self->pages->all)
    ];

    return $self->generate($pages, $depth); # XXX

    $self->hub->load_class('cache')->process(
        sub { $self->generate($pages) }, 'atom', $depth, int(time / 120)
    );
}

use Spiffy '-yaml';
sub generate {
    my ($pages, $depth) = @_;
    my $datetime = @$pages 
        ? DateTime->from_epoch( epoch => $pages->[0]->metadata->edit_unixtime )
        : DateTime->now;

    my $url = $self->server->uri;
    my $link_html = XML::Atom::Link->new;
    $link_html->type('text/html');
    $link_html->rel('alternate');
    $link_html->title($self->config->site_title);
    $link_html->href($url);

    my $link_post = XML::Atom::Link->new;
    $link_post->type('application/atom+xml');
    $link_post->rel('service.post');
    $link_post->title($self->config->site_title);
    $link_post->href("$url?action=atom_post");

    my $feed = XML::Atom::Feed->new;
    $feed->title($self->config->site_title);
    $feed->info($self->config->site_title);
    $feed->add_link($link_html);
    $feed->add_link($link_post);
    $feed->modified($datetime->iso8601 . 'Z');

    my $author = XML::Atom::Person->new;
    $author->name($self->config->site_url);

    $self->config->script_name($url);

    for my $page (@$pages) {
        $feed->add_entry( $self->make_entry($page, $depth) );
    }

    $self->munge($feed->as_xml);
}

sub munge {
    my $xml = shift;
    $xml =~ s/version="1.0"/version="1.0" encoding="UTF-8"/;
    $xml =~ s/(<\w+)/$1 version="0.3"/;
    return $xml;
}

package Kwiki::Atom::CGI;
use Kwiki::CGI '-base';

cgi 'depth';
cgi 'POSTDATA';

1;

package Kwiki::Atom;

1;

__DATA__

=head1 NAME 

Kwiki::Atom - Kwiki Atom Plugin

=head1 VERSION

This document describes version 0.10 of Kwiki::Atom, released
September 4, 2004.

=head1 SYNOPSIS

    % cd /path/to/kwiki
    % kwiki -add Kwiki::Atom

=head1 DESCRIPTION

This Kwiki plugin provides Atom 0.3 integration with Kwiki.

For more info about this kind of integration, please refer to
L<http://www.xml.com/pub/a/2004/04/14/atomwiki.html>.

Currently, this plugin has been tested with the following AtomAPI clients:

=over 4

=item * wxAtomClient.py

L<http://piki.bitworking.org/piki.cgi>

=item * ecto

L<http://ecto.kung-foo.tv/>

=back

=head1 AUTHOR

Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>

=head1 COPYRIGHT

Copyright 2004 by Autrijus Tang E<lt>autrijus@autrijus.orgE<gt>.

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

See http://www.perl.com/perl/misc/Artistic.html

=cut
__config/atom.yaml__
site_description: The Kwiki Wiki
site_url: http://localhost/par/
__template/tt2/recent_changes_atom_button.html__
<!-- BEGIN recent_changes_atom_button.html -->
<a href="[% script_name %]?action=atom_feed&depth=15" title="AtomFeed">
[% INCLUDE recent_changes_atom_button_icon.html %]
</a>
<!-- END recent_changes_atom_button.html -->
__template/tt2/recent_changes_atom_button_icon.html__
<!-- BEGIN recent_changes_atom_button_icon.html -->
Atom
<!-- END recent_changes_atom_button_icon.html -->
__icons/gnome/template/recent_changes_atom_button_icon.html__
<!-- BEGIN recent_changes_atom_button_icon.html -->
<img src="icons/gnome/image/atom_feed.png" alt="Atom" />
<!-- END recent_changes_atom_button_icon.html -->
__icons/gnome/image/atom_feed.png
iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPBAMAAADJ+Ih5AAAAMFBMVEX////yZ2fh4eH5+fm+
v7/r6+u3SEjV1dWmenqzs7PPz8/Hx8elpaXGxsbMzMyUlJQfgNlcAAAAAXRSTlMAQObYZgAA
ABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjQuNqQzgxcAAAB7SURBVHjaY2DAAEy3d++NelfM
wBBrwHzziuodA4ZFDOvKy51YFjBctdVqy7iieoCh6JGFYFqr7wQGewU1QbE8/g0MlUBGWpb9
BIbNSy3S0lqnTmC4YLeyLeNIwQGGCwxaV+ZMYjjA8IiB+c7PUJ0CBrvbWyZZvduKsBMAMi0q
dW1+s4IAAAAASUVORK5CYII=
__template/tt2/edit_atom_button.html__
<!-- BEGIN edit_atom_button.html -->
<a href="[% script_name %]?action=atom_edit;page_name=[% page_uri %]" title="AtomEdit">
[% INCLUDE edit_atom_button_icon.html %]
</a>
<!-- END edit_button.html -->
__template/tt2/edit_atom_button_icon.html__
<!-- BEGIN edit_atom_button_icon.html -->
Atom
<!-- END edit_atom_button_icon.html -->

__icons/gnome/template/edit_atom_button_icon.html__
<!-- BEGIN edit_atom_button_icon.html -->
<img src="icons/gnome/image/atom_edit.png" alt="Atom" />
<!-- END edit_atom_button_icon.html -->

__icons/gnome/image/atom_edit.png__
iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPBAMAAADJ+Ih5AAAAMFBMVEX////yZ2fh4eH5+fm+
v7/r6+u3SEjV1dWmenqzs7PPz8/Hx8elpaXGxsbMzMyUlJQfgNlcAAAAAXRSTlMAQObYZgAA
ABZ0RVh0U29mdHdhcmUAZ2lmMnBuZyAyLjQuNqQzgxcAAAB7SURBVHjaY2DAAEy3d++NelfM
wBBrwHzziuodA4ZFDOvKy51YFjBctdVqy7iieoCh6JGFYFqr7wQGewU1QbE8/g0MlUBGWpb9
BIbNSy3S0lqnTmC4YLeyLeNIwQGGCwxaV+ZMYjjA8IiB+c7PUJ0CBrvbWyZZvduKsBMAMi0q
dW1+s4IAAAAASUVORK5CYII=
__template/tt2/kwiki_begin.html__
<!-- BEGIN kwiki_begin.html -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <title>
[% IF hub.action == 'display' || 
      hub.action == 'edit' || 
      hub.action == 'revisions' 
%]
  [% hub.cgi.page_name %] -
[% END %]
[% IF hub.action != 'display' %]
  [% self.class_title %] - 
[% END %]
  [% site_title %]</title>
[% hub.load_class('atom').fill_links %]
[% FOR link = hub.links.all -%]
  <link rel="[% link.rel %]" type="[% link.type %]" href="[% link.href %]" />
[% END %]
[% FOR css_file = hub.css.files -%]
  <link rel="stylesheet" type="text/css" href="[% css_file %]" />
[% END -%]
[% FOR javascript_file = hub.javascript.files -%]
  <script type="text/javascript" src="[% javascript_file %]"></script>
[% END -%]
  <link rel="shortcut icon" href="" />
  <link rel="start" href="[% script_name %]" title="Home" />
</head>
<body>
<!-- END kwiki_begin.html -->
