root/veekun/trunk/lib/Vee/Controller/Forum/Post.pm @ 406

Revision 406, 8.5 KB (checked in by eevee, 22 months ago)

Database refactoring. Renamed columns and tables to be more consistent and more readable. (#58)

Line 
1package Vee::Controller::Forum::Post;
2
3use strict;
4use warnings;
5use base 'Catalyst::Controller';
6use Vee::BBCode;
7use Vee::Utils;
8use Vee::Utils::Forum;
9
10=head1 NAME
11
12Vee::Controller::Forum::Post - Post utilities Controller
13
14=head1 SYNOPSIS
15
16See L<Vee>
17
18=head1 DESCRIPTION
19
20Catalyst Controller for various post utilities (editing, deleting, nuking), and post flags.
21
22=head1 METHODS
23
24=cut
25
26
27=head2 auto
28
29=cut
30
31sub auto : Private {
32    my ($self, $c) = @_;
33   
34    if (!$c->user) {
35        $c->vee_abort('You must be logged in to perform any of these actions.');
36    }
37   
38    return 1;
39}
40
41=head2 utils
42
43Code for post utilities (deleting, nuking, undeleting)
44
45This code will probably undergo many changes. I am trying to optimize
46it as much as possible to take up very few lines. Any assistance in
47doing so is greatly appreciated.
48
49=cut
50
51sub utils : LocalRegex('^(\d*)/(delete|undelete|nuke)') : Args(0) {
52    my ( $self, $c ) = @_;
53    my ($post_id, $method) = @{ $c->req->captures };
54   
55    my $forum_id = undef;
56    # This is the post to be deleted, as well as the entire result set of things to be modified
57    my $post = $c->model('DBIC::Posts')->find({ 'me.id' => $post_id }, { prefetch => ['user', { 'thread' => 'forum' }], })
58        or $c->vee_abort("There is no post with id of ", $post_id, ". Perhaps another admin nuked it?");
59    $c->vee_abort("The post with id of ", $post_id, " has already been ", $method, "d.")
60        if (($post->flags =~ /deleted/ && $method eq 'delete') || ($post->flags !~ /deleted/ && $method eq 'undelete'));
61    $c->vee_abort("You don't have permission to ", $method, " this post.")
62        unless (($c->user->obj->id == $post->user_id && $method ne 'nuke') || $c->can_i("post$method"));
63
64    # Do everything in a transaction for rollback purposes
65    my $action = $c->model('DBIC')->schema->txn_do( sub {
66        my ($thread, $forum, $user) = ($post->thread, $post->thread->forum, $post->user);
67        # If any of the post (or thread) counts are 0 or less, this will break. Of course, if that's the case, something is broken
68        return undef unless (($forum->post_count >= 0 || $forum->thread_count >= 0 || $thread->post_count >= 0 || $user->post_count >= 0) || ($method eq 'undelete'));
69        my $thread_postct = $c->model('DBIC::Posts')->count({ 'me.thread_id' => $post->thread_id, 'me.flags' => { '!=', 'deleted' } });
70        # Unfortunately, this must be done here, since this needs to include deleted posts as well
71        my ($last_post_id, $last_post_time) = ((map {($_->id)} $c->model('DBIC::Posts')->search({ 'thread.forum_id' => $forum->id, 'me.thread_id' => $post->thread_id }, { prefetch => 'thread', columns => [qw/me.id me.thread_id thread.forum_id/], order_by => 'me.time DESC' })), undef);
72
73        if ($method eq 'nuke') { $c->model('DBIC::Posts')->search({ 'me.id' => $post_id })->delete; }
74        else { toggleflag( $post, 'deleted' ) or return undef; } 
75
76        # If there's only one post left, delete the thread as well
77        if ($thread_postct > 1 && $last_post_id == $post->id) {
78            ($last_post_time, $last_post_id) = map {($_->time, $_->id)} $c->model('DBIC::Posts')->search({ 'thread.forum_id' => $forum->id, 'me.thread_id' => $post->thread_id, 'me.flags' => { '!=', 'deleted' } }, { prefetch => 'thread', rows => 1, order_by => 'me.time DESC' });
79        } elsif ($thread_postct <= 1) {
80            #try moving this elsewhere later, i hate it here
81            if ($method eq 'nuke') { $c->model('DBIC::Threads')->search({ 'me.id' => $post->thread_id })->delete; }
82            elsif (($method eq 'undelete' && $thread_postct == 0) || ($method eq 'delete' && $thread_postct <= 1)) {
83                toggleflag( $thread, 'deleted' ) or return undef;
84            }
85            ($last_post_time, $last_post_id) = map {($_->time, $_->id)} $c->model('DBIC::Posts')->search({ 'thread.forum_id' => $forum->id, 'thread.flags' => { '!=', 'deleted' }, 'me.flags' => { '!=', 'deleted' } }, { prefetch => 'thread', rows => 1, order_by => 'me.time DESC' });
86            $forum_id = $forum->id;
87            $last_post_id = 0 unless $last_post_id;
88        }
89           
90        if (($method eq 'nuke' && $post->flags !~ /deleted/) || ($method eq 'delete')) { 
91            $thread->post_count($thread->post_count - 1);
92            $forum->post_count($forum->post_count - 1);
93            $forum->thread_count($forum->thread_count - 1) unless ($thread_postct > 1);
94            $user->post_count($user->post_count - 1);
95        } elsif ($method eq 'undelete') {
96            $thread->post_count($thread->post_count + 1);
97            $forum->post_count($forum->post_count + 1);
98            $forum->thread_count($forum->thread_count + 1) unless ($thread_postct > 1);
99            $user->post_count($user->post_count + 1);
100        }
101        # Only update last post and time if this is actually the last post
102        if ($last_post_time && $post->id == $thread->last_post_id) {
103            $thread->last_post_id($last_post_id);
104            $thread->last_post_time($last_post_time);
105        }
106        # Needs to be done here, in case the deleted post is the last post in a forum :(
107        $forum->last_post_id($last_post_id) if $post->id == $forum->last_post_id;
108        $thread->update if ($method ne 'nuke');
109        $forum->update;
110        $user->update;
111   
112        return $post;
113    });
114
115    $c->error("This error message should never be displayed unless someone is screwing with things; sorry.") unless $action;
116    $c->res->redirect("/forum/" . (($forum_id) ? "$forum_id" : "post/$post_id"));
117}
118
119=head2 edits
120
121Code for listing edits to a post.
122
123=cut
124
125sub edits : LocalRegex('^(\d*)/edits') {
126    my ($self, $c) = @_;
127    my ($s, $post_id) = ($c->stash, $c->req->captures->[0]);
128   
129    $c->vee_abort("You don't have permission to view post edits.") unless ($c->can_i('post_edits'));
130
131    my $post   = $c->model('DBIC::Posts')->find({ 'me.id' => $post_id}, { prefetch => [qw/user edits/, { thread => 'forum' }], order_by => 'edits.time DESC' });
132   
133    $s->{post}      = $post;
134    $s->{extra_css} = ['forum', 'bbcode'];
135    $s->{template}  = 'forum/thread/edits.tt';
136}
137
138=head2 edit
139
140Code for editing a post.
141
142=cut
143
144my $reply_fields = {
145    content => { type => 'textarea', rows => '10', cols => '100' },
146    id => { type => 'hidden' },
147};
148
149sub edit : LocalRegex('^(\d*)/edit') : Args(0) {
150    my ($self, $c) = @_;
151    my ($s, $post_id) = ($c->stash, $c->req->captures->[0]);
152   
153    my $post_rs = $c->model('DBIC::Posts')->search({ 'me.id' => $post_id }, { prefetch => 'user' })->single;
154    $c->vee_abort("You must be logged in to edit posts.") unless $c->user;
155    $c->vee_abort("You don't have permission to edit posts.") unless ($c->can_i('post_edit') || $c->user->obj->id == $post_rs->user_id);
156   
157    # Form submited stuff
158    if ('post' eq lc $c->req->params->{submit}) {
159        my ($parsed_message, @bbcode_errors) = Vee::BBCode::validate_bbcode( $c->req->params->{content} );
160        if (@bbcode_errors) {
161            $c->vee_abort("Your post contains invalid bbcode.  Please go back and fix it.");
162        }
163        $parsed_message = Vee::Utils::fix_newlines( $parsed_message );
164       
165        # If post wasn't edited, don't do anything further; results in redirect only
166        unless ($parsed_message eq $post_rs->content) {
167            my $post = $c->model('DBIC')->schema->txn_do( sub {
168                my $edit = $c->model('DBIC::Edits')->create({
169                    post_id     => $post_rs->id,
170                    user_id     => $c->user->obj->id,
171                    time        => time,
172                    old_content => $post_rs->content,
173                });
174               
175                $post_rs->last_edit_id( $edit->id );
176                $post_rs->content($parsed_message);
177                $post_rs->update;
178               
179                return $post_rs;
180            });
181        }
182        $c->res->redirect( $c->uri('Forum', 'post', $post_rs->id) );
183    }
184   
185    my $form = new Vee::Form(
186        id => 'reply',
187        fields => $reply_fields,
188        params => $c->req->params,
189        copy_params => 1,
190    );
191    $form->force( id => $post_rs->thread_id );
192    $form->force( content => $c->req->params->{content} || $post_rs->content || '' );
193   
194    $s->{page_header} = "Edit Post";
195    $s->{page_title}  = "Edit";
196    $s->{post}        = $post_rs;
197    $s->{user}        = $post_rs->user;
198    $s->{form}        = $form;
199    $s->{extra_css}   = [qw/forum bbcode/];
200    $s->{template}    = 'forum/thread/edit.tt';
201}
202
203
204=head1 AUTHOR
205
206Maintainer: Alex "Eevee" Munroe (C<veekun@veekun.com>)
207
208See the included F<AUTHORS> file for a full list of contributers.
209
210=head1 LICENSE
211
212See the included F<LICENSE> file.
213
214=cut
215
2161;
Note: See TracBrowser for help on using the browser.