/** * Display existing assets. * * @return Response */ public function putAssets(Request $request, Board $board) { if (!$board->canEditConfig($this->user)) { return abort(403); } $input = Input::all(); $validator = Validator::make($input, ['asset_type' => ["required", "in:board_banner,file_deleted,file_none,file_spoiler"], 'new_board_banner' => ["required_if:asset_type,board_banner", "image", "image_size:<=300,<=100"]]); if (!$validator->passes()) { return redirect()->back()->withErrors($validator->errors()); } // Fetch the asset. $upload = Input::file("new_{$input['asset_type']}"); if (file_exists($upload->getPathname())) { $storage = FileStorage::storeUpload($upload); $asset = new BoardAsset(); $asset->asset_type = "board_banner"; $asset->board_uri = $board->board_uri; $asset->file_id = $storage->file_id; $asset->save(); } return $this->view(static::VIEW_CONFIG, ['board' => $board, 'banners' => $board->getBanners(), 'tab' => "assets"]); }
/** * Imports board attachments. * * @return void */ public function importInfinityBoardAssets() { $this->info("\tImporting board assets ..."); BoardAsset::whereDoesntHave('flagPosts')->forceDelete(); Board::orderBy('board_uri', 'asc')->chunk(1, function ($boards) { foreach ($boards as $board) { $this->line("\t\tImporting assets from /{$board->board_uri}/"); $flagsMade = 0; $bannersMade = 0; # FLAGS $flagsPath = "{$this->targetLocation}/static/custom-flags/{$board->board_uri}/"; $flagSerPath = "{$this->targetLocation}/{$board->board_uri}/flags.ser"; $flags = []; if (file_exists($flagSerPath)) { try { $flags = @unserialize(@file_get_contents("{$this->targetLocation}/{$board->board_uri}/flags.ser")); } catch (\Exception $e) { $this->warn("\t\t\tFailed to unserialize flags.ser"); } if (is_array($flags) && count($flags)) { foreach ($flags as $flagFile => $flagName) { $flag = new File("{$flagsPath}{$flagFile}.png", false); if ($flag->isReadable()) { $storage = FileStorage::storeUpload($flag); $asset = $board->assets()->create(['file_id' => $storage->file_id, 'asset_type' => "board_flags", 'asset_name' => $flagName]); ++$flagsMade; } } } } # BANNERS $bannersPath = "{$this->targetLocation}/static/banners/{$board->board_uri}/"; if (is_readable($bannersPath)) { $banners = array_filter(scandir($bannersPath), function ($item) use($bannersPath) { return !is_dir("{$bannersPath}{$item}"); }); foreach ($banners as $bannerName) { $banner = new File("{$bannersPath}{$bannerName}", false); if ($banner->isReadable() && !!FileFacade::get($banner)) { $storage = FileStorage::storeUpload($banner); $asset = $board->assets()->create(['file_id' => $storage->file_id, 'asset_type' => "board_banner", 'asset_name' => null]); ++$bannersMade; } } } $this->line("\t\tImported {$flagsMade} flags and {$bannersMade} banners."); } }); }
/** * Uploads a single file. * * @param Request $request * @param Board $board * @return json */ public function putFile(Request $request, Board $board) { $input = Input::all(); $rules = []; PostRequest::rulesForFiles($board, $rules); $rules['files'][] = "required"; $validator = Validator::make($input, $rules); if (!$validator->passes()) { return json_encode(['errors' => $validator->errors()]); } $storage = new Collection(); foreach ($input['files'] as $file) { $newStorage = FileStorage::storeUpload($file); $storage[$newStorage->hash] = $newStorage; } return $storage; }
/** * Add new assets. * * @return Response */ public function putAssets(Request $request, Board $board) { if (!$board->canEditConfig($this->user)) { return abort(403); } if (!!Input::get('delete', false)) { return $this->deleteAssets($request, $board); } $input = Input::all(); $assetType = Input::get('asset_type', false); $validator = Validator::make($input, ['asset_type' => ["required", "in:board_banner,board_banned,board_icon,file_deleted,file_spoiler"], 'new_board_banned' => ["required_if:asset_type,board_banned", "image", "image_size:100-500", "max:250"], 'new_board_banner' => ["required_if:asset_type,board_banner", "image", "image_size:<=300,<=100", "max:1024"], 'new_board_icon' => ["required_if:asset_type,board_icon", "image", "image_aspect:1", "image_size:64,64", "max:50"], 'new_file_deleted' => ["required_if:asset_type,file_deleted", "image", "image_size:100-500", "max:250"], 'new_file_spoiler' => ["required_if:asset_type,file_spoiler", "image", "image_size:100-500", "max:250"]]); if (!$validator->passes()) { return redirect()->back()->withErrors($validator->errors()); } // Fetch the asset. $upload = Input::file("new_{$input['asset_type']}"); $multiples = $assetType == "board_banner" || $assetType == "board_banned"; if (file_exists($upload->getPathname())) { $storage = FileStorage::storeUpload($upload); if ($storage->exists) { if (!$multiples) { $assets = $board->assets()->with('storage')->where('asset_type', $input['asset_type'])->get(); foreach ($assets as $asset) { $asset->delete(); $asset->storage->challengeExistence(); } } $asset = new BoardAsset(); $asset->asset_type = $input['asset_type']; $asset->board_uri = $board->board_uri; $asset->file_id = $storage->file_id; $asset->save(); } else { return redirect()->back()->withErrors(["validation.custom.file_generic"]); } } Event::fire(new BoardWasModified($board)); return $this->getAssets($board); }
/** * Uploads a single file. * * @param Request $request * @param Board $board * @return json */ public function putFile(Request $request, Board $board) { $input = Input::all(); $rules = []; PostRequest::rulesForFiles($board, $rules); $rules['files'][] = "required"; $validator = Validator::make($input, $rules); if (!$validator->passes()) { return json_encode(['errors' => $validator->errors()]); } $storage = new Collection(); foreach ($input['files'] as $file) { $ip = new IP($request->ip()); $uploadSize = (int) Cache::get("upstream_data_for_" . $ip->toLong(), 0); if ($uploadSize <= 52430000) { Cache::increment("upstream_data_for_" . $ip->toLong(), $file->getSize(), 2); $newStorage = FileStorage::storeUpload($file); $storage[$newStorage->hash] = $newStorage; Cache::decrement("upstream_data_for_" . $ip->toLong(), $file->getSize()); } else { return abort(429); } } return $storage; }
/** * 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)); } }
/** * Add new assets. * * @return Response */ public function putAssets(Request $request, Board $board) { if (!$board->canEditConfig($this->user)) { return abort(403); } if (!!Input::get('delete', false)) { return $this->deleteAssets($request, $board); } $input = Input::all(); $assetType = Input::get('asset_type', false); $validator = Validator::make($input, ['asset_type' => ["required", "in:board_banner,board_banned,board_icon,board_flags,file_deleted,file_spoiler"], 'new_board_banned' => ["required_if:asset_type,board_banned", "image", "image_size:100-500", "max:250"], 'new_board_banner' => ["required_if:asset_type,board_banner", "image", "image_size:<=300,<=100", "max:1024"], 'new_board_flags' => ["required_if:asset_type,board_flags", "array", "min:1", "max:500"], 'new_board_icon' => ["required_if:asset_type,board_icon", "image", "image_aspect:1", "image_size:64,64", "max:50"], 'new_file_deleted' => ["required_if:asset_type,file_deleted", "image", "image_size:100-500", "max:250"], 'new_file_spoiler' => ["required_if:asset_type,file_spoiler", "image", "image_size:100-500", "max:250"]]); if (!$validator->passes()) { return redirect()->back()->withErrors($validator->errors()); } // Fetch the asset. $multiples = $assetType == "board_banner" || $assetType == "board_banned" || $assetType == "board_flags"; if ($assetType == "board_flags") { $new = $input["new_{$input['asset_type']}"]; $names = isset($new['name']) ? $new['name'] : []; $uploads = isset($new['file']) ? $new['file'] : []; $rules = []; $nameRules = ["required", "string", "between:1,128"]; $imageRules = array_merge(["required"], BoardAsset::getRulesForFlags($board)); foreach (range(0, count($uploads) - 1) as $index) { $rules["name.{$index}"] = $nameRules; $rules["file.{$index}"] = $imageRules; } $validator = Validator::make($new, $rules); if (!$validator->passes()) { return redirect()->back()->withErrors($validator->errors()); } } else { $uploads = [Input::file("new_{$input['asset_type']}")]; } foreach ((array) $uploads as $index => $upload) { if (file_exists($upload->getPathname())) { $storage = FileStorage::storeUpload($upload); if ($storage->exists) { if (!$multiples) { $assets = $board->assets()->with('storage')->where('asset_type', $input['asset_type'])->get(); foreach ($assets as $asset) { $asset->delete(); $asset->storage->challengeExistence(); } } $asset = new BoardAsset(); $asset->asset_type = $input['asset_type']; $asset->asset_name = isset($names[$index]) ? $names[$index] : null; $asset->board_uri = $board->board_uri; $asset->file_id = $storage->file_id; $asset->save(); } else { return redirect()->back()->withErrors(["validation.custom.file_generic"]); } } } Event::fire(new BoardWasModified($board)); return $this->getAssets($board); }