Skip to content

Commit

Permalink
Merge branch 'master' into team-file-null
Browse files Browse the repository at this point in the history
  • Loading branch information
notbakaneko authored Feb 21, 2025
2 parents 676b344 + 91b0ed4 commit aecdc46
Show file tree
Hide file tree
Showing 25 changed files with 306 additions and 61 deletions.
7 changes: 1 addition & 6 deletions app/Http/Controllers/Teams/ApplicationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
namespace App\Http\Controllers\Teams;

use App\Http\Controllers\Controller;
use App\Jobs\Notifications\TeamApplicationAccept;
use App\Jobs\Notifications\TeamApplicationReject;
use App\Models\Team;
use App\Models\TeamApplication;
Expand All @@ -30,12 +29,8 @@ public function accept(string $teamId, string $id): Response

priv_check('TeamApplicationAccept', $application)->ensureCan();

\DB::transaction(function () use ($application, $team) {
$application->delete();
$team->members()->create(['user_id' => $application->getKey()]);
});
$team->addMember($application);

(new TeamApplicationAccept($application, \Auth::user()))->dispatch();
\Session::flash('popup', osu_trans('teams.applications.accept.ok'));

return response(null, 204);
Expand Down
8 changes: 3 additions & 5 deletions app/Http/Controllers/Teams/MembersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ public function destroy(string $teamId, string $userId): Response
'team_id' => $teamId,
'user_id' => $userId,
])->firstOrFail();
$team = $teamMember->team;

if ($teamMember->user_id === \Auth::user()->getKey()) {
abort(422, 'can not remove self from team');
}
priv_check('TeamUpdate', $team)->ensureCan();

priv_check('TeamUpdate', $teamMember->team)->ensureCan();
$team->removeMember($teamMember);

$teamMember->delete();
\Session::flash('popup', osu_trans('teams.members.destroy.success'));

return response(null, 204);
Expand Down
5 changes: 4 additions & 1 deletion app/Http/Controllers/TeamsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ public function part(string $id): Response
$team = Team::findOrFail($id);
priv_check('TeamPart', $team)->ensureCan();

$team->members()->findOrFail(\Auth::user()->getKey())->delete();
$teamMember = $team->members()->findOrFail(\Auth::user()->getKey());
$team->removeMember($teamMember);
\Session::flash('popup', osu_trans('teams.part.ok'));

return ujs_redirect(route('teams.show', ['team' => $team]));
Expand Down Expand Up @@ -145,8 +146,10 @@ public function store(): Response
$team = (new Team([...$params, 'leader_id' => $user->getKey()]));
try {
\DB::transaction(function () use ($team, $user) {
$channel = $team->createChannel();
$team->saveOrExplode();
$team->members()->create(['user_id' => $user->getKey()]);
$channel->addUser($user);
});
} catch (ModelNotSavedException) {
return ext_view('teams.create', compact('team'), status: 422);
Expand Down
58 changes: 35 additions & 23 deletions app/Libraries/ImageProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,36 +83,48 @@ public function process()
$this->inputDim[0] <= $this->targetDim[0] &&
$this->inputDim[1] <= $this->targetDim[1]
) {
if ($this->inputFileSize < $this->targetFileSize) {
if (
$this->inputFileSize < $this->targetFileSize &&
$this->inputDim[0] === $this->targetDim[0] &&
$this->inputDim[1] === $this->targetDim[1]
) {
return;
}

$image = $inputImage;
} else {
$start = [0, 0];
$inDim = [$this->inputDim[0], $this->inputDim[1]];
$outDim = [$this->targetDim[0], $this->targetDim[1]];

// figure out how to crop.
if ($this->inputDim[0] / $this->inputDim[1] >= $this->targetDim[0] / $this->targetDim[1]) {
$inDim[0] = $this->targetDim[0] / $this->targetDim[1] * $this->inputDim[1];
$start[0] = ($this->inputDim[0] - $inDim[0]) / 2;
} else {
$inDim[1] = $this->targetDim[1] / $this->targetDim[0] * $this->inputDim[0];
$start[1] = ($this->inputDim[1] - $inDim[1]) / 2;
}
$newHeight = $this->inputDim[0] * $this->targetDim[1] / $this->targetDim[0];
$this->targetDim = $newHeight <= $this->inputDim[1]
? [
$this->inputDim[0],
$newHeight,
] : [
$this->inputDim[1] * $this->targetDim[0] / $this->targetDim[1],
$this->inputDim[1],
];
}

// don't scale if input image is smaller.
if ($inDim[0] < $outDim[0] || $inDim[1] < $outDim[1]) {
$outDim = $inDim;
}
$start = [0, 0];
$inDim = [$this->inputDim[0], $this->inputDim[1]];
$outDim = [$this->targetDim[0], $this->targetDim[1]];

$image = imagecreatetruecolor($outDim[0], $outDim[1]);
imagesavealpha($image, true);
imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127));
imagecopyresampled($image, $inputImage, 0, 0, $start[0], $start[1], $outDim[0], $outDim[1], $inDim[0], $inDim[1]);
// figure out how to crop.
if ($this->inputDim[0] / $this->inputDim[1] >= $this->targetDim[0] / $this->targetDim[1]) {
$inDim[0] = $this->targetDim[0] / $this->targetDim[1] * $this->inputDim[1];
$start[0] = ($this->inputDim[0] - $inDim[0]) / 2;
} else {
$inDim[1] = $this->targetDim[1] / $this->targetDim[0] * $this->inputDim[0];
$start[1] = ($this->inputDim[1] - $inDim[1]) / 2;
}

// don't scale if input image is smaller.
if ($inDim[0] < $outDim[0] || $inDim[1] < $outDim[1]) {
$outDim = $inDim;
}

$image = imagecreatetruecolor($outDim[0], $outDim[1]);
imagesavealpha($image, true);
imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127));
imagecopyresampled($image, $inputImage, 0, 0, $start[0], $start[1], $outDim[0], $outDim[1], $inDim[0], $inDim[1]);

$toJpeg = true;

if ($this->inputDim[2] === IMAGETYPE_PNG || $this->inputDim[2] === IMAGETYPE_GIF) {
Expand Down
6 changes: 6 additions & 0 deletions app/Models/Chat/Channel.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class Channel extends Model
'temporary' => 'TEMPORARY',
'pm' => 'PM',
'group' => 'GROUP',
'team' => 'TEAM',
];

public static function ack(int $channelId, int $userId, ?int $timestamp = null, ?Redis $redis = null): void
Expand Down Expand Up @@ -366,6 +367,11 @@ public function isGroup()
return $this->type === static::TYPES['group'];
}

public function isTeam(): bool
{
return $this->type === static::TYPES['team'];
}

public function isBanchoMultiplayerChat()
{
return $this->type === static::TYPES['temporary'] && starts_with($this->name, ['#mp_', '#spect_']);
Expand Down
114 changes: 98 additions & 16 deletions app/Models/Team.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace App\Models;

use App\Exceptions\InvariantException;
use App\Jobs\Notifications\TeamApplicationAccept;
use App\Libraries\BBCodeForDB;
use App\Libraries\Uploader;
use App\Libraries\UsernameValidation;
Expand Down Expand Up @@ -39,6 +41,11 @@ public function applications(): HasMany
return $this->hasMany(TeamApplication::class);
}

public function channel(): BelongsTo
{
return $this->belongsTo(Chat\Channel::class, 'channel_id');
}

public function leader(): BelongsTo
{
return $this->belongsTo(User::class, 'leader_id');
Expand Down Expand Up @@ -88,13 +95,31 @@ public function setUrlAttribute(?string $value): void
);
}

public function descriptionHtml(): string
public function addMember(TeamApplication $application): void
{
$description = presence($this->description);
$this->getConnection()->transaction(function () use ($application) {
$application->delete();
$this->members()->create(['user_id' => $application->getKey()]);
$this->channel->addUser($application->user);
});

return $description === null
? ''
: bbcode((new BBCodeForDB($description))->generate());
(new TeamApplicationAccept($application, $this->leader))->dispatch();
}

public function createChannel(): Chat\Channel
{
if ($this->channel !== null) {
return $this->channel;
}

$channel = new Chat\Channel([
'name' => truncate($this->name, 50),
'type' => Chat\Channel::TYPES['team'],
]);
$channel->saveOrExplode();
$this->channel()->associate($channel);

return $channel;
}

public function delete()
Expand All @@ -108,12 +133,36 @@ public function delete()
if ($ret) {
$this->applications()->delete();
$this->members()->delete();

$channel = $this->channel;
if ($channel !== null) {
$channel->loadMissing('userChannels.user');
$channel->update(['name' => "#DeletedTeam_{$this->getKey()}"]);

foreach ($channel->userChannels as $userChannel) {
$user = $userChannel->user;
if ($user === null) {
$userChannel->delete();
} else {
$channel->removeUser($user);
}
}
}
}

return $ret;
});
}

public function descriptionHtml(): string
{
$description = presence($this->description);

return $description === null
? ''
: bbcode((new BBCodeForDB($description))->generate());
}

public function emptySlots(): int
{
$max = $this->maxMembers();
Expand All @@ -122,13 +171,29 @@ public function emptySlots(): int
return max(0, $max - $current);
}

public function flag(): Uploader
{
return $this->flag ??= new Uploader(
'teams/flag',
$this,
'flag_file',
['image' => [
'maxDimensions' => static::FLAG_MAX_DIMENSIONS,
'maxFilesize' => 200_000,
]],
);
}

public function header(): Uploader
{
return $this->header ??= new Uploader(
'teams/header',
$this,
'header_file',
['image' => ['maxDimensions' => static::HEADER_MAX_DIMENSIONS]],
['image' => [
'maxDimensions' => static::HEADER_MAX_DIMENSIONS,
'maxFilesize' => 4_000_000,
]],
);
}

Expand Down Expand Up @@ -171,16 +236,6 @@ public function isValid(): bool
return $this->validationErrors()->isEmpty();
}

public function flag(): Uploader
{
return $this->flag ??= new Uploader(
'teams/flag',
$this,
'flag_file',
['image' => ['maxDimensions' => static::FLAG_MAX_DIMENSIONS]],
);
}

public function maxMembers(): int
{
$this->loadMissing('members.user');
Expand All @@ -190,6 +245,33 @@ public function maxMembers(): int
return min(8 + (4 * $supporterCount), $GLOBALS['cfg']['osu']['team']['max_members']);
}

public function removeMember(TeamMember $member): void
{
if ($member->user_id === $this->leader_id) {
throw new InvariantException('can not remove leader from the team');
}

$this->getConnection()->transaction(function () use ($member) {
$member->delete();
$user = $member->user;
if ($user !== null) {
$this->channel->removeUser($user);
}
});
}

public function resetChannelUsers(): void
{
$channel = $this->channel;
$this->loadMissing('members.user');

foreach ($this->members->pluck('user') as $user) {
if ($user !== null) {
$channel->addUser($user);
}
}
}

public function save(array $options = [])
{
if (!$this->isValid()) {
Expand Down
7 changes: 5 additions & 2 deletions app/Singletons/OsuAuthorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static function alwaysCheck($ability)
static $set;

$set ??= new Ds\Set([
'ChannelPart',
'ContestJudge',
'IsNotOAuth',
'IsOwnClient',
Expand Down Expand Up @@ -1046,7 +1047,8 @@ public function checkChatChannelJoin(?User $user, Channel $channel): ?string
$this->ensureCleanRecord($user, $prefix);

// joining multiplayer room is done through room endpoint
if ($channel->isMultiplayer()) {
// team channel handling is done through team model
if ($channel->isMultiplayer() || $channel->isTeam()) {
return null;
}

Expand All @@ -1070,7 +1072,8 @@ public function checkChatChannelPart(?User $user, Channel $channel): string

$this->ensureLoggedIn($user);

if ($channel->type !== Channel::TYPES['private']) {
// team channel handling is done through team model
if (!$channel->isTeam() && $channel->type !== Channel::TYPES['private']) {
return 'ok';
}

Expand Down
4 changes: 4 additions & 0 deletions database/factories/TeamFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Database\Factories;

use App\Models\Chat\Channel;
use App\Models\Team;
use App\Models\User;

Expand All @@ -18,6 +19,7 @@ public function configure(): static
{
return $this->afterCreating(function (Team $team): void {
$team->members()->create(['user_id' => $team->leader_id]);
$team->channel->userChannels()->create(['user_id' => $team->leader_id]);
});
}

Expand All @@ -27,6 +29,8 @@ public function definition(): array
'name' => fn () => strtr($this->faker->unique()->userName(), '.', ' '),
'short_name' => fn () => substr(strtr($this->faker->unique()->userName(), '.', ' '), 0, 4),
'leader_id' => User::factory(),

'channel_id' => fn (array $attrs) => Channel::factory()->state(fn () => ['name' => $attrs['name']]),
];
}
}
Loading

0 comments on commit aecdc46

Please sign in to comment.