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