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