| 1 | package Vee::Controller::Forum::Create; |
|---|
| 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 | use DateTime; |
|---|
| 11 | |
|---|
| 12 | =head1 NAME |
|---|
| 13 | |
|---|
| 14 | Vee::Controller::Forum::Create - Forum posting Controller |
|---|
| 15 | |
|---|
| 16 | =head1 SYNOPSIS |
|---|
| 17 | |
|---|
| 18 | See L<Vee> |
|---|
| 19 | |
|---|
| 20 | =head1 DESCRIPTION |
|---|
| 21 | |
|---|
| 22 | Catalyst Controller for creating new posts or threads. |
|---|
| 23 | |
|---|
| 24 | =head1 METHODS |
|---|
| 25 | |
|---|
| 26 | =cut |
|---|
| 27 | |
|---|
| 28 | =head2 auto |
|---|
| 29 | |
|---|
| 30 | =cut |
|---|
| 31 | |
|---|
| 32 | sub auto : Private { |
|---|
| 33 | my ($self, $c) = @_; |
|---|
| 34 | |
|---|
| 35 | if (!$c->user) { |
|---|
| 36 | $c->vee_abort('You must be logged in to post.'); |
|---|
| 37 | } elsif ($c->req->method ne 'POST') { |
|---|
| 38 | $c->vee_abort('This page should only be called as a <code>POST</code>.'); |
|---|
| 39 | } |
|---|
| 40 | |
|---|
| 41 | return 1; |
|---|
| 42 | } |
|---|
| 43 | |
|---|
| 44 | =head2 post |
|---|
| 45 | |
|---|
| 46 | =cut |
|---|
| 47 | |
|---|
| 48 | sub post : Local : Args(0) { |
|---|
| 49 | my ($self, $c) = @_; |
|---|
| 50 | |
|---|
| 51 | # TODO: make these redir to an actual post page when one exists |
|---|
| 52 | $c->vee_abort('Please enter a message.') unless $c->req->params->{content} =~ /\S/; |
|---|
| 53 | $c->vee_abort('No thread id specified. This should not happen. Sorry?') unless $c->req->params->{id}; |
|---|
| 54 | |
|---|
| 55 | my $thread = $c->model('DBIC::Threads')->find( $c->req->params->{id}, { prefetch => 'forum' } ) |
|---|
| 56 | or $c->vee_abort('There is no thread with an id of ', $c->req->params->{id}, '. It may have been nuked while you were typing your reply.'); |
|---|
| 57 | |
|---|
| 58 | if (!can_post($c, $thread)) { |
|---|
| 59 | $c->vee_abort('You do not have permission to reply to this thread.'); |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | my ($parsed_message, @bbcode_errors) = Vee::BBCode::validate_bbcode( $c->req->params->{content} ); |
|---|
| 63 | if (@bbcode_errors) { |
|---|
| 64 | $c->vee_abort("Your post contains invalid bbcode. Please go back and fix it."); |
|---|
| 65 | } |
|---|
| 66 | $parsed_message = Vee::Utils::fix_newlines( $parsed_message ); |
|---|
| 67 | |
|---|
| 68 | if ('preview' eq lc $c->req->params->{submit}) { |
|---|
| 69 | $c->detach('post_preview', [ $thread ]); |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | # check for daabaru-post |
|---|
| 73 | # TODO: apply this to thread creation too? not as common.. and merge as well? |
|---|
| 74 | my $last_post = $c->model('DBIC::Posts')->search({ |
|---|
| 75 | thread_id => $thread->id, |
|---|
| 76 | time => { '>=', time - $c->site_opts->{post_automerge_time} }, |
|---|
| 77 | }, { |
|---|
| 78 | order_by => 'time DESC', |
|---|
| 79 | })->single; |
|---|
| 80 | |
|---|
| 81 | my $post; |
|---|
| 82 | # only do merging/prevention if the last post is this user's |
|---|
| 83 | if ($last_post and $last_post->user_id == $c->user->obj->id and $last_post->flags !~ /deleted/) { |
|---|
| 84 | if ($last_post->content eq $parsed_message) { |
|---|
| 85 | $c->vee_abort("You have already posted that message recently."); |
|---|
| 86 | } |
|---|
| 87 | |
|---|
| 88 | # TODO: what about formats/options of both posts? not a problem now, but later.. |
|---|
| 89 | # XXX LATIEDIT: The only flag, afaik, is "deleted", which is considered above. Formats, I have no idea ... |
|---|
| 90 | $post = $c->model('DBIC')->schema->txn_do( sub { |
|---|
| 91 | my $edit = $c->model('DBIC::Edits')->create({ |
|---|
| 92 | post_id => $last_post->id, |
|---|
| 93 | user_id => $c->user->obj->id, |
|---|
| 94 | time => time, |
|---|
| 95 | old_content => $last_post->content, |
|---|
| 96 | }); |
|---|
| 97 | $last_post->content( |
|---|
| 98 | $last_post->content . |
|---|
| 99 | "\n[hr][i]Automerged:[/i]\n" . |
|---|
| 100 | $parsed_message |
|---|
| 101 | ); |
|---|
| 102 | $last_post->last_edit_id( $edit->id ); |
|---|
| 103 | $last_post->time( time ); |
|---|
| 104 | $last_post->update; |
|---|
| 105 | |
|---|
| 106 | $thread->last_post_id( $last_post->id ); |
|---|
| 107 | $thread->last_post_time( time ); |
|---|
| 108 | $thread->update; |
|---|
| 109 | |
|---|
| 110 | return $last_post; |
|---|
| 111 | } ); |
|---|
| 112 | } else { |
|---|
| 113 | # create the post |
|---|
| 114 | $post = $c->model('DBIC')->schema->txn_do( sub { |
|---|
| 115 | my $post = $c->model('DBIC::Posts')->create({ |
|---|
| 116 | thread_id => $thread->id, |
|---|
| 117 | user_id => $c->user->obj->id, |
|---|
| 118 | time => time, |
|---|
| 119 | format => 'bbcode', |
|---|
| 120 | content => $parsed_message, |
|---|
| 121 | }); |
|---|
| 122 | # update thread's last-post stats |
|---|
| 123 | $thread->last_post_id( $post->id ); |
|---|
| 124 | $thread->last_post_time( time ); |
|---|
| 125 | $thread->post_count( $thread->post_count + 1 ); |
|---|
| 126 | $thread->update; |
|---|
| 127 | # update forum's last-post stats |
|---|
| 128 | $thread->forum->last_post_id( $post->id ); |
|---|
| 129 | $thread->forum->post_count( $thread->forum->post_count + 1 ); |
|---|
| 130 | $thread->forum->update; |
|---|
| 131 | # update user's postcount |
|---|
| 132 | $c->user->obj->post_count( $c->user->post_count + 1 ); |
|---|
| 133 | $c->user->obj->update; |
|---|
| 134 | return $post; |
|---|
| 135 | } ); |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | # finito! |
|---|
| 139 | # Latiedit: this should link to post/ urls. |
|---|
| 140 | $c->res->redirect( $c->uri('Forum', 'post', $post->id) ); |
|---|
| 141 | } |
|---|
| 142 | |
|---|
| 143 | =head2 post_preview |
|---|
| 144 | |
|---|
| 145 | =cut |
|---|
| 146 | |
|---|
| 147 | sub post_preview : Private { |
|---|
| 148 | my ($self, $c, $thread) = @_; |
|---|
| 149 | my $s = $c->stash; |
|---|
| 150 | |
|---|
| 151 | my ($parsed_message, @bbcode_errors) = Vee::BBCode::validate_bbcode( $c->req->params->{content} ); |
|---|
| 152 | if (@bbcode_errors) { |
|---|
| 153 | $c->vee_abort("Your post contains invalid bbcode. Please go back and fix it."); |
|---|
| 154 | } |
|---|
| 155 | $parsed_message = Vee::Utils::fix_newlines( $parsed_message ); |
|---|
| 156 | |
|---|
| 157 | my $rows = $c->site_opts->{page_sizes}{posts_preview}; |
|---|
| 158 | my $offset = $c->model('DBIC::Posts')->count({ thread_id => $thread->id }, { order_by => 'me.time ASC' } ); |
|---|
| 159 | $offset = $offset - $rows; |
|---|
| 160 | |
|---|
| 161 | my $posts_rs = $c->model('DBIC::Posts')->search({ thread_id => $thread->id }, { |
|---|
| 162 | order_by => 'me.time ASC', |
|---|
| 163 | offset => $offset, |
|---|
| 164 | rows => $rows |
|---|
| 165 | } ); |
|---|
| 166 | |
|---|
| 167 | # form generation stuff |
|---|
| 168 | my $reply_fields = { |
|---|
| 169 | content => { type => 'textarea', rows => '10', cols => '100' }, |
|---|
| 170 | id => { type => 'hidden' }, |
|---|
| 171 | }; |
|---|
| 172 | |
|---|
| 173 | my $form = new Vee::Form( |
|---|
| 174 | id => 'reply', |
|---|
| 175 | fields => $reply_fields, |
|---|
| 176 | params => $c->req->params, |
|---|
| 177 | copy_params => 1, |
|---|
| 178 | ); |
|---|
| 179 | $form->force( id => $c->req->params->{id} ); |
|---|
| 180 | |
|---|
| 181 | $s->{template} = 'forum/thread/preview.tt'; |
|---|
| 182 | $s->{extra_css} = [qw/forum bbcode/]; |
|---|
| 183 | $s->{posts_rs} = $posts_rs; |
|---|
| 184 | $s->{form} = $form; |
|---|
| 185 | $s->{tid} = $c->req->params->{id}; |
|---|
| 186 | $s->{text} = $parsed_message; |
|---|
| 187 | $s->{page_title} = "[PREVIEW] Reply to " . $thread->subject . " - Forums"; |
|---|
| 188 | $s->{page_header} = "Preview Reply to " . $thread->subject; |
|---|
| 189 | $s->{user} = $c->user->obj; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | =head2 thread |
|---|
| 193 | |
|---|
| 194 | =cut |
|---|
| 195 | |
|---|
| 196 | sub thread : Local : Args(0) { |
|---|
| 197 | my ($self, $c) = @_; |
|---|
| 198 | |
|---|
| 199 | $c->vee_abort('No forum id specified. This should not happen. Sorry?') unless $c->req->params->{id}; |
|---|
| 200 | |
|---|
| 201 | my $forum = $c->model('DBIC::Forums')->find( $c->req->params->{id} ) |
|---|
| 202 | or $c->vee_abort('There is no forum with an id of ', $c->req->params->{id}, '. It may have been deleted while you were typing?'); |
|---|
| 203 | |
|---|
| 204 | if (!can_thread($c, $forum)) { |
|---|
| 205 | $c->vee_abort('You do not have permission to create threads in this forum.'); |
|---|
| 206 | } |
|---|
| 207 | |
|---|
| 208 | # TODO: make these redir to an actual post page when one exists |
|---|
| 209 | $c->vee_abort("You must enter a message.") unless $c->req->params->{content} =~ /\S/; |
|---|
| 210 | $c->vee_abort("You must enter a subject.") unless $c->req->params->{subject} =~ /\S/; |
|---|
| 211 | |
|---|
| 212 | my ($parsed_message, @bbcode_errors) = Vee::BBCode::validate_bbcode( $c->req->params->{content} ); |
|---|
| 213 | if (@bbcode_errors) { |
|---|
| 214 | $c->detach('thread_preview'); |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | if ('preview' eq lc $c->req->params->{submit}) { |
|---|
| 218 | $c->detach('thread_preview'); |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | my $subject = $c->req->params->{subject}; |
|---|
| 222 | $subject =~ s/\x0d\x0a|\x0a|\x0d/ /g; |
|---|
| 223 | $subject = Vee::Utils::cleanse($subject); |
|---|
| 224 | my $blurb = $c->req->params->{blurb}; |
|---|
| 225 | $blurb =~ s/\x0d\x0a|\x0a|\x0d/ /g; |
|---|
| 226 | $blurb = Vee::Utils::cleanse($blurb); |
|---|
| 227 | |
|---|
| 228 | # create the thread |
|---|
| 229 | my $thread = $c->model('DBIC')->schema->txn_do( sub { |
|---|
| 230 | my $thread = $c->model('DBIC::Threads')->create({ |
|---|
| 231 | forum_id => $forum->id, |
|---|
| 232 | subject => $subject, |
|---|
| 233 | first_post_id => 0, |
|---|
| 234 | last_post_id => 0, |
|---|
| 235 | last_post_time => time, |
|---|
| 236 | post_count => 1, |
|---|
| 237 | blurb => $blurb, |
|---|
| 238 | }); |
|---|
| 239 | # create the post |
|---|
| 240 | my $post = $c->model('DBIC::Posts')->create({ |
|---|
| 241 | thread_id => $thread->id, |
|---|
| 242 | user_id => $c->user->obj->id, |
|---|
| 243 | time => time, |
|---|
| 244 | format => 'bbcode', |
|---|
| 245 | content => Vee::Utils::fix_newlines( $parsed_message ), |
|---|
| 246 | }); |
|---|
| 247 | # update thread's last-post stats |
|---|
| 248 | $thread->first_post_id( $post->id ); |
|---|
| 249 | $thread->last_post_id( $post->id ); |
|---|
| 250 | $thread->update; |
|---|
| 251 | # update forum's last-post stats |
|---|
| 252 | $forum->last_post_id( $post->id ); |
|---|
| 253 | $forum->post_count( $forum->post_count + 1 ); |
|---|
| 254 | $forum->thread_count( $forum->thread_count + 1 ); |
|---|
| 255 | $forum->update; |
|---|
| 256 | # update user's postcount |
|---|
| 257 | $c->user->obj->post_count( $c->user->post_count + 1 ); |
|---|
| 258 | $c->user->obj->update; |
|---|
| 259 | return $thread; |
|---|
| 260 | } ); |
|---|
| 261 | |
|---|
| 262 | # finito! |
|---|
| 263 | $c->res->redirect( $c->uri('Forum', 'thread', $thread->id) ); |
|---|
| 264 | } |
|---|
| 265 | |
|---|
| 266 | =head2 thread_preview |
|---|
| 267 | |
|---|
| 268 | =cut |
|---|
| 269 | |
|---|
| 270 | sub thread_preview : Private { |
|---|
| 271 | my ($self, $c) = @_; |
|---|
| 272 | my $s = $c->stash; |
|---|
| 273 | |
|---|
| 274 | # Set up some variables? |
|---|
| 275 | my $subject = $c->req->params->{subject}; |
|---|
| 276 | my $blurb = $c->req->params->{blurb}; |
|---|
| 277 | my $content = $c->req->params->{content}; |
|---|
| 278 | my $forum = $c->model('DBIC::Forums')->find( $c->req->params->{id} ) |
|---|
| 279 | or $c->vee_abort('There is no forum with an id of ', $c->req->params->{id}, '. It may have been deleted while you were typing?'); |
|---|
| 280 | |
|---|
| 281 | if (!can_thread($c, $forum)) { |
|---|
| 282 | $c->vee_abort('You do not have permission to create threads in this forum.'); |
|---|
| 283 | } |
|---|
| 284 | |
|---|
| 285 | my ($parsed_message, @bbcode_errors) = Vee::BBCode::validate_bbcode($content); |
|---|
| 286 | if (@bbcode_errors) { |
|---|
| 287 | $c->vee_abort("Your post contains invalid bbcode. Please fix it."); |
|---|
| 288 | } |
|---|
| 289 | |
|---|
| 290 | # form generation stuff |
|---|
| 291 | my $reply_fields = { |
|---|
| 292 | content => { type => 'textarea', rows => '10', cols => '100' }, |
|---|
| 293 | id => { type => 'hidden' }, |
|---|
| 294 | subject => { type => 'text', maxlength => 48 }, |
|---|
| 295 | blurb => { type => 'text', maxlength => 96 }, |
|---|
| 296 | }; |
|---|
| 297 | |
|---|
| 298 | my $form = new Vee::Form( |
|---|
| 299 | id => 'reply', |
|---|
| 300 | fields => $reply_fields, |
|---|
| 301 | params => $c->req->params, |
|---|
| 302 | copy_params => 1, |
|---|
| 303 | ); |
|---|
| 304 | $form->force( id => $c->req->params->{id} ); |
|---|
| 305 | |
|---|
| 306 | $s->{extra_css} = 'forum'; |
|---|
| 307 | $s->{form} = $form; |
|---|
| 308 | $s->{fid} = $c->req->params->{id}; |
|---|
| 309 | $s->{subject} = $subject; |
|---|
| 310 | $s->{text} = $parsed_message; |
|---|
| 311 | $s->{blurb} = $blurb; |
|---|
| 312 | $s->{page_title} = "[PREVIEW] $subject - Forums"; |
|---|
| 313 | $s->{page_header} = "Preview New Thread"; |
|---|
| 314 | $s->{template} = 'forum/preview.tt'; |
|---|
| 315 | $s->{user} = $c->user->obj; |
|---|
| 316 | } |
|---|
| 317 | |
|---|
| 318 | =head1 AUTHOR |
|---|
| 319 | |
|---|
| 320 | Maintainer: Alex "Eevee" Munroe (C<veekun@veekun.com>) |
|---|
| 321 | |
|---|
| 322 | See the included F<AUTHORS> file for a full list of contributers. |
|---|
| 323 | |
|---|
| 324 | =head1 LICENSE |
|---|
| 325 | |
|---|
| 326 | See the included F<LICENSE> file. |
|---|
| 327 | |
|---|
| 328 | =cut |
|---|
| 329 | |
|---|
| 330 | 1; |
|---|