the browser-facing portion of osu!
at master 5.3 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 6namespace App\Models\Forum; 7 8use App\Traits\Validatable; 9use Carbon\Carbon; 10use DB; 11 12class TopicPoll 13{ 14 use Validatable; 15 16 private $topic; 17 private $validated = false; 18 private $params = [ 19 'hide_results' => false, 20 'length_days' => 0, 21 'max_options' => 1, 22 'options' => [], 23 'title' => null, 24 'vote_change' => false, 25 ]; 26 private $votedBy = []; 27 28 public function __get(string $field) 29 { 30 return $this->params[$field]; 31 } 32 33 public function canEdit() 34 { 35 return $this->topic->topic_time > Carbon::now()->subHours($GLOBALS['cfg']['osu']['forum']['poll_edit_hours']); 36 } 37 38 public function exists() 39 { 40 return present($this->topic->poll_title); 41 } 42 43 public function fill($params) 44 { 45 $this->params = [ 46 ...$this->params, 47 ...$params, 48 ]; 49 $this->validated = false; 50 51 return $this; 52 } 53 54 public function isOpen() 55 { 56 if ($this->topic === null) { 57 return false; 58 } 59 60 return $this->topic->pollEnd() === null || $this->topic->pollEnd()->isFuture(); 61 } 62 63 public function isValid($revalidate = false) 64 { 65 if (!$this->validated || $revalidate) { 66 $this->validated = true; 67 $this->validationErrors()->reset(); 68 69 if (!present($this->params['title'])) { 70 $this->validationErrors()->add('title', 'required'); 71 } 72 73 $this->validateFieldLength(255, 'title'); 74 75 if (count($this->params['options']) > count(array_unique($this->params['options']))) { 76 $this->validationErrors()->add('options', '.duplicate_options'); 77 } 78 79 if (count($this->params['options']) < 2) { 80 $this->validationErrors()->add('options', '.minimum_two_options'); 81 } 82 83 if (count($this->params['options']) > 10) { 84 $this->validationErrors()->add('options', '.too_many_options'); 85 } 86 87 if ($this->params['max_options'] < 1) { 88 $this->validationErrors()->add('max_options', '.minimum_one_selection'); 89 } 90 91 if ($this->params['max_options'] > count($this->params['options'])) { 92 $this->validationErrors()->add('max_options', '.invalid_max_options'); 93 } 94 95 if ($this->params['hide_results'] && $this->params['length_days'] === 0) { 96 $this->validationErrors()->add('hide_results', '.hiding_results_forever'); 97 } 98 99 if ($this->topic !== null && $this->topic->exists && !$this->canEdit()) { 100 $this->validationErrors()->add( 101 'edit', 102 '.grace_period_expired', 103 ['limit' => $GLOBALS['cfg']['osu']['forum']['poll_edit_hours']] 104 ); 105 } 106 } 107 108 return $this->validationErrors()->isEmpty(); 109 } 110 111 public function save() 112 { 113 if (!$this->isValid()) { 114 return false; 115 } 116 117 return DB::transaction(function () { 118 $this->topic->update([ 119 'poll_title' => $this->params['title'], 120 'poll_start' => Carbon::now(), 121 'poll_length' => $this->params['length_days'] * 3600 * 24, 122 'poll_max_options' => $this->params['max_options'], 123 'poll_vote_change' => $this->params['vote_change'], 124 'poll_hide_results' => $this->params['hide_results'], 125 ]); 126 127 $this 128 ->topic 129 ->pollVotes() 130 ->delete(); 131 132 $this 133 ->topic 134 ->pollOptions() 135 ->delete(); 136 137 foreach ($this->params['options'] as $index => $value) { 138 PollOption::create([ 139 'topic_id' => $this->topic->topic_id, 140 'poll_option_id' => $index, 141 'poll_option_text' => $value, 142 ]); 143 } 144 145 return true; 146 }); 147 } 148 149 public function setTopic($topic) 150 { 151 $this->topic = $topic; 152 153 return $this; 154 } 155 156 /** 157 * Get the aggregate vote count of this poll, or `0` if the poll doesn't exist. If the poll 158 * allows selecting more than one option, this may be greater than the number of users who voted. 159 */ 160 public function totalVoteCount(): int 161 { 162 return $this->exists() 163 ? $this->topic->pollOptions->sum('poll_option_total') 164 : 0; 165 } 166 167 public function validationErrorsTranslationPrefix(): string 168 { 169 return 'forum.topic_poll'; 170 } 171 172 public function votedBy($user) 173 { 174 if ($user === null) { 175 return false; 176 } 177 178 if ($this->topic === null) { 179 return false; 180 } 181 182 $userId = $user->getKey(); 183 184 if (!isset($this->votedBy[$userId])) { 185 $this->votedBy[$userId] = $this->topic->pollVotes()->where('vote_user_id', $userId)->exists(); 186 } 187 188 return $this->votedBy[$userId]; 189 } 190}