public function getBoardHistory(Board $board, Post $post) { $posts = $board->posts()->with('op')->withEverything()->where('author_ip', $post->author_ip)->orderBy('post_id', 'desc')->paginate(15); foreach ($posts as $item) { $item->setRelation('board', $board); } return $this->view(static::VIEW_HISTORY, ['posts' => $posts, 'multiboard' => false]); }
/** * Returns a post to the client. * * @var Board $board * @var Post $post * @return Response */ public function getPost(Board $board, Post $post) { // Pull the post. $post = $board->posts()->where('board_id', $post)->withEverything()->firstOrFail(); if (!$post) { return abort(404); } return $post; }
public function getBoardHistory(Board $board, Post $post) { if (!$this->user->canViewHistory($post)) { return abort(403); } $posts = $board->posts()->with('op')->withEverything()->where('author_ip', $post->author_ip)->orderBy('post_id', 'desc')->paginate(15); foreach ($posts as $item) { $item->setRelation('board', $board); } return $this->view(static::VIEW_HISTORY, ['posts' => $posts, 'multiboard' => false, 'ip' => ip_less($post->author_ip->toText())]); }
/** * Automatically adds a R9K ban to the specified board. * * @static * @param \App\Board $board The board the ban is to be added in. * @param binary|string|null $ip Optional. The IP to ban. Defaults to client IP. * @return static */ public static function addRobotBan(Board $board, $ip = null) { $ip = new IP($ip); // Default time is 2 seconds. $time = 2; // Pull the ban that expires the latest. $ban = $board->bans()->whereIpInBan($ip)->whereRobot()->orderBy('expires_at', 'desc')->first(); if ($ban instanceof static) { if (!$ban->isExpired()) { return $ban; } $time = $ban->created_at->diffInSeconds($ban->expires_at) * 2; } return static::create(['ban_ip_start' => $ip, 'ban_ip_end' => $ip, 'expires_at' => Carbon::now()->addSeconds($time), 'board_uri' => $board->board_uri, 'is_robot' => true, 'justification' => trans('validation.custom.unoriginal_content')]); }
/** * Returns a thread and its replies. * * @param \App\Board $board * @return Response */ public function getThreads(Board $board) { // Determine what page we are on. $pages = $board->getPageCount(); $response = []; for ($i = 0; $i < $pages; ++$i) { $pageArray = ['page' => $i, 'threads' => []]; $threads = $board->getThreadsForIndex($i); foreach ($threads as $thread) { $pageArray['threads'][] = ['no' => (int) $thread->board_id, 'last_modified' => (int) $thread->bumped_last->timestamp]; } $response[] = $pageArray; } return response()->json($response); }
/** * Returns the information specified by boardStats * * @return array (indexes and vales based on $boardStats) */ protected function boardStats() { $controller =& $this; $stats = Cache::remember('index.boardstats', 60, function () use($controller) { $stats = []; foreach ($controller->boardStats as $boardStat) { switch ($boardStat) { case "boardIndexedCount": $stats[$boardStat] = Board::whereIndexed()->count(); break; case "startDate": $stats[$boardStat] = Board::orderBy('created_at', 'desc')->take(1)->pluck('created_at')->format("M jS, Y"); break; case "boardTotalCount": $stats[$boardStat] = Board::count(); break; case "postCount": $stats[$boardStat] = Board::sum('posts_total'); break; case "postRecentCount": $stats[$boardStat] = Post::recent()->count(); break; } } return $stats; }); return $stats; }
/** * Get all form input. * * @return array */ public function all() { $input = parent::all(); $this->dropzone = isset($input['dropzone']); if (isset($input['files']) && is_array($input['files'])) { // Having an [null] file array passes validation. $input['files'] = array_filter($input['files']); } if (isset($input['capcode']) && $input['capcode']) { $user = $this->user; if ($user && !$user->isAnonymous()) { $role = $user->roles->where('role_id', (int) $input['capcode'])->first(); if ($role && $role->capcode != "") { $input['capcode_id'] = (int) $role->role_id; // $input['author'] = $user->username; } else { $this->failedAuthorization(); } } else { unset($input['capcode']); } } if (!$this->board->canPostWithAuthor($this->user, !!$this->thread)) { unset($input['author']); } if (!$this->board->canPostWithSubject($this->user, !!$this->thread)) { unset($input['subject']); } return $input; }
/** * Run the migrations. * * @return void */ public function up() { Schema::create('board_adventures', function (Blueprint $table) { $table->increments('adventure_id'); $table->binary('adventurer_ip'); $table->string('board_uri', 32); $table->integer('post_id')->unsigned()->nullable()->default(NULL); $table->timestamps(); $table->timestamp('expires_at'); $table->timestamp('expended_at')->nullable()->default(NULL); $table->foreign('board_uri')->references('board_uri')->on('boards')->onDelete('cascade')->onUpdate('cascade'); }); Schema::table('posts', function (Blueprint $table) { $table->integer('adventure_id')->unsigned()->nullable()->default(NULL)->after('locked_at'); $table->foreign('adventure_id')->references('adventure_id')->on('board_adventures')->onDelete('set null')->onUpdate('cascade'); }); Schema::table('boards', function (Blueprint $table) { $table->timestamp('last_post_at')->nullable()->default(NULL)->after('updated_at'); }); Board::chunk(100, function ($boards) { foreach ($boards as $board) { $lastPost = $board->posts()->orderBy('created_at', 'desc')->limit(1)->get()->last(); if ($lastPost) { $board->last_post_at = $lastPost->created_at; $board->save(); } } }); }
/** * Returns the information specified by boardStats * * @return array (indexes and vales based on $boardStats) */ protected function boardStats() { $controller =& $this; $stats = Cache::remember('index.boardstats', 60, function () use($controller) { $stats = []; foreach ($controller->boardStats as $boardStat) { switch ($boardStat) { case "boardCount": $stats[$boardStat] = Board::whereIndexed()->count(); break; case "boardIndexedCount": $stats[$boardStat] = Board::count(); break; case "postCount": $stats[$boardStat] = Board::sum('posts_total'); break; case "postRecentCount": $stats[$boardStat] = Post::recent()->count(); break; } } return $stats; }); return $stats; }
/** * Returns a thread and its replies to the client. * * @var Board $board * @var integer|null $thread * @return Response */ public function getThread(Request $request, Board $board, $thread) { if (is_null($thread)) { return abort(404); } $input = $request->only('updatesOnly', 'updateHtml', 'updatedSince'); if (isset($input['updatesOnly'])) { $updatedSince = Carbon::createFromTimestamp($request->input('updatedSince', 0)); $posts = Post::where('posts.board_uri', $board->board_uri)->withEverything()->where(function ($query) use($thread) { $query->where('posts.reply_to_board_id', $thread); $query->orWhere('posts.board_id', $thread); })->where(function ($query) use($updatedSince) { $query->where('posts.updated_at', '>=', $updatedSince); $query->orWhere('posts.deleted_at', '>=', $updatedSince); })->withTrashed()->orderBy('posts.created_at', 'asc')->get(); if (isset($input['updateHtml'])) { foreach ($posts as $post) { $appends = $post->getAppends(); $appends[] = "html"; $post->setAppends($appends); } } return $posts; } else { // Pull the thread. $thread = $board->getThreadByBoardId($thread); if (!$thread) { return abort(404); } } return $thread; }
public function createBoard() { $table = json_encode([[['state' => 0], ['state' => 0], ['state' => 0]], [['state' => 0], ['state' => 0], ['state' => 0]], [['state' => 0], ['state' => 0], ['state' => 0]]]); $board = Board::create(['table' => $table, 'moves' => 0, 'finished' => false]); $player1 = Player::create(['user_id' => 1, 'board_id' => $board->id, 'winner' => null]); $player2 = Player::create(['user_id' => 1, 'board_id' => $board->id, 'winner' => null]); return redirect('/'); }
/** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { $board = Board::find($id); if (!$board) { return \Response::json(['error' => ['message' => 'Board does not exist']], 404); } return \Response::json(['data' => $board->toArray()], 200); }
/** * Create a new user instance after a valid registration. * * @param array $data * @return User */ public function create(array $data) { $user = User::create(['username' => $data['username'], 'email' => $data['email'], 'password' => bcrypt($data['password'])]); // Create the user's personal pinboard if ($user) { $personalboard = Board::Create(['title' => 'My Personal Activities', 'body' => 'I will do these All Day! Erry Day!', 'shared_with' => 0, 'id_users' => $user->id]); } return $user; }
/** * Returns if the client has access to this form. * * @return boolean */ public function authorize() { // Ban check. $ban = Ban::getBan($this->ip(), $this->board->board_uri); if ($ban) { $this->ban = $ban; return false; } return $this->board->canPost($this->user, $this->thread); }
public function run() { $this->command->info("Seeding boards."); if (Board::count() < 1) { $board = Board::firstOrCreate(['board_uri' => "test", 'title' => "Test", 'description' => "Discover software features on your own", 'created_by' => 1, 'operated_by' => 1]); $this->command->info("Success. At least one board exists now. Accessible at /test/."); } else { $this->command->info("Skipped. Site has at least one board."); } }
public function run() { $this->command->info('Seeding role to user associations.'); $userRoles = [['user_id' => 1, 'role_id' => Role::ID_ADMIN]]; foreach (Board::get() as $board) { $userRoles[] = ['user_id' => $board->operated_by, 'role_id' => $board->getOwnerRole()->role_id]; } foreach ($userRoles as $userRole) { UserRole::firstOrCreate($userRole); } }
/** * Validate and save changes. * * @return Response */ public function patchIndex(BoardConfigRequest $request, Board $board) { $input = $request->all(); $optionGroups = $request->getBoardOptions(); foreach ($optionGroups as $optionGroup) { foreach ($optionGroup->options as $option) { $setting = BoardSetting::firstOrNew(['option_name' => $option->option_name, 'board_uri' => $board->board_uri]); $option->option_value = $input[$option->option_name]; $setting->option_value = $input[$option->option_name]; $setting->save(); } } $board->title = $input['boardBasicTitle']; $board->description = $input['boardBasicDesc']; $board->is_overboard = isset($input['boardBasicOverboard']) && !!$input['boardBasicOverboard']; $board->is_indexed = isset($input['boardBasicIndexed']) && !!$input['boardBasicIndexed']; $board->is_worksafe = isset($input['boardBasicWorksafe']) && !!$input['boardBasicWorksafe']; $board->save(); Event::fire(new BoardWasModified($board)); return $this->view(static::VIEW_CONFIG, ['board' => $board, 'groups' => $optionGroups]); }
/** * @return void */ public function __construct(UserManager $manager) { $this->userManager = $manager; $this->auth = $manager->auth; $this->registrar = $manager->registrar; $this->user = $manager->user; if ($this->option('boardListShow')) { View::share('boardbar', Board::getBoardListBar()); } View::share('user', $this->user); $this->boot(); }
/** * Adds new staff. * * @return Response */ public function putAdd(Board $board) { if (!$board->canEditConfig($this->user)) { return abort(403); } $createUser = false; $roles = $this->user->getAssignableRoles($board); $rules = []; $existing = Input::get('staff-source') == "existing"; if ($existing) { $rules = ['existinguser' => ["required", "string", "exists:users,username"], 'captcha' => ["required", "captcha"]]; $input = Input::only('existinguser', 'captcha'); $validator = Validator::make($input, $rules); } else { $createUser = true; $validator = $this->registrationValidator(); } $castes = $roles->pluck('role_id'); $casteRules = ['castes' => ["required", "array"]]; $casteInput = Input::only('castes'); $casteValidator = Validator::make($casteInput, $casteRules); $casteValidator->each('castes', ["in:" . $castes->implode(",")]); if ($validator->fails()) { return redirect()->back()->withInput()->withErrors($validator->errors()); } else { if ($casteValidator->fails()) { return redirect()->back()->withInput()->withErrors($casteValidator->errors()); } else { if ($createUser) { $user = $this->registrar->create(Input::all()); } else { $user = User::whereUsername(Input::only('existinguser'))->firstOrFail(); } } } $user->roles()->detach($roles->pluck('role_id')->toArray()); $user->roles()->attach($casteInput['castes']); Event::fire(new UserRolesModified($user)); return redirect("/cp/board/{$board->board_uri}/staff"); }
/** * Tests the board model and depndant views. * * @return void */ public function testModelAndViews() { // Get a board. $boards = Board::take(1)->get(); // Get a board that cannot exist. // The maximum `board_uri` length should be, at most, 31. $noBoards = Board::where('board_uri', "12345678901234567890123456789012")->take(1)->get(); $this->assertInstanceOf("Illuminate\\Database\\Eloquent\\Collection", $boards); $this->assertInstanceOf("Illuminate\\Database\\Eloquent\\Collection", $noBoards); $this->assertEquals(0, count($noBoards)); if (count($boards)) { $this->board = $board = $boards[0]; $this->assertInstanceOf("App\\Board", $board); // Test relationships $this->assertInstanceOf("Illuminate\\Database\\Eloquent\\Relations\\HasMany", $board->posts()); $this->assertInstanceOf("Illuminate\\Database\\Eloquent\\Relations\\HasMany", $board->logs()); $this->assertInstanceOf("Illuminate\\Database\\Eloquent\\Relations\\HasMany", $board->threads()); $this->assertInstanceOf("Illuminate\\Database\\Eloquent\\Relations\\HasMany", $board->roles()); // Test modern routes $response = $this->call('GET', url("{$board->board_uri}")); $this->assertEquals(200, $response->getStatusCode()); $this->doBoardIndexAssertions(); $response = $this->call('GET', url("{$board->board_uri}/1")); $this->assertEquals(200, $response->getStatusCode()); $this->doBoardIndexAssertions(); $response = $this->call('GET', url("{$board->board_uri}/catalog")); $this->assertEquals(200, $response->getStatusCode()); $response = $this->call('GET', url("{$board->board_uri}/logs")); $this->assertEquals(200, $response->getStatusCode()); $this->assertViewHas('board'); $this->assertViewHas('logs'); // Test legacy routes $legacyCode = env('LEGACY_ROUTES', false) ? 302 : 404; $response = $this->call('GET', url("{$board->board_uri}/index.html")); $this->assertEquals($legacyCode, $response->getStatusCode()); $response = $this->call('GET', url("{$board->board_uri}/1.html")); $this->assertEquals($legacyCode, $response->getStatusCode()); $response = $this->call('GET', url("{$board->board_uri}/catalog.html")); $this->assertEquals($legacyCode, $response->getStatusCode()); } else { $this->assertEquals(0, Board::count()); } }
/** * Execute the console command. * * @return mixed */ public function handle() { $firstPost = Post::orderBy('post_id', 'asc')->first()->pluck('created_at'); $trackTime = clone $firstPost; $nowTime = Carbon::now()->minute(0)->second(0)->timestamp; $boards = Board::all(); $this->comment("Reviewing all records."); while ($firstPost->timestamp < $nowTime) { $firstPost = $firstPost->addHour(); $hourCount = 0; foreach ($boards as $board) { if ($board->posts_total > 0) { $newRows = $board->createStatsSnapshot($firstPost); $hourCount += count($newRows); } } if ($hourCount > 0) { $this->comment("\tAdded {$hourCount} new stat row(s) from " . $firstPost->diffForHumans()); } } }
public function getIndex() { if (!$this->option('adventureEnabled')) { return abort(404); } $adventures = BoardAdventure::select('board_uri')->where('adventurer_ip', inet_pton(Request::ip()))->get(); $board_uris = []; foreach ($adventures as $adventure) { $board_uris[] = $adventure->board_uri; } $board = Board::select('board_uri')->whereNotIn('board_uri', $adventures)->wherePublic()->whereIndexed()->whereLastPost(48)->get(); if (count($board)) { $board = $board->random(1); $newAdventure = new BoardAdventure(['board_uri' => $board->board_uri, 'adventurer_ip' => inet_pton(Request::ip())]); $newAdventure->expires_at = $newAdventure->freshTimestamp()->addHours(1); $newAdventure->save(); } else { $board = false; } return $this->view(static::VIEW_ADVENTURE, ['board' => $board]); }
/** * Execute the console command. * * @return mixed */ public function handle() { $firstPost = Post::orderBy('post_id', 'asc')->first()->pluck('created_at'); $nowTime = Carbon::now()->minute(0)->second(0)->timestamp; $boards = Board::all(); $this->comment("Reviewing all records."); while ($firstPost->timestamp < $nowTime) { $firstPost = $firstPost->addHour(); $hourCount = 0; foreach ($boards as $board) { if ($board->posts_total > 0) { $newRows = $board->createStatsSnapshot($firstPost); $hourCount += $newRows->count(); } } if ($hourCount > 0) { $this->comment("\tAdded {$hourCount} new stat row(s) from " . $firstPost->diffForHumans()); } } // Drop boardlist cache. Cache::forget('site.boardlist'); // Generate boardlist again. Board::getBoardsForBoardlist(); }
/** * Run the migrations. * * @return void */ public function up() { Schema::dropIfExists('stats_uniques'); Schema::dropIfExists('stats'); Schema::create('stats', function (Blueprint $table) { $table->bigIncrements('stats_id'); $table->string('board_uri', 32); $table->timestamp('stats_time'); $table->string('stats_type', 25); $table->bigInteger('counter')->unsigned()->default(0); $table->unique(['stats_time', 'board_uri', 'stats_type']); }); Schema::create('stats_uniques', function (Blueprint $table) { $table->bigIncrements('stats_bit_id'); $table->bigInteger('stats_id')->unsigned(); $table->bigInteger('unique'); $table->foreign('stats_id')->references('stats_id')->on('stats')->onDelete('cascade')->onUpdate('cascade'); }); $firstPost = Post::orderBy('post_id', 'asc')->first()->pluck('created_at'); $trackTime = clone $firstPost; $nowTime = \Carbon\Carbon::now()->minute(0)->second(0)->timestamp; $boards = Board::all(); while ($firstPost->timestamp < $nowTime) { $firstPost = $firstPost->addHour(); $hourCount = 0; foreach ($boards as $board) { if ($board->posts_total > 0) { $newRows = $board->createStatsSnapshot($firstPost); $hourCount += count($newRows); } } if ($hourCount > 0) { echo "\tAdded {$hourCount} new stat row(s) from " . $firstPost->diffForHumans() . "\n"; } } }
/** * Returns rules specifically for dropzone files for a board. * * @param Board $board * @param array $rules (POINTER, MODIFIED) * @return array Validation rules. */ public static function rulesForFileHashes(Board $board, array &$rules) { global $app; $attachmentsMax = $board->getConfig('postAttachmentsMax', 1); $rules['files'][] = "array"; $rules['files'][] = "min:1"; $rules['files'][] = "max:{$attachmentsMax}"; // Create an additional rule for each possible file. for ($attachment = 0; $attachment < $attachmentsMax; ++$attachment) { $rules["files.name.{$attachment}"] = ["string", "required_with:files.hash.{$attachment}", "between:1,254", "file_name"]; $rules["files.hash.{$attachment}"] = ["string", "required_with:files.name.{$attachment}", "md5", "exists:files,hash,banned,0"]; $rules["files.spoiler.{$attachment}"] = ["boolean"]; } }
protected function boardListSearch($perPage = 25) { $input = $this->boardListInput(); $title = $input['title']; $lang = $input['lang']; $page = $input['page']; $tags = $input['tags']; $sfw = $input['sfw']; $sort = $input['sort']; $sortBy = $input['sortBy']; $boards = collect(Board::getBoardsForBoardlist()); $boards = $boards->filter(function ($item) use($lang, $tags, $sfw, $title) { // Are we able to view unindexed boards? if (!$item['is_indexed'] && !$this->user->canViewUnindexedBoards()) { return false; } // Are we requesting SFW only? if ($sfw && !$item['is_worksafe']) { return false; } // Are we searching by language? if ($lang) { $boardLang = $item->settings['boardLanguage']; if ($lang != $boardLang) { return false; } } // Are we searching tags? if ($tags && (!count($item['tags']) || count(array_intersect($tags, array_fetch($item['tags'], 'tag'))) < count($tags))) { return false; } // Are we searching titles and descriptions? if ($title && stripos($item['board_uri'], $title) === false && stripos($item['title'], $title) === false && stripos($item['description'], $title) === false) { return false; } return true; }); if ($title || $sort && $sortBy) { $sortWeight = $sortBy == "asc" ? -1 : 1; $boards = $boards->sort(function ($a, $b) use($title, $sort, $sortWeight) { // Sort by active users, then last post time. $aw = 0; $bw = 0; if ($title) { $aw += $a['board_uri'] === $title ? 80 : 0; $aw += stripos($a['board_uri'], $title) !== false ? 40 : 0; $aw += stripos($a['title'], $title) !== false ? 20 : 0; $aw += stripos($a['description'], $title) !== false ? 10 : 0; $bw += $b['board_uri'] === $title ? 80 : 0; $aw += stripos($b['board_uri'], $title) !== false ? 40 : 0; $aw += stripos($b['title'], $title) !== false ? 20 : 0; $aw += stripos($b['description'], $title) !== false ? 10 : 0; } if ($sort) { if ($a[$sort] > $b[$sort]) { $aw += $sortWeight; } else { if ($a[$sort] < $b[$sort]) { $bw += $sortWeight; } } } return $bw - $aw; }); } $paginator = new LengthAwarePaginator($boards->forPage($page, $perPage), $boards->count(), $perPage, $page); $paginator->setPath(url("boards.html")); foreach ($input as $inputIndex => $inputValue) { if ($inputIndex == "sfw") { $inputIndex = (int) (!!$inputValue); } $paginator->appends($inputIndex, $inputValue); } return $paginator; }
/** * Show the catalog (gridded) board view. * * @param Board $board * @return Response */ public function getCatalog(Board $board) { // Load our list of threads and their latest replies. return $this->apiResponse($board->getThreadsForCatalog()); }
/** * Supplies a clean thumbnail URL for embedding an attachment on a board. * * @param App\Board $board * @return string */ public function getThumbnailURL(Board $board) { $baseURL = "/{$board->board_uri}/file/thumb/{$this->hash}/"; $ext = $this->guessExtension(); if ($this->isSpoiler()) { return $board->getSpoilerUrl(); } if ($this->isImage()) { $ext = Settings::get('attachmentThumbnailJpeg') ? "jpg" : "png"; } else { if ($this->isVideo()) { $ext = "jpg"; } else { if ($this->isAudio()) { if (!$this->hasThumb()) { return $board->getAudioArtURL(); } $ext = "png"; } else { if ($this->isImageVector()) { // With the SVG filetype, we do not generate a thumbnail, so just serve the actual SVG. $baseURL = "/{$board->board_uri}/file/{$this->hash}/"; } } } } return url("{$baseURL}{$this->getFileName()}.{$ext}"); }
/** * Supplies a clean thumbnail URL for embedding an attachment on a board. * * @param \App\Board $board * @return string */ public function getThumbnailURL(Board $board) { $ext = $this->guessExtension(); if ($this->isSpoiler()) { return $board->getSpoilerUrl(); } if ($this->isImage() || $this->isDocument()) { $ext = Settings::get('attachmentThumbnailJpeg') ? "jpg" : "png"; } else { if ($this->isVideo()) { $ext = "jpg"; } else { if ($this->isAudio()) { if (!$this->hasThumb()) { return $board->getAudioArtURL(); } $ext = "png"; } else { if ($this->isImageVector()) { // With the SVG filetype, we do not generate a thumbnail, so just serve the actual SVG. return $this->getDownloadURL($board); } } } } $params = ['attachment' => $this->getIdentifier(), 'filename' => $this->getDownloadName() . ".{$ext}"]; if (!env('APP_MEDIA_URL', false)) { $params['board'] = $board; } // "False" generates a relativel URL. // Attachments get cached, so we want that. return route('static.thumb.attachment', $params, false); }
/** * Manage threads that have expired. * * @param App\Board $board * @param Carbon\Carbon $time Optional Carbon that will be the xed_at timestamps. Defaults to now. * @return void */ protected function handleThreadEphemeral(Board $board, Carbon $time = null) { if (is_null($time)) { $time = Carbon::now(); } // Get important settings. $threadsPerPage = (int) $board->getConfig('postsPerPage', 10); // Collect a list of threads which have been modified. $threadsToSave = []; // There are two groups of autoprune settings. // x on day since last reply $sageOnDay = (int) $board->getConfig('epheSageThreadDays', false); $lockOnDay = (int) $board->getConfig('epheLockThreadDays', false); $deleteOnDay = (int) $board->getConfig('epheDeleteThreadDays', false); // x on page (meaning the thread has fallen to this page) $sageOnPage = (int) $board->getConfig('epheSageThreadPage', false); $lockOnPage = (int) $board->getConfig('epheLockThreadPage', false); $deleteOnPage = (int) $board->getConfig('epheDeleteThreadPage', false); // Don't do anything unless we have to. if ($sageOnDay || $lockOnDay || $deleteOnDay || $sageOnPage || $lockOnPage || $deleteOnPage) { $this->comment(" Pruning /{$board->board_uri}/..."); // Modify threads based on these settings. foreach ($board->threads as $threadIndex => $thread) { $threadPage = (int) (floor($threadIndex / $threadsPerPage) + 1); $modified = false; $replyLast = clone $thread->reply_last; // x on day since last reply // This is asking if: // 1) The setting is set ($x > 0) // 2) the last reply date + the number of days permitted by each setting is < now. if (!$thread->isBumplocked() && ($sageOnDay > 0 && $replyLast->addDays($sageOnDay)->isPast() || $sageOnPage > 0 && $sageOnPage <= $threadPage)) { $this->comment(" Bumplocking #{$thread->board_id}"); $modified = true; $thread->bumplocked_at = $time; } if (!$thread->isLocked() && ($lockOnDay > 0 && $replyLast->addDays($lockOnDay)->isPast() || $lockOnPage > 0 && $lockOnPage <= $threadPage)) { $this->comment(" Locking #{$thread->board_id}"); $modified = true; $thread->locked_at = $time; } if (!$thread->isDeleted() && ($deleteOnDay > 0 && $replyLast->addDays($sageOnDay)->isPast() || $deleteOnPage > 0 && $deleteOnPage <= $threadPage)) { $this->comment(" Deleting #{$thread->board_id}"); $modified = true; $thread->deleted_at = $time; } if ($modified) { $threadsToSave[] = $thread; } } if (count($threadsToSave)) { // Save all at once. $board->threads()->saveMany($threadsToSave); } else { $this->comment(" Nothing to do."); } } }