--- /dev/null
+<?php
+
+namespace BookStack\Auth\Queries;
+
+
+use BookStack\Auth\User;
+use Illuminate\Pagination\LengthAwarePaginator;
+
+/**
+ * Get all the users with their permissions in a paginated format.
+ * Note: Due to the use of email search this should only be used when
+ * user is assumed to be trusted. (Admin users).
+ * Email search can be abused to extract email addresses.
+ */
+class AllUsersPaginatedAndSorted
+{
+
+ /**
+ * @param array{sort: string, order: string, search: string} $sortData
+ */
+ public function run(int $count, array $sortData): LengthAwarePaginator
+ {
+ $sort = $sortData['sort'];
+
+ $query = User::query()->select(['*'])
+ ->scopes(['withLastActivityAt'])
+ ->with(['roles', 'avatar'])
+ ->withCount('mfaValues')
+ ->orderBy($sort, $sortData['order']);
+
+ if ($sortData['search']) {
+ $term = '%' . $sortData['search'] . '%';
+ $query->where(function ($query) use ($term) {
+ $query->where('name', 'like', $term)
+ ->orWhere('email', 'like', $term);
+ });
+ }
+
+ return $query->paginate($count);
+ }
+
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace BookStack\Auth\Queries;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+
+/**
+ * Get asset created counts for the given user.
+ */
+class UserContentCounts
+{
+ /**
+ * @return array{pages: int, chapters: int, books: int, shelves: int}
+ */
+ public function run(User $user): array
+ {
+ $createdBy = ['created_by' => $user->id];
+
+ return [
+ 'pages' => Page::visible()->where($createdBy)->count(),
+ 'chapters' => Chapter::visible()->where($createdBy)->count(),
+ 'books' => Book::visible()->where($createdBy)->count(),
+ 'shelves' => Bookshelf::visible()->where($createdBy)->count(),
+ ];
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace BookStack\Auth\Queries;
+
+use BookStack\Auth\User;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+
+/**
+ * Get the recently created content for the provided user.
+ */
+class UserRecentlyCreatedContent
+{
+ /**
+ * @return array{pages: Collection, chapters: Collection, books: Collection, shelves: Collection}
+ */
+ public function run(User $user, int $count): array
+ {
+ $query = function (Builder $query) use ($user, $count) {
+ return $query->orderBy('created_at', 'desc')
+ ->where('created_by', '=', $user->id)
+ ->take($count)
+ ->get();
+ };
+
+ return [
+ 'pages' => $query(Page::visible()->where('draft', '=', false)),
+ 'chapters' => $query(Chapter::visible()),
+ 'books' => $query(Book::visible()),
+ 'shelves' => $query(Bookshelf::visible()),
+ ];
+ }
+}
\ No newline at end of file
/**
* This holds the user's permissions when loaded.
- *
- * @var ?Collection
*/
- protected $permissions;
+ protected ?Collection $permissions;
/**
* This holds the default user when loaded.
*
* @var null|User
*/
- protected static $defaultUser = null;
+ protected static ?User $defaultUser = null;
/**
* Returns the default public user.
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\UserInviteService;
use BookStack\Entities\EntityProvider;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Bookshelf;
-use BookStack\Entities\Models\Chapter;
-use BookStack\Entities\Models\Page;
-use BookStack\Exceptions\NotFoundException;
use BookStack\Exceptions\NotifyException;
use BookStack\Exceptions\UserUpdateException;
use BookStack\Facades\Activity;
use BookStack\Uploads\UserAvatars;
use Exception;
-use Illuminate\Database\Eloquent\Builder;
-use Illuminate\Database\Eloquent\Collection;
-use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
class UserRepo
{
- protected $userAvatar;
- protected $inviteService;
+ protected UserAvatars $userAvatar;
+ protected UserInviteService $inviteService;
/**
* UserRepo constructor.
return User::query()->where('slug', '=', $slug)->firstOrFail();
}
- /**
- * Get all users as Builder for API.
- */
- public function getApiUsersBuilder(): Builder
- {
- return User::query()->select(['*'])
- ->scopes('withLastActivityAt')
- ->with(['avatar']);
- }
-
- /**
- * Get all the users with their permissions in a paginated format.
- * Note: Due to the use of email search this should only be used when
- * user is assumed to be trusted. (Admin users).
- * Email search can be abused to extract email addresses.
- */
- public function getAllUsersPaginatedAndSorted(int $count, array $sortData): LengthAwarePaginator
- {
- $sort = $sortData['sort'];
-
- $query = User::query()->select(['*'])
- ->scopes(['withLastActivityAt'])
- ->with(['roles', 'avatar'])
- ->withCount('mfaValues')
- ->orderBy($sort, $sortData['order']);
-
- if ($sortData['search']) {
- $term = '%' . $sortData['search'] . '%';
- $query->where(function ($query) use ($term) {
- $query->where('name', 'like', $term)
- ->orWhere('email', 'like', $term);
- });
- }
-
- return $query->paginate($count);
- }
-
- /**
- * Assign a user to a system-level role.
- *
- * @throws NotFoundException
- */
- public function attachSystemRole(User $user, string $systemRoleName)
- {
- $role = Role::getSystemRole($systemRoleName);
- if (is_null($role)) {
- throw new NotFoundException("Role '{$systemRoleName}' not found");
- }
- $user->attachRole($role);
- }
-
- /**
- * Checks if the give user is the only admin.
- */
- public function isOnlyAdmin(User $user): bool
- {
- if (!$user->hasSystemRole('admin')) {
- return false;
- }
-
- $adminRole = Role::getSystemRole('admin');
- if ($adminRole->users()->count() > 1) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Set the assigned user roles via an array of role IDs.
- *
- * @throws UserUpdateException
- */
- public function setUserRoles(User $user, array $roles)
- {
- if ($this->demotingLastAdmin($user, $roles)) {
- throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl());
- }
-
- $user->roles()->sync($roles);
- }
-
- /**
- * Check if the given user is the last admin and their new roles no longer
- * contains the admin role.
- */
- protected function demotingLastAdmin(User $user, array $newRoles): bool
- {
- if ($this->isOnlyAdmin($user)) {
- $adminRole = Role::getSystemRole('admin');
- if (!in_array(strval($adminRole->id), $newRoles)) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Create a new basic instance of user with the given pre-validated data.
*
}
/**
- * Get the recently created content for this given user.
+ * Get an avatar image for a user and set it as their avatar.
+ * Returns early if avatars disabled or not set in config.
*/
- public function getRecentlyCreated(User $user, int $count = 20): array
+ protected function downloadAndAssignUserAvatar(User $user): void
{
- $query = function (Builder $query) use ($user, $count) {
- return $query->orderBy('created_at', 'desc')
- ->where('created_by', '=', $user->id)
- ->take($count)
- ->get();
- };
-
- return [
- 'pages' => $query(Page::visible()->where('draft', '=', false)),
- 'chapters' => $query(Chapter::visible()),
- 'books' => $query(Book::visible()),
- 'shelves' => $query(Bookshelf::visible()),
- ];
+ try {
+ $this->userAvatar->fetchAndAssignToUser($user);
+ } catch (Exception $e) {
+ Log::error('Failed to save user avatar image');
+ }
}
/**
- * Get asset created counts for the give user.
+ * Checks if the give user is the only admin.
*/
- public function getAssetCounts(User $user): array
+ protected function isOnlyAdmin(User $user): bool
{
- $createdBy = ['created_by' => $user->id];
-
- return [
- 'pages' => Page::visible()->where($createdBy)->count(),
- 'chapters' => Chapter::visible()->where($createdBy)->count(),
- 'books' => Book::visible()->where($createdBy)->count(),
- 'shelves' => Bookshelf::visible()->where($createdBy)->count(),
- ];
+ if (!$user->hasSystemRole('admin')) {
+ return false;
+ }
+
+ $adminRole = Role::getSystemRole('admin');
+ if ($adminRole->users()->count() > 1) {
+ return false;
+ }
+
+ return true;
}
/**
- * Get the roles in the system that are assignable to a user.
+ * Set the assigned user roles via an array of role IDs.
+ *
+ * @throws UserUpdateException
*/
- public function getAllRoles(): Collection
+ protected function setUserRoles(User $user, array $roles)
{
- return Role::query()->orderBy('display_name', 'asc')->get();
+ if ($this->demotingLastAdmin($user, $roles)) {
+ throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl());
+ }
+
+ $user->roles()->sync($roles);
}
/**
- * Get an avatar image for a user and set it as their avatar.
- * Returns early if avatars disabled or not set in config.
+ * Check if the given user is the last admin and their new roles no longer
+ * contains the admin role.
*/
- public function downloadAndAssignUserAvatar(User $user): void
+ protected function demotingLastAdmin(User $user, array $newRoles): bool
{
- try {
- $this->userAvatar->fetchAndAssignToUser($user);
- } catch (Exception $e) {
- Log::error('Failed to save user avatar image');
+ if ($this->isOnlyAdmin($user)) {
+ $adminRole = Role::getSystemRole('admin');
+ if (!in_array(strval($adminRole->id), $newRoles)) {
+ return true;
+ }
}
+
+ return false;
}
}
namespace BookStack\Console\Commands;
+use BookStack\Auth\Role;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\NotFoundException;
use Illuminate\Console\Command;
}
$user = $this->userRepo->createWithoutActivity($validator->validated());
- $this->userRepo->attachSystemRole($user, 'admin');
+ $user->attachRole(Role::getSystemRole('admin'));
$user->email_confirmed = true;
$user->save();
*/
public function list()
{
- $users = $this->userRepo->getApiUsersBuilder();
+ $users = User::query()->select(['*'])
+ ->scopes('withLastActivityAt')
+ ->with(['avatar']);
return $this->apiListingResponse($users, [
'id', 'name', 'slug', 'email', 'external_auth_id',
namespace BookStack\Http\Controllers;
use BookStack\Auth\Access\SocialAuthService;
+use BookStack\Auth\Queries\AllUsersPaginatedAndSorted;
+use BookStack\Auth\Role;
use BookStack\Auth\User;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\ImageUploadException;
'search' => $request->get('search', ''),
'sort' => $request->get('sort', 'name'),
];
- $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
+
+ $users = (new AllUsersPaginatedAndSorted())->run(20, $listDetails);
$this->setPageTitle(trans('settings.users'));
$users->appends($listDetails);
- return view('users.index', ['users' => $users, 'listDetails' => $listDetails]);
+ return view('users.index', [
+ 'users' => $users,
+ 'listDetails' => $listDetails
+ ]);
}
/**
{
$this->checkPermission('users-manage');
$authMethod = config('auth.method');
- $roles = $this->userRepo->getAllRoles();
+ $roles = Role::query()->orderBy('display_name', 'asc')->get();
$this->setPageTitle(trans('settings.users_add_new'));
return view('users.create', ['authMethod' => $authMethod, 'roles' => $roles]);
$activeSocialDrivers = $socialAuthService->getActiveDrivers();
$mfaMethods = $user->mfaValues->groupBy('method');
$this->setPageTitle(trans('settings.user_profile'));
- $roles = $this->userRepo->getAllRoles();
+ $roles = Role::query()->orderBy('display_name', 'asc')->get();
return view('users.edit', [
'user' => $user,
namespace BookStack\Http\Controllers;
use BookStack\Actions\ActivityQueries;
+use BookStack\Auth\Queries\UserContentCounts;
+use BookStack\Auth\Queries\UserRecentlyCreatedContent;
use BookStack\Auth\UserRepo;
class UserProfileController extends Controller
$user = $repo->getBySlug($slug);
$userActivity = $activities->userActivity($user);
- $recentlyCreated = $repo->getRecentlyCreated($user, 5);
- $assetCounts = $repo->getAssetCounts($user);
+ $recentlyCreated = (new UserRecentlyCreatedContent())->run($user, 5);
+ $assetCounts = (new UserContentCounts())->run($user);
$this->setPageTitle($user->name);