diff --git a/.env.example b/.env.example
index 214b4621d..b48465052 100644
--- a/.env.example
+++ b/.env.example
@@ -1,19 +1,31 @@
-APP_ENV=local
-APP_DEBUG=true
+# Environment
+APP_ENV=production
+APP_DEBUG=false
 APP_KEY=SomeRandomString
 
+# Database details
 DB_HOST=localhost
-DB_DATABASE=homestead
-DB_USERNAME=homestead
-DB_PASSWORD=secret
+DB_DATABASE=database_database
+DB_USERNAME=database_username
+DB_PASSWORD=database__user_password
 
+# Cache and session
 CACHE_DRIVER=file
 SESSION_DRIVER=file
 QUEUE_DRIVER=sync
 
+# Social Authentication
+GITHUB_APP_ID=false
+GITHUB_APP_SECRET=false
+GOOGLE_APP_ID=false
+GOOGLE_APP_SECRET=false
+# URL for social login redirects, NO TRAILING SLASH
+APP_URL=http://bookstack.dev
+
+# Mail settings
 MAIL_DRIVER=smtp
-MAIL_HOST=mailtrap.io
-MAIL_PORT=2525
+MAIL_HOST=localhost
+MAIL_PORT=1025
 MAIL_USERNAME=null
 MAIL_PASSWORD=null
 MAIL_ENCRYPTION=null
\ No newline at end of file
diff --git a/app/Entity.php b/app/Entity.php
index 23b61e417..d2c5bd5c1 100644
--- a/app/Entity.php
+++ b/app/Entity.php
@@ -4,7 +4,7 @@ namespace Oxbow;
 
 use Illuminate\Database\Eloquent\Model;
 
-class Entity extends Model
+abstract class Entity extends Model
 {
     /**
      * Relation for the user that created this entity.
@@ -86,4 +86,10 @@ class Entity extends Model
         return $search->get();
     }
 
+    /**
+     * Get the url for this item.
+     * @return string
+     */
+    abstract public function getUrl();
+
 }
diff --git a/app/Http/Controllers/Auth/AuthController.php b/app/Http/Controllers/Auth/AuthController.php
index eacbe2bb9..c207f404c 100644
--- a/app/Http/Controllers/Auth/AuthController.php
+++ b/app/Http/Controllers/Auth/AuthController.php
@@ -2,15 +2,13 @@
 
 namespace Oxbow\Http\Controllers\Auth;
 
-use Oxbow\Exceptions\SocialDriverNotConfigured;
 use Oxbow\Exceptions\UserNotFound;
-use Oxbow\Repos\UserRepo;
+use Oxbow\Services\SocialAuthService;
 use Oxbow\User;
 use Validator;
 use Oxbow\Http\Controllers\Controller;
 use Illuminate\Foundation\Auth\ThrottlesLogins;
 use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
-use Laravel\Socialite\Contracts\Factory as Socialite;
 
 class AuthController extends Controller
 {
@@ -31,21 +29,16 @@ class AuthController extends Controller
     protected $redirectPath = '/';
     protected $redirectAfterLogout = '/login';
 
-    protected $validSocialDrivers = ['google', 'github'];
-
-    protected $socialite;
-    protected $userRepo;
+    protected $socialAuthService;
 
     /**
      * Create a new authentication controller instance.
-     * @param Socialite $socialite
-     * @param UserRepo  $userRepo
+     * @param SocialAuthService $socialAuthService
      */
-    public function __construct(Socialite $socialite, UserRepo $userRepo)
+    public function __construct(SocialAuthService $socialAuthService)
     {
         $this->middleware('guest', ['except' => 'getLogout']);
-        $this->socialite = $socialite;
-        $this->userRepo = $userRepo;
+        $this->socialAuthService = $socialAuthService;
     }
 
     /**
@@ -90,7 +83,7 @@ class AuthController extends Controller
             return view('auth.authenticate');
         }
 
-        $socialDrivers = $this->getActiveSocialDrivers();
+        $socialDrivers = $this->socialAuthService->getActiveDrivers();
 
         return view('auth.login', ['socialDrivers' => $socialDrivers]);
     }
@@ -102,8 +95,7 @@ class AuthController extends Controller
      */
     public function getSocialLogin($socialDriver)
     {
-        $driver = $this->validateSocialDriver($socialDriver);
-        return $this->socialite->driver($driver)->redirect();
+        return $this->socialAuthService->logIn($socialDriver);
     }
 
     /**
@@ -115,61 +107,9 @@ class AuthController extends Controller
      */
     public function socialCallback($socialDriver)
     {
-        $driver = $this->validateSocialDriver($socialDriver);
-        // Get user details from social driver
-        $socialUser = $this->socialite->driver($driver)->user();
-        $user = $this->userRepo->getByEmail($socialUser->getEmail());
-
-        // Redirect if the email is not a current user.
-        if ($user === null) {
-            throw new UserNotFound('A user with the email ' . $socialUser->getEmail() . ' was not found.', '/login');
-        }
-
+        $user = $this->socialAuthService->getUserFromCallback($socialDriver);
         \Auth::login($user, true);
         return redirect($this->redirectPath);
     }
 
-    /**
-     * Ensure the social driver is correct and supported.
-     *
-     * @param $socialDriver
-     * @return string
-     * @throws SocialDriverNotConfigured
-     */
-    protected function validateSocialDriver($socialDriver)
-    {
-        $driver = trim(strtolower($socialDriver));
-
-        if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found');
-        if(!$this->checkSocialDriverConfigured($driver)) throw new SocialDriverNotConfigured;
-
-        return $driver;
-    }
-
-    /**
-     * Check a social driver has been configured correctly.
-     * @param $driver
-     * @return bool
-     */
-    protected function checkSocialDriverConfigured($driver)
-    {
-        $upperName = strtoupper($driver);
-        $config = [env($upperName . '_APP_ID', false), env($upperName . '_APP_SECRET', false), env('APP_URL', false)];
-        return (!in_array(false, $config) && !in_array(null, $config));
-    }
-
-    /**
-     * Gets the names of the active social drivers.
-     * @return array
-     */
-    protected function getActiveSocialDrivers()
-    {
-        $activeDrivers = [];
-        foreach($this->validSocialDrivers as $driverName) {
-            if($this->checkSocialDriverConfigured($driverName)) {
-                $activeDrivers[$driverName] = true;
-            }
-        }
-        return $activeDrivers;
-    }
 }
diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php
index edad13636..7f8bada8c 100644
--- a/app/Http/Controllers/BookController.php
+++ b/app/Http/Controllers/BookController.php
@@ -144,6 +144,7 @@ class BookController extends Controller
         $this->checkPermission('book-delete');
         $book = $this->bookRepo->getBySlug($bookSlug);
         Activity::addMessage('book_delete', 0, $book->name);
+        Activity::removeEntity($book);
         $this->bookRepo->destroyBySlug($bookSlug);
         return redirect('/books');
     }
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index 0266d2671..63c27fa0b 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -100,7 +100,7 @@ class UserController extends Controller
         });
         $this->validate($request, [
             'name'             => 'required',
-            'email'            => 'required|email',
+            'email'            => 'required|email|unique:users,email,' . $id,
             'password'         => 'min:5',
             'password-confirm' => 'same:password',
             'role'             => 'exists:roles,id'
diff --git a/app/Image.php b/app/Image.php
index f80f33fff..cb8ee7c59 100644
--- a/app/Image.php
+++ b/app/Image.php
@@ -13,4 +13,12 @@ class Image extends Entity
         return storage_path() . $this->url;
     }
 
+    /**
+     * Get the url for this item.
+     * @return string
+     */
+    public function getUrl()
+    {
+        return public_path() . $this->url;
+    }
 }
diff --git a/app/Repos/BookRepo.php b/app/Repos/BookRepo.php
index 7f7517e92..5a5da5138 100644
--- a/app/Repos/BookRepo.php
+++ b/app/Repos/BookRepo.php
@@ -54,9 +54,11 @@ class BookRepo
     {
         $book = $this->getBySlug($bookSlug);
         foreach($book->pages as $page) {
+            \Activity::removeEntity($page);
             $page->delete();
         }
         foreach($book->chapters as $chapter) {
+            \Activity::removeEntity($chapter);
             $chapter->delete();
         }
         $book->delete();
diff --git a/app/Services/SocialAuthService.php b/app/Services/SocialAuthService.php
new file mode 100644
index 000000000..c8f3278c5
--- /dev/null
+++ b/app/Services/SocialAuthService.php
@@ -0,0 +1,101 @@
+<?php namespace Oxbow\Services;
+
+use Laravel\Socialite\Contracts\Factory as Socialite;
+use Oxbow\Exceptions\SocialDriverNotConfigured;
+use Oxbow\Exceptions\UserNotFound;
+use Oxbow\Repos\UserRepo;
+
+class SocialAuthService
+{
+
+    protected $userRepo;
+    protected $socialite;
+
+    protected $validSocialDrivers = ['google', 'github'];
+
+    /**
+     * SocialAuthService constructor.
+     * @param $userRepo
+     * @param $socialite
+     */
+    public function __construct(UserRepo $userRepo, Socialite $socialite)
+    {
+        $this->userRepo = $userRepo;
+        $this->socialite = $socialite;
+    }
+
+    public function logIn($socialDriver)
+    {
+        $driver = $this->validateDriver($socialDriver);
+        return $this->socialite->driver($driver)->redirect();
+    }
+
+    /**
+     * Get a user from socialite after a oAuth callback.
+     *
+     * @param $socialDriver
+     * @return mixed
+     * @throws SocialDriverNotConfigured
+     * @throws UserNotFound
+     */
+    public function getUserFromCallback($socialDriver)
+    {
+        $driver = $this->validateDriver($socialDriver);
+        // Get user details from social driver
+        $socialUser = $this->socialite->driver($driver)->user();
+        $user = $this->userRepo->getByEmail($socialUser->getEmail());
+
+        // Redirect if the email is not a current user.
+        if ($user === null) {
+            throw new UserNotFound('A user with the email ' . $socialUser->getEmail() . ' was not found.', '/login');
+        }
+
+        return $user;
+    }
+
+    /**
+     * Ensure the social driver is correct and supported.
+     *
+     * @param $socialDriver
+     * @return string
+     * @throws SocialDriverNotConfigured
+     */
+    private function validateDriver($socialDriver)
+    {
+        $driver = trim(strtolower($socialDriver));
+
+        if (!in_array($driver, $this->validSocialDrivers)) abort(404, 'Social Driver Not Found');
+        if (!$this->checklDriverConfigured($driver)) throw new SocialDriverNotConfigured;
+
+        return $driver;
+    }
+
+    /**
+     * Check a social driver has been configured correctly.
+     * @param $driver
+     * @return bool
+     */
+    private function checklDriverConfigured($driver)
+    {
+        $upperName = strtoupper($driver);
+        $config = [env($upperName . '_APP_ID', false), env($upperName . '_APP_SECRET', false), env('APP_URL', false)];
+        return (!in_array(false, $config) && !in_array(null, $config));
+    }
+
+    /**
+     * Gets the names of the active social drivers.
+     * @return array
+     */
+    public function getActiveDrivers()
+    {
+        $activeDrivers = [];
+        foreach ($this->validSocialDrivers as $driverName) {
+            if ($this->checklDriverConfigured($driverName)) {
+                $activeDrivers[$driverName] = true;
+            }
+        }
+        return $activeDrivers;
+    }
+
+
+}
\ No newline at end of file