/** * 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; }
/** * 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; }
public function importInfinityPostAttachments(Post $model, Board &$board, $post) { $post_id = $model->post_id; if (!$post_id) { return 0; } $attachments = @json_decode($post->files, true); if (!is_array($attachments)) { return 0; } $aModels = []; foreach ($attachments as $aIndex => $attachment) { if (isset($attachment['error']) && $attachment['error']) { continue; } if (!isset($attachment['file_path']) || !isset($attachment['thumb_path'])) { continue; } $storage = null; $path = "{$this->targetLocation}/{$attachment['file_path']}"; $thumb = "{$this->targetLocation}/{$attachment['thumb_path']}"; if (file_exists($path)) { if (!isset($attachment['type'])) { continue; } if (!isset($attachment['hash'])) { $attachment['hash'] = md5(file_get_contents($path)); } $storage = FileStorage::getHash($attachment['hash']); if (!$storage || (!file_exists($storage->getFullPath()) || !file_exists($storage->getFullPathThumb()))) { $height = null; $width = null; if (isset($attachment['width']) && isset($attachment['height'])) { $height = $attachment['height']; $width = $attachment['width']; } if (!$storage) { $storage = new FileStorage(['hash' => $attachment['hash'], 'banned' => false, 'filesize' => $attachment['size'], 'file_width' => $width, 'file_height' => $height, 'mime' => $attachment['type'], 'meta' => null, 'first_uploaded_at' => Carbon::now(), 'last_uploaded_at' => Carbon::now(), 'upload_count' => 1]); } Storage::makeDirectory($storage->getDirectory()); if (!file_exists($storage->getFullPath())) { if (is_link($storage->getFullPath())) { unlink($storage->getFullPath()); } symlink($path, $storage->getFullPath()); } if ($attachment['thumbwidth'] && file_exists($thumb)) { $storage->has_thumbnail = true; $storage->thumbnail_width = $attachment['thumbwidth']; $storage->thumbnail_height = $attachment['thumbheight']; Storage::makeDirectory($storage->getDirectoryThumb()); if (!file_exists($storage->getFullPathThumb())) { if (is_link($storage->getFullPathThumb())) { unlink($storage->getFullPathThumb()); } symlink($thumb, $storage->getFullPathThumb()); } } $storage->save(); } if ($storage && $storage->exists) { $aModel = ['post_id' => $post_id, 'file_id' => $storage->file_id, 'filename' => $attachment['filename'], 'is_spoiler' => false, 'is_deleted' => false, 'position' => $aIndex]; $aModels[] = $aModel; } else { ++$skips; } } } FileAttachment::insert($aModels); return count($aModels); }
/** * 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()); }
/** * 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)); } }
/** * Define your route model bindings, pattern filters, etc. * * @param \Illuminate\Routing\Router $router * @return void */ public function boot(Router $router) { // Sets up our routing tokens. $router->pattern('attachment', '[0-9]\\d*'); $router->pattern('board', Board::URI_PATTERN); $router->pattern('id', '[0-9]\\d*'); $router->model('attachment', '\\App\\FileAttachment'); $router->model('ban', '\\App\\Ban'); $router->model('board', '\\App\\Board'); $router->model('post', '\\App\\Post'); $router->model('report', '\\App\\Report'); $router->model('role', '\\App\\Role'); $router->bind('user', function ($value, $route) { if (is_numeric($value)) { return \App\User::find($value); } else { if (preg_match('/^[a-z0-9]{1,64}\\.(?P<id>\\d+)$/i', $value, $matches)) { return \App\User::find($matches['id']); } } }); $router->bind('board', function ($value, $route) { $board = Board::getBoardForRouter($this->app, $value); if ($board) { $board->applicationSingleton = true; return $board; } return abort(404); }); $router->bind('attachment', function ($value, $route) { return \App\FileAttachment::where('is_deleted', false)->with('storage')->find($value); }); $router->bind('role', function ($value, $route) { if (is_numeric($value)) { return \App\Role::find($value); } else { if (preg_match('/^[a-z0-9]{1,64}\\.(?P<id>\\d+)$/i', $value, $matches)) { return \App\Role::find($matches['id']); } } }); $router->bind('post_id', function ($value, $route) { $board = $route->getParameter('board'); if (!$board instanceof Board) { $board = $this->app->make("\\App\\Board"); } if (is_numeric($value) && $board instanceof Board) { return $board->getThreadByBoardId($value); } }); // Binds a matched instance of a {board} as a singleton instance. $router->matched(function ($route, $request) { $board = $route->getParameter('board'); if ($board instanceof Board && $board->exists) { // Binds the board to the application if it exists. $this->app->singleton("\\App\\Board", function ($app) use($board) { return $board; }); } // Binds the post to the application if it exists. $post = $route->getParameter('post_id'); if ($post instanceof Post && $post->exists) { $route->setParameter('post', $post); //$this->app->instance("\App\Post", $post); $this->app->singleton("\\App\\Post", function ($app) use($post) { return $post; }); } }); parent::boot($router); }