| 1 | package Vee::Controller::Forum::Post; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use warnings; |
|---|
| 5 | use base 'Catalyst::Controller'; |
|---|
| 6 | use Vee::BBCode; |
|---|
| 7 | use Vee::Utils; |
|---|
| 8 | use Vee::Utils::Forum; |
|---|
| 9 | |
|---|
| 10 | =head1 NAME |
|---|
| 11 | |
|---|
| 12 | Vee::Controller::Forum::Post - Post utilities Controller |
|---|
| 13 | |
|---|
| 14 | =head1 SYNOPSIS |
|---|
| 15 | |
|---|
| 16 | See L<Vee> |
|---|
| 17 | |
|---|
| 18 | =head1 DESCRIPTION |
|---|
| 19 | |
|---|
| 20 | Catalyst 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 | |
|---|
| 31 | sub 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 | |
|---|
| 43 | Code for post utilities (deleting, nuking, undeleting) |
|---|
| 44 | |
|---|
| 45 | This code will probably undergo many changes. I am trying to optimize |
|---|
| 46 | it as much as possible to take up very few lines. Any assistance in |
|---|
| 47 | doing so is greatly appreciated. |
|---|
| 48 | |
|---|
| 49 | =cut |
|---|
| 50 | |
|---|
| 51 | sub 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 | |
|---|
| 121 | Code for listing edits to a post. |
|---|
| 122 | |
|---|
| 123 | =cut |
|---|
| 124 | |
|---|
| 125 | sub 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 | |
|---|
| 140 | Code for editing a post. |
|---|
| 141 | |
|---|
| 142 | =cut |
|---|
| 143 | |
|---|
| 144 | my $reply_fields = { |
|---|
| 145 | content => { type => 'textarea', rows => '10', cols => '100' }, |
|---|
| 146 | id => { type => 'hidden' }, |
|---|
| 147 | }; |
|---|
| 148 | |
|---|
| 149 | sub 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 | |
|---|
| 206 | Maintainer: Alex "Eevee" Munroe (C<veekun@veekun.com>) |
|---|
| 207 | |
|---|
| 208 | See the included F<AUTHORS> file for a full list of contributers. |
|---|
| 209 | |
|---|
| 210 | =head1 LICENSE |
|---|
| 211 | |
|---|
| 212 | See the included F<LICENSE> file. |
|---|
| 213 | |
|---|
| 214 | =cut |
|---|
| 215 | |
|---|
| 216 | 1; |
|---|