Moved invitation from jetstream to API; Deactived moved jetstream features

This commit is contained in:
Constantin Graf
2024-07-15 17:17:56 +02:00
committed by Constantin Graf
parent 555417dbbd
commit fd8d596e9b
25 changed files with 294 additions and 348 deletions

View File

@@ -7,7 +7,6 @@ namespace App\Actions\Jetstream;
use App\Enums\Role;
use App\Models\Organization;
use App\Models\User;
use App\Service\UserService;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Builder;
@@ -43,10 +42,6 @@ class AddOrganizationMember implements AddsTeamMembers
$organization->users()->attach(
$newOrganizationMember, ['role' => $role]
);
if ($role === Role::Owner->value) {
app(UserService::class)->changeOwnership($organization, $newOrganizationMember);
}
});
TeamMemberAdded::dispatch($organization, $newOrganizationMember);
@@ -84,7 +79,6 @@ class AddOrganizationMember implements AddsTeamMembers
'required',
'string',
Rule::in([
Role::Owner->value,
Role::Admin->value,
Role::Manager->value,
Role::Employee->value,

View File

@@ -15,6 +15,7 @@ class DeleteOrganization implements DeletesTeams
*/
public function delete(Organization $organization): void
{
/** @see ValidateOrganizationDeletion */
app(DeletionService::class)->deleteOrganization($organization);
}
}

View File

@@ -14,6 +14,8 @@ class DeleteUser implements DeletesUsers
{
/**
* Delete the given user.
*
* @throws ValidationException
*/
public function delete(User $user): void
{

View File

@@ -4,103 +4,21 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Enums\Role;
use App\Exceptions\MovedToApiException;
use App\Models\Organization;
use App\Models\OrganizationInvitation;
use App\Models\User;
use App\Service\PermissionStore;
use Closure;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\In;
use Korridor\LaravelModelValidationRules\Rules\UniqueEloquent;
use Exception;
use Laravel\Jetstream\Contracts\InvitesTeamMembers;
use Laravel\Jetstream\Events\InvitingTeamMember;
use Laravel\Jetstream\Mail\TeamInvitation;
class InviteOrganizationMember implements InvitesTeamMembers
{
/**
* Invite a new team member to the given team.
*
* @throws AuthorizationException
* @throws Exception
*/
public function invite(User $user, Organization $organization, string $email, ?string $role = null): void
{
if (! app(PermissionStore::class)->has($organization, 'invitations:create')) {
throw new AuthorizationException();
}
$this->validate($organization, $email, $role);
InvitingTeamMember::dispatch($organization, $email, $role);
/** @var OrganizationInvitation $invitation */
$invitation = $organization->teamInvitations()->create([
'email' => $email,
'role' => $role,
]);
Mail::to($email)->send(new TeamInvitation($invitation));
}
/**
* Validate the invite member operation.
*/
protected function validate(Organization $organization, string $email, ?string $role): void
{
Validator::make([
'email' => $email,
'role' => $role,
], $this->rules($organization))->after(
$this->ensureUserIsNotAlreadyOnTeam($organization, $email)
)->validateWithBag('addTeamMember');
}
/**
* Get the validation rules for inviting a team member.
*
* @return array<string, array<ValidationRule|Rule|string|In>>
*/
protected function rules(Organization $organization): array
{
return array_filter([
'email' => [
'required',
'email',
(new UniqueEloquent(OrganizationInvitation::class, 'email', function (Builder $builder) use ($organization) {
/** @var Builder<OrganizationInvitation> $builder */
return $builder->whereBelongsTo($organization, 'organization');
}))->withMessage(__('This user has already been invited to the team.')),
],
'role' => [
'required',
'string',
Rule::in([
Role::Owner->value,
Role::Admin->value,
Role::Manager->value,
Role::Employee->value,
]),
],
]);
}
/**
* Ensure that the user is not already on the team.
*/
protected function ensureUserIsNotAlreadyOnTeam(Organization $organization, string $email): Closure
{
return function ($validator) use ($organization, $email) {
$validator->errors()->addIf(
$organization->hasRealUserWithEmail($email),
'email',
__('This user already belongs to the team.')
);
};
throw new MovedToApiException();
}
}

View File

@@ -4,50 +4,21 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Exceptions\MovedToApiException;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\ValidationException;
use Exception;
use Laravel\Jetstream\Contracts\RemovesTeamMembers;
use Laravel\Jetstream\Events\TeamMemberRemoved;
class RemoveOrganizationMember implements RemovesTeamMembers
{
/**
* Remove the team member from the given team.
*
* @throws Exception
*/
public function remove(User $user, Organization $organization, User $teamMember): void
{
$this->authorize($user, $organization, $teamMember);
$this->ensureUserDoesNotOwnTeam($teamMember, $organization);
$organization->removeUser($teamMember);
TeamMemberRemoved::dispatch($organization, $teamMember);
}
/**
* Authorize that the user can remove the team member.
*/
protected function authorize(User $user, Organization $organization, User $teamMember): void
{
if (! Gate::forUser($user)->check('removeTeamMember', $organization) &&
$user->id !== $teamMember->id) {
throw new AuthorizationException;
}
}
/**
* Ensure that the currently authenticated user does not own the team.
*/
protected function ensureUserDoesNotOwnTeam(User $teamMember, Organization $organization): void
{
if ($teamMember->id === $organization->owner->id) {
throw ValidationException::withMessages([
'team' => [__('You may not leave a team that you created.')],
])->errorBag('removeTeamMember');
}
throw new MovedToApiException();
}
}

View File

@@ -5,63 +5,21 @@ declare(strict_types=1);
namespace App\Actions\Jetstream;
use App\Enums\Role;
use App\Exceptions\MovedToApiException;
use App\Models\Member;
use App\Models\Organization;
use App\Models\User;
use App\Service\PermissionStore;
use App\Service\UserService;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use Laravel\Jetstream\Events\TeamMemberUpdated;
use Exception;
class UpdateMemberRole
{
/**
* Update the role for the given team member.
*
* @throws AuthorizationException
* @throws ValidationException
* @throws Exception
*/
public function update(User $actingUser, Organization $organization, string $userId, string $role): void
{
if (! app(PermissionStore::class)->has($organization, 'members:change-ownership')) {
throw new AuthorizationException();
}
$user = User::where('id', '=', $userId)->firstOrFail();
$member = Member::whereBelongsTo($user)->whereBelongsTo($organization)->firstOrFail();
if ($member->role === Role::Placeholder->value) {
abort(403, 'Cannot update the role of a placeholder member.');
}
Validator::make([
'role' => $role,
], [
'role' => [
'required',
'string',
Rule::in([
Role::Owner->value,
Role::Admin->value,
Role::Manager->value,
Role::Employee->value,
]),
],
])->validate();
DB::transaction(function () use ($organization, $userId, $role, $user) {
$organization->users()->updateExistingPivot($userId, [
'role' => $role,
]);
if ($role === Role::Owner->value) {
app(UserService::class)->changeOwnership($organization, $user);
}
});
TeamMemberUpdated::dispatch($organization->fresh(), User::findOrFail($userId));
throw new MovedToApiException();
}
}