/** * Creates a new FileAttachment for a post using a hash. * * @param Post $post * @param string $filename * @param boolean $spoiler * @return FileAttachment */ public function createAttachmentWithThis(Post $post, $filename, $spoiler = false, $autosave = true) { $fileName = pathinfo($filename, PATHINFO_FILENAME); $fileExt = $this->guessExtension(); $attachment = new FileAttachment(); $attachment->post_id = $post->post_id; $attachment->file_id = $this->file_id; $attachment->filename = urlencode("{$fileName}.{$fileExt}"); $attachment->is_spoiler = !!$spoiler; if ($autosave) { $attachment->save(); $this->upload_count++; $this->save(); } return $attachment; }
/** * Delete a post's attachment. * * @param \App\FileAttachment $attachment * @return Response */ public function postSpoilerAttachment(Board $board, FileAttachment $attachment) { if (!$attachment->exists) { return abort(404); } $input = Input::all(); $validator = Validator::make($input, ['scope' => "required|string|in:other,self", 'confirm' => "boolean|required_if:scope,other", 'password' => "string|required_if:scope,self"]); if (!$validator->passes()) { return redirect()->back()->withInput($input)->withErrors($validator->errors()); } if ($input['scope'] == "other") { if ($this->user->canDeleteGlobally() || $this->user->canDeleteLocally($board)) { $this->log(!$attachment->is_spoiler ? 'log.attachment.spoiler' : 'log.attachment.unspoiler', $attachment->post, ["board_uri" => $attachment->post->board_uri, "board_id" => $attachment->post->board_id, "post_id" => $attachment->post->post_id, "file" => $attachment->storage->hash]); } else { abort(403); } } else { if ($input['scope'] == "self") { if ($this->user->canDeletePostWithPassword($board)) { if (!$attachment->post->checkPassword($input['password'])) { return redirect()->back()->withInput($input)->withErrors(['password' => \Lang::trans('validation.password', ['attribute' => "password"])]); } } } } $attachment->is_spoiler = !$attachment->is_spoiler; $attachment->save(); Event::fire(new AttachmentWasModified($attachment)); return redirect($attachment->post->getURL()); }
/** * Creates a new FileAttachment for a post. * * @param UploadedFile $file * @param Post $post * @return FileAttachment */ public static function createAttachment(UploadedFile $file, Post $post) { $storage = static::storeUpload($file); $uploadName = urlencode($file->getClientOriginalName()); $uploadExt = pathinfo($uploadName, PATHINFO_EXTENSION); $fileName = basename($uploadName, "." . $uploadExt); $fileExt = $storage->guessExtension(); $attachment = new FileAttachment(); $attachment->post_id = $post->post_id; $attachment->file_id = $storage->file_id; $attachment->filename = urlencode("{$fileName}.{$fileExt}"); $attachment->save(); return $attachment; }
/** * Pushes the post to the specified board, as a new thread or as a reply. * This autoatically handles concurrency issues. Creating a new reply without * using this method is forbidden by the `creating` event in ::boot. * * * @param App\Board &$board * @param App\Post &$thread * @return void */ public function submitTo(Board &$board, &$thread = null) { $this->board_uri = $board->board_uri; $this->author_ip = Request::getClientIp(); $this->reply_last = $this->freshTimestamp(); $this->bumped_last = $this->reply_last; $this->setCreatedAt($this->reply_last); $this->setUpdatedAt($this->reply_last); if (!is_null($thread) && !$thread instanceof Post) { $thread = $board->getLocalThread($thread); $this->reply_to = $thread->post_id; $this->reply_to_board_id = $thread->board_id; } // Store attachments $uploads = []; if (is_array($files = Input::file('files'))) { $uploads = array_filter($files); } // Store the post in the database. DB::transaction(function () use($thread) { // The objective of this transaction is to prevent concurrency issues in the database // on the unique joint index [`board_uri`,`board_id`] which is generated procedurally // alongside the primary autoincrement column `post_id`. // First instruction is to add +1 to posts_total. DB::table('boards')->where('board_uri', $this->board_uri)->increment('posts_total'); // Second, we record this value and lock the table. $boards = DB::table('boards')->where('board_uri', $this->board_uri)->lockForUpdate()->select('posts_total')->get(); $posts_total = $boards[0]->posts_total; // Optionally, the OP of this thread needs a +1 to reply count. if ($thread instanceof Post) { if (!$this->isBumpless() && !$thread->isBumplocked()) { $thread->bumped_last = $this->created_at; } $thread->reply_last = $this->created_at; $thread->reply_count += 1; $thread->save(); } // Finally, we set our board_id and save. $this->board_id = $posts_total; $this->save(); // Queries and locks are handled automatically after this closure ends. }); // Process uploads. if (count($uploads) > 0) { foreach ($uploads as $uploadIndex => $upload) { if (file_exists($upload->getPathname())) { $uploadName = urlencode($upload->getClientOriginalName()); $uploadExt = pathinfo($uploadName, PATHINFO_EXTENSION); $fileName = basename($uploadName, "." . $uploadExt); $fileExt = $upload->guessExtension(); $storage = FileStorage::storeUpload($upload); $attachment = new FileAttachment(); $attachment->post_id = $this->post_id; $attachment->file_id = $storage->file_id; $attachment->filename = urlencode("{$fileName}.{$fileExt}"); $attachment->save(); } } } // Finally fire event on OP, if it exists. if ($thread instanceof Post) { Event::fire(new ThreadNewReply($thread)); } }