2021-06-26 11:23:15 -04:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace BookStack\Uploads;
|
2020-12-08 18:46:38 -05:00
|
|
|
|
|
|
|
use BookStack\Auth\User;
|
|
|
|
use BookStack\Exceptions\HttpFetchException;
|
|
|
|
use Exception;
|
2021-01-10 08:29:13 -05:00
|
|
|
use Illuminate\Support\Facades\Log;
|
2020-12-08 18:46:38 -05:00
|
|
|
|
|
|
|
class UserAvatars
|
|
|
|
{
|
|
|
|
protected $imageService;
|
|
|
|
protected $http;
|
|
|
|
|
|
|
|
public function __construct(ImageService $imageService, HttpFetcher $http)
|
|
|
|
{
|
|
|
|
$this->imageService = $imageService;
|
|
|
|
$this->http = $http;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fetch and assign an avatar image to the given user.
|
|
|
|
*/
|
|
|
|
public function fetchAndAssignToUser(User $user): void
|
|
|
|
{
|
|
|
|
if (!$this->avatarFetchEnabled()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2021-05-24 13:45:08 -04:00
|
|
|
$this->destroyAllForUser($user);
|
2020-12-08 18:46:38 -05:00
|
|
|
$avatar = $this->saveAvatarImage($user);
|
|
|
|
$user->avatar()->associate($avatar);
|
|
|
|
$user->save();
|
|
|
|
} catch (Exception $e) {
|
|
|
|
Log::error('Failed to save user avatar image');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-24 13:45:08 -04: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();
|
|
|
|
} catch (Exception $e) {
|
|
|
|
Log::error('Failed to save user avatar image');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroy all user avatars uploaded to the given user.
|
|
|
|
*/
|
|
|
|
public function destroyAllForUser(User $user)
|
|
|
|
{
|
|
|
|
$profileImages = Image::query()->where('type', '=', 'user')
|
|
|
|
->where('uploaded_to', '=', $user->id)
|
|
|
|
->get();
|
|
|
|
|
|
|
|
foreach ($profileImages as $image) {
|
|
|
|
$this->imageService->destroy($image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-08 18:46:38 -05:00
|
|
|
/**
|
|
|
|
* Save an avatar image from an external service.
|
2021-06-26 11:23:15 -04:00
|
|
|
*
|
2020-12-08 18:46:38 -05:00
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
protected function saveAvatarImage(User $user, int $size = 500): Image
|
|
|
|
{
|
|
|
|
$avatarUrl = $this->getAvatarUrl();
|
|
|
|
$email = strtolower(trim($user->email));
|
|
|
|
|
|
|
|
$replacements = [
|
2021-06-26 11:23:15 -04:00
|
|
|
'${hash}' => md5($email),
|
|
|
|
'${size}' => $size,
|
2020-12-08 18:46:38 -05:00
|
|
|
'${email}' => urlencode($email),
|
|
|
|
];
|
|
|
|
|
|
|
|
$userAvatarUrl = strtr($avatarUrl, $replacements);
|
|
|
|
$imageData = $this->getAvatarImageData($userAvatarUrl);
|
2021-06-26 11:23:15 -04:00
|
|
|
|
2021-05-24 13:45:08 -04: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
|
|
|
|
{
|
|
|
|
$imageName = str_replace(' ', '-', $user->id . '-avatar.' . $extension);
|
2020-12-08 18:46:38 -05: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 11:23:15 -04:00
|
|
|
*
|
2020-12-08 18:46:38 -05:00
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
protected function getAvatarImageData(string $url): string
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
$imageData = $this->http->fetch($url);
|
|
|
|
} catch (HttpFetchException $exception) {
|
|
|
|
throw new Exception(trans('errors.cannot_get_image_from_url', ['url' => $url]));
|
|
|
|
}
|
2021-06-26 11:23:15 -04:00
|
|
|
|
2020-12-08 18:46:38 -05:00
|
|
|
return $imageData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if fetching external avatars is enabled.
|
|
|
|
*/
|
|
|
|
protected function avatarFetchEnabled(): bool
|
|
|
|
{
|
|
|
|
$fetchUrl = $this->getAvatarUrl();
|
2021-06-26 11:23:15 -04:00
|
|
|
|
2020-12-08 18:46:38 -05:00
|
|
|
return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the URL to fetch avatars from.
|
|
|
|
*/
|
|
|
|
protected function getAvatarUrl(): string
|
|
|
|
{
|
|
|
|
$url = trim(config('services.avatar_url'));
|
|
|
|
|
|
|
|
if (empty($url) && !config('services.disable_services')) {
|
|
|
|
$url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $url;
|
|
|
|
}
|
2021-03-07 17:24:05 -05:00
|
|
|
}
|