The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Foorum::Controller::Forum;

use strict;
use warnings;
our $VERSION = '1.001000';
use parent 'Catalyst::Controller';
use Foorum::Utils qw/get_page_from_url/;
use Foorum::Formatter qw/filter_format/;

sub board : Path {
    my ( $self, $c ) = @_;

    my @forums = $c->model('DBIC')->resultset('Forum')->search(
        {   forum_type => 'classical',
            status     => { '!=', 'banned' },
        },
        { order_by => 'me.forum_id', }
    )->all;

    # get last_post and the author
    foreach (@forums) {
        next unless $_->last_post_id;
        $_->{last_post} = $c->model('DBIC::Topic')->get( $_->last_post_id );
        next unless $_->{last_post};
        $_->{last_post}->{updator} = $c->model('DBIC::User')
            ->get( { user_id => $_->{last_post}->{last_updator_id} } );
    }

    $c->cache_page('300');

    # get all moderators
    my @forum_ids;
    push @forum_ids, $_->forum_id foreach (@forums);
    if ( scalar @forum_ids ) {
        my $roles = $c->model('DBIC::UserForum')
            ->get_forum_moderators( \@forum_ids );
        $c->stash->{forum_roles} = $roles;
    }

    $c->stash->{whos_view_this_page} = 1;
    $c->stash->{forums}              = \@forums;
    $c->stash->{template}            = 'forum/board.html';
}

sub forum : PathPart('forum') Chained('/') CaptureArgs(1) {
    my ( $self, $c, $forum_code ) = @_;

    my $forum = $c->controller('Get')->forum( $c, $forum_code );
}

sub forum_list : Regex('^forum/(\w+)$') {
    my ( $self, $c ) = @_;

    my $is_elite = ( $c->req->path =~ /\/elite(\/|$)/ ) ? 1 : 0;
    my $page     = get_page_from_url( $c->req->path );
    my $rss      = ( $c->req->path =~ /\/rss(\/|$)/ ) ? 1 : 0;  # /forum/1/rss

    # get the forum information
    my $forum_code = $c->req->snippets->[0];
    my $forum      = $c->controller('Get')->forum( $c, $forum_code );
    my $forum_id   = $forum->{forum_id};
    $forum_code = $forum->{forum_code};

    my @extra_cols = ($is_elite) ? ( 'elite', 1 ) : ();
    my $rows
        = ($rss)
        ? 10
        : $c->config->{per_page}->{forum};    # 10 for RSS is enough
    my $it = $c->model('DBIC')->resultset('Topic')->search(
        {   forum_id    => $forum_id,
            'me.status' => { '!=', 'banned' },
            @extra_cols,
        },
        {   order_by => \'sticky DESC, last_update_date DESC',
            rows     => $rows,
            page     => $page,
            prefetch => [ 'author', 'last_updator' ],
        }
    );
    my @topics = $it->all;

    if ($rss) {
        foreach (@topics) {
            my $rs = $c->model('DBIC::Comment')->find(
                {   object_type => 'topic',
                    object_id   => $_->topic_id,
                },
                {   order_by => 'post_on',
                    rows     => 1,
                    page     => 1,
                    columns  => [ 'text', 'formatter' ],
                }
            );
            next unless ($rs);
            $_->{text} = $rs->text;

            # filter format by Foorum::Filter
            $_->{text} = $c->model('DBIC::FilterWord')
                ->convert_offensive_word( $_->{text} );
            $_->{text}
                = filter_format( $_->{text}, { format => $rs->formatter } );
        }
        $c->stash->{topics}   = \@topics;
        $c->stash->{template} = 'forum/forum.rss.html';
        $c->cache_page('600');
        return;
    }

    # above is for RSS, left is for HTML

    # get all moderators
    $c->stash->{forum_roles}
        = $c->model('DBIC::UserForum')->get_forum_moderators($forum_id);

    # for page 1 and normal mode
    if ( $page == 1 and not $is_elite ) {

        # for private forum
        if ( $forum->{policy} eq 'private' ) {
            my $pending_count = $c->model('DBIC::UserForum')->count(
                {   forum_id => $forum_id,
                    status   => 'pending',
                }
            );
            $c->stash( { pending_count => $pending_count, } );
        }

        # check announcement
        $c->stash->{announcement}
            = $c->model('DBIC::Forum')->get_announcement($forum);
    }

    $c->cache_page('300');

    if ( $c->user_exists ) {
        my @all_topic_ids = map { $_->topic_id } @topics;
        $c->stash->{is_visited}
            = $c->model('DBIC::Visit')
            ->is_visited( 'topic', \@all_topic_ids, $c->user->user_id )
            if ( scalar @all_topic_ids );
    }

    # Pager
    my $pager = $it->pager;

    # For Tabs
    $c->stash->{poll_count} = $c->model('DBIC')->resultset('Poll')->count(
        {   forum_id => $forum_id,
            duration => { '>', time() },
        }
    );

    # Forum Links
    my @links = $c->model('DBIC::ForumSettings')->get_forum_links($forum_id);
    $c->stash->{forum_links} = \@links;

    $c->stash->{whos_view_this_page} = 1;
    $c->stash->{pager}               = $pager;
    $c->stash->{topics}              = \@topics;
    $c->stash->{template}            = 'forum/forum.html';
}

sub join : Chained('forum') Arg(0) {
    my ( $self, $c ) = @_;

    return $c->res->redirect('/login') unless ( $c->user_exists );

    my $forum      = $c->stash->{forum};
    my $forum_id   = $forum->{forum_id};
    my $forum_code = $forum->{forum_code};

    if ( $forum->{policy} eq 'private' ) {

        # check if already requested
        if ( $c->req->method eq 'POST' ) {
            my $rs = $c->model('DBIC::UserForum')->search(
                {   user_id  => $c->user->user_id,
                    forum_id => $forum_id,
                },
                { columns => ['status'], }
            )->first;
            if ($rs) {
                if (   $rs->status eq 'user'
                    or $rs->status eq 'moderator'
                    or $rs->status eq 'admin' ) {
                    return $c->res->redirect( $forum->{forum_url} );
                } elsif ( $rs->status eq 'blocked'
                    or $rs->status eq 'pending'
                    or $rs->status eq 'rejected' ) {
                    my $status = uc( $rs->status );
                    $c->detach( '/print_error', ["ERROR_USER_$status"] );
                }
            } else {
                $c->model('DBIC::UserForum')->create_user_forum(
                    {   user_id  => $c->user->user_id,
                        forum_id => $forum_id,
                        status   => 'pending',
                    }
                );

                my $forum_admin = $c->model('DBIC::UserForum')
                    ->get_forum_admin($forum_id);
                my $requestor = $c->model('DBIC::User')
                    ->get( { user_id => $c->user->user_id } );

                my $forum;
                if (    $c->stash->{forum}
                    and $c->stash->{forum}->{forum_id} == $forum_id ) {
                    $forum = $c->stash->{forum};
                } else {
                    $forum = $c->model('DBIC::Forum')->get($forum_id);
                }

                # Send Notification Email
                $c->model('DBIC::ScheduledEmail')->create_email(
                    {   template => 'forum_pending_request',
                        to       => $forum_admin->{email},
                        lang     => $c->stash->{lang},
                        stash    => {
                            rept  => $forum_admin,
                            from  => $requestor,
                            forum => $forum,
                        }
                    }
                );

                $c->detach(
                    '/print_message',
                    [   'Successfully Requested. You need wait for admin\'s approval'
                    ]
                );
            }
        } else {
            $c->stash(
                {   simple_wrapper => 1,
                    template       => 'forum/join_us.html',
                }
            );
        }
    } else {
        $c->model('DBIC::UserForum')->create_user_forum(
            {   user_id  => $c->user->user_id,
                forum_id => $forum_id,
                status   => 'user',
            }
        );
        $c->res->redirect( $forum->{forum_url} );
    }
}

sub members : Chained('forum') Args {
    my ( $self, $c, $member_type ) = @_;

    my $forum      = $c->stash->{forum};
    my $forum_id   = $forum->{forum_id};
    my $forum_code = $forum->{forum_code};

    $member_type ||= 'user';
    if (    'pending' ne $member_type
        and 'blocked'  ne $member_type
        and 'rejected' ne $member_type ) {
        $member_type = 'user';
    }

    my $page = get_page_from_url( $c->req->path );

    my ( @query_cols, @attr_cols );
    if ( 'user' eq $member_type ) {
        @query_cols = ( 'status', [ 'admin', 'moderator', 'user' ] );
        @attr_cols = ( 'order_by' => 'status ASC' );
    } else {
        @query_cols = ( 'status', $member_type );
    }
    my $rs = $c->model('DBIC::UserForum')->search(
        { @query_cols, forum_id => $forum_id, },
        {   @attr_cols,
            rows => 20,
            page => $page,
        }
    );
    my @user_roles = $rs->all;
    my @all_user_ids = map { $_->user_id } @user_roles;

    my @members;
    my %members;
    if ( scalar @all_user_ids ) {
        @members = $c->model('DBIC::User')->search(
            { user_id => { 'IN', \@all_user_ids }, },
            {   columns => [
                    'user_id',  'username',
                    'nickname', 'gender',
                    'register_time'
                ],
            }
        )->all;
        %members = map { $_->user_id => $_ } @members;
    }

    $c->stash(
        {   template            => 'forum/members.html',
            member_type         => $member_type,
            pager               => $rs->pager,
            user_roles          => \@user_roles,
            whos_view_this_page => 1,
            members             => \%members,
        }
    );
}

sub action_log : Chained('forum') Args(0) {
    my ( $self, $c ) = @_;

    my $forum      = $c->stash->{forum};
    my $forum_id   = $forum->{forum_id};
    my $forum_code = $forum->{forum_code};

    my $page = get_page_from_url( $c->req->path );
    my $rs   = $c->model('DBIC')->resultset('LogAction')->search(
        { forum_id => $forum_id, },
        {   order_by => \'time DESC',
            page     => $page,
            rows     => 20,
        }
    );

    my @actions = $rs->all;

    my @all_user_ids;
    my %unique_user_ids;
    foreach (@actions) {
        next if ( $unique_user_ids{ $_->user_id } );
        push @all_user_ids, $_->user_id;
        $unique_user_ids{ $_->user_id } = 1;
    }
    if ( scalar @all_user_ids ) {
        my $authors
            = $c->model('DBIC::User')->get_multi( 'user_id', \@all_user_ids );
        foreach (@actions) {
            $_->{operator} = $authors->{ $_->user_id };
        }
    }

    $c->stash(
        {   template => 'forum/action_log.html',
            pager    => $rs->pager,
            logs     => \@actions,
        }
    );
}

sub create : Local {
    my ( $self, $c ) = @_;

    return $c->res->redirect('/login') unless ( $c->user_exists );

    my $is_admin = $c->model('Policy')->is_admin( $c, 'site' );

    # if function_on.create_forum is off, check is admin
    if ( not $c->config->{function_on}->{create_forum} and not $is_admin ) {
        $c->detach( '/print_error', ['ERROR_PERMISSION_DENIED'] );
    }

    $c->stash( { template => 'forum/create.html' } );

    return unless ( $c->req->method eq 'POST' );

    $c->form(
        name        => [ qw/NOT_BLANK/, [qw/LENGTH 1 40/] ],
        description => [ qw/NOT_BLANK/, [qw/LENGTH 1 200/] ],
    );
    return if ( $c->form->has_error );

    # check forum_code
    my $forum_code = $c->req->param('forum_code');
    my $err = $c->model('DBIC::Forum')->validate_forum_code($forum_code);
    if ($err) {
        $c->set_invalid_form( forum_code => $err );
        return;
    }

    my $name        = $c->req->param('name');
    my $description = $c->req->param('description');
    my $moderators  = $c->req->param('moderators');
    my $private     = $c->req->param('private');

    # validate the admin for roles.site.admin
    my $admin_user;
    if ($is_admin) {
        my $admin = $c->req->param('admin');
        $admin_user = $c->model('DBIC::User')->get( { username => $admin } );
        unless ($admin_user) {
            return $c->set_invalid_form( admin => 'ADMIN_NONEXISTENCE' );
        }
    } else {
        $admin_user = $c->user;
    }

    # validate the moderators
    my $total_members = 1;
    my @moderators = split( /\s*\,\s*/, $moderators );
    my @moderator_users;
    foreach (@moderators) {
        next if ( $_ eq $admin_user->{username} );    # avoid the same man
        last
            if ( scalar @moderator_users > 2 )
            ;    # only allow 3 moderators at most
        my $moderator_user
            = $c->model('DBIC::User')->get( { username => $_ } );
        unless ($moderator_user) {
            $c->stash->{non_existence_user} = $_;
            return $c->set_invalid_form( moderators => 'ADMIN_NONEXISTENCE' );
        }
        $total_members++;
        push @moderator_users, $moderator_user;
    }

    # insert data into table.
    my $policy = ( $private and $private == 1 ) ? 'private' : 'public';
    my $forum = $c->model('DBIC::Forum')->create(
        {   name          => $name,
            forum_code    => $forum_code,
            description   => $description,
            forum_type    => 'classical',
            policy        => $policy,
            total_members => $total_members,
        }
    );
    $c->model('DBIC::UserForum')->create_user_forum(
        {   user_id  => $admin_user->{user_id},
            status   => 'admin',
            forum_id => $forum->forum_id,
        }
    );
    foreach (@moderator_users) {
        $c->model('DBIC::UserForum')->create_user_forum(
            {   user_id  => $_->{user_id},
                status   => 'moderator',
                forum_id => $forum->forum_id,
            }
        );
    }

    # create time
    $c->model('DBIC')->resultset('ForumSettings')->create(
        {   forum_id => $forum->forum_id,
            type     => 'created_time',
            value    => time(),
        }
    );
    $c->model('DBIC')->resultset('ForumSettings')
        ->clear_cache( $forum->forum_id );

    $c->res->redirect("/forum/$forum_code");
}

sub about : Chained('forum') Args(0) {
    my ( $self, $c ) = @_;

    my $forum      = $c->stash->{forum};
    my $forum_id   = $forum->{forum_id};
    my $forum_code = $forum->{forum_code};

    # get all settings, so that we have created_time
    $c->stash->{settings} = $c->model('DBIC')->resultset('ForumSettings')
        ->get_all( $forum->{forum_id} );

    # get all moderators
    $c->stash->{forum_roles}
        = $c->model('DBIC::UserForum')->get_forum_moderators($forum_id);

    $c->stash->{template} = 'forum/about.html';
}

1;
__END__

=pod

=head1 AUTHOR

Fayland Lam <fayland at gmail.com>

=cut