Foorum::ResultSet::Comment - Foorum Comment System


Foorum documentation Contained in the Foorum distribution.

Index


Code Index:

NAME

Top

Foorum::ResultSet::Comment - Foorum Comment System

SYNOPSIS

Top

        # get comments
        my ($view_mode)  = ( $c->req->path =~ /\/view_mode=(thread|flat)(\/|$)/ );
        my ($comment_id) = ( $c->req->path =~ /\/comment_id=(\d+)(\/|$)/ );
        ( $c->stash->{comments}, $c->stash->{comments_pager} )
            = $c->model('DBIC::Comment')->get_comments_by_object(
            {   object_type => 'topic',
                object_id   => $topic_id,
                page        => $page,
                view_mode   => $view_mode,
                comment_id  => $comment_id,
            }
            );

FUNCTIONS

Top

get_comments_by_object

Usually it's used in Topic or User page, to show the comments up. opts:

    object_type => 'topic',     # or 'user_profile'
    object_type => $topic_id,   # or $user_id,
    page        => $page,       # show which page
    rows        => 20,          # optional, default as c.config.per_page.topic || 10;
    view_mode   => 'flat',      # flat or thread
    comment_id  => $comment_id, # for URL like /topic/$topic_id/comment_id=12/
                                # go comment_id=12's page

get_children_comments

That's mainly for thread mode. when comment_id=2's reply_to=1, that means comment_id=2 is the child of comment_id=1. meanwhile comment_id=3's reply_to=2, when we get children of comment_id=1, that's included too.

For get_comment_by_object and remove_children using.

get_all_comments_by_object($object_type, $object_id)

just get the @comments from table. no other action. with upload and text filtered.

get($comment_id, $attrs)

get one comment. attrs:

    with_text => 1, # get the $comment->{text} filtered.

remove_by_object($object_type, $object_id)

remove all comments belong to one certain object.

remove_children($comment)

check the CONCEPT above.

remove_one_item($comment)

remove one comment with upload and others.

AUTHOR

Top

Fayland Lam <fayland at gmail.com>


Foorum documentation Contained in the Foorum distribution.

package Foorum::ResultSet::Comment;

use strict;
use warnings;
our $VERSION = '1.001000';
use base 'DBIx::Class::ResultSet';

use Foorum::Utils qw/get_page_from_url encodeHTML/;
use Foorum::Formatter qw/filter_format/;
use Data::Page;
use List::MoreUtils qw/uniq first_index part/;
use List::Util qw/first/;
use Scalar::Util qw/blessed/;

sub get_comments_by_object {
    my ( $self, $info ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();
    my $config = $schema->config();

    my $object_type = $info->{object_type};
    my $object_id   = $info->{object_id};
    my $page        = $info->{page} || 1;
    my $rows        = $info->{rows} || $config->{per_page}->{topic} || 10;
    my $selected_comment_id = $info->{comment_id};

    # 'thread' or 'flat'
    my $view_mode = $info->{view_mode};

    #$view_mode ||= ($object_type eq 'topic') ? 'thread' : 'flat';
    $view_mode ||= 'thread';    # XXX? Temp

    my @comments
        = $self->get_all_comments_by_object( $object_type, $object_id );

    # we return the top_comment_id for "Reply Topic"
    my $top_comment_id = 0;
    $top_comment_id = $comments[0]->{comment_id} if ( $comments[0] );

    my $pager = Data::Page->new();
    $pager->current_page($page);
    $pager->entries_per_page($rows);

    if ( 'flat' eq $view_mode ) {

        # when url contains /comment_id=$comment_id/
        # we need show that page including $comment_id
        if ( scalar @comments > $rows and $selected_comment_id ) {
            my $first_index
                = first_index { $_->{comment_id} == $selected_comment_id }
            @comments;
            $page = int( $first_index / $rows ) + 1 if ($first_index);
            $pager->current_page($page);
        }
        $pager->total_entries( scalar @comments );
        if ( 'user_profile' eq $object_type ) {
            @comments = reverse(@comments);
        }
        @comments = splice( @comments, ( $page - 1 ) * $rows, $rows );
    } else {    # thread mode
                # top_comments: the top level comments
        my ( @top_comments, @result_comments );

        # for topic. reply_to == 0 means the topic comments
        #            reply_to == topic.comments[0].comment_id means top level.
        if ( 'topic' eq $object_type ) {
            ( my $top_comments ) = part {
                (          $_->{reply_to} == 0
                        or $_->{reply_to} == $comments[0]->{comment_id}
                    )
                    ? 0
                    : 1;
            }
            @comments;
            @top_comments = @$top_comments;
        } else {
            ( my $top_comments ) = part {
                $_->{reply_to} == 0 ? 0 : 1;
            }
            @comments;
            $top_comments ||= [];
            @top_comments = @$top_comments;
        }

        # when url contains /comment_id=$comment_id/
        # we need show that page including $comment_id
        if ( scalar @top_comments > $rows
            and $selected_comment_id ) {

            # need to find out the top comment's comment_id
            my $top_comment
                = first { $_->{comment_id} == $selected_comment_id }
            @comments;
            while (1) {
                my $reply_to = $top_comment->{reply_to};
                $selected_comment_id = $top_comment->{comment_id};
                last if ( $reply_to == 0 );
                last
                    if ( 'topic' eq $object_type
                    and $reply_to == $comments[0]->{comment_id} );
                $top_comment
                    = first { $_->{comment_id} == $reply_to } @comments;
            }
            my $first_index
                = first_index { $_->{comment_id} == $selected_comment_id }
            @top_comments;
            $page = int( $first_index / $rows ) + 1 if ($first_index);
            $pager->current_page($page);
        }

        # paged by top_comments
        $pager->total_entries( scalar @top_comments );
        if ( 'user_profile' eq $object_type ) {
            @top_comments = reverse(@top_comments);
        }
        @top_comments = splice( @top_comments, ( $page - 1 ) * $rows, $rows );

        foreach (@top_comments) {
            $_->{level} = 0;
            push @result_comments, $_;
            next
                if ( 'topic' eq $object_type
                and $_->{comment_id} == $comments[0]->{comment_id} );

            # get children, 10 lines below
            $self->get_children_comments( $_->{comment_id}, 1, \@comments,
                \@result_comments );
        }
        @comments = @result_comments;
    }

    @comments = $self->prepare_comments_for_view(@comments);

    return ( \@comments, $pager, $top_comment_id );
}

sub get_children_comments {
    my ( $self, $reply_to, $level, $comments, $result_comments ) = @_;

    my ( $tmp_comments, $left_comments ) = part {
        $_->{reply_to} == $reply_to ? 0 : 1;
    }
    @$comments;
    return unless ($tmp_comments);

    foreach (@$tmp_comments) {
        $_->{level} = $level;
        push @$result_comments, $_;
        $self->get_children_comments(
            $_->{comment_id}, $level + 1,
            $left_comments,   $result_comments
        );
    }
}

sub get_all_comments_by_object {
    my ( $self, $object_type, $object_id ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();

    my $cache_key   = "comment|object_type=$object_type|object_id=$object_id";
    my $cache_value = $cache->get($cache_key);

    my @comments;
    if ($cache_value) {
        @comments = @{ $cache_value->{comments} };
    } else {
        my $it = $self->search(
            {   object_type => $object_type,
                object_id   => $object_id,
            },
            {   order_by => 'post_on',
                prefetch => ['upload'],
            }
        );

        while ( my $rec = $it->next ) {
            my $upload = ( $rec->upload ) ? $rec->upload : undef;
            $rec = $rec->{_column_data};    # for cache using
            $rec->{upload} = $upload->{_column_data} if ($upload);

            # filter format by Foorum::Filter
            $rec->{title} = $schema->resultset('FilterWord')
                ->convert_offensive_word( $rec->{title} );
            $rec->{text} = $schema->resultset('FilterWord')
                ->convert_offensive_word( $rec->{text} );
            $rec->{text} = filter_format( $rec->{text},
                { format => $rec->{formatter} } );

            push @comments, $rec;
        }
        $cache_value = { comments => \@comments };
        $cache->set( $cache_key, $cache_value, 3600 );    # 1 hour
    }

    return wantarray ? @comments : \@comments;
}

# add author and others
sub prepare_comments_for_view {
    my ( $self, @comments ) = @_;

    my $schema = $self->result_source->schema;

    my @all_user_ids;
    foreach (@comments) {
        push @all_user_ids, $_->{author_id};
    }
    if ( scalar @all_user_ids ) {
        @all_user_ids = uniq @all_user_ids;
        my $authors = $schema->resultset('User')
            ->get_multi( 'user_id', \@all_user_ids );
        foreach (@comments) {
            $_->{author} = $authors->{ $_->{author_id} };
        }
    }

    return wantarray ? @comments : \@comments;
}

sub get {
    my ( $self, $comment_id, $attrs ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();

    my $comment = $self->find( { comment_id => $comment_id, } );
    return unless ($comment);

    $comment = $comment->{_column_data};
    if ( $attrs->{with_text} ) {

        # filter format by Foorum::Filter
        $comment->{text} = $schema->resultset('FilterWord')
            ->convert_offensive_word( $comment->{text} );
        $comment->{text} = filter_format( $comment->{text},
            { format => $comment->{formatter} } );
    }

    return $comment;
}

sub create_comment {
    my ( $self, $info ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();

    my $object_type = $info->{object_type};
    my $object_id   = $info->{object_id};
    my $user_id     = $info->{user_id};
    my $post_ip     = $info->{post_ip};
    my $forum_id    = $info->{forum_id} || 0;
    my $reply_to    = $info->{reply_to} || 0;
    my $formatter   = $info->{formatter} || 'ubb';
    my $title       = $info->{title};
    my $text        = $info->{text} || '';
    my $lang        = $info->{lang} || 'en';

  # we don't use [% | html %] now because there is too many title around in TT
    $title = encodeHTML($title);

    my $comment = $self->create(
        {   object_type => $object_type,
            object_id   => $object_id,
            author_id   => $user_id,
            title       => $title,
            text        => $text,
            formatter   => $formatter,
            post_on     => time(),
            post_ip     => $post_ip,
            reply_to    => $reply_to,
            forum_id    => $forum_id,
            upload_id   => $info->{upload_id} || 0,
        }
    );

    my $cache_key = "comment|object_type=$object_type|object_id=$object_id";
    $cache->remove($cache_key);

    # update user stat
    my $user = $schema->resultset('User')->get( { user_id => $user_id } );
    $schema->resultset('User')->update_user(
        $user,
        {   replies => \'replies + 1',
            point   => \'point + 1',
        }
    );

    # Email Sent
    if ( 'user_profile' eq $object_type ) {
        my $rept
            = $schema->resultset('User')->get( { user_id => $object_id } );
        my $from = $schema->resultset('User')->get( { user_id => $user_id } );

        # Send Notification Email
        $schema->resultset('ScheduledEmail')->create_email(
            {   template => 'new_comment',
                to       => $rept->{email},
                lang     => $lang,
                stash    => {
                    rept    => $rept,
                    from    => $from,
                    comment => $comment,
                }
            }
        );
    } else {

        # Send Update Notification for Starred Item
        my $client = $schema->theschwartz();
        $client->insert( 'Foorum::TheSchwartz::Worker::SendStarredNofication',
            [ $object_type, $object_id, $user_id ] );
    }

    return $comment;
}

sub remove_by_object {
    my ( $self, $object_type, $object_id ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();

    my $comment_rs = $self->search(
        {   object_type => $object_type,
            object_id   => $object_id,
        }
    );
    my $delete_counts = 0;
    while ( my $comment = $comment_rs->next ) {
        $self->remove_one_item($comment);
        $delete_counts++;
    }

    my $cache_key = "comment|object_type=$object_type|object_id=$object_id";
    $cache->remove($cache_key);

    return $delete_counts;
}

sub remove_children {
    my ( $self, $comment ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();

    if ( blessed $comment ) {
        $comment = $comment->{_column_data};
    }

    my $comment_id  = $comment->{comment_id};
    my $object_type = $comment->{object_type};
    my $object_id   = $comment->{object_id};

    my @comments
        = $self->get_all_comments_by_object( $object_type, $object_id );
    my @result_comments;
    $self->get_children_comments( $comment_id, 1, \@comments,
        \@result_comments );

    my $delete_counts = 1;
    $self->remove_one_item($comment);
    foreach (@result_comments) {
        $self->remove_one_item($_);
        $delete_counts++;
    }

    my $cache_key = "comment|object_type=$object_type|object_id=$object_id";
    $cache->remove($cache_key);

    return $delete_counts;
}

sub remove_one_item {
    my ( $self, $comment ) = @_;

    my $schema = $self->result_source->schema;
    my $cache  = $schema->cache();

    if ( blessed $comment ) {
        $comment = $comment->{_column_data};
    }

    if ( $comment->{upload_id} ) {
        $schema->resultset('Upload')
            ->remove_file_by_upload_id( $comment->{upload_id} );
    }
    $self->search( { comment_id => $comment->{comment_id} } )->delete;

    my $user = $schema->resultset('User')
        ->get( { user_id => $comment->{user_id} } );
    $schema->resultset('User')->update_user(
        $user,
        {   replies => \'replies - 1',
            point   => \'point - 1',
        }
    );

    return 1;
}

sub validate_params {
    my ( $self, $params ) = @_;

    my $schema = $self->result_source->schema;

    my $title = $params->{'title'};
    my $text  = $params->{'text'};
    unless ( $title and length($title) < 80 ) {
        return 'ERROR_TITLE_LENGTH';
    } else {
        my $bad_word = $schema->resultset('FilterWord')->has_bad_word($title);
        if ( '0' ne $bad_word ) {
            return "BAD_TITLE_$bad_word";
        }
    }
    unless ( length($text) ) {
        return 'ERROR_TEXT_REQUIRED';
    } else {
        my $bad_word = $schema->resultset('FilterWord')->has_bad_word($text);
        if ( '0' ne $bad_word ) {
            return "BAD_TEXT_$bad_word";
        }
    }

    return 0;
}

1;
__END__