| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Uploads; | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | use BookStack\Exceptions\HttpFetchException; | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  | use BookStack\Http\HttpRequestService; | 
					
						
							| 
									
										
										
										
											2023-05-18 00:56:55 +08:00
										 |  |  | use BookStack\Users\Models\User; | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  | use Exception; | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  | use GuzzleHttp\Psr7\Request; | 
					
						
							| 
									
										
										
										
											2021-01-10 21:29:13 +08:00
										 |  |  | use Illuminate\Support\Facades\Log; | 
					
						
							| 
									
										
										
										
											2022-07-26 19:10:19 +08:00
										 |  |  | use Illuminate\Support\Str; | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  | use Psr\Http\Client\ClientExceptionInterface; | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | class UserAvatars | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  |     public function __construct( | 
					
						
							|  |  |  |         protected ImageService $imageService, | 
					
						
							|  |  |  |         protected HttpRequestService $http | 
					
						
							|  |  |  |     ) { | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Fetch and assign an avatar image to the given user. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function fetchAndAssignToUser(User $user): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->avatarFetchEnabled()) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |             $this->destroyAllForUser($user); | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |             $avatar = $this->saveAvatarImage($user); | 
					
						
							|  |  |  |             $user->avatar()->associate($avatar); | 
					
						
							|  |  |  |             $user->save(); | 
					
						
							| 
									
										
										
										
											2023-06-19 14:47:47 +08:00
										 |  |  |         } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2023-06-14 20:09:52 +08:00
										 |  |  |             Log::error('Failed to save user avatar image', ['exception' => $e]); | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Assign a new avatar image to the given user using the given image data. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function assignToUserFromExistingData(User $user, string $imageData, string $extension): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             $this->destroyAllForUser($user); | 
					
						
							|  |  |  |             $avatar = $this->createAvatarImageFromData($user, $imageData, $extension); | 
					
						
							|  |  |  |             $user->avatar()->associate($avatar); | 
					
						
							|  |  |  |             $user->save(); | 
					
						
							| 
									
										
										
										
											2023-06-19 14:47:47 +08:00
										 |  |  |         } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2023-06-14 20:09:52 +08:00
										 |  |  |             Log::error('Failed to save user avatar image', ['exception' => $e]); | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Destroy all user avatars uploaded to the given user. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-09-19 02:04:59 +08:00
										 |  |  |     public function destroyAllForUser(User $user): void | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $profileImages = Image::query()->where('type', '=', 'user') | 
					
						
							|  |  |  |             ->where('uploaded_to', '=', $user->id) | 
					
						
							|  |  |  |             ->get(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         foreach ($profileImages as $image) { | 
					
						
							|  |  |  |             $this->imageService->destroy($image); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Save an avatar image from an external service. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-09-19 02:04:59 +08:00
										 |  |  |      * @throws HttpFetchException | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected function saveAvatarImage(User $user, int $size = 500): Image | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $avatarUrl = $this->getAvatarUrl(); | 
					
						
							|  |  |  |         $email = strtolower(trim($user->email)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $replacements = [ | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             '${hash}'  => md5($email), | 
					
						
							|  |  |  |             '${size}'  => $size, | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |             '${email}' => urlencode($email), | 
					
						
							|  |  |  |         ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $userAvatarUrl = strtr($avatarUrl, $replacements); | 
					
						
							|  |  |  |         $imageData = $this->getAvatarImageData($userAvatarUrl); | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |         return $this->createAvatarImageFromData($user, $imageData, 'png'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Creates a new image instance and saves it in the system as a new user avatar image. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function createAvatarImageFromData(User $user, string $imageData, string $extension): Image | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-07-26 19:10:19 +08:00
										 |  |  |         $imageName = Str::random(10) . '-avatar.' . $extension; | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $image = $this->imageService->saveNew($imageName, $imageData, 'user', $user->id); | 
					
						
							|  |  |  |         $image->created_by = $user->id; | 
					
						
							|  |  |  |         $image->updated_by = $user->id; | 
					
						
							|  |  |  |         $image->save(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $image; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Gets an image from url and returns it as a string of image data. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2023-06-14 20:09:52 +08:00
										 |  |  |      * @throws HttpFetchException | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected function getAvatarImageData(string $url): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  |             $client = $this->http->buildClient(5); | 
					
						
							|  |  |  |             $response = $client->sendRequest(new Request('GET', $url)); | 
					
						
							| 
									
										
										
										
											2023-09-19 02:04:59 +08:00
										 |  |  |             if ($response->getStatusCode() !== 200) { | 
					
						
							|  |  |  |                 throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url])); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return (string) $response->getBody(); | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  |         } catch (ClientExceptionInterface $exception) { | 
					
						
							| 
									
										
										
										
											2023-06-14 20:09:52 +08:00
										 |  |  |             throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]), $exception->getCode(), $exception); | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Check if fetching external avatars is enabled. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-09-19 22:53:01 +08:00
										 |  |  |     public function avatarFetchEnabled(): bool | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $fetchUrl = $this->getAvatarUrl(); | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-09 00:16:57 +08:00
										 |  |  |         return str_starts_with($fetchUrl, 'http'); | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the URL to fetch avatars from. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2023-09-19 22:53:01 +08:00
										 |  |  |     public function getAvatarUrl(): string | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-07-26 19:10:19 +08:00
										 |  |  |         $configOption = config('services.avatar_url'); | 
					
						
							|  |  |  |         if ($configOption === false) { | 
					
						
							|  |  |  |             return ''; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $url = trim($configOption); | 
					
						
							| 
									
										
										
										
											2020-12-09 07:46:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (empty($url) && !config('services.disable_services')) { | 
					
						
							|  |  |  |             $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $url; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-03-08 06:24:05 +08:00
										 |  |  | } |