Skip to content

Commit

Permalink
Merge pull request #11835 from nanaya/team-create
Browse files Browse the repository at this point in the history
Add team creation page
  • Loading branch information
notbakaneko authored Feb 19, 2025
2 parents 56c33a8 + 0af9a66 commit 551a11a
Show file tree
Hide file tree
Showing 18 changed files with 442 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ CLIENT_CHECK_VERSION=false

# OAUTH_MAX_USER_CLIENTS=1

# TEAM_CREATE_REQUIRE_SUPPORTER=false
# TEAM_MAX_MEMBERS=40

# USER_REPORT_NOTIFICATION_ENDPOINT_CHEATING=
# default if nothing specified for specific type
# USER_REPORT_NOTIFICATION_ENDPOINT_MODERATION=
Expand Down
43 changes: 41 additions & 2 deletions app/Http/Controllers/TeamsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace App\Http\Controllers;

use App\Exceptions\InvariantException;
use App\Exceptions\ModelNotSavedException;
use App\Models\Beatmap;
use App\Models\Team;
use App\Models\User;
Expand All @@ -19,7 +20,7 @@ class TeamsController extends Controller
public function __construct()
{
parent::__construct();
$this->middleware('auth', ['only' => ['part']]);
$this->middleware('auth', ['only' => ['create', 'part']]);
}

public static function pageLinks(string $current, Team $team): array
Expand Down Expand Up @@ -55,6 +56,19 @@ public static function pageLinks(string $current, Team $team): array
return $ret;
}

public function create(): Response
{
$currentUser = \Auth::user();
$teamId = $currentUser?->team?->getKey() ?? $currentUser?->teamApplication?->team_id;
if ($teamId !== null) {
return ujs_redirect(route('teams.show', $teamId));
}

return ext_view('teams.create', [
'team' => new Team(),
]);
}

public function destroy(string $id): Response
{
$team = Team::findOrFail($id);
Expand Down Expand Up @@ -118,6 +132,31 @@ public function show(string $id): Response
return ext_view('teams.show', compact('team'));
}

public function store(): Response
{
priv_check('TeamStore')->ensureCan();

$params = get_params(\Request::all(), 'team', [
'name',
'short_name',
]);

$user = \Auth::user();
$team = (new Team([...$params, 'leader_id' => $user->getKey()]));
try {
\DB::transaction(function () use ($team, $user) {
$team->saveOrExplode();
$team->members()->create(['user_id' => $user->getKey()]);
});
} catch (ModelNotSavedException) {
return ext_view('teams.create', compact('team'), status: 422);
}

\Session::flash('popup', osu_trans('teams.store.ok'));

return ujs_redirect(route('teams.show', $team));
}

public function update(string $id): Response
{
$team = Team::findOrFail($id);
Expand All @@ -135,7 +174,7 @@ public function update(string $id): Response

$team->fill($params)->saveOrExplode();

\Session::flash('popup', osu_trans('teams.edit.saved'));
\Session::flash('popup', osu_trans('teams.edit.ok'));

return response(null, 201);
}
Expand Down
18 changes: 13 additions & 5 deletions app/Libraries/UsernameValidation.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@

class UsernameValidation
{
public static function allowedName(string $username): bool
{
foreach (model_pluck(DB::table('phpbb_disallow'), 'disallow_username') as $check) {
if (preg_match('#^'.str_replace('%', '.*?', preg_quote($check, '#')).'$#i', $username)) {
return false;
}
}

return true;
}

public static function validateAvailability(string $username): ValidationErrors
{
$errors = new ValidationErrors('user');
Expand Down Expand Up @@ -72,11 +83,8 @@ public static function validateUsername($username)
$errors->add('username', '.username_no_space_userscore_mix');
}

foreach (model_pluck(DB::table('phpbb_disallow'), 'disallow_username') as $check) {
if (preg_match('#^'.str_replace('%', '.*?', preg_quote($check, '#')).'$#i', $username)) {
$errors->add('username', '.username_not_allowed');
break;
}
if (!static::allowedName($username)) {
$errors->add('username', '.username_not_allowed');
}

return $errors;
Expand Down
61 changes: 58 additions & 3 deletions app/Models/Team.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,27 @@

use App\Libraries\BBCodeForDB;
use App\Libraries\Uploader;
use App\Libraries\UsernameValidation;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Team extends Model
{
const MAX_FIELD_LENGTHS = [
'name' => 100,
'short_name' => 4,
];

protected $casts = ['is_open' => 'bool'];

private Uploader $header;
private Uploader $logo;

private static function sanitiseName(?string $value): ?string
{
return presence(preg_replace('/ +/', ' ', trim($value ?? '')));
}

public function applications(): HasMany
{
return $this->hasMany(TeamApplication::class);
Expand All @@ -34,6 +45,11 @@ public function members(): HasMany
return $this->hasMany(TeamMember::class);
}

public function setDefaultRulesetIdAttribute(?int $value): void
{
$this->attributes['default_ruleset_id'] = Beatmap::MODES[Beatmap::modeStr($value) ?? 'osu'];
}

public function setHeaderAttribute(?string $value): void
{
if ($value === null) {
Expand All @@ -52,9 +68,14 @@ public function setLogoAttribute(?string $value): void
}
}

public function setDefaultRulesetIdAttribute(?int $value): void
public function setNameAttribute(?string $value): void
{
$this->attributes['default_ruleset_id'] = Beatmap::MODES[Beatmap::modeStr($value) ?? 'osu'];
$this->attributes['name'] = static::sanitiseName($value);
}

public function setShortNameAttribute(?string $value): void
{
$this->attributes['short_name'] = static::sanitiseName($value);
}

public function setUrlAttribute(?string $value): void
Expand Down Expand Up @@ -114,6 +135,24 @@ public function isValid(): bool
{
$this->validationErrors()->reset();

$wordFilters = app('chat-filters');
foreach (['name', 'short_name'] as $field) {
$value = $this->$field;
if ($value === null) {
$this->validationErrors()->add($field, 'required');
} elseif ($this->isDirty($field)) {
if (!preg_match('#^[A-Za-z0-9-\[\]_ ]+$#u', $value)) {
$this->validationErrors()->add($field, '.invalid_characters');
} elseif (!$wordFilters->isClean($value) || !UsernameValidation::allowedName($value)) {
$this->validationErrors()->add($field, '.word_not_allowed');
} elseif (static::whereNot('id', $this->getKey())->where($field, $value)->exists()) {
$this->validationErrors()->add($field, '.used');
}
}
}

$this->validateDbFieldLengths();

if ($this->isDirty('url')) {
$url = $this->url;
if ($url !== null && !is_http($url)) {
Expand Down Expand Up @@ -144,6 +183,22 @@ public function maxMembers(): int
{
$this->loadMissing('members.user');

return 8 + (4 * $this->members->filter(fn ($member) => $member->user?->osu_subscriber ?? false)->count());
$supporterCount = $this->members->filter(fn ($member) => $member->user?->isSupporter() ?? false)->count();

return min(8 + (4 * $supporterCount), $GLOBALS['cfg']['osu']['team']['max_members']);
}

public function save(array $options = [])
{
if (!$this->isValid()) {
return false;
}

return parent::save($options);
}

public function validationErrorsTranslationPrefix(): string
{
return 'team';
}
}
61 changes: 45 additions & 16 deletions app/Singletons/ChatFilters.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@ private static function combinedFilterRegex($filters): string
return "/{$regex}/iu";
}

public function isClean(string $text): bool
{
$filters = $this->filterRegexps();

foreach ($filters['non_whitespace_delimited_replaces'] as $search => $_replacement) {
if (stripos($text, $search) !== false) {
return false;
}
}

$patterns = [
$filters['block_regex'] ?? null,
...array_keys($filters['whitespace_delimited_replaces']),
];

foreach ($patterns as $pattern) {
if ($pattern !== null && preg_match($pattern, $text)) {
return false;
}
}

return true;
}

/**
* Applies all active chat filters to the provided text.
* @param string $text The text to filter.
Expand All @@ -38,7 +62,27 @@ private static function combinedFilterRegex($filters): string
*/
public function filter(string $text): string
{
$filters = $this->memoize(__FUNCTION__, function () {
$filters = $this->filterRegexps();

if (isset($filters['block_regex']) && preg_match($filters['block_regex'], $text)) {
throw new ContentModerationException();
}

$text = str_ireplace(
array_keys($filters['non_whitespace_delimited_replaces']),
array_values($filters['non_whitespace_delimited_replaces']),
$text
);
return preg_replace(
array_keys($filters['whitespace_delimited_replaces']),
array_values($filters['whitespace_delimited_replaces']),
$text
);
}

private function filterRegexps(): array
{
return $this->memoize(__FUNCTION__, function () {
$ret = [];

$allFilters = ChatFilter::all();
Expand All @@ -63,20 +107,5 @@ public function filter(string $text): string

return $ret;
});

if (isset($filters['block_regex']) && preg_match($filters['block_regex'], $text)) {
throw new ContentModerationException();
}

$text = str_ireplace(
array_keys($filters['non_whitespace_delimited_replaces']),
array_values($filters['non_whitespace_delimited_replaces']),
$text
);
return preg_replace(
array_keys($filters['whitespace_delimited_replaces']),
array_values($filters['whitespace_delimited_replaces']),
$text
);
}
}
23 changes: 23 additions & 0 deletions app/Singletons/OsuAuthorize.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public static function alwaysCheck($ability)
'TeamApplicationAccept',
'TeamApplicationStore',
'TeamPart',
'TeamStore',
'TeamUpdate',
'UserUpdateEmail',
]);

Expand Down Expand Up @@ -1966,6 +1968,27 @@ public function checkTeamPart(?User $user, Team $team): ?string
return 'ok';
}

public function checkTeamStore(?User $user): ?string
{
$this->ensureLoggedIn($user);
$this->ensureCleanRecord($user);
$this->ensureHasPlayed($user);

if ($GLOBALS['cfg']['osu']['team']['create_require_supporter'] && !$user->isSupporter()) {
return 'team.store.require_supporter_tag';
}

if ($user->team !== null) {
return 'team.application.store.already_other_member';
}

if ($user->teamApplication !== null) {
return 'team.application.store.currently_applying';
}

return 'ok';
}

public function checkTeamUpdate(?User $user, Team $team): ?string
{
$this->ensureLoggedIn($user);
Expand Down
4 changes: 4 additions & 0 deletions config/osu.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@
'tags_cache_duration' => 60 * (get_int(env('TAGS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds
'beatmap_tags_cache_duration' => 60 * (get_int(env('BEATMAP_TAGS_CACHE_DURATION')) ?? 60), // in minutes, converted to seconds
],
'team' => [
'create_require_supporter' => get_bool(env('TEAM_CREATE_REQUIRE_SUPPORTER')) ?? false,
'max_members' => get_int(env('TEAM_MAX_MEMBERS')) ?? 40,
],
'twitch_client_id' => presence(env('TWITCH_CLIENT_ID')),
'twitch_client_secret' => presence(env('TWITCH_CLIENT_SECRET')),
'urls' => [
Expand Down
4 changes: 2 additions & 2 deletions database/factories/TeamFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public function configure(): static
public function definition(): array
{
return [
'name' => fn () => $this->faker->name(),
'short_name' => fn () => $this->faker->domainWord(),
'name' => fn () => strtr($this->faker->unique()->userName(), '.', ' '),
'short_name' => fn () => substr(strtr($this->faker->unique()->userName(), '.', ' '), 0, 4),
'leader_id' => User::factory(),
];
}
Expand Down
Loading

0 comments on commit 551a11a

Please sign in to comment.