# Copyright 2001, 2002 Benjamin Trott. This code cannot be redistributed without
# permission from www.movabletype.org.
#
# $Id: Comments.pm,v 1.13 2002/09/30 01:21:59 btrott Exp $

package MT::App::Comments;
use strict;

use MT::Comment;
use MT::Util qw( remove_html );
use MT::App;
@MT::App::Comments::ISA = qw( MT::App );

sub init {
    my $app = shift;
    $app->SUPER::init(@_) or return;
    $app->add_methods(
        preview => \&preview,
        post => \&post,
        view => \&view,
    );
    $app->{default_mode} = 'view';
    my $q = $app->{query};

    ## We don't really have a __mode parameter, because we have to
    ## use named submit buttons for Preview and Post. So we hack it.
    if ($q->param('post')) {
        $q->param('__mode', 'post');
    } elsif ($q->param('preview')) {
        $q->param('__mode', 'preview');
    }
    $app;
}

sub post {
    my $app = shift;
    my $q = $app->{query};
    if (my $state = $q->param('comment_state')) {
        require MT::Serialize;
        my $ser = MT::Serialize->new($app->{cfg}->Serializer);
        $state = $ser->unserialize(pack 'H*', $state);
        $state = $$state;
        for my $f (keys %$state) {
            $q->param($f, $state->{$f});
        }
    }
    require MT::Entry;
    my $entry = MT::Entry->load(scalar $q->param('entry_id'))
        or return $app->error($app->translate(
            "Invalid entry ID '[_1]'.", scalar $q->param('entry_id')));
    require MT::IPBanList;
    my $iter = MT::IPBanList->load_iter({ blog_id => $entry->blog_id });
    my $user_ip = $app->remote_ip;
    while (my $ban = $iter->()) {
        my $banned_ip = $ban->ip;
        if ($user_ip =~ /$banned_ip/) {
            return $app->handle_error($app->translate(
                "You are not allowed to post comments."));
        }
    }
    require MT::Blog;
    my $blog = MT::Blog->load($entry->blog_id);
    if (!$blog->allow_anon_comments &&
        (!$q->param('author') || !$q->param('email'))) {
        return $app->handle_error($app->translate(
            "Name and email address are required."));
    }
    if (!$q->param('text')) {
        return $app->handle_error($app->translate("Comment text is required."));
    }
    my $comment = MT::Comment->new;
    $comment->ip($app->remote_ip);
    $comment->blog_id($entry->blog_id);
    $comment->entry_id($q->param('entry_id'));
    $comment->author(remove_html(scalar $q->param('author')));
    my $email = $q->param('email') || '';
    if ($email) {
        require MT::Util;
        if (my $fixed = MT::Util::is_valid_email($email)) {
            $email = $fixed;
        } else {
            return $app->handle_error($app->translate(
                "Invalid email address '[_1]'", $email));
        }
    }
    $comment->email(remove_html($email));
    my $url = $q->param('url') || '';
    if ($url) {
        require MT::Util;
        if (my $fixed = MT::Util::is_valid_url($url)) {
            $url = $fixed;
        } else {
            return $app->handle_error($app->translate(
                "Invalid URL '[_1]'", $url));
        }
    }
    $comment->url(remove_html($url));
    $comment->text($q->param('text'));
    $comment->save;
    $app->rebuild_indexes( Blog => $blog )
        or return $app->error($app->translate(
            "Rebuild failed: [_1]", $app->errstr));
    $app->rebuild_entry( Entry => $entry )
        or return $app->error($app->translate(
            "Rebuild failed: [_1]", $app->errstr));
    my $link_url;
    if (!$q->param('static')) {
        my $url = $app->base . $app->uri;
        $url .= '?entry_id=' . $q->param('entry_id');
        $link_url = $url;
    } else {
        my $static = $q->param('static');
        if ($static == 1) {
            my $url = $blog->archive_url;
            $url .= '/' unless $url =~ m!/$!;
            $url .= $entry->archive_file;
            $link_url = $url;
        } else {
            $link_url = $static . '#' . $comment->id;
        }
    }
    if ($blog->email_new_comments) {
        require MT::Mail;
        my $author = $entry->author;
        if ($author->email) {
            my %head = ( To => $author->email,
                         From => $comment->email || $author->email,
                         Subject =>
                             '[' . $blog->name . '] New Comment Posted to \'' .
                             $entry->title . '\'' );
            MT::Mail->send(\%head, <<BODY);
A new comment has been posted on your blog @{[ $blog->name ]}, on
entry #@{[ $entry->id ]} (@{[ $entry->title ]}).

$link_url

IP Address: @{[ $comment->ip ]}
Name: @{[ $comment->author ]}
Email Address: @{[ $comment->email ]}
URL: @{[ $comment->url ]}

Comments:

@{[ $comment->text ]}
BODY
        }
    }
    return $app->redirect($link_url);
}

sub preview { do_preview($_[0], $_[0]->{query}) }

sub view {
    my $app = shift;
    my $q = $app->{query};
    require MT::Template;
    require MT::Template::Context;
    require MT::Entry;
    my $entry_id = $q->param('entry_id');
    my $entry = MT::Entry->load($entry_id)
        or return $app->error($app->translate(
            "Invalid entry ID '[_1]'", $entry_id));
    my $ctx = MT::Template::Context->new;
    $ctx->stash('entry', $entry);
    $ctx->{current_timestamp} = $entry->created_on;
    my $tmpl = MT::Template->load({ type => 'comments',
                                    blog_id => $entry->blog_id })
        or return $app->error($app->translate(
            "You must define a Comment Listing template in order to " .
            "display dynamic comments."));
    my $html = $tmpl->build($ctx);
    $html = MT::Util::encode_html($tmpl->errstr) unless defined $html;
    $html;
}

sub handle_error {
    my $app = shift;
    my($err) = @_;
    my $html = do_preview($app, $app->{query}, $err);
    $html;
}

sub do_preview {
    my($app, $q, $err) = @_;
    require MT::Template;
    require MT::Template::Context;
    require MT::Entry;
    require MT::Util;
    require MT::Comment;
    my $entry_id = $q->param('entry_id');
    my $entry = MT::Entry->load($entry_id);
    my $ctx = MT::Template::Context->new;
    my $comment = MT::Comment->new;
    $comment->blog_id($entry->blog_id);
    for my $f (qw( entry_id author email url text )) {
        $comment->$f(scalar $q->param($f));
    }
    $comment->ip($app->remote_ip);
    ## Set timestamp as we would usually do in ObjectDriver.
    my @ts = MT::Util::offset_time_list(time, $entry->blog_id);
    my $ts = sprintf "%04d%02d%02d%02d%02d%02d",
        $ts[5]+1900, $ts[4]+1, @ts[3,2,1,0];
    $comment->created_on($ts);
    $ctx->stash('comment_preview', $comment);
    unless ($err) {
        ## Serialize comment state, then hex-encode it.
        require MT::Serialize;
        my $ser = MT::Serialize->new($app->{cfg}->Serializer);
        my $state = $comment->column_values;
        $state->{static} = $q->param('static');
        $ctx->stash('comment_state', unpack 'H*', $ser->serialize(\$state));
    }
    $ctx->stash('comment_is_static', $q->param('static'));
    $ctx->stash('entry', $entry);
    $ctx->{current_timestamp} = $ts;
    my($tmpl);
    if ($err) {
        $ctx->stash('error_message', $err);
        $tmpl = MT::Template->load({ type => 'comment_error',
                                     blog_id => $entry->blog_id })
        or return $app->error($app->translate(
            "You must define a Comment Error template."));
    } else {
        $tmpl = MT::Template->load({ type => 'comment_preview',
                                     blog_id => $entry->blog_id })
        or return $app->error($app->translate(
            "You must define a Comment Preview template."));
    }
    my $html = $tmpl->build($ctx);
    $html = $tmpl->errstr unless defined $html;
    $html;
}

1;
