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