Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show Loved polls on beatmapset pages #9451

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
10 changes: 10 additions & 0 deletions app/Http/Controllers/BeatmapsetsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use App\Models\BeatmapsetWatch;
use App\Models\Genre;
use App\Models\Language;
use App\Models\LovedPoll;
use App\Transformers\BeatmapsetTransformer;
use Auth;
use Carbon\Carbon;
Expand Down Expand Up @@ -369,8 +370,16 @@ private function showJson($beatmapset)
'beatmaps.failtimes',
'genre',
'language',
'lovedPolls',
'lovedPolls.descriptionAuthor',
'lovedPolls.topic',
'lovedPolls.topic.firstPost',
'lovedPolls.topic.pollOptions',
'user',
]);
$beatmapset->lovedPolls->each(
fn (LovedPoll $poll) => $poll->setRelation('beatmapset', $beatmapset),
);

return json_item($beatmapset, 'Beatmapset', [
'beatmaps',
Expand All @@ -382,6 +391,7 @@ private function showJson($beatmapset)
'description',
'genre',
'language',
'loved_polls',
'ratings',
'recent_favourites',
'user',
Expand Down
104 changes: 104 additions & 0 deletions app/Http/Controllers/LovedPollsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Forum\Topic;
use App\Models\LovedPoll;
use App\Transformers\LovedPollTransformer;

/**
* @group Loved Polls
*/
class LovedPollsController extends Controller
{
public function __construct()
{
$this->middleware('require-scopes:loved', ['only' => ['destroy', 'store']]);
}

/**
* Delete Loved Poll
*
* Delete the Loved poll association from the given [ForumTopic](#forum-topic). The topic and its regular poll data are left unchanged.
*
* @urlParam topic integer required ID of the [ForumTopic](#forum-topic).
* @response 204
*/
public function destroy(int $topicId)
{
LovedPoll::findOrFail($topicId)->delete();

return response(null, 204);
}

/**
* Create Loved Poll
*
* Mark a [ForumTopic](#forum-topic) as a Loved poll, associating it with a [Beatmapset](#beatmapset) and ruleset. The topic must already be in valid Loved poll format:
*
* - Topic has a poll
* - Poll has an end time
* - Poll has options "Yes" and "No"
* - First post contains a closing bold tag followed by line feed (`[/b]\n`) to mark the beginning of the description
*
* @bodyParam beatmapset_id integer required ID of the [Beatmapset](#beatmapset).
* @bodyParam description_author_id integer ID of the [User](#user) who wrote the "captain's description".
* @bodyParam excluded_beatmap_ids integer[] required IDs of [Beatmap](#beatmap)s that won't enter Loved even if the poll passes.
* @bodyParam pass_threshold float required Portion of "Yes" votes required for the map to enter Loved. Example: 0.85
* @bodyParam ruleset GameMode required Ruleset of the poll. Example: osu
* @bodyParam topic_id integer required ID of the [ForumTopic](#forum-topic).
* @response 204
*/
public function store()
{
$params = get_params(request()->input(), null, [
'beatmapset_id:int',
'description_author_id:int',
'excluded_beatmap_ids:int[]',
'pass_threshold:float',
'ruleset:string',
'topic_id:int',
], ['null_missing' => true]);

foreach ($params as $key => $value) {
abort_if(
$key !== 'description_author_id' && $value === null,
422,
"Missing required parameter {$key}",
);
}

(new LovedPoll($params))->saveOrExplode();

return response(null, 204);
}

public function vote(int $topicId)
{
$pollOptionId = LovedPoll::pollOptionId(request()->input('poll_option'));

abort_if($pollOptionId === null, 422, 'Invalid poll option');

$topic = Topic::findOrFail($topicId);

abort_if($topic->lovedPoll === null, 404);
priv_check('ForumTopicVote', $topic)->ensureCan();

$params = [
'ip' => request()->ip(),
'option_ids' => [$pollOptionId],
'user_id' => auth()->id(),
];

if (!$topic->vote()->fill($params)->save()) {
return error_popup($topic->vote()->validationErrors()->toSentence());
}

return json_item($topic->lovedPoll, new LovedPollTransformer());
}
}
8 changes: 8 additions & 0 deletions app/Models/Beatmapset.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use Cache;
use Carbon\Carbon;
use DB;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\QueryException;

Expand Down Expand Up @@ -75,6 +76,7 @@
* @property Language $language
* @property int $language_id
* @property \Carbon\Carbon $last_update
* @property \Illuminate\Database\Eloquent\Collection<LovedPoll> $lovedPolls
* @property int $nominations
* @property bool $nsfw
* @property int $offset
Expand Down Expand Up @@ -916,6 +918,11 @@ public function language()
return $this->belongsTo(Language::class, 'language_id');
}

public function lovedPolls(): HasMany
{
return $this->hasMany(LovedPoll::class)->orderBy('topic_id', 'DESC');
}

public function getAttribute($key)
{
return match ($key) {
Expand Down Expand Up @@ -997,6 +1004,7 @@ public function getAttribute($key)
'favourites',
'genre',
'language',
'lovedPolls',
'reportedIn',
'topic',
'track',
Expand Down
8 changes: 8 additions & 0 deletions app/Models/Forum/Topic.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
use App\Libraries\Transactions\AfterCommit;
use App\Models\Beatmapset;
use App\Models\Log;
use App\Models\LovedPoll;
use App\Models\Notification;
use App\Models\User;
use App\Traits\Memoizes;
use App\Traits\Validatable;
use Carbon\Carbon;
use DB;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\QueryException;

Expand All @@ -30,6 +32,7 @@
* @property int $forum_id
* @property int $icon_id
* @property \Illuminate\Database\Eloquent\Collection $logs Log
* @property-read LovedPoll|null $lovedPoll
* @property mixed $osu_lastreplytype
* @property int $osu_starpriority
* @property \Illuminate\Database\Eloquent\Collection $pollOptions PollOption
Expand Down Expand Up @@ -208,6 +211,11 @@ public function featureVotes()
return $this->hasMany(FeatureVote::class);
}

public function lovedPoll(): HasOne
{
return $this->hasOne(LovedPoll::class);
}

public function pollOptions()
{
return $this->hasMany(PollOption::class);
Expand Down
Loading