the browser-facing portion of osu!
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\Beatmapset;
9
10use App\Exceptions\InvariantException;
11use App\Jobs\Notifications\BeatmapOwnerChange;
12use App\Models\Beatmap;
13use App\Models\BeatmapOwner;
14use App\Models\BeatmapsetEvent;
15use App\Models\User;
16use Ds\Set;
17
18class ChangeBeatmapOwners
19{
20 private Set $userIds;
21
22 public function __construct(private Beatmap $beatmap, array $newUserIds, private User $source)
23 {
24 priv_check_user($source, 'BeatmapUpdateOwner', $beatmap->beatmapset)->ensureCan();
25
26 $this->userIds = new Set($newUserIds);
27
28 if ($this->userIds->count() > $GLOBALS['cfg']['osu']['beatmaps']['owners_max']) {
29 throw new InvariantException(osu_trans('beatmaps.change_owner.too_many'));
30 }
31
32 if ($this->userIds->isEmpty()) {
33 throw new InvariantException('user_ids must be specified');
34 }
35 }
36
37 public function handle(): void
38 {
39 $currentOwners = new Set($this->beatmap->getOwners()->pluck('user_id'));
40 if ($currentOwners->xor($this->userIds)->isEmpty()) {
41 return;
42 }
43
44 $newUserIds = $this->userIds->diff($currentOwners);
45
46 if (User::whereIn('user_id', $newUserIds->toArray())->default()->withoutBots()->withoutNoProfile()->count() !== $newUserIds->count()) {
47 throw new InvariantException('invalid user_id');
48 }
49
50 $this->beatmap->getConnection()->transaction(function () {
51 $params = array_map(
52 fn ($userId) => ['beatmap_id' => $this->beatmap->getKey(), 'user_id' => $userId],
53 $this->userIds->toArray()
54 );
55
56 $this->beatmap->fill(['user_id' => $this->userIds->first()])->saveOrExplode();
57 $this->beatmap->beatmapOwners()->delete();
58 BeatmapOwner::insert($params);
59
60 $this->beatmap->refresh();
61
62 $newUsers = $this->beatmap->getOwners()->select('id', 'username')->all();
63 $beatmapset = $this->beatmap->beatmapset;
64 $firstMapper = $newUsers[0];
65
66 BeatmapsetEvent::log(BeatmapsetEvent::BEATMAP_OWNER_CHANGE, $this->source, $beatmapset, [
67 'beatmap_id' => $this->beatmap->getKey(),
68 'beatmap_version' => $this->beatmap->version,
69 // TODO: mainly for compatibility during dev when switching branches, can be removed after deployed.
70 'new_user_id' => $firstMapper['id'],
71 'new_user_username' => $firstMapper['username'],
72 'new_users' => $newUsers,
73 ])->saveOrExplode();
74
75 $beatmapset->update(['eligible_main_rulesets' => null]);
76 });
77
78 (new BeatmapOwnerChange($this->beatmap, $this->source))->dispatch();
79 }
80}