avatarFetchEnabled()) { return; } try { $this->destroyAllForUser($user); $avatar = $this->saveAvatarImage($user); $user->avatar()->associate($avatar); $user->save(); } catch (Exception $e) { Log::error('Failed to save user avatar image', ['exception' => $e]); } } /** * 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(); } catch (Exception $e) { Log::error('Failed to save user avatar image', ['exception' => $e]); } } /** * Destroy all user avatars uploaded to the given user. */ public function destroyAllForUser(User $user): void { $profileImages = Image::query()->where('type', '=', 'user') ->where('uploaded_to', '=', $user->id) ->get(); foreach ($profileImages as $image) { $this->imageService->destroy($image); } } /** * Save an avatar image from an external service. * * @throws HttpFetchException */ protected function saveAvatarImage(User $user, int $size = 500): Image { $avatarUrl = $this->getAvatarUrl(); $email = strtolower(trim($user->email)); $replacements = [ '${hash}' => md5($email), '${size}' => $size, '${email}' => urlencode($email), ]; $userAvatarUrl = strtr($avatarUrl, $replacements); $imageData = $this->getAvatarImageData($userAvatarUrl); 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 { $imageName = Str::random(10) . '-avatar.' . $extension; $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. * * @throws HttpFetchException */ protected function getAvatarImageData(string $url): string { try { $client = $this->http->buildClient(5); $response = $client->sendRequest(new Request('GET', $url)); if ($response->getStatusCode() !== 200) { throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url])); } return (string) $response->getBody(); } catch (ClientExceptionInterface $exception) { throw new HttpFetchException(trans('errors.cannot_get_image_from_url', ['url' => $url]), $exception->getCode(), $exception); } } /** * Check if fetching external avatars is enabled. */ public function avatarFetchEnabled(): bool { $fetchUrl = $this->getAvatarUrl(); return str_starts_with($fetchUrl, 'http'); } /** * Get the URL to fetch avatars from. */ public function getAvatarUrl(): string { $configOption = config('services.avatar_url'); if ($configOption === false) { return ''; } $url = trim($configOption); if (empty($url) && !config('services.disable_services')) { $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon'; } return $url; } }