Skip to content

Commit

Permalink
Merge branch 'master' into daily-challenge-completion-marker
Browse files Browse the repository at this point in the history
  • Loading branch information
nanaya authored Jan 16, 2025
2 parents 8e30dde + f7d0f0b commit 3c70a3c
Show file tree
Hide file tree
Showing 19 changed files with 216 additions and 18 deletions.
11 changes: 11 additions & 0 deletions app/Http/Controllers/TeamsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ public function __construct()
$this->middleware('auth', ['only' => ['part']]);
}

public function destroy(string $id): Response
{
$team = Team::findOrFail($id);
priv_check('TeamUpdate', $team)->ensureCan();

$team->delete();
\Session::flash('popup', osu_trans('teams.destroy.ok'));

return ujs_redirect(route('home'));
}

public function edit(string $id): Response
{
$team = Team::findOrFail($id);
Expand Down
18 changes: 13 additions & 5 deletions app/Http/Controllers/Users/LookupController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ public function __construct()
public function index()
{
// TODO: referer check?
$ids = array_slice(array_reject_null(get_arr(request('ids'), presence(...)) ?? []), 0, 50);
$params = get_params(\Request::all(), null, [
'exclude_bots:bool',
'ids:string[]',
]);

$ids = array_slice(array_reject_null(get_arr($params['ids'] ?? [], presence(...))), 0, 50);

$numericIds = [];
$stringIds = [];
Expand All @@ -35,13 +40,16 @@ public function index()
}

$users = User::where(fn ($q) => $q->whereIn('user_id', $numericIds)->orWhereIn('username', $stringIds))
->where('group_id', '<>', app('groups')->byIdentifier('no_profile')->getKey())
->default()
->with(UserCompactTransformer::CARD_INCLUDES_PRELOAD)
->get();
->withoutNoProfile()
->with(UserCompactTransformer::CARD_INCLUDES_PRELOAD);

if ($params['exclude_bots'] ?? false) {
$users = $users->withoutBots();
}

return [
'users' => json_collection($users, new UserCompactTransformer(), UserCompactTransformer::CARD_INCLUDES),
'users' => json_collection($users->get(), new UserCompactTransformer(), UserCompactTransformer::CARD_INCLUDES),
];
}
}
2 changes: 1 addition & 1 deletion app/Libraries/Beatmapset/ChangeBeatmapOwners.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function handle(): void

$newUserIds = $this->userIds->diff($currentOwners);

if (User::whereIn('user_id', $newUserIds->toArray())->default()->count() !== $newUserIds->count()) {
if (User::whereIn('user_id', $newUserIds->toArray())->default()->withoutBots()->withoutNoProfile()->count() !== $newUserIds->count()) {
throw new InvariantException('invalid user_id');
}

Expand Down
10 changes: 10 additions & 0 deletions app/Models/Multiplayer/PlaylistItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* @property int $owner_id
* @property int|null $playlist_order
* @property json|null $required_mods
* @property bool $freestyle
* @property Room $room
* @property int $room_id
* @property int|null $ruleset_id
Expand All @@ -35,6 +36,7 @@ class PlaylistItem extends Model
protected $casts = [
'allowed_mods' => 'object',
'expired' => 'boolean',
'freestyle' => 'boolean',
'required_mods' => 'object',
];

Expand Down Expand Up @@ -64,6 +66,7 @@ public static function fromJsonParams(User $owner, $json)
$obj->$field = $value;
}

$obj->freestyle = get_bool($json['freestyle'] ?? false);
$obj->max_attempts = get_int($json['max_attempts'] ?? null);

$modsHelper = app('mods');
Expand Down Expand Up @@ -169,6 +172,13 @@ private function assertValidRuleset()

private function assertValidMods()
{
if ($this->freestyle) {
if (count($this->allowed_mods) !== 0 || count($this->required_mods) !== 0) {
throw new InvariantException("mod isn't allowed in freestyle");
}
return;
}

$allowedModIds = array_column($this->allowed_mods, 'acronym');
$requiredModIds = array_column($this->required_mods, 'acronym');

Expand Down
33 changes: 26 additions & 7 deletions app/Models/Multiplayer/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -662,9 +662,21 @@ public function startPlay(User $user, PlaylistItem $playlistItem, array $rawPara
{
priv_check_user($user, 'MultiplayerScoreSubmit', $this)->ensureCan();

$this->assertValidStartPlay($user, $playlistItem);
$params = get_params($rawParams, null, [
'beatmap_hash',
'beatmap_id:int',
'build_id',
'ruleset_id:int',
], ['null_missing' => true]);

if (!$playlistItem->freestyle) {
$params['beatmap_id'] = $playlistItem->beatmap_id;
$params['ruleset_id'] = $playlistItem->ruleset_id;
}

return $this->getConnection()->transaction(function () use ($playlistItem, $rawParams, $user) {
$this->assertValidStartPlay($user, $playlistItem, $params);

return $this->getConnection()->transaction(function () use ($params, $playlistItem, $user) {
$agg = UserScoreAggregate::new($user, $this);
if ($agg->wasRecentlyCreated) {
$this->incrementInstance('participant_count');
Expand All @@ -676,11 +688,11 @@ public function startPlay(User $user, PlaylistItem $playlistItem, array $rawPara
$playlistItemAgg->updateUserAttempts();

return ScoreToken::create([
'beatmap_hash' => get_string($rawParams['beatmap_hash'] ?? null),
'beatmap_id' => $playlistItem->beatmap_id,
'build_id' => $rawParams['build_id'],
'beatmap_hash' => $params['beatmap_hash'],
'beatmap_id' => $params['beatmap_id'],
'build_id' => $params['build_id'],
'playlist_item_id' => $playlistItem->getKey(),
'ruleset_id' => $playlistItem->ruleset_id,
'ruleset_id' => $params['ruleset_id'],
'user_id' => $user->getKey(),
]);
});
Expand Down Expand Up @@ -741,14 +753,21 @@ private function assertValidStartGame()
}
}

private function assertValidStartPlay(User $user, PlaylistItem $playlistItem)
private function assertValidStartPlay(User $user, PlaylistItem $playlistItem, array $params): void
{
// todo: check against room's end time (to see if player has enough time to play this beatmap) and is under the room's max attempts limit

if ($this->hasEnded()) {
throw new InvariantException('Room has already ended.');
}

if ($playlistItem->freestyle) {
// assert the beatmap_id is part of playlist item's beatmapset
if ($playlistItem->beatmap->beatmapset_id !== Beatmap::find($params['beatmap_id'])?->beatmapset_id) {
throw new InvariantException('Specified beatmap_id is not allowed');
}
}

$userId = $user->getKey();
if ($this->max_attempts !== null) {
$roomStats = $this->userHighScores()->where('user_id', $userId)->first();
Expand Down
16 changes: 16 additions & 0 deletions app/Models/Team.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public function descriptionHtml(): string
: bbcode((new BBCodeForDB($description))->generate());
}

public function delete()
{
$this->header()->delete();
$this->logo()->delete();

return $this->getConnection()->transaction(function () {
$ret = parent::delete();

if ($ret) {
$this->members()->delete();
}

return $ret;
});
}

public function header(): Uploader
{
return $this->header ??= new Uploader(
Expand Down
10 changes: 10 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -2039,6 +2039,16 @@ public function scopeOnline($query)
->where('user_lastvisit', '>', time() - $GLOBALS['cfg']['osu']['user']['online_window']);
}

public function scopeWithoutBots(Builder $query): Builder
{
return $query->whereNot('group_id', app('groups')->byIdentifier('bot')->getKey());
}

public function scopeWithoutNoProfile(Builder $query): Builder
{
return $query->whereNot('group_id', app('groups')->byIdentifier('no_profile')->getKey());
}

public function checkPassword($password)
{
return Hash::check($password, $this->getAuthPassword());
Expand Down
1 change: 1 addition & 0 deletions app/Transformers/Multiplayer/PlaylistItemTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function transform(PlaylistItem $item)
'ruleset_id' => $item->ruleset_id,
'allowed_mods' => $item->allowed_mods,
'required_mods' => $item->required_mods,
'freestyle' => $item->freestyle,
'expired' => $item->expired,
'owner_id' => $item->owner_id,
'playlist_order' => $item->playlist_order,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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);

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('multiplayer_playlist_items', function (Blueprint $table) {
$table->boolean('freestyle')->after('required_mods')->default(false);
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('multiplayer_playlist_items', function (Blueprint $table) {
$table->dropColumn('freestyle');
});
}
};
1 change: 1 addition & 0 deletions resources/js/beatmap-discussions/beatmap-owner-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export default class BeatmapOwnerEditor extends React.Component<Props> {
<div className={classWithModifiers('beatmap-owner-editor-owners', { editing: this.editing })}>
{this.editing ? (
<UsernameInput
excludeBots
initialUsers={this.owners}
// initialValue not set for owner editor as value is reset when cancelled.
modifiers='beatmap-owner-editor'
Expand Down
1 change: 1 addition & 0 deletions resources/js/chat/create-announcement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default class CreateAnnouncement extends React.Component<Props> {
<div className='chat-form-users'>
<UserCardBrick user={core.currentUserOrFail} />
<UsernameInput
excludeBots
id='chat-form-users'
ignoreCurrentUser
name='users'
Expand Down
3 changes: 2 additions & 1 deletion resources/js/components/username-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Spinner } from './spinner';
import UserCardBrick from './user-card-brick';

interface Props {
excludeBots?: boolean;
id?: string;
ignoreCurrentUser?: boolean;
initialUsers?: UserJson[];
Expand Down Expand Up @@ -166,7 +167,7 @@ export default class UsernameInput extends React.PureComponent<Props> {
}

try {
this.xhr = apiLookupUsers(userIds);
this.xhr = apiLookupUsers(userIds, this.props.excludeBots);
const response = await this.xhr;
this.extractValidUsers(response.users);
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion resources/js/store/store-supporter-tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export default class StoreSupporterTag extends React.Component<Props> {

@action
private readonly getUser = (username: string) => {
this.xhr = apiLookupUsers([`@${username}`]);
this.xhr = apiLookupUsers([`@${username}`], true);

this.xhr
.done((response) => runInAction(() => {
Expand Down
4 changes: 2 additions & 2 deletions resources/js/utils/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import UserJson from 'interfaces/user-json';
import { route } from 'laroute';

export function apiLookupUsers(idsOrUsernames: (string | null | undefined)[]) {
export function apiLookupUsers(idsOrUsernames: (string | null | undefined)[], excludeBots?: boolean) {
return $.ajax(route('users.lookup'), {
data: { ids: idsOrUsernames },
data: { exclude_bots: excludeBots, ids: idsOrUsernames },
dataType: 'json',
}) as JQuery.jqXHR<{ users: UserJson[] }>;
}
5 changes: 5 additions & 0 deletions resources/lang/en/teams.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
// See the LICENCE file in the repository root for full licence text.

return [
'destroy' => [
'ok' => 'Team removed',
],

'edit' => [
'saved' => 'Settings saved successfully',
'title' => 'Team Settings',
Expand Down Expand Up @@ -66,6 +70,7 @@

'show' => [
'bar' => [
'destroy' => 'Disband Team',
'part' => 'Leave Team',
],

Expand Down
15 changes: 15 additions & 0 deletions resources/views/teams/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
if (priv_check('TeamPart', $team)->can()) {
$buttons->add('part');
}
if (priv_check('TeamUpdate', $team)->can()) {
$buttons->add('destroy');
}
@endphp

@extends('master', [
Expand Down Expand Up @@ -76,6 +79,18 @@ class="btn-circle btn-circle--page-toggle"
</div>
@if (!$buttons->isEmpty())
<div class="profile-detail-bar profile-detail-bar--team">
@if ($buttons->contains('destroy'))
<form
action="{{ route('teams.destroy', $team) }}"
data-turbo-confirm="{{ osu_trans('common.confirmation') }}"
method="POST"
>
<input type="hidden" name="_method" value="DELETE">
<button class="team-action-button team-action-button--part">
{{ osu_trans('teams.show.bar.destroy') }}
</button>
</form>
@endif
@if ($buttons->contains('part'))
<form
action="{{ route('teams.part', ['team' => $team]) }}"
Expand Down
2 changes: 1 addition & 1 deletion routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
Route::post('part', 'TeamsController@part')->name('part');
Route::resource('members', 'Teams\MembersController', ['only' => ['destroy', 'index']]);
});
Route::resource('teams', 'TeamsController', ['only' => ['edit', 'show', 'update']]);
Route::resource('teams', 'TeamsController', ['only' => ['destroy', 'edit', 'show', 'update']]);

Route::post('users/check-username-availability', 'UsersController@checkUsernameAvailability')->name('users.check-username-availability');
Route::get('users/lookup', 'Users\LookupController@index')->name('users.lookup');
Expand Down
Loading

0 comments on commit 3c70a3c

Please sign in to comment.