first commit
This commit is contained in:
384
app/Rules/TeamName.php
Normal file
384
app/Rules/TeamName.php
Normal file
@@ -0,0 +1,384 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Routing\Route as RouteElement;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Translation\PotentiallyTranslatedString;
|
||||
|
||||
class TeamName implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param Closure(string, ?string=): PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$name = strtolower(trim($value));
|
||||
|
||||
if (in_array($name, $this->reservedNames(), true)) {
|
||||
$fail('This team name is reserved and cannot be used.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all reserved names.
|
||||
*/
|
||||
protected function reservedNames(): array
|
||||
{
|
||||
return once(fn () => collect($this->routesPrefixes())
|
||||
->merge([
|
||||
'300',
|
||||
'302',
|
||||
'400',
|
||||
'401',
|
||||
'402',
|
||||
'403',
|
||||
'404',
|
||||
'405',
|
||||
'406',
|
||||
'407',
|
||||
'408',
|
||||
'409',
|
||||
'410',
|
||||
'411',
|
||||
'412',
|
||||
'413',
|
||||
'414',
|
||||
'415',
|
||||
'416',
|
||||
'417',
|
||||
'418',
|
||||
'419',
|
||||
'420',
|
||||
'421',
|
||||
'422',
|
||||
'423',
|
||||
'424',
|
||||
'425',
|
||||
'426',
|
||||
'427',
|
||||
'428',
|
||||
'429',
|
||||
'430',
|
||||
'431',
|
||||
'500',
|
||||
'501',
|
||||
'502',
|
||||
'503',
|
||||
'504',
|
||||
'505',
|
||||
'506',
|
||||
'507',
|
||||
'508',
|
||||
'509',
|
||||
'510',
|
||||
'511',
|
||||
'693',
|
||||
'694',
|
||||
'695',
|
||||
'900',
|
||||
'about',
|
||||
'account',
|
||||
'accounts',
|
||||
'accountspayable',
|
||||
'accounts-payable',
|
||||
'accountsreceivable',
|
||||
'accounts-receivable',
|
||||
'admin',
|
||||
'advisories',
|
||||
'anonymous',
|
||||
'any',
|
||||
'api',
|
||||
'app',
|
||||
'apps',
|
||||
'attributes',
|
||||
'auth',
|
||||
'billing',
|
||||
'bills',
|
||||
'blob',
|
||||
'blog',
|
||||
'bounty',
|
||||
'branches',
|
||||
'business',
|
||||
'businesses',
|
||||
'cache',
|
||||
'careers',
|
||||
'case-studies',
|
||||
'categories',
|
||||
'central',
|
||||
'certification',
|
||||
'changelog',
|
||||
'chat',
|
||||
'cla',
|
||||
'claim',
|
||||
'claims',
|
||||
'cloud',
|
||||
'codereview',
|
||||
'code-review',
|
||||
'code-reviews',
|
||||
'collection',
|
||||
'collections',
|
||||
'comments',
|
||||
'commit',
|
||||
'commits',
|
||||
'companies',
|
||||
'compare',
|
||||
'contact',
|
||||
'contributing',
|
||||
'cookbook',
|
||||
'copilot',
|
||||
'coupons',
|
||||
'customer',
|
||||
'customer-stories',
|
||||
'customers',
|
||||
'dashboard',
|
||||
'dashboard-feed',
|
||||
'dashboards',
|
||||
'delete',
|
||||
'deploy',
|
||||
'design',
|
||||
'develop',
|
||||
'developer',
|
||||
'diff',
|
||||
'discover',
|
||||
'discussions',
|
||||
'downloads',
|
||||
'downtime',
|
||||
'editor',
|
||||
'editors',
|
||||
'edu',
|
||||
'education',
|
||||
'enterprise',
|
||||
'events',
|
||||
'explore',
|
||||
'featured',
|
||||
'features',
|
||||
'files',
|
||||
'fixtures',
|
||||
'forked',
|
||||
'fraud',
|
||||
'garage',
|
||||
'gist',
|
||||
'gists',
|
||||
'graphs',
|
||||
'groups',
|
||||
'guide',
|
||||
'guides',
|
||||
'help',
|
||||
'help-wanted',
|
||||
'home',
|
||||
'hooks',
|
||||
'hosting',
|
||||
'hovercards',
|
||||
'identity',
|
||||
'images',
|
||||
'inbox',
|
||||
'index',
|
||||
'individual',
|
||||
'info',
|
||||
'integration',
|
||||
'interfaces',
|
||||
'introduction',
|
||||
'invalid-email-address',
|
||||
'investors',
|
||||
'invoice',
|
||||
'invoices',
|
||||
'issues',
|
||||
'jobs',
|
||||
'join',
|
||||
'journal',
|
||||
'journals',
|
||||
'lab',
|
||||
'labs',
|
||||
'languages',
|
||||
'launch',
|
||||
'layouts',
|
||||
'learn',
|
||||
'legal',
|
||||
'library',
|
||||
'linux',
|
||||
'listings',
|
||||
'lists',
|
||||
'livewire',
|
||||
'login',
|
||||
'logos',
|
||||
'logout',
|
||||
'mac',
|
||||
'maintenance',
|
||||
'malware',
|
||||
'man',
|
||||
'mariadb',
|
||||
'marketing',
|
||||
'marketplace',
|
||||
'mention',
|
||||
'mentioned',
|
||||
'mentioning',
|
||||
'mentions',
|
||||
'migrating',
|
||||
'milestones',
|
||||
'mine',
|
||||
'mirrors',
|
||||
'mobile',
|
||||
'mysql',
|
||||
'n8n',
|
||||
'navigation',
|
||||
'network',
|
||||
'network-rules',
|
||||
'new',
|
||||
'news',
|
||||
'none',
|
||||
'nonprofit',
|
||||
'nonprofits',
|
||||
'notices',
|
||||
'notifications',
|
||||
'oauth',
|
||||
'offer',
|
||||
'open-source',
|
||||
'org',
|
||||
'organisations',
|
||||
'organizations',
|
||||
'orgs',
|
||||
'pages',
|
||||
'partners',
|
||||
'pay',
|
||||
'payable',
|
||||
'payments',
|
||||
'percona',
|
||||
'personal',
|
||||
'plans',
|
||||
'plugins',
|
||||
'popular',
|
||||
'popularity',
|
||||
'posts',
|
||||
'postgres',
|
||||
'postgresql',
|
||||
'press',
|
||||
'preview',
|
||||
'pricing',
|
||||
'professional',
|
||||
'projects',
|
||||
'pulls',
|
||||
'raw',
|
||||
'readme',
|
||||
'recommendations',
|
||||
'receivable',
|
||||
'redeem',
|
||||
'register',
|
||||
'releases',
|
||||
'render',
|
||||
'reply',
|
||||
'repositories',
|
||||
'resources',
|
||||
'restore',
|
||||
'revert',
|
||||
'review',
|
||||
'reviews',
|
||||
'save-net-neutrality',
|
||||
'saved',
|
||||
'scraping',
|
||||
'search',
|
||||
'security',
|
||||
'services',
|
||||
'sessions',
|
||||
'settings',
|
||||
'shareholders',
|
||||
'shop',
|
||||
'showcases',
|
||||
'signin',
|
||||
'sign-in',
|
||||
'signup',
|
||||
'sign-up',
|
||||
'site',
|
||||
'socials',
|
||||
'spam',
|
||||
'sponsors',
|
||||
'ssh',
|
||||
'staff',
|
||||
'starred',
|
||||
'stars',
|
||||
'start',
|
||||
'static',
|
||||
'status',
|
||||
'statuses',
|
||||
'storage',
|
||||
'store',
|
||||
'stories',
|
||||
'stripe',
|
||||
'styleguide',
|
||||
'subscriptions',
|
||||
'suggest',
|
||||
'suggestion',
|
||||
'suggestions',
|
||||
'support',
|
||||
'suspended',
|
||||
'suspension',
|
||||
'talks',
|
||||
'teach',
|
||||
'teacher',
|
||||
'teachers',
|
||||
'teaching',
|
||||
'team',
|
||||
'teams',
|
||||
'ten',
|
||||
'terms',
|
||||
'timeline',
|
||||
'topic',
|
||||
'topics',
|
||||
'tos',
|
||||
'tour',
|
||||
'train',
|
||||
'training',
|
||||
'translations',
|
||||
'tree',
|
||||
'trending',
|
||||
'undefined',
|
||||
'update',
|
||||
'updates',
|
||||
'upstash',
|
||||
'username',
|
||||
'users',
|
||||
'visualization',
|
||||
'watching',
|
||||
'wiki',
|
||||
'wikis',
|
||||
'windows',
|
||||
'works-with',
|
||||
'www',
|
||||
'www0',
|
||||
'www1',
|
||||
'www2',
|
||||
'www3',
|
||||
'www4',
|
||||
'www5',
|
||||
'www6',
|
||||
'www7',
|
||||
'www8',
|
||||
'www9',
|
||||
'zones',
|
||||
])
|
||||
->unique()
|
||||
->sort()
|
||||
->values()
|
||||
->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of reserved names from the application's route prefixes.
|
||||
*/
|
||||
protected function routesPrefixes(): array
|
||||
{
|
||||
return collect(Route::getRoutes()->getRoutes())
|
||||
->map(fn (RouteElement $route) => $route->uri)
|
||||
->map(fn (string $uri) => explode('/', $uri)[0])
|
||||
->reject(fn (string $uri) => str_contains($uri, '{'))
|
||||
->filter(fn (string $uri) => $uri !== '')
|
||||
->unique()
|
||||
->sort()
|
||||
->values()
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
50
app/Rules/UniqueTeamInvitation.php
Normal file
50
app/Rules/UniqueTeamInvitation.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Models\TeamInvitation;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Translation\PotentiallyTranslatedString;
|
||||
|
||||
class UniqueTeamInvitation implements ValidationRule
|
||||
{
|
||||
public function __construct(protected Team $team)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param Closure(string, ?string=): PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$email = strtolower($value);
|
||||
|
||||
$isMember = $this->team->members()
|
||||
->whereRaw('LOWER(email) = ?', [$email])
|
||||
->exists();
|
||||
|
||||
if ($isMember) {
|
||||
$fail(__('This user is already a member of the team.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$hasPendingInvitation = TeamInvitation::where('team_id', $this->team->id)
|
||||
->whereRaw('LOWER(email) = ?', [$email])
|
||||
->whereNull('accepted_at')
|
||||
->where(function ($query) {
|
||||
$query->whereNull('expires_at')
|
||||
->orWhere('expires_at', '>', now());
|
||||
})
|
||||
->exists();
|
||||
|
||||
if ($hasPendingInvitation) {
|
||||
$fail(__('An invitation has already been sent to this email address.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
47
app/Rules/ValidTeamInvitation.php
Normal file
47
app/Rules/ValidTeamInvitation.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Models\TeamInvitation;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Translation\PotentiallyTranslatedString;
|
||||
|
||||
class ValidTeamInvitation implements ValidationRule
|
||||
{
|
||||
public function __construct(protected ?User $user)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param Closure(string, ?string=): PotentiallyTranslatedString $fail
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (! $value instanceof TeamInvitation || ! $this->user instanceof User) {
|
||||
$fail(__('This invitation was sent to a different email address.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value->isAccepted()) {
|
||||
$fail(__('This invitation has already been accepted.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value->isExpired()) {
|
||||
$fail(__('This invitation has expired.'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strtolower($value->email) !== strtolower($this->user->email)) {
|
||||
$fail(__('This invitation was sent to a different email address.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user