]> BookStack Code Mirror - bookstack/blob - app/Uploads/UserAvatars.php
Apply fixes from StyleCI
[bookstack] / app / Uploads / UserAvatars.php
1 <?php
2
3 namespace BookStack\Uploads;
4
5 use BookStack\Auth\User;
6 use BookStack\Exceptions\HttpFetchException;
7 use Exception;
8 use Illuminate\Support\Facades\Log;
9
10 class UserAvatars
11 {
12     protected $imageService;
13     protected $http;
14
15     public function __construct(ImageService $imageService, HttpFetcher $http)
16     {
17         $this->imageService = $imageService;
18         $this->http = $http;
19     }
20
21     /**
22      * Fetch and assign an avatar image to the given user.
23      */
24     public function fetchAndAssignToUser(User $user): void
25     {
26         if (!$this->avatarFetchEnabled()) {
27             return;
28         }
29
30         try {
31             $this->destroyAllForUser($user);
32             $avatar = $this->saveAvatarImage($user);
33             $user->avatar()->associate($avatar);
34             $user->save();
35         } catch (Exception $e) {
36             Log::error('Failed to save user avatar image');
37         }
38     }
39
40     /**
41      * Assign a new avatar image to the given user using the given image data.
42      */
43     public function assignToUserFromExistingData(User $user, string $imageData, string $extension): void
44     {
45         try {
46             $this->destroyAllForUser($user);
47             $avatar = $this->createAvatarImageFromData($user, $imageData, $extension);
48             $user->avatar()->associate($avatar);
49             $user->save();
50         } catch (Exception $e) {
51             Log::error('Failed to save user avatar image');
52         }
53     }
54
55     /**
56      * Destroy all user avatars uploaded to the given user.
57      */
58     public function destroyAllForUser(User $user)
59     {
60         $profileImages = Image::query()->where('type', '=', 'user')
61             ->where('uploaded_to', '=', $user->id)
62             ->get();
63
64         foreach ($profileImages as $image) {
65             $this->imageService->destroy($image);
66         }
67     }
68
69     /**
70      * Save an avatar image from an external service.
71      *
72      * @throws Exception
73      */
74     protected function saveAvatarImage(User $user, int $size = 500): Image
75     {
76         $avatarUrl = $this->getAvatarUrl();
77         $email = strtolower(trim($user->email));
78
79         $replacements = [
80             '${hash}'  => md5($email),
81             '${size}'  => $size,
82             '${email}' => urlencode($email),
83         ];
84
85         $userAvatarUrl = strtr($avatarUrl, $replacements);
86         $imageData = $this->getAvatarImageData($userAvatarUrl);
87
88         return $this->createAvatarImageFromData($user, $imageData, 'png');
89     }
90
91     /**
92      * Creates a new image instance and saves it in the system as a new user avatar image.
93      */
94     protected function createAvatarImageFromData(User $user, string $imageData, string $extension): Image
95     {
96         $imageName = str_replace(' ', '-', $user->id . '-avatar.' . $extension);
97
98         $image = $this->imageService->saveNew($imageName, $imageData, 'user', $user->id);
99         $image->created_by = $user->id;
100         $image->updated_by = $user->id;
101         $image->save();
102
103         return $image;
104     }
105
106     /**
107      * Gets an image from url and returns it as a string of image data.
108      *
109      * @throws Exception
110      */
111     protected function getAvatarImageData(string $url): string
112     {
113         try {
114             $imageData = $this->http->fetch($url);
115         } catch (HttpFetchException $exception) {
116             throw new Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
117         }
118
119         return $imageData;
120     }
121
122     /**
123      * Check if fetching external avatars is enabled.
124      */
125     protected function avatarFetchEnabled(): bool
126     {
127         $fetchUrl = $this->getAvatarUrl();
128
129         return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
130     }
131
132     /**
133      * Get the URL to fetch avatars from.
134      */
135     protected function getAvatarUrl(): string
136     {
137         $url = trim(config('services.avatar_url'));
138
139         if (empty($url) && !config('services.disable_services')) {
140             $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon';
141         }
142
143         return $url;
144     }
145 }