40
common/Workspaces/Actions/CrupdateWorkspace.php
Executable file
40
common/Workspaces/Actions/CrupdateWorkspace.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Actions;
|
||||
|
||||
use Auth;
|
||||
use Common\Workspaces\Workspace;
|
||||
|
||||
class CrupdateWorkspace
|
||||
{
|
||||
public function __construct(protected Workspace $workspace)
|
||||
{
|
||||
}
|
||||
|
||||
public function execute(
|
||||
array $data,
|
||||
Workspace|null $initialWorkspace = null
|
||||
): Workspace {
|
||||
if ($initialWorkspace) {
|
||||
$workspace = $initialWorkspace;
|
||||
} else {
|
||||
$workspace = $this->workspace->newInstance([
|
||||
'owner_id' => Auth::id(),
|
||||
]);
|
||||
}
|
||||
|
||||
$attributes = [
|
||||
'name' => $data['name'],
|
||||
];
|
||||
|
||||
$workspace->fill($attributes)->save();
|
||||
|
||||
if (!$initialWorkspace) {
|
||||
$workspace
|
||||
->members()
|
||||
->create(['user_id' => Auth::id(), 'is_owner' => true]);
|
||||
}
|
||||
|
||||
return $workspace;
|
||||
}
|
||||
}
|
||||
30
common/Workspaces/Actions/DeleteInviteNotification.php
Executable file
30
common/Workspaces/Actions/DeleteInviteNotification.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Actions;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Workspaces\Notifications\WorkspaceInvitation;
|
||||
use Common\Workspaces\WorkspaceInvite;
|
||||
use Illuminate\Notifications\DatabaseNotification;
|
||||
|
||||
class DeleteInviteNotification
|
||||
{
|
||||
public function execute(WorkspaceInvite $invite, User $user): void
|
||||
{
|
||||
$notifications = $user
|
||||
->notifications()
|
||||
->where('type', WorkspaceInvitation::class)
|
||||
->limit(20)
|
||||
->get();
|
||||
|
||||
$notification = $notifications->first(function (
|
||||
DatabaseNotification $notification,
|
||||
) use ($invite) {
|
||||
return $notification->data['inviteId'] === $invite->id;
|
||||
});
|
||||
|
||||
if ($notification) {
|
||||
$notification->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
36
common/Workspaces/Actions/DeleteWorkspaces.php
Executable file
36
common/Workspaces/Actions/DeleteWorkspaces.php
Executable file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Actions;
|
||||
|
||||
use Common\Workspaces\Events\WorkspaceDeleted;
|
||||
use Common\Workspaces\Workspace;
|
||||
use Common\Workspaces\WorkspaceMember;
|
||||
|
||||
class DeleteWorkspaces
|
||||
{
|
||||
/**
|
||||
* @var Workspace
|
||||
*/
|
||||
private $workspace;
|
||||
|
||||
public function __construct(Workspace $workspace)
|
||||
{
|
||||
$this->workspace = $workspace;
|
||||
}
|
||||
|
||||
public function execute($ids)
|
||||
{
|
||||
$workspaces = $this->workspace->whereIn('id', $ids)->get();
|
||||
|
||||
$workspaces->each(function(Workspace $workspace) {
|
||||
$workspace->invites()->delete();
|
||||
$workspace->members->each(function (WorkspaceMember $member) use($workspace) {
|
||||
app(RemoveMemberFromWorkspace::class)->execute($workspace, $member->id);
|
||||
});
|
||||
event(new WorkspaceDeleted($workspace->id, $workspace->owner_id));
|
||||
});
|
||||
|
||||
$this->workspace->whereIn('id', $ids)->delete();
|
||||
}
|
||||
|
||||
}
|
||||
27
common/Workspaces/Actions/JoinWorkspace.php
Executable file
27
common/Workspaces/Actions/JoinWorkspace.php
Executable file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Actions;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Workspaces\WorkspaceInvite;
|
||||
use Session;
|
||||
|
||||
class JoinWorkspace
|
||||
{
|
||||
public function execute(WorkspaceInvite $invite, User $user)
|
||||
{
|
||||
if ($invite->email === $user->email) {
|
||||
$invite->workspace
|
||||
->members()
|
||||
->firstOrCreate(
|
||||
['user_id' => $user->id],
|
||||
['role_id' => $invite->role_id],
|
||||
);
|
||||
|
||||
app(DeleteInviteNotification::class)->execute($invite, $user);
|
||||
|
||||
$invite->delete();
|
||||
}
|
||||
Session::remove('activeWorkspace');
|
||||
}
|
||||
}
|
||||
38
common/Workspaces/Actions/RemoveMemberFromWorkspace.php
Executable file
38
common/Workspaces/Actions/RemoveMemberFromWorkspace.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Actions;
|
||||
|
||||
use Common\Workspaces\Workspace;
|
||||
use Common\Workspaces\WorkspaceMember;
|
||||
use const App\Providers\WORKSPACED_RESOURCES;
|
||||
|
||||
class RemoveMemberFromWorkspace
|
||||
{
|
||||
public function execute(Workspace $workspace, int $userToBeRemoved)
|
||||
{
|
||||
// transfer workspace resources to owner
|
||||
if ($workspace->owner_id !== $userToBeRemoved) {
|
||||
foreach (WORKSPACED_RESOURCES as $model) {
|
||||
$baseName = class_basename($model);
|
||||
$namespace = "App\Workspaces\Transfer{$baseName}";
|
||||
if (class_exists($namespace)) {
|
||||
app($namespace)->execute(
|
||||
$workspace->id,
|
||||
$workspace->owner_id,
|
||||
$userToBeRemoved,
|
||||
);
|
||||
} else {
|
||||
app($model)
|
||||
->where('workspace_id', $workspace->id)
|
||||
->where('user_id', $userToBeRemoved)
|
||||
->update(['user_id' => $workspace->owner_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app(WorkspaceMember::class)
|
||||
->where('workspace_id', $workspace->id)
|
||||
->where('user_id', $userToBeRemoved)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
76
common/Workspaces/ActiveWorkspace.php
Executable file
76
common/Workspaces/ActiveWorkspace.php
Executable file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces;
|
||||
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
|
||||
class ActiveWorkspace
|
||||
{
|
||||
protected Workspace|null $cachedWorkspace = null;
|
||||
public array $memberCache = [];
|
||||
public int $id = 0;
|
||||
|
||||
/**
|
||||
* Whether selected workspace was explicitly specified via request query params or defaulted to personal.
|
||||
*/
|
||||
public bool $explicitlySelected = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->explicitlySelected = request()->has('workspaceId');
|
||||
$this->id = (int) request()->get('workspaceId', 0);
|
||||
}
|
||||
|
||||
public function workspace(): ?Workspace
|
||||
{
|
||||
if (
|
||||
is_null($this->cachedWorkspace) ||
|
||||
$this->cachedWorkspace->id !== $this->id
|
||||
) {
|
||||
$this->cachedWorkspace = $this->isPersonal()
|
||||
? null
|
||||
: Workspace::find($this->id);
|
||||
}
|
||||
|
||||
return $this->cachedWorkspace ?: null;
|
||||
}
|
||||
|
||||
public function isPersonal(): bool
|
||||
{
|
||||
return !$this->id;
|
||||
}
|
||||
|
||||
public function owner(): User
|
||||
{
|
||||
return $this->workspace()->owner_id === Auth::id()
|
||||
? Auth::user()
|
||||
: $this->workspace()->owner;
|
||||
}
|
||||
|
||||
public function currentUserIsOwner(): bool
|
||||
{
|
||||
if ($this->isPersonal()) {
|
||||
return true;
|
||||
}
|
||||
return $this->workspace() &&
|
||||
$this->workspace()->owner_id === Auth::id();
|
||||
}
|
||||
|
||||
public function member(int $userId): ?WorkspaceMember
|
||||
{
|
||||
if (!$this->workspace()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($this->memberCache[$userId])) {
|
||||
$this->memberCache[$userId] = app(WorkspaceMember::class)
|
||||
->where([
|
||||
'user_id' => $userId,
|
||||
'workspace_id' => $this->workspace()->id,
|
||||
])
|
||||
->first();
|
||||
}
|
||||
return $this->memberCache[$userId];
|
||||
}
|
||||
}
|
||||
108
common/Workspaces/Controllers/WorkspaceController.php
Executable file
108
common/Workspaces/Controllers/WorkspaceController.php
Executable file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Controllers;
|
||||
|
||||
use Auth;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Database\Datasource\Datasource;
|
||||
use Common\Workspaces\Actions\CrupdateWorkspace;
|
||||
use Common\Workspaces\Actions\DeleteWorkspaces;
|
||||
use Common\Workspaces\Requests\CrupdateWorkspaceRequest;
|
||||
use Common\Workspaces\Workspace;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class WorkspaceController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
protected Workspace $workspace,
|
||||
protected Request $request
|
||||
) {
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$userId = $this->request->get('userId');
|
||||
$this->authorize('index', [Workspace::class, $userId]);
|
||||
|
||||
$builder = $this->workspace
|
||||
->newQuery()
|
||||
->withCount(['members'])
|
||||
->with([
|
||||
'members' => function (HasMany $builder) {
|
||||
$builder->with('permissions')->currentUserAndOwnerOnly();
|
||||
},
|
||||
]);
|
||||
if ($userId) {
|
||||
$builder->forUser($userId);
|
||||
}
|
||||
|
||||
$dataSource = new Datasource($builder, $this->request->all());
|
||||
|
||||
$pagination = $dataSource->paginate();
|
||||
|
||||
$pagination->transform(function (Workspace $workspace) {
|
||||
return $workspace->setCurrentUserAndOwner();
|
||||
});
|
||||
|
||||
return $this->success(['pagination' => $pagination]);
|
||||
}
|
||||
|
||||
public function show(Workspace $workspace)
|
||||
{
|
||||
$this->authorize('show', $workspace);
|
||||
|
||||
$workspace->load(['invites', 'members']);
|
||||
|
||||
if (
|
||||
$workspace->currentUser = $workspace->members
|
||||
->where('id', Auth::id())
|
||||
->first()
|
||||
) {
|
||||
$workspace->currentUser->load('permissions');
|
||||
}
|
||||
|
||||
return $this->success(['workspace' => $workspace]);
|
||||
}
|
||||
|
||||
public function store(CrupdateWorkspaceRequest $request)
|
||||
{
|
||||
$this->authorize('store', Workspace::class);
|
||||
|
||||
$workspace = app(CrupdateWorkspace::class)->execute($request->all());
|
||||
$workspace->loadCount('members');
|
||||
$workspace
|
||||
->load([
|
||||
'members' => function (HasMany $builder) {
|
||||
$builder->currentUserAndOwnerOnly();
|
||||
},
|
||||
])
|
||||
->setCurrentUserAndOwner();
|
||||
|
||||
return $this->success(['workspace' => $workspace]);
|
||||
}
|
||||
|
||||
public function update(
|
||||
Workspace $workspace,
|
||||
CrupdateWorkspaceRequest $request
|
||||
) {
|
||||
$this->authorize('update', $workspace);
|
||||
|
||||
$workspace = app(CrupdateWorkspace::class)->execute(
|
||||
$request->all(),
|
||||
$workspace,
|
||||
);
|
||||
|
||||
return $this->success(['workspace' => $workspace]);
|
||||
}
|
||||
|
||||
public function destroy(string $ids)
|
||||
{
|
||||
$workspaceIds = explode(',', $ids);
|
||||
$this->authorize('destroy', [Workspace::class, $workspaceIds]);
|
||||
|
||||
app(DeleteWorkspaces::class)->execute($workspaceIds);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
212
common/Workspaces/Controllers/WorkspaceInvitesController.php
Executable file
212
common/Workspaces/Controllers/WorkspaceInvitesController.php
Executable file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Arr;
|
||||
use Auth;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Settings\Settings;
|
||||
use Common\Validation\Validators\EmailsAreValid;
|
||||
use Common\Workspaces\Actions\DeleteInviteNotification;
|
||||
use Common\Workspaces\Notifications\WorkspaceInvitation;
|
||||
use Common\Workspaces\Workspace;
|
||||
use Common\Workspaces\WorkspaceInvite;
|
||||
use Common\Workspaces\WorkspaceMember;
|
||||
use Illuminate\Http\Request;
|
||||
use Notification;
|
||||
use Str;
|
||||
|
||||
class WorkspaceInvitesController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var WorkspaceInvite
|
||||
*/
|
||||
private $workspaceInvite;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var Settings
|
||||
*/
|
||||
private $settings;
|
||||
|
||||
public function __construct(
|
||||
Request $request,
|
||||
WorkspaceInvite $workspaceInvite,
|
||||
User $user,
|
||||
Settings $settings,
|
||||
) {
|
||||
$this->request = $request;
|
||||
$this->workspaceInvite = $workspaceInvite;
|
||||
$this->user = $user;
|
||||
$this->settings = $settings;
|
||||
}
|
||||
|
||||
public function resend(
|
||||
Workspace $workspace,
|
||||
WorkspaceInvite $workspaceInvite,
|
||||
) {
|
||||
$this->authorize('store', [WorkspaceMember::class, $workspace, false]);
|
||||
|
||||
$notification = new WorkspaceInvitation(
|
||||
$workspace,
|
||||
Auth::user()->display_name,
|
||||
$workspaceInvite['id'],
|
||||
);
|
||||
|
||||
if ($workspaceInvite->user) {
|
||||
Notification::send($workspaceInvite->user, $notification);
|
||||
} else {
|
||||
Notification::route('mail', $workspaceInvite['email'])->notify(
|
||||
$notification,
|
||||
);
|
||||
}
|
||||
$workspaceInvite->touch();
|
||||
|
||||
return $this->success(['invite' => $workspaceInvite]);
|
||||
}
|
||||
|
||||
public function store(Workspace $workspace)
|
||||
{
|
||||
$this->authorize('store', [WorkspaceMember::class, $workspace]);
|
||||
|
||||
$emailsRules = ['required', 'array'];
|
||||
if (settings('registration.disable')) {
|
||||
$emailsRules[] = new EmailsAreValid();
|
||||
}
|
||||
$validatedData = $this->request->validate([
|
||||
'emails' => $emailsRules,
|
||||
'emails.*' => 'required|email',
|
||||
'roleId' => 'required|int',
|
||||
]);
|
||||
|
||||
$invites = app(WorkspaceInvite::class)
|
||||
->where('workspace_id', $workspace->id)
|
||||
->whereIn('email', $validatedData['emails'])
|
||||
->pluck('email');
|
||||
$alreadyInvitedEmails = app(WorkspaceMember::class)
|
||||
->where('workspace_id', $workspace->id)
|
||||
->join('users', 'users.id', 'workspace_user.user_id')
|
||||
->where('users.email', $validatedData['emails'])
|
||||
->pluck('email')
|
||||
->merge($invites)
|
||||
->toArray();
|
||||
|
||||
$validatedData['emails'] = array_diff(
|
||||
$validatedData['emails'],
|
||||
$alreadyInvitedEmails,
|
||||
);
|
||||
|
||||
if (!empty($validatedData['emails'])) {
|
||||
$existingUsers = $this->user
|
||||
->whereIn('email', $validatedData['emails'])
|
||||
->get()
|
||||
->keyBy('email');
|
||||
|
||||
$workspaceInvites = collect($validatedData['emails'])
|
||||
->map(function ($email) use (
|
||||
$existingUsers,
|
||||
$validatedData,
|
||||
$workspace,
|
||||
) {
|
||||
// if registration is disabled, only allow inviting already registered users
|
||||
if (
|
||||
settings('registration.disable') &&
|
||||
!isset($existingUsers[$email])
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return [
|
||||
'id' => Str::orderedUuid(),
|
||||
'email' => $email,
|
||||
'user_id' => $existingUsers[$email]['id'] ?? null,
|
||||
'workspace_id' => $workspace->id,
|
||||
'avatar' => isset($existingUsers[$email])
|
||||
? $existingUsers[$email]->getRawOriginal(
|
||||
'avatar',
|
||||
) ?? null
|
||||
: null,
|
||||
'role_id' => $validatedData['roleId'],
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
})
|
||||
->filter();
|
||||
|
||||
$this->workspaceInvite->insert($workspaceInvites->toArray());
|
||||
|
||||
$workspaceInvites->each(function ($invite) use (
|
||||
$workspace,
|
||||
$existingUsers,
|
||||
) {
|
||||
$notification = new WorkspaceInvitation(
|
||||
$workspace,
|
||||
Auth::user()->display_name,
|
||||
$invite['id'],
|
||||
);
|
||||
if ($user = Arr::get($existingUsers, $invite['email'])) {
|
||||
Notification::send($user, $notification);
|
||||
} else {
|
||||
Notification::route('mail', $invite['email'])->notify(
|
||||
$notification,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
$invites = $workspace
|
||||
->invites()
|
||||
->whereIn(
|
||||
'workspace_invites.id',
|
||||
$workspaceInvites->pluck('id'),
|
||||
)
|
||||
->get();
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'invites' => $invites ?? [],
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(WorkspaceInvite $workspaceInvite)
|
||||
{
|
||||
$workspace = Workspace::findOrFail($workspaceInvite->workspace_id);
|
||||
$this->authorize('destroy', [
|
||||
WorkspaceMember::class,
|
||||
$workspace,
|
||||
$workspaceInvite->user_id,
|
||||
]);
|
||||
|
||||
if ($workspaceInvite->user) {
|
||||
app(DeleteInviteNotification::class)->execute(
|
||||
$workspaceInvite,
|
||||
$workspaceInvite->user,
|
||||
);
|
||||
}
|
||||
|
||||
$workspaceInvite->delete();
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function changeRole(Workspace $workspace, string $inviteId)
|
||||
{
|
||||
$this->authorize('update', [WorkspaceMember::class, $workspace]);
|
||||
|
||||
$validatedData = $this->request->validate([
|
||||
'roleId' => 'required|integer',
|
||||
]);
|
||||
|
||||
app(WorkspaceInvite::class)
|
||||
->where('id', $inviteId)
|
||||
->update(['role_id' => $validatedData['roleId']]);
|
||||
}
|
||||
}
|
||||
79
common/Workspaces/Controllers/WorkspaceMembersController.php
Executable file
79
common/Workspaces/Controllers/WorkspaceMembersController.php
Executable file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
use Auth;
|
||||
use Common\Core\BaseController;
|
||||
use Common\Workspaces\Actions\JoinWorkspace;
|
||||
use Common\Workspaces\Actions\RemoveMemberFromWorkspace;
|
||||
use Common\Workspaces\Workspace;
|
||||
use Common\Workspaces\WorkspaceInvite;
|
||||
use Common\Workspaces\WorkspaceMember;
|
||||
use Illuminate\Http\Request;
|
||||
use Session;
|
||||
use const App\Providers\WORKSPACE_HOME_ROUTE;
|
||||
|
||||
class WorkspaceMembersController extends BaseController
|
||||
{
|
||||
public function __construct(
|
||||
protected Request $request,
|
||||
protected User $user
|
||||
) {
|
||||
}
|
||||
|
||||
public function join(WorkspaceInvite $workspaceInvite)
|
||||
{
|
||||
if ($user = Auth::user()) {
|
||||
app(JoinWorkspace::class)->execute($workspaceInvite, $user);
|
||||
if ($this->request->expectsJson()) {
|
||||
return $this->success([
|
||||
'workspace' => $workspaceInvite->workspace->loadCount(
|
||||
'members',
|
||||
),
|
||||
]);
|
||||
} else {
|
||||
return redirect(WORKSPACE_HOME_ROUTE);
|
||||
}
|
||||
} else {
|
||||
Session::put('workspaceInvite', $workspaceInvite->id);
|
||||
if (User::where('email', $workspaceInvite->email)->exists()) {
|
||||
return redirect(
|
||||
"workspace/join/login?email={$workspaceInvite->email}",
|
||||
);
|
||||
} else {
|
||||
return redirect(
|
||||
"workspace/join/register?email={$workspaceInvite->email}",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(Workspace $workspace, int $userId)
|
||||
{
|
||||
$this->authorize('destroy', [
|
||||
WorkspaceMember::class,
|
||||
$workspace,
|
||||
$userId,
|
||||
]);
|
||||
|
||||
app(RemoveMemberFromWorkspace::class)->execute($workspace, $userId);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
|
||||
public function changeRole(Workspace $workspace, int $memberId)
|
||||
{
|
||||
$this->authorize('update', [WorkspaceMember::class, $workspace]);
|
||||
|
||||
$validatedData = $this->request->validate([
|
||||
'roleId' => 'required|integer',
|
||||
]);
|
||||
|
||||
app(WorkspaceMember::class)
|
||||
->where('id', $memberId)
|
||||
->update(['role_id' => $validatedData['roleId']]);
|
||||
|
||||
return $this->success();
|
||||
}
|
||||
}
|
||||
28
common/Workspaces/Events/WorkspaceDeleted.php
Executable file
28
common/Workspaces/Events/WorkspaceDeleted.php
Executable file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Events;
|
||||
|
||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class WorkspaceDeleted
|
||||
{
|
||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $workspaceId;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $ownerId;
|
||||
|
||||
public function __construct(int $workspaceId, int $ownerId)
|
||||
{
|
||||
$this->workspaceId = $workspaceId;
|
||||
$this->ownerId = $ownerId;
|
||||
}
|
||||
}
|
||||
26
common/Workspaces/Listeners/AttachWorkspaceToUser.php
Executable file
26
common/Workspaces/Listeners/AttachWorkspaceToUser.php
Executable file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Listeners;
|
||||
|
||||
use Common\Workspaces\Actions\JoinWorkspace;
|
||||
use Common\Workspaces\WorkspaceInvite;
|
||||
use Illuminate\Auth\Events\Login;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
|
||||
class AttachWorkspaceToUser
|
||||
{
|
||||
/**
|
||||
* @param Login|Registered $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
$inviteId = session()->get('workspaceInvite');
|
||||
if ( ! $inviteId) return;
|
||||
|
||||
$invite = app(WorkspaceInvite::class)->find($inviteId);
|
||||
if ($invite) {
|
||||
app(JoinWorkspace::class)->execute($invite, $event->user);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
common/Workspaces/Notifications/WorkspaceInvitation.php
Executable file
118
common/Workspaces/Notifications/WorkspaceInvitation.php
Executable file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Notifications;
|
||||
|
||||
use Common\Workspaces\Workspace;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class WorkspaceInvitation extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
const NOTIF_ID = 'W01';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $workspace;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $inviterName;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $joinCode;
|
||||
|
||||
public function __construct(
|
||||
Workspace $workspace,
|
||||
string $inviterName,
|
||||
string $joinCode
|
||||
) {
|
||||
$this->workspace = $workspace;
|
||||
$this->inviterName = $inviterName;
|
||||
$this->joinCode = $joinCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the notification's delivery channels.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function via($notifiable)
|
||||
{
|
||||
return ['mail', 'database'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mail representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return MailMessage
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
$data = [
|
||||
'inviter' => ucfirst($this->inviterName),
|
||||
'workspace' => ucfirst($this->workspace->name),
|
||||
'siteName' => config('app.name'),
|
||||
];
|
||||
|
||||
return (new MailMessage())
|
||||
->subject(__(':inviter invited you to :siteName :workspace', $data))
|
||||
->line(__('Join your :workspace teammates on :siteName', $data))
|
||||
->action(
|
||||
__('Join your team'),
|
||||
url("workspace/join/{$this->joinCode}"),
|
||||
)
|
||||
->line(__('This invitation link will expire in 3 days.'))
|
||||
->line(
|
||||
__(
|
||||
'If you do not wish to join this workspace, no further action is required.',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array representation of the notification.
|
||||
*
|
||||
* @param mixed $notifiable
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($notifiable)
|
||||
{
|
||||
$translateData = [
|
||||
'inviter' => ucfirst($this->inviterName),
|
||||
'workspace' => ucfirst($this->workspace->name),
|
||||
];
|
||||
|
||||
return [
|
||||
'inviteId' => $this->joinCode,
|
||||
'lines' => [
|
||||
[
|
||||
'content' => __(
|
||||
':inviter invited you to join `:workspace.`',
|
||||
$translateData,
|
||||
),
|
||||
],
|
||||
[
|
||||
'content' => __(
|
||||
'Accepting the invitation will give you access to links, domains, overlays and other resources in this workspace.',
|
||||
),
|
||||
],
|
||||
],
|
||||
'buttonActions' => [
|
||||
['label' => 'Join', 'action' => 'join', 'color' => 'primary'],
|
||||
[
|
||||
'label' => 'Decline',
|
||||
'action' => 'decline',
|
||||
'color' => 'error',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
76
common/Workspaces/Policies/WorkspaceMemberPolicy.php
Executable file
76
common/Workspaces/Policies/WorkspaceMemberPolicy.php
Executable file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Core\Policies\BasePolicy;
|
||||
use Common\Workspaces\Workspace;
|
||||
|
||||
class WorkspaceMemberPolicy extends BasePolicy
|
||||
{
|
||||
public function store(
|
||||
User $currentUser,
|
||||
Workspace $workspace,
|
||||
$checkMemberCount = true
|
||||
) {
|
||||
$member = $workspace->findMember($currentUser);
|
||||
|
||||
if (!$member || !$member->hasPermission('workspace_members.invite')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$owner =
|
||||
$currentUser->id === $workspace->owner_id
|
||||
? $currentUser
|
||||
: $workspace->owner;
|
||||
$maxMemberCount = $owner->getRestrictionValue(
|
||||
'workspaces.create',
|
||||
'member_count',
|
||||
);
|
||||
|
||||
if (!$checkMemberCount || !$maxMemberCount) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$currentMemberCount =
|
||||
$workspace->members()->count() + $workspace->invites->count();
|
||||
|
||||
if ($currentMemberCount >= $maxMemberCount) {
|
||||
$message = __('policies.workspace_member_quota_exceeded');
|
||||
return $this->denyWithAction(
|
||||
$message,
|
||||
$owner->id === $currentUser->id ? $this->upgradeAction() : null,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function update(User $currentUser, Workspace $workspace)
|
||||
{
|
||||
if ($workspace->isOwner($currentUser)) {
|
||||
return true;
|
||||
} else {
|
||||
return $workspace
|
||||
->findMember($currentUser)
|
||||
->hasPermission('workspace_members.update');
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(
|
||||
User $currentUser,
|
||||
Workspace $workspace,
|
||||
int $userId = null
|
||||
) {
|
||||
if ($workspace->isOwner($currentUser)) {
|
||||
return true;
|
||||
} elseif ($currentUser->id === $userId) {
|
||||
// user is trying to delete their own membership, aka leaving workspace
|
||||
return true;
|
||||
} else {
|
||||
return $workspace
|
||||
->findMember($currentUser)
|
||||
->hasPermission('workspace_members.delete');
|
||||
}
|
||||
}
|
||||
}
|
||||
43
common/Workspaces/Policies/WorkspacePolicy.php
Executable file
43
common/Workspaces/Policies/WorkspacePolicy.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Policies;
|
||||
|
||||
use Common\Auth\BaseUser;
|
||||
use Common\Core\Policies\BasePolicy;
|
||||
use Common\Workspaces\Workspace;
|
||||
|
||||
class WorkspacePolicy extends BasePolicy
|
||||
{
|
||||
public function index(BaseUser $user, int $userId = null)
|
||||
{
|
||||
return $user->hasPermission('workspaces.view') || $user->id === $userId;
|
||||
}
|
||||
|
||||
public function show(BaseUser $user, Workspace $workspace)
|
||||
{
|
||||
return $user->hasPermission('workspaces.view') || $workspace->owner_id === $user->id || $workspace->isMember($user);
|
||||
}
|
||||
|
||||
public function store(BaseUser $user)
|
||||
{
|
||||
return $this->storeWithCountRestriction($user, Workspace::class);
|
||||
}
|
||||
|
||||
public function update(BaseUser $user, Workspace $workspace)
|
||||
{
|
||||
return $user->hasPermission('workspaces.update') || $workspace->owner_id === $user->id;
|
||||
}
|
||||
|
||||
public function destroy(BaseUser $user, $workspaceIds)
|
||||
{
|
||||
if ($user->hasPermission('workspaces.delete')) {
|
||||
return true;
|
||||
} else {
|
||||
$dbCount = app(Workspace::class)
|
||||
->whereIn('id', $workspaceIds)
|
||||
->where('owner_id', $user->id)
|
||||
->count();
|
||||
return $dbCount === count($workspaceIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
common/Workspaces/Policies/WorkspacedResourcePolicy.php
Executable file
123
common/Workspaces/Policies/WorkspacedResourcePolicy.php
Executable file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Policies;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Core\Policies\BasePolicy;
|
||||
use Common\Workspaces\ActiveWorkspace;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
abstract class WorkspacedResourcePolicy extends BasePolicy
|
||||
{
|
||||
protected string $resource;
|
||||
|
||||
const NO_PERMISSION = 1;
|
||||
const NO_WORKSPACE_PERMISSION = 2;
|
||||
|
||||
public function index(User $currentUser, int $userId = null)
|
||||
{
|
||||
$userId = $userId ?? (int) $this->request->get('userId');
|
||||
|
||||
[, $permission] = $this->parseNamespace($this->resource, 'view');
|
||||
|
||||
// filtering resources by user id
|
||||
if ($userId) {
|
||||
return $currentUser->id === $userId;
|
||||
|
||||
// if we're requesting resources for a particular workspace,let user view the resources
|
||||
// as long as they are a member, even without explicit "resource.view" permission
|
||||
} elseif ($this->userIsWorkspaceMember($currentUser)) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->userHasPermission($currentUser, $permission);
|
||||
}
|
||||
}
|
||||
|
||||
public function show(User $currentUser, Model $resource)
|
||||
{
|
||||
[, $permission] = $this->parseNamespace($this->resource, 'view');
|
||||
|
||||
if ($resource->user_id === $currentUser->id) {
|
||||
return true;
|
||||
// if we're requesting resources for a particular workspace,let user view the resources
|
||||
// as long as they are a member, event without explicit "resource.view" permission
|
||||
} elseif ($this->userIsWorkspaceMember($currentUser)) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->userHasPermission($currentUser, $permission);
|
||||
}
|
||||
}
|
||||
|
||||
public function store(User $currentUser)
|
||||
{
|
||||
return $this->storeWithCountRestriction($currentUser, $this->resource);
|
||||
}
|
||||
|
||||
public function update(User $currentUser, Model $resource)
|
||||
{
|
||||
[, $permission] = $this->parseNamespace($this->resource, 'update');
|
||||
|
||||
if ($resource->user_id === $currentUser->id) {
|
||||
return true;
|
||||
} else {
|
||||
return $this->userHasPermission($currentUser, $permission);
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(User $currentUser, $resourceIds = null)
|
||||
{
|
||||
[, $permission] = $this->parseNamespace($this->resource, 'delete');
|
||||
|
||||
$response = $this->userHasPermission($currentUser, $permission);
|
||||
|
||||
if ($response->allowed()) {
|
||||
return $response;
|
||||
} elseif ($resourceIds) {
|
||||
$dbCount = app($this->resource)
|
||||
->whereIn('id', $resourceIds)
|
||||
->where('user_id', $currentUser->id)
|
||||
->count();
|
||||
return $dbCount === count($resourceIds);
|
||||
} else {
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
protected function userHasPermission(
|
||||
User $user,
|
||||
string $permission,
|
||||
): Response {
|
||||
$permission = Str::snake($permission);
|
||||
|
||||
$activeWorkspace = app(ActiveWorkspace::class);
|
||||
$userOwnsWorkspace =
|
||||
$activeWorkspace->isPersonal() ||
|
||||
!$activeWorkspace->workspace() ||
|
||||
$user->id === $activeWorkspace->workspace()->owner_id;
|
||||
|
||||
// check if user has permission when they own workspace or no workspace at all
|
||||
if ($userOwnsWorkspace && !parent::hasPermission($user, $permission)) {
|
||||
return Response::deny('No permission', self::NO_PERMISSION);
|
||||
}
|
||||
|
||||
// check if user has this permission for the workspace as well if they are not the owner
|
||||
elseif (!$userOwnsWorkspace) {
|
||||
$workspaceUser = $activeWorkspace->member($user->id);
|
||||
if (!$workspaceUser?->hasPermission($permission)) {
|
||||
return Response::deny(
|
||||
'No permission',
|
||||
self::NO_WORKSPACE_PERMISSION,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Response::allow();
|
||||
}
|
||||
|
||||
protected function userIsWorkspaceMember(User $user): bool
|
||||
{
|
||||
return !is_null(app(ActiveWorkspace::class)->member($user->id));
|
||||
}
|
||||
}
|
||||
31
common/Workspaces/Requests/CrupdateWorkspaceRequest.php
Executable file
31
common/Workspaces/Requests/CrupdateWorkspaceRequest.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Requests;
|
||||
|
||||
use Auth;
|
||||
use Common\Core\BaseFormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class CrupdateWorkspaceRequest extends BaseFormRequest
|
||||
{
|
||||
public function rules(): array
|
||||
{
|
||||
$required = $this->getMethod() === 'POST' ? 'required' : '';
|
||||
$ignore =
|
||||
$this->getMethod() === 'PUT' ? $this->route('workspace')->id : '';
|
||||
$userId = $this->route('workspace')
|
||||
? $this->route('workspace')->user_id
|
||||
: Auth::id();
|
||||
|
||||
return [
|
||||
'name' => [
|
||||
$required,
|
||||
'string',
|
||||
'min:3',
|
||||
Rule::unique('workspaces')
|
||||
->where('owner_id', $userId)
|
||||
->ignore($ignore),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
20
common/Workspaces/Rules/UniqueWorkspacedResource.php
Executable file
20
common/Workspaces/Rules/UniqueWorkspacedResource.php
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Rules;
|
||||
|
||||
use Auth;
|
||||
use Common\Workspaces\ActiveWorkspace;
|
||||
use Illuminate\Validation\Rules\Unique;
|
||||
|
||||
class UniqueWorkspacedResource extends Unique
|
||||
{
|
||||
public function __construct($table, $column = 'NULL', $userId = null)
|
||||
{
|
||||
parent::__construct($table, $column);
|
||||
if (!app(ActiveWorkspace::class)->isPersonal()) {
|
||||
$this->where('workspace_id', app(ActiveWorkspace::class)->id);
|
||||
} else {
|
||||
$this->where('user_id', $userId ?? Auth::id());
|
||||
}
|
||||
}
|
||||
}
|
||||
38
common/Workspaces/Traits/BelongsToWorkspace.php
Executable file
38
common/Workspaces/Traits/BelongsToWorkspace.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces\Traits;
|
||||
|
||||
use Common\Workspaces\ActiveWorkspace;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
trait BelongsToWorkspace
|
||||
{
|
||||
protected string $ownerColumn = 'user_id';
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (Model $builder) {
|
||||
$builder->workspace_id = app(ActiveWorkspace::class)->id;
|
||||
});
|
||||
}
|
||||
|
||||
public function scopeForActiveWorkspaceOrOwner(Builder $builder, int|string $userId): Builder
|
||||
{
|
||||
if ($workspaceId = app(ActiveWorkspace::class)->id) {
|
||||
$builder->where('workspace_id', $workspaceId);
|
||||
} else {
|
||||
$builder->where($this->ownerColumn, $userId);
|
||||
}
|
||||
return $builder;
|
||||
}
|
||||
|
||||
protected function workspaceId(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn($value) => $value ?? 0,
|
||||
set: fn($value) => $value ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
31
common/Workspaces/UserWorkspacesController.php
Executable file
31
common/Workspaces/UserWorkspacesController.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces;
|
||||
|
||||
use Auth;
|
||||
use Common\Core\BaseController;
|
||||
|
||||
class UserWorkspacesController extends BaseController
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$workspaces = Workspace::forUser(Auth::id())
|
||||
->with(['members'])
|
||||
->withCount(['members'])
|
||||
->limit(20)
|
||||
->get()
|
||||
->map(function (Workspace $workspace) {
|
||||
$workspace->setCurrentUserAndOwner();
|
||||
return $workspace;
|
||||
});
|
||||
|
||||
return $this->success([
|
||||
'workspaces' => $workspaces,
|
||||
]);
|
||||
}
|
||||
}
|
||||
154
common/Workspaces/Workspace.php
Executable file
154
common/Workspaces/Workspace.php
Executable file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Workspaces\WorkspaceRelationships;
|
||||
use Auth;
|
||||
use Common\Core\BaseModel;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Workspace extends BaseModel
|
||||
{
|
||||
use WorkspaceRelationships, HasFactory;
|
||||
|
||||
const MODEL_TYPE = 'workspace';
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'owner_id' => 'integer',
|
||||
];
|
||||
|
||||
public function invites(): HasMany
|
||||
{
|
||||
return $this->hasMany(WorkspaceInvite::class)
|
||||
->join('roles', 'roles.id', '=', 'workspace_invites.role_id')
|
||||
->select([
|
||||
'workspace_invites.id',
|
||||
'workspace_invites.workspace_id',
|
||||
'roles.name as role_name',
|
||||
'workspace_invites.email',
|
||||
'workspace_invites.role_id',
|
||||
'email',
|
||||
'avatar',
|
||||
]);
|
||||
}
|
||||
|
||||
public function owner()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'owner_id')->select([
|
||||
'id',
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'avatar',
|
||||
]);
|
||||
}
|
||||
|
||||
public function members()
|
||||
{
|
||||
return $this->hasMany(WorkspaceMember::class)
|
||||
->join('roles', 'roles.id', '=', 'workspace_user.role_id', 'left')
|
||||
->join('users', 'users.id', '=', 'workspace_user.user_id')
|
||||
->select([
|
||||
'roles.name as role_name',
|
||||
'users.email',
|
||||
'workspace_user.workspace_id',
|
||||
'workspace_user.created_at as joined_at',
|
||||
'workspace_user.role_id',
|
||||
'workspace_user.is_owner',
|
||||
'workspace_user.id as member_id',
|
||||
'users.id',
|
||||
'users.first_name',
|
||||
'users.last_name',
|
||||
'users.avatar',
|
||||
]);
|
||||
}
|
||||
|
||||
public function isMember(User $user): bool
|
||||
{
|
||||
return $this->members()
|
||||
->where('user_id', $user->id)
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function isOwner(User $user): bool
|
||||
{
|
||||
return $this->owner_id === $user->id;
|
||||
}
|
||||
|
||||
public function findMember(User $user): WorkspaceMember
|
||||
{
|
||||
return $this->members()
|
||||
->where('user_id', $user->id)
|
||||
->first();
|
||||
}
|
||||
|
||||
public function scopeForUser(Builder $builder, int $userId): Builder
|
||||
{
|
||||
return $builder
|
||||
->where('owner_id', $userId)
|
||||
->orWhereHas('members', function (Builder $builder) use ($userId) {
|
||||
return $builder->where('workspace_user.user_id', $userId);
|
||||
});
|
||||
}
|
||||
|
||||
public function setCurrentUserAndOwner(): self
|
||||
{
|
||||
$this->setRelation(
|
||||
'owner',
|
||||
$this->members->where('is_owner', true)->first(),
|
||||
);
|
||||
$this->currentUser = $this->members->where('id', Auth::id())->first();
|
||||
$this->unsetRelation('members');
|
||||
|
||||
// load workspace permissions for current user in case front-end needs it
|
||||
if (
|
||||
app(ActiveWorkspace::class)->id === $this->id &&
|
||||
$this->currentUser &&
|
||||
!$this->currentUser->relationLoaded('permissions')
|
||||
) {
|
||||
$this->currentUser->load('permissions');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toNormalizedArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'model_type' => self::MODEL_TYPE,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'name' => $this->name,
|
||||
'created_at' => $this->created_at->timestamp ?? '_null',
|
||||
'updated_at' => $this->updated_at->timestamp ?? '_null',
|
||||
];
|
||||
}
|
||||
|
||||
public static function filterableFields(): array
|
||||
{
|
||||
return ['id', 'created_at', 'updated_at'];
|
||||
}
|
||||
|
||||
protected static function newFactory(): WorkspaceFactory
|
||||
{
|
||||
return WorkspaceFactory::new();
|
||||
}
|
||||
|
||||
public static function getModelTypeAttribute(): string
|
||||
{
|
||||
return self::MODEL_TYPE;
|
||||
}
|
||||
}
|
||||
18
common/Workspaces/WorkspaceFactory.php
Executable file
18
common/Workspaces/WorkspaceFactory.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class WorkspaceFactory extends Factory
|
||||
{
|
||||
protected $model = Workspace::class;
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->words(2, true),
|
||||
'owner_id' => $this->faker->numberBetween(1, 100),
|
||||
];
|
||||
}
|
||||
}
|
||||
39
common/Workspaces/WorkspaceInvite.php
Executable file
39
common/Workspaces/WorkspaceInvite.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces;
|
||||
|
||||
use App\Models\User;
|
||||
use Common\Auth\Traits\HasAvatarAttribute;
|
||||
use Common\Auth\Traits\HasDisplayNameAttribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class WorkspaceInvite extends Model
|
||||
{
|
||||
use HasDisplayNameAttribute, HasAvatarAttribute;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
protected $appends = ['display_name', 'model_type'];
|
||||
|
||||
protected $keyType = 'orderedUuid';
|
||||
public $incrementing = false;
|
||||
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
];
|
||||
|
||||
public function workspace(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Workspace::class);
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public static function getModelTypeAttribute(): string
|
||||
{
|
||||
return 'invite';
|
||||
}
|
||||
}
|
||||
80
common/Workspaces/WorkspaceMember.php
Executable file
80
common/Workspaces/WorkspaceMember.php
Executable file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace Common\Workspaces;
|
||||
|
||||
use Auth;
|
||||
use Common\Auth\Permissions\Permission;
|
||||
use Common\Auth\Roles\Role;
|
||||
use Common\Auth\Traits\HasAvatarAttribute;
|
||||
use Common\Auth\Traits\HasDisplayNameAttribute;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class WorkspaceMember extends Model
|
||||
{
|
||||
use HasAvatarAttribute, HasDisplayNameAttribute;
|
||||
|
||||
protected $table = 'workspace_user';
|
||||
protected $guarded = ['id'];
|
||||
protected $appends = ['display_name', 'model_type'];
|
||||
protected $casts = ['is_owner' => 'boolean'];
|
||||
|
||||
public function permissions()
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
Permission::class,
|
||||
'permissionables',
|
||||
'permissionable_id',
|
||||
'permission_id',
|
||||
'role_id',
|
||||
)
|
||||
->where('permissionable_type', Role::MODEL_TYPE)
|
||||
->select([
|
||||
'permissions.id',
|
||||
'permissions.name',
|
||||
'permissions.restrictions',
|
||||
]);
|
||||
}
|
||||
|
||||
public function workspace()
|
||||
{
|
||||
return $this->belongsTo(Workspace::class);
|
||||
}
|
||||
|
||||
public function scopeCurrentUserAndOwnerOnly(Builder $builder): self
|
||||
{
|
||||
$builder->where(function (Builder $builder) {
|
||||
$builder
|
||||
->where('workspace_user.user_id', Auth::id())
|
||||
->orWhere('workspace_user.is_owner', true);
|
||||
});
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function getModelTypeAttribute(): string
|
||||
{
|
||||
return 'member';
|
||||
}
|
||||
|
||||
public function hasPermission(string $name): bool
|
||||
{
|
||||
return $this->is_owner || !is_null($this->getPermission($name));
|
||||
}
|
||||
|
||||
public function getPermission(string $name): ?Permission
|
||||
{
|
||||
return $this->permissions->first(function (Permission $permission) use (
|
||||
$name
|
||||
) {
|
||||
return $permission->name === $name;
|
||||
});
|
||||
}
|
||||
|
||||
public function getRoleNameAttribute()
|
||||
{
|
||||
return $this->is_owner
|
||||
? 'Workspace Owner'
|
||||
: $this->attributes['role_name'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user