# Copyright 2002-2008 Josh Clark and Global Moxie, LLC. This code cannot be
# redistributed without permission from globalmoxie.com.  For more
# information, consult your Big Medium license.
#
# $Id: Comment.pm 3236 2008-08-21 14:30:44Z josh $

package BigMed::App::Web::Comment;
use strict;
use warnings;
use utf8;
use Carp;
$Carp::Verbose = 1;

use base qw(BigMed::App::Web::Public);
use BigMed::Comment;
use BigMed::Site;
use BigMed::Content::Page;
use BigMed::NoBots (
    'antispam_referrer_match', 'confirm_captcha_response',
    'query_antispam_fields',   'antispam_display_info',
    'get_captcha_html',        'is_form_fresh',
);
use BigMed::PageUtils;
use BigMed::Filter;
use BigMed::PageAlert;
use BigMed::Search::Scheduler;

sub setup {
    my $app = shift;
    $app->start_mode('email-page');
    $app->run_modes(
        'AUTOLOAD' => sub { $_[0]->rm_submit() },
        'submit'   => 'rm_submit',
    );
    return;
}

sub rm_submit {
    my $app     = shift;
    my $site_id = $app->path_site;
    my ( $sec, $pid ) = $app->path_args;
    foreach ( $sec, $pid ) {
        ( $_ ||= q{} ) =~ s/\D+//msg;
    }

    #fetch site and page objects
    my ( $site, $page );
    if (   !$pid
        || !$site_id
        || !$sec
        || !( $site = BigMed::Site->fetch($site_id) )
        || !(
            $page =
            BigMed::Content::Page->fetch( { site => $site_id, id => $pid } )
        )
        || $page->pub_status ne 'published'
      )
    {
        $app->set_error(
            head => 'COMMENT_Could not determine page',
            text => 'COMMENT_TEXT_Could not determine page',
        );
        return $app->error_stop;
    }

    #coming from a valid-ish url?
    my $page_url = $page->page_url( $site, { section => $sec } );
    my $refer_ok = $app->antispam_referrer_match($page)
      || $app->_referrer_commentcgi_match;
    return $app->return_to_page($page_url)
      if !$app->require_post || !$refer_ok;

    #are comments active?
    BigMed::Comment->register_comment_prefs;
    my %flag = $page->flags;
    return $app->return_to_page($page_url)
      if !$site->get_pref_value('html_comments_enabled')
      || $flag{html_dcomments};

    #gather field names (if coming from cgi, could be resubmit request,
    #let it through with short min_seconds time)
    my $min_seconds = $refer_ok eq 'cgi' ? 2 : undef;
    my $rfield = $app->query_antispam_fields(
        $page,
        {   fields      => [qw(id name email url comment)],
            min_seconds => $min_seconds,
        }
      )
      or return $app->return_to_page($page_url);
    my %fieldname = %{$rfield};

    #confirm id match, and gather field
    return $app->return_to_page($page_url)
      if $app->utf8_param( $fieldname{id} ) != $pid;
    my %field = $app->parse_submission(
        {   id       => $fieldname{name},
            parse_as => 'simple_text',
            required => 1,
        },
        {   id       => $fieldname{email},
            parse_as => 'email',
            required => 1,
        },
        {   id       => $fieldname{url},
            parse_as => 'url',
        },
        {   id       => $fieldname{comment},
            parse_as => 'rich_text_public',
            required => 1,
        },
    );

    #check fields and show field if there's a hitch
    my $caught_error = !$app->confirm_captcha_response( $site, $page )
      || $field{_ERROR};
    if ( !$app->is_form_fresh($page) ) {    #form is older than 30 minutes
        $caught_error = 1;
        $app->set_error(
            head => 'NOBOTS_Timeout',
            text => 'NOBOTS_TEXT_Timeout',
        );
    }
    if ($caught_error) {                    #give the form again
        return $app->show_form_again(
            site       => $site,
            sec_id     => $sec,
            page       => $page,
            fieldnames => \%fieldname,
            field_msg  => $field{_ERROR},
        );
    }

    #truncate some fields
    my $url = $field{ $fieldname{url} };
    undef $url
      if $url !~ m{^https?://}ms
      || length $url > 255;
    my $commenter = $field{ $fieldname{name} };
    $commenter = substr( $commenter, 0, 47 ) . '...'
      if length $commenter > 50;
    my $content = $field{ $fieldname{comment} };
    $content = substr( $content, 0, 1024 * 32 ) . '[...]'
      if length $content > 1024 * 32;

    #create the object
    my $time_obj = $app->bigmed->time_obj;
    my $comment  = BigMed::Comment->new();
    $comment->set_page($pid);
    $comment->set_site($site_id);
    $comment->set_commenter($commenter);
    $comment->set_email( $field{ $fieldname{email} } );
    $comment->set_url($url);
    $comment->set_content($content);
    $comment->set_post_time( $time_obj->bigmed_time );
    $comment->set_ip( $ENV{REMOTE_ADDR} );
    $comment->set_status('ok');

    #if preview, show it with the form and stop.
    #have to request submit buttons separately from other fields,
    #because they're submitted only when clicked; checker would call it a bot
    my $rpreview = $app->query_antispam_fields(
        $page,
        {   fields      => ['preview'],
            min_seconds => $min_seconds,
        }
    );
    my $pname = $rpreview && $rpreview->{preview};
    if ( $app->utf8_param($pname) ) {
        my $preview =
          $app->html_template( 'wi_comments_preview.tmpl',
            %{ $comment->prep_tmpl_params($site) } );
        return $app->show_form_again(
            site       => $site,
            sec_id     => $sec,
            page       => $page,
            fieldnames => \%fieldname,
            preview    => $preview,
        );
    }

    $comment->call_trigger( 'comment_submitted', $site );

    #discard duplicates posted within last minute
    $time_obj->shift_time('-1M');
    my $dupes = BigMed::Comment->select(
        {   site      => $site_id,
            page      => $pid,
            post_time => { from => $time_obj->bigmed_time },
            commenter => $comment->commenter,
            email     => $comment->email
        }
      )
      or return $app->error_stop;
    my $d;
    my $c_url = $comment->url || q{};
    while ( $d = $dupes->next ) {
        my $d_url = $d->url || q{};
        next
          if $d->content ne $comment->content
          || $d_url     ne $c_url;
        return $app->return_to_fresh_page( $page_url, 'bmc' . $d->id );
    }
    
    #check moderation pref and hold if necessary
    $comment->set_status('mod')
      if $comment->status eq 'ok'
      && $site->get_pref_value('html_comments_mod');

    #proceed with the save
    $comment->save or return $app->error_stop;

    #moderated or spam? show message and stop
    if ( $comment->status ne 'ok' ) {
        BigMed::PageAlert->notify('comment_mod',$page,$site,$comment)
          if $comment->status eq 'mod';
    
        my $mod_text =
          BigMed::Filter->filter(
            $site->get_pref_value('html_comments_modtext') );
        $mod_text .= qq{<p><a href="$page_url">$page_url</a></p>};
        return $app->html_template_screen(
            'screen_public_msg.tmpl',
            bmcp_title => $site->get_pref_value('html_comments_modtitle'),
            message    => $mod_text,
        );
    }

    #comment is ok; build comments for page and send back
    $comment->build_page_comments( $site, $page ) or return $app->error_stop;
    BigMed::PageAlert->notify('comment_new',$page,$site,$comment);
    schedule_index($site, $page) or return $app->error_stop;
    return $app->return_to_fresh_page( $page_url, 'bmc' . $comment->id );
}

sub return_to_page {
    my ( $app, $url ) = @_;
    $app->header_type('redirect');
    $app->header_props( -url => $url );
    return "Redirecting to $url";
}

sub return_to_fresh_page {
    my ( $app, $url, $anchor ) = @_;
    $anchor = $anchor ? "#$anchor" : q{};
    my $rand = int( rand(99999999) );
    return $app->return_to_page("$url?$rand$anchor");
}

sub show_form_again {
    my $app    = shift;
    my %option = @_;
    my ( $site, $sec_id, $page, $roldfield, $preview ) =
      @option{qw(site sec_id page fieldnames preview)};
    my %oldfield = %{$roldfield};
    my %lookup   = reverse %oldfield;

    my %display_info = $app->antispam_display_info(
        $page,
        [qw(id preview name email url comment submit)],
        { no_include => 1 }
    );
    my %realname = %{ $display_info{realname} };
    my %fakename = %{ $display_info{fakename} };

    #revise field_msg errors and query values for new field names
    my %err;
    if ( $option{field_msg} ) {
        my %msg = %{ $option{field_msg} };
        foreach my $f ( keys %msg ) {
            next if !$lookup{$f};
            $err{ $realname{ $lookup{$f} } } = $msg{$f};
        }
    }
    
    #reset both query param and utf8_param values
    my $q = $app->query;
    $app->clear_utf8_param( $display_info{tstamp} );
    foreach my $f (qw(id name email url comment)) {
        $app->clear_utf8_param( $realname{$f} );
        $q->param( $realname{$f}, $q->param( $oldfield{$f} ) );
    }

    #make the fields
    BigMed::Comment->register_comment_prefs;
    my %type = (
        name    => 'simple_text',
        email   => 'email',
        url     => 'url',
        comment => 'raw_text',
    );
    my @fields = (
        $app->prompt_field_ref(
            id        => $display_info{tstamp},
            value     => $display_info{local_time},
            prompt_as => 'hidden',
        ),
        $app->prompt_field_ref(
            id        => $realname{id},
            value     => $page->id,
            prompt_as => 'hidden',
        ),
    );
    my $comment_desc =
        '<a href="http://daringfireball.net/projects/markdown/basics" '
      . 'class="preview">'
      . $site->get_pref_value('html_comments_format') . '</a>';
    foreach my $f (qw(name email url comment)) {
        push @fields,
          ( $app->prompt_field_ref(
                id        => $realname{$f},
                prompt_as => $type{$f},
                required  => $f ne 'url',
            ),
            $app->prompt_field_ref(
                id        => $fakename{$f},
                prompt_as => $type{$f},
                hidden    => 1,
            ),
          );

        #add labels after (prompt_field_ref runs them through language)
        $fields[-2]->[2]->{label} = $site->get_pref_value("html_comments_$f");
        $fields[-2]->[2]->{description} = $comment_desc if $f eq 'comment';
        $fields[-1]->[2]->{label}       = 'Leave blank';
    }

    my @fieldsets = (
        $app->prompt_fieldset_ref(
            fields    => \@fields,
            query     => $q,
            field_msg => \%err,
            post_html => $app->get_captcha_html($site),
        ),
    );

    my ( $title, $message ) = $app->title_and_message(
        title     => $option{title},
        message   => $option{message},
        field_msg => \%err,
    );
    $title
      ||= $preview ? $site->get_pref_value('html_comments_preview') : q{};
    my $form_url = $app->build_url(
        script => 'bm-comment.cgi',
        rm     => 'submit',
        site   => $site->id,
        args   => [$sec_id, $page->id],
    );
    my $self_url =
      $page->page_url( $site,
        { section => $site->section_obj_by_id($sec_id) } );
    my $html = $app->html_template_screen(
        'screen_public_commentform.tmpl',
        bmcp_title      => $title,
        message         => $message,
        fieldsets       => \@fieldsets,
        form_url        => $form_url,
        self_url        => $self_url,
        submitfield     => $realname{submit},
        submit          => $site->get_pref_value('html_comments_submit'),
        previewfield    => $realname{preview},
        preview         => $site->get_pref_value('html_comments_preview'),
        comment_preview => $preview,

    );

    #handle the challenge question if appropriate
    return BigMed::PageUtils::replace_all_includes( $site, $html );
}

sub _referrer_commentcgi_match {
    my $app = shift;
    return 1 if !$ENV{HTTP_REFERER};
    my $url = $app->query->url( -path_info => 1 );
    return $ENV{HTTP_REFERER} =~ m{\Q$url\E}ms ? 'cgi' : 0;
}

1;

__END__

