the browser-facing portion of osu!
at master 101 lines 3.4 kB view raw
1<?php 2 3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0. 4// See the LICENCE file in the repository root for full licence text. 5 6declare(strict_types=1); 7 8namespace App\Libraries\BeatmapsetDiscussion; 9 10use App\Exceptions\InvariantException; 11use App\Jobs\Notifications\BeatmapsetDiscussionPostNew; 12use App\Libraries\BeatmapsetDiscussion\Traits\HandlesProblem; 13use App\Models\BeatmapDiscussion; 14use App\Models\BeatmapDiscussionPost; 15use App\Models\BeatmapsetEvent; 16use App\Models\User; 17 18class Reply 19{ 20 use HandlesProblem; 21 22 private array $posts = []; 23 24 public function __construct(private User $user, private BeatmapDiscussion $discussion, private ?string $message, private ?bool $resolve = null) 25 { 26 priv_check_user($this->user, 'BeatmapsetDiscussionReply', $discussion->beatmapset)->ensureCan(); 27 28 if (!$discussion->exists) { 29 throw new InvariantException('Cannot reply to a new discussion.'); 30 } 31 32 // Treat resolving to the same state as not changing. 33 // Maybe throw instead? 34 if ($resolve === $discussion->resolved) { 35 $this->resolve = null; 36 } 37 38 if ($this->resolve !== null) { 39 if (!$discussion->canBeResolved()) { 40 throw new InvariantException("{$discussion->message_type} does not support resolving."); 41 } 42 43 if ($discussion->resolved !== $resolve) { 44 $priv = $resolve ? 'BeatmapDiscussionResolve' : 'BeatmapDiscussionReopen'; 45 priv_check_user($user, $priv, $discussion)->ensureCan(); 46 } 47 } 48 49 $this->maybeSetProblemDiscussion($discussion, false); 50 } 51 52 /** 53 * @return BeatmapDiscussionPost[] 54 */ 55 public function handle(): array 56 { 57 $newPost = $this->discussion->getConnection()->transaction(function () { 58 $post = $this->discussion->beatmapDiscussionPosts()->make(['message' => $this->message]); 59 $post->user()->associate($this->user); 60 61 $post->saveOrExplode(); 62 $this->posts[] = $post; 63 64 $this->handleResolvedChange($post); 65 $this->handleProblemDiscussion(); 66 67 return $post; 68 }); 69 70 // TODO: make transactional 71 72 (new BeatmapsetDiscussionPostNew($newPost, $this->user))->dispatch(); 73 74 return $this->posts; 75 } 76 77 private function handleResolvedChange(BeatmapDiscussionPost $post) 78 { 79 if ($this->resolve === null) { 80 return; 81 } 82 83 // if a resolved state change was requested, check if someone else got to it first. 84 $discussion = $this->discussion->lockSelf(); 85 if ($discussion->resolved !== $this->discussion->resolved) { 86 throw new InvariantException('resolved state of the discussion has changed'); 87 } 88 89 $this->discussion->resolved = $this->resolve; 90 $event = $this->discussion->resolved ? BeatmapsetEvent::ISSUE_RESOLVE : BeatmapsetEvent::ISSUE_REOPEN; 91 92 $systemPost = BeatmapDiscussionPost::generateLogResolveChange($this->user, $this->discussion->resolved); 93 $systemPost->beatmap_discussion_id = $this->discussion->getKey(); 94 $systemPost->saveOrExplode(); 95 BeatmapsetEvent::log($event, $this->user, $post)->saveOrExplode(); 96 97 $this->posts[] = $systemPost; 98 99 $this->discussion->saveOrExplode(); 100 } 101}