]> BookStack Code Mirror - bookstack/blob - app/Auth/UserRepo.php
Added test case for avatar failed fetch
[bookstack] / app / Auth / UserRepo.php
1 <?php namespace BookStack\Auth;
2
3 use Activity;
4 use BookStack\Entities\EntityProvider;
5 use BookStack\Entities\Models\Book;
6 use BookStack\Entities\Models\Bookshelf;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Page;
9 use BookStack\Exceptions\NotFoundException;
10 use BookStack\Exceptions\UserUpdateException;
11 use BookStack\Uploads\Image;
12 use BookStack\Uploads\UserAvatars;
13 use Exception;
14 use Illuminate\Database\Eloquent\Builder;
15 use Illuminate\Database\Eloquent\Collection;
16 use Illuminate\Pagination\LengthAwarePaginator;
17 use Images;
18 use Log;
19
20 class UserRepo
21 {
22     protected $userAvatar;
23
24     /**
25      * UserRepo constructor.
26      */
27     public function __construct(UserAvatars $userAvatar)
28     {
29         $this->userAvatar = $userAvatar;
30     }
31
32     /**
33      * Get a user by their email address.
34      */
35     public function getByEmail(string $email): ?User
36     {
37         return User::query()->where('email', '=', $email)->first();
38     }
39
40     /**
41      * Get a user by their ID.
42      */
43     public function getById(int $id): User
44     {
45         return User::query()->findOrFail($id);
46     }
47
48     /**
49      * Get all the users with their permissions.
50      */
51     public function getAllUsers(): Collection
52     {
53         return User::query()->with('roles', 'avatar')->orderBy('name', 'asc')->get();
54     }
55
56     /**
57      * Get all the users with their permissions in a paginated format.
58      */
59     public function getAllUsersPaginatedAndSorted(int $count, array $sortData): LengthAwarePaginator
60     {
61         $sort = $sortData['sort'];
62         if ($sort === 'latest_activity') {
63             $sort = \BookStack\Actions\Activity::query()->select('created_at')
64                 ->whereColumn('activities.user_id', 'users.id')
65                 ->latest()
66                 ->take(1);
67         }
68
69         $query = User::query()->with(['roles', 'avatar', 'latestActivity'])
70             ->orderBy($sort, $sortData['order']);
71
72         if ($sortData['search']) {
73             $term = '%' . $sortData['search'] . '%';
74             $query->where(function ($query) use ($term) {
75                 $query->where('name', 'like', $term)
76                     ->orWhere('email', 'like', $term);
77             });
78         }
79
80         return $query->paginate($count);
81     }
82
83      /**
84      * Creates a new user and attaches a role to them.
85      */
86     public function registerNew(array $data, bool $emailConfirmed = false): User
87     {
88         $user = $this->create($data, $emailConfirmed);
89         $user->attachDefaultRole();
90         $this->downloadAndAssignUserAvatar($user);
91
92         return $user;
93     }
94
95     /**
96      * Assign a user to a system-level role.
97      * @throws NotFoundException
98      */
99     public function attachSystemRole(User $user, string $systemRoleName)
100     {
101         $role = Role::getSystemRole($systemRoleName);
102         if (is_null($role)) {
103             throw new NotFoundException("Role '{$systemRoleName}' not found");
104         }
105         $user->attachRole($role);
106     }
107
108     /**
109      * Checks if the give user is the only admin.
110      */
111     public function isOnlyAdmin(User $user): bool
112     {
113         if (!$user->hasSystemRole('admin')) {
114             return false;
115         }
116
117         $adminRole = Role::getSystemRole('admin');
118         if ($adminRole->users()->count() > 1) {
119             return false;
120         }
121
122         return true;
123     }
124
125     /**
126      * Set the assigned user roles via an array of role IDs.
127      * @throws UserUpdateException
128      */
129     public function setUserRoles(User $user, array $roles)
130     {
131         if ($this->demotingLastAdmin($user, $roles)) {
132             throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl());
133         }
134
135         $user->roles()->sync($roles);
136     }
137
138     /**
139      * Check if the given user is the last admin and their new roles no longer
140      * contains the admin role.
141      */
142     protected function demotingLastAdmin(User $user, array $newRoles) : bool
143     {
144         if ($this->isOnlyAdmin($user)) {
145             $adminRole = Role::getSystemRole('admin');
146             if (!in_array(strval($adminRole->id), $newRoles)) {
147                 return true;
148             }
149         }
150
151         return false;
152     }
153
154     /**
155      * Create a new basic instance of user.
156      */
157     public function create(array $data, bool $emailConfirmed = false): User
158     {
159         $details = [
160             'name'     => $data['name'],
161             'email'    => $data['email'],
162             'password' => bcrypt($data['password']),
163             'email_confirmed' => $emailConfirmed,
164             'external_auth_id' => $data['external_auth_id'] ?? '',
165         ];
166         return User::query()->forceCreate($details);
167     }
168
169     /**
170      * Remove the given user from storage, Delete all related content.
171      * @throws Exception
172      */
173     public function destroy(User $user, ?int $newOwnerId = null)
174     {
175         $user->socialAccounts()->delete();
176         $user->apiTokens()->delete();
177         $user->delete();
178         
179         // Delete user profile images
180         $profileImages = Image::query()->where('type', '=', 'user')
181             ->where('uploaded_to', '=', $user->id)
182             ->get();
183
184         foreach ($profileImages as $image) {
185             Images::destroy($image);
186         }
187
188         if (!empty($newOwnerId)) {
189             $newOwner = User::query()->find($newOwnerId);
190             if (!is_null($newOwner)) {
191                 $this->migrateOwnership($user, $newOwner);
192             }
193         }
194     }
195
196     /**
197      * Migrate ownership of items in the system from one user to another.
198      */
199     protected function migrateOwnership(User $fromUser, User $toUser)
200     {
201         $entities = (new EntityProvider)->all();
202         foreach ($entities as $instance) {
203             $instance->newQuery()->where('owned_by', '=', $fromUser->id)
204                 ->update(['owned_by' => $toUser->id]);
205         }
206     }
207
208     /**
209      * Get the latest activity for a user.
210      */
211     public function getActivity(User $user, int $count = 20, int $page = 0): array
212     {
213         return Activity::userActivity($user, $count, $page);
214     }
215
216     /**
217      * Get the recently created content for this given user.
218      */
219     public function getRecentlyCreated(User $user, int $count = 20): array
220     {
221         $query = function (Builder $query) use ($user, $count) {
222             return $query->orderBy('created_at', 'desc')
223                 ->where('created_by', '=', $user->id)
224                 ->take($count)
225                 ->get();
226         };
227
228         return [
229             'pages'    => $query(Page::visible()->where('draft', '=', false)),
230             'chapters' => $query(Chapter::visible()),
231             'books'    => $query(Book::visible()),
232             'shelves'  => $query(Bookshelf::visible()),
233         ];
234     }
235
236     /**
237      * Get asset created counts for the give user.
238      */
239     public function getAssetCounts(User $user): array
240     {
241         $createdBy = ['created_by' => $user->id];
242         return [
243             'pages'    =>  Page::visible()->where($createdBy)->count(),
244             'chapters'    =>  Chapter::visible()->where($createdBy)->count(),
245             'books'    =>  Book::visible()->where($createdBy)->count(),
246             'shelves'    =>  Bookshelf::visible()->where($createdBy)->count(),
247         ];
248     }
249
250     /**
251      * Get the roles in the system that are assignable to a user.
252      */
253     public function getAllRoles(): Collection
254     {
255         return Role::query()->orderBy('display_name', 'asc')->get();
256     }
257
258     /**
259      * Get an avatar image for a user and set it as their avatar.
260      * Returns early if avatars disabled or not set in config.
261      */
262     public function downloadAndAssignUserAvatar(User $user): void
263     {
264         try {
265             $this->userAvatar->fetchAndAssignToUser($user);
266         } catch (Exception $e) {
267             Log::error('Failed to save user avatar image');
268         }
269     }
270 }