From 5e6c039b08f3c0c90329201b4eb083e637405992 Mon Sep 17 00:00:00 2001
From: Vinrobot <vinrobot97@gmail.com>
Date: Thu, 8 Nov 2018 09:14:11 +0100
Subject: [PATCH 1/2] Added config to change Gravatar URL

---
 .env.example                 |  5 +++++
 app/Auth/UserRepo.php        |  2 +-
 app/Uploads/ImageService.php | 14 ++++++++++----
 config/services.php          |  1 +
 4 files changed, 17 insertions(+), 5 deletions(-)

diff --git a/.env.example b/.env.example
index eda20ea26..06394f54f 100644
--- a/.env.example
+++ b/.env.example
@@ -61,6 +61,11 @@ DISCORD_APP_SECRET=false
 
 # External services such as Gravatar and Draw.IO
 DISABLE_EXTERNAL_SERVICES=false
+# Default GRAVATAR_URL set to Gravatar service
+GRAVATAR_URL=false
+# To use a different service to get user's avatar like libravatar
+# Possible placeholders: %{hash} %{size} %{email}
+#GRAVATAR_URL=https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon
 
 # LDAP Settings
 LDAP_SERVER=false
diff --git a/app/Auth/UserRepo.php b/app/Auth/UserRepo.php
index 7c88badb8..abff7c641 100644
--- a/app/Auth/UserRepo.php
+++ b/app/Auth/UserRepo.php
@@ -251,7 +251,7 @@ class UserRepo
         }
 
         try {
-            $avatar = Images::saveUserGravatar($user);
+            $avatar = Images::saveUserGravatar($user, config('services.gravatar_url'));
             $user->avatar()->associate($avatar);
             $user->save();
             return true;
diff --git a/app/Uploads/ImageService.php b/app/Uploads/ImageService.php
index f109db600..b65a476f4 100644
--- a/app/Uploads/ImageService.php
+++ b/app/Uploads/ImageService.php
@@ -281,16 +281,22 @@ class ImageService extends UploadService
     /**
      * Save a gravatar image and set a the profile image for a user.
      * @param \BookStack\Auth\User $user
+     * @param null|string $gravatarUrl
      * @param int $size
      * @return mixed
      * @throws Exception
      */
-    public function saveUserGravatar(User $user, $size = 500)
+    public function saveUserGravatar(User $user, $gravatarUrl, $size = 500)
     {
-        $emailHash = md5(strtolower(trim($user->email)));
-        $url = 'https://www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon';
+        if (!is_string($gravatarUrl) || empty($gravatarUrl)) {
+            $gravatarUrl = 'https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon';
+        }
+        $email = strtolower(trim($user->email));
+        $gravatarUrl = str_replace('%{hash}', md5($email), $gravatarUrl);
+        $gravatarUrl = str_replace('%{size}', $size, $gravatarUrl);
+        $gravatarUrl = str_replace('%{email}', urlencode($email), $gravatarUrl);
         $imageName = str_replace(' ', '-', $user->name . '-gravatar.png');
-        $image = $this->saveNewFromUrl($url, 'user', $imageName);
+        $image = $this->saveNewFromUrl($gravatarUrl, 'user', $imageName);
         $image->created_by = $user->id;
         $image->updated_by = $user->id;
         $image->save();
diff --git a/config/services.php b/config/services.php
index 711040386..b8f152124 100644
--- a/config/services.php
+++ b/config/services.php
@@ -19,6 +19,7 @@ return [
     'gravatar' => env('GRAVATAR', !env('DISABLE_EXTERNAL_SERVICES', false)),
     'drawio' => env('DRAWIO', !env('DISABLE_EXTERNAL_SERVICES', false)),
 
+    'gravatar_url' => env('GRAVATAR_URL', false),
 
     'callback_url' => env('APP_URL', false),
 

From b56fc21aafa08f8fd7a9909e20d7b9032b3080cb Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sat, 22 Dec 2018 19:29:19 +0000
Subject: [PATCH 2/2] Abstracted user avatar fetching away from gravatar

Still uses gravatar as a default.
Updated URL placeholders to follow LDAP format.
Potential breaking config change: `GRAVATAR=false` replaced by `AVATAR_URL=false`
Builds upon #1111
---
 .env.example                            | 12 +++----
 app/Auth/UserRepo.php                   | 17 ++++------
 app/Console/Commands/CreateAdmin.php    |  2 +-
 app/Http/Controllers/UserController.php |  2 +-
 app/Uploads/ImageService.php            | 45 ++++++++++++++++++-------
 config/services.php                     |  9 +++--
 6 files changed, 55 insertions(+), 32 deletions(-)

diff --git a/.env.example b/.env.example
index c839d254b..1005ad208 100644
--- a/.env.example
+++ b/.env.example
@@ -60,13 +60,13 @@ GITLAB_BASE_URI=false
 DISCORD_APP_ID=false
 DISCORD_APP_SECRET=false
 
-# External services such as Gravatar and Draw.IO
+
+# Disable default services such as Gravatar and Draw.IO
 DISABLE_EXTERNAL_SERVICES=false
-# Default GRAVATAR_URL set to Gravatar service
-GRAVATAR_URL=false
-# To use a different service to get user's avatar like libravatar
-# Possible placeholders: %{hash} %{size} %{email}
-#GRAVATAR_URL=https://seccdn.libravatar.org/avatar/%{hash}?s=%{size}&d=identicon
+# Use custom avatar service, Sets fetch URL
+# Possible placeholders: ${hash} ${size} ${email}
+# If set, Avatars will be fetched regardless of DISABLE_EXTERNAL_SERVICES option.
+# AVATAR_URL=https://seccdn.libravatar.org/avatar/${hash}?s=${size}&d=identicon
 
 # LDAP Settings
 LDAP_SERVER=false
diff --git a/app/Auth/UserRepo.php b/app/Auth/UserRepo.php
index abff7c641..d436ab8eb 100644
--- a/app/Auth/UserRepo.php
+++ b/app/Auth/UserRepo.php
@@ -85,9 +85,7 @@ class UserRepo
     {
         $user = $this->create($data, $verifyEmail);
         $this->attachDefaultRole($user);
-
-        // Get avatar from gravatar and save
-        $this->downloadGravatarToUserAvatar($user);
+        $this->downloadAndAssignUserAvatar($user);
 
         return $user;
     }
@@ -238,25 +236,24 @@ class UserRepo
     }
 
     /**
-     * Get a gravatar image for a user and set it as their avatar.
-     * Does not run if gravatar disabled in config.
+     * Get an avatar image for a user and set it as their avatar.
+     * Returns early if avatars disabled or not set in config.
      * @param User $user
      * @return bool
      */
-    public function downloadGravatarToUserAvatar(User $user)
+    public function downloadAndAssignUserAvatar(User $user)
     {
-        // Get avatar from gravatar and save
-        if (!config('services.gravatar')) {
+        if (!Images::avatarFetchEnabled()) {
             return false;
         }
 
         try {
-            $avatar = Images::saveUserGravatar($user, config('services.gravatar_url'));
+            $avatar = Images::saveUserAvatar($user);
             $user->avatar()->associate($avatar);
             $user->save();
             return true;
         } catch (Exception $e) {
-            \Log::error('Failed to save user gravatar image');
+            \Log::error('Failed to save user avatar image');
             return false;
         }
     }
diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php
index 6bfc54469..90c1ddb1c 100644
--- a/app/Console/Commands/CreateAdmin.php
+++ b/app/Console/Commands/CreateAdmin.php
@@ -76,7 +76,7 @@ class CreateAdmin extends Command
 
         $user = $this->userRepo->create(['email' => $email, 'name' => $name, 'password' => $password]);
         $this->userRepo->attachSystemRole($user, 'admin');
-        $this->userRepo->downloadGravatarToUserAvatar($user);
+        $this->userRepo->downloadAndAssignUserAvatar($user);
         $user->email_confirmed = true;
         $user->save();
 
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index 5f5c8365e..24f8b67cb 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -92,7 +92,7 @@ class UserController extends Controller
             $user->roles()->sync($roles);
         }
 
-        $this->userRepo->downloadGravatarToUserAvatar($user);
+        $this->userRepo->downloadAndAssignUserAvatar($user);
 
         return redirect('/settings/users');
     }
diff --git a/app/Uploads/ImageService.php b/app/Uploads/ImageService.php
index b65a476f4..d5f4068ef 100644
--- a/app/Uploads/ImageService.php
+++ b/app/Uploads/ImageService.php
@@ -279,30 +279,51 @@ class ImageService extends UploadService
     }
 
     /**
-     * Save a gravatar image and set a the profile image for a user.
+     * Save an avatar image from an external service.
      * @param \BookStack\Auth\User $user
-     * @param null|string $gravatarUrl
      * @param int $size
-     * @return mixed
+     * @return Image
      * @throws Exception
      */
-    public function saveUserGravatar(User $user, $gravatarUrl, $size = 500)
+    public function saveUserAvatar(User $user, $size = 500)
     {
-        if (!is_string($gravatarUrl) || empty($gravatarUrl)) {
-            $gravatarUrl = 'https://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon';
-        }
+        $avatarUrl = $this->getAvatarUrl();
         $email = strtolower(trim($user->email));
-        $gravatarUrl = str_replace('%{hash}', md5($email), $gravatarUrl);
-        $gravatarUrl = str_replace('%{size}', $size, $gravatarUrl);
-        $gravatarUrl = str_replace('%{email}', urlencode($email), $gravatarUrl);
-        $imageName = str_replace(' ', '-', $user->name . '-gravatar.png');
-        $image = $this->saveNewFromUrl($gravatarUrl, 'user', $imageName);
+
+        $replacements = [
+            '${hash}' => md5($email),
+            '${size}' => $size,
+            '${email}' => urlencode($email),
+        ];
+
+        $userAvatarUrl = strtr($avatarUrl, $replacements);
+        $imageName = str_replace(' ', '-', $user->name . '-avatar.png');
+        $image = $this->saveNewFromUrl($userAvatarUrl, 'user', $imageName);
         $image->created_by = $user->id;
         $image->updated_by = $user->id;
         $image->save();
+
         return $image;
     }
 
+    /**
+     * Check if fetching external avatars is enabled.
+     * @return bool
+     */
+    public function avatarFetchEnabled()
+    {
+        $fetchUrl = $this->getAvatarUrl();
+        return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0;
+    }
+
+    /**
+     * Get the URL to fetch avatars from.
+     * @return string|mixed
+     */
+    protected function getAvatarUrl()
+    {
+        return trim(config('services.avatar_url'));
+    }
 
     /**
      * Delete gallery and drawings that are not within HTML content of pages or page revisions.
diff --git a/config/services.php b/config/services.php
index 310ea295f..7b9cf4e74 100644
--- a/config/services.php
+++ b/config/services.php
@@ -16,11 +16,16 @@ return [
 
     // Single option to disable non-auth external services such as Gravatar and Draw.io
     'disable_services' => env('DISABLE_EXTERNAL_SERVICES', false),
-    'gravatar' => env('GRAVATAR', !env('DISABLE_EXTERNAL_SERVICES', false)),
+
+    // Draw.io integration active
     'drawio' => env('DRAWIO', !env('DISABLE_EXTERNAL_SERVICES', false)),
 
-    'gravatar_url' => env('GRAVATAR_URL', false),
+    // URL for fetching avatars
+    'avatar_url' => env('AVATAR_URL',
+        env('DISABLE_EXTERNAL_SERVICES', false) ? false : 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon'
+    ),
 
+    // Callback URL for social authentication methods
     'callback_url' => env('APP_URL', false),
 
     'mailgun'  => [