]> BookStack Code Mirror - bookstack/blob - app/Auth/User.php
07232e1cc902f823427e7aec19db9e26fa37dab0
[bookstack] / app / Auth / User.php
1 <?php namespace BookStack\Auth;
2
3 use BookStack\Api\ApiToken;
4 use BookStack\Interfaces\Loggable;
5 use BookStack\Interfaces\Sluggable;
6 use BookStack\Model;
7 use BookStack\Notifications\ResetPassword;
8 use BookStack\Uploads\Image;
9 use Carbon\Carbon;
10 use Exception;
11 use Illuminate\Auth\Authenticatable;
12 use Illuminate\Auth\Passwords\CanResetPassword;
13 use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
14 use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
15 use Illuminate\Database\Eloquent\Builder;
16 use Illuminate\Database\Eloquent\Relations\BelongsTo;
17 use Illuminate\Database\Eloquent\Relations\BelongsToMany;
18 use Illuminate\Database\Eloquent\Relations\HasMany;
19 use Illuminate\Notifications\Notifiable;
20 use Illuminate\Support\Collection;
21
22 /**
23  * Class User
24  * @property string $id
25  * @property string $name
26  * @property string $slug
27  * @property string $email
28  * @property string $password
29  * @property Carbon $created_at
30  * @property Carbon $updated_at
31  * @property bool $email_confirmed
32  * @property int $image_id
33  * @property string $external_auth_id
34  * @property string $system_name
35  * @property Collection $roles
36  */
37 class User extends Model implements AuthenticatableContract, CanResetPasswordContract, Loggable, Sluggable
38 {
39     use Authenticatable, CanResetPassword, Notifiable;
40
41     /**
42      * The database table used by the model.
43      * @var string
44      */
45     protected $table = 'users';
46
47     /**
48      * The attributes that are mass assignable.
49      * @var array
50      */
51     protected $fillable = ['name', 'email'];
52
53     protected $casts = ['last_activity_at' => 'datetime'];
54
55     /**
56      * The attributes excluded from the model's JSON form.
57      * @var array
58      */
59     protected $hidden = [
60         'password', 'remember_token', 'system_name', 'email_confirmed', 'external_auth_id', 'email',
61         'created_at', 'updated_at', 'image_id',
62     ];
63
64     /**
65      * This holds the user's permissions when loaded.
66      * @var ?Collection
67      */
68     protected $permissions;
69
70     /**
71      * This holds the default user when loaded.
72      * @var null|User
73      */
74     protected static $defaultUser = null;
75
76     /**
77      * Returns the default public user.
78      */
79     public static function getDefault(): User
80     {
81         if (!is_null(static::$defaultUser)) {
82             return static::$defaultUser;
83         }
84         
85         static::$defaultUser = static::query()->where('system_name', '=', 'public')->first();
86         return static::$defaultUser;
87     }
88
89     /**
90      * Check if the user is the default public user.
91      */
92     public function isDefault(): bool
93     {
94         return $this->system_name === 'public';
95     }
96
97     /**
98      * The roles that belong to the user.
99      * @return BelongsToMany
100      */
101     public function roles()
102     {
103         if ($this->id === 0) {
104             return ;
105         }
106         return $this->belongsToMany(Role::class);
107     }
108
109     /**
110      * Check if the user has a role.
111      */
112     public function hasRole($roleId): bool
113     {
114         return $this->roles->pluck('id')->contains($roleId);
115     }
116
117     /**
118      * Check if the user has a role.
119      */
120     public function hasSystemRole(string $roleSystemName): bool
121     {
122         return $this->roles->pluck('system_name')->contains($roleSystemName);
123     }
124
125     /**
126      * Attach the default system role to this user.
127      */
128     public function attachDefaultRole(): void
129     {
130         $roleId = setting('registration-role');
131         if ($roleId && $this->roles()->where('id', '=', $roleId)->count() === 0) {
132             $this->roles()->attach($roleId);
133         }
134     }
135
136     /**
137      * Check if the user has a particular permission.
138      */
139     public function can(string $permissionName): bool
140     {
141         if ($this->email === 'guest') {
142             return false;
143         }
144
145         return $this->permissions()->contains($permissionName);
146     }
147
148     /**
149      * Get all permissions belonging to a the current user.
150      */
151     protected function permissions(): Collection
152     {
153         if (isset($this->permissions)) {
154             return $this->permissions;
155         }
156
157         $this->permissions = $this->newQuery()->getConnection()->table('role_user', 'ru')
158             ->select('role_permissions.name as name')->distinct()
159             ->leftJoin('permission_role', 'ru.role_id', '=', 'permission_role.role_id')
160             ->leftJoin('role_permissions', 'permission_role.permission_id', '=', 'role_permissions.id')
161             ->where('ru.user_id', '=', $this->id)
162             ->get()
163             ->pluck('name');
164
165         return $this->permissions;
166     }
167
168     /**
169      * Clear any cached permissions on this instance.
170      */
171     public function clearPermissionCache()
172     {
173         $this->permissions = null;
174     }
175
176     /**
177      * Attach a role to this user.
178      */
179     public function attachRole(Role $role)
180     {
181         $this->roles()->attach($role->id);
182     }
183
184     /**
185      * Get the social account associated with this user.
186      */
187     public function socialAccounts(): HasMany
188     {
189         return $this->hasMany(SocialAccount::class);
190     }
191
192     /**
193      * Check if the user has a social account,
194      * If a driver is passed it checks for that single account type.
195      * @param bool|string $socialDriver
196      * @return bool
197      */
198     public function hasSocialAccount($socialDriver = false)
199     {
200         if ($socialDriver === false) {
201             return $this->socialAccounts()->count() > 0;
202         }
203
204         return $this->socialAccounts()->where('driver', '=', $socialDriver)->exists();
205     }
206
207     /**
208      * Returns a URL to the user's avatar
209      */
210     public function getAvatar(int $size = 50): string
211     {
212         $default = url('/user_avatar.png');
213         $imageId = $this->image_id;
214         if ($imageId === 0 || $imageId === '0' || $imageId === null) {
215             return $default;
216         }
217
218         try {
219             $avatar = $this->avatar ? url($this->avatar->getThumb($size, $size, false)) : $default;
220         } catch (Exception $err) {
221             $avatar = $default;
222         }
223         return $avatar;
224     }
225
226     /**
227      * Get the avatar for the user.
228      */
229     public function avatar(): BelongsTo
230     {
231         return $this->belongsTo(Image::class, 'image_id');
232     }
233
234     /**
235      * Get the API tokens assigned to this user.
236      */
237     public function apiTokens(): HasMany
238     {
239         return $this->hasMany(ApiToken::class);
240     }
241
242     /**
243      * Get the last activity time for this user.
244      */
245     public function scopeWithLastActivityAt(Builder $query)
246     {
247         $query->addSelect(['activities.created_at as last_activity_at'])
248             ->leftJoinSub(function (\Illuminate\Database\Query\Builder $query) {
249                 $query->from('activities')->select('user_id')
250                     ->selectRaw('max(created_at) as created_at')
251                     ->groupBy('user_id');
252             }, 'activities', 'users.id', '=', 'activities.user_id');
253     }
254
255     /**
256      * Get the url for editing this user.
257      */
258     public function getEditUrl(string $path = ''): string
259     {
260         $uri = '/settings/users/' . $this->id . '/' . trim($path, '/');
261         return url(rtrim($uri, '/'));
262     }
263
264     /**
265      * Get the url that links to this user's profile.
266      */
267     public function getProfileUrl(): string
268     {
269         return url('/user/' . $this->id);
270     }
271
272     /**
273      * Get a shortened version of the user's name.
274      */
275     public function getShortName(int $chars = 8): string
276     {
277         if (mb_strlen($this->name) <= $chars) {
278             return $this->name;
279         }
280
281         $splitName = explode(' ', $this->name);
282         if (mb_strlen($splitName[0]) <= $chars) {
283             return $splitName[0];
284         }
285
286         return '';
287     }
288
289     /**
290      * Send the password reset notification.
291      * @param  string  $token
292      * @return void
293      */
294     public function sendPasswordResetNotification($token)
295     {
296         $this->notify(new ResetPassword($token));
297     }
298
299     /**
300      * @inheritdoc
301      */
302     public function logDescriptor(): string
303     {
304         return "({$this->id}) {$this->name}";
305     }
306 }