diff --git a/PlexRequests.Api/ApiRequest.cs b/PlexRequests.Api/ApiRequest.cs
index 704880f9b..8cb023ea6 100644
--- a/PlexRequests.Api/ApiRequest.cs
+++ b/PlexRequests.Api/ApiRequest.cs
@@ -63,6 +63,7 @@ namespace PlexRequests.Api
             Log.Trace("Api Content Response:");
             Log.Trace(response.Content);
 
+
             if (response.ErrorException != null)
             {
                 var message = "Error retrieving response. Check inner details for more info.";
diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj
index e4448d873..2e2d1b3a1 100644
--- a/PlexRequests.Core/PlexRequests.Core.csproj
+++ b/PlexRequests.Core/PlexRequests.Core.csproj
@@ -86,6 +86,7 @@
     <Compile Include="SettingModels\HeadphonesSettings.cs" />
     <Compile Include="SettingModels\LandingPageSettings.cs" />
     <Compile Include="SettingModels\NotificationSettings.cs" />
+    <Compile Include="SettingModels\RequestSettings.cs" />
     <Compile Include="SettingModels\ScheduledJobsSettings.cs" />
     <Compile Include="SettingModels\SlackNotificationSettings.cs" />
     <Compile Include="SettingModels\PushoverNotificationSettings.cs" />
diff --git a/PlexRequests.Core/SettingModels/PlexSettings.cs b/PlexRequests.Core/SettingModels/PlexSettings.cs
index e83e61f4d..09be5fb15 100644
--- a/PlexRequests.Core/SettingModels/PlexSettings.cs
+++ b/PlexRequests.Core/SettingModels/PlexSettings.cs
@@ -24,6 +24,9 @@
 //    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //  ************************************************************************/
 #endregion
+
+using Newtonsoft.Json;
+
 namespace PlexRequests.Core.SettingModels
 {
     public sealed class PlexSettings : ExternalSettings
@@ -36,5 +39,6 @@ namespace PlexRequests.Core.SettingModels
         public bool EnableTvEpisodeSearching { get; set; }
 
         public string PlexAuthToken { get; set; }
+        public string MachineIdentifier { get; set; }
     }
 }
\ No newline at end of file
diff --git a/PlexRequests.Core/SettingModels/RequestSettings.cs b/PlexRequests.Core/SettingModels/RequestSettings.cs
new file mode 100644
index 000000000..5a9140947
--- /dev/null
+++ b/PlexRequests.Core/SettingModels/RequestSettings.cs
@@ -0,0 +1,57 @@
+#region Copyright
+// /************************************************************************
+//    Copyright (c) 2016 Jamie Rees
+//    File: RequestSettings.cs
+//    Created By: Jamie Rees
+//   
+//    Permission is hereby granted, free of charge, to any person obtaining
+//    a copy of this software and associated documentation files (the
+//    "Software"), to deal in the Software without restriction, including
+//    without limitation the rights to use, copy, modify, merge, publish,
+//    distribute, sublicense, and/or sell copies of the Software, and to
+//    permit persons to whom the Software is furnished to do so, subject to
+//    the following conditions:
+//   
+//    The above copyright notice and this permission notice shall be
+//    included in all copies or substantial portions of the Software.
+//   
+//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//  ************************************************************************/
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace PlexRequests.Core.SettingModels
+{
+    public sealed class RequestSettings : Settings
+    {
+        public OrderType Order { get; set; }
+        public List<FilterType> Filters { get; set; }
+    }
+
+    public enum OrderType
+    {
+        NewRequests,
+        OldRequests,
+        NewReleases,
+        OldReleases
+    }
+
+    public enum FilterType
+    {
+        // ALL is not here, it's managed in the angular controller
+        Approved,
+        NotApproved,
+        Available,
+        NotAvailable,
+        Released,
+        NotReleased
+    }
+}
\ No newline at end of file
diff --git a/PlexRequests.Core/Setup.cs b/PlexRequests.Core/Setup.cs
index 50b27dd2d..dac15a731 100644
--- a/PlexRequests.Core/Setup.cs
+++ b/PlexRequests.Core/Setup.cs
@@ -26,6 +26,7 @@
 #endregion
 
 using System;
+using System.Linq;
 using System.Text.RegularExpressions;
 
 using Mono.Data.Sqlite;
@@ -66,6 +67,11 @@ namespace PlexRequests.Core
                 {
                     MigrateToVersion1900();
                 }
+
+                if(version > 1899 && version <= 1910)
+                {
+                    MigrateToVersion1910();
+                }
             }
 
             return Db.DbConnection().ConnectionString;
@@ -244,5 +250,30 @@ namespace PlexRequests.Core
                 Log.Error(e);
             }
         }
+
+        /// <summary>
+        /// Migrates to version1910.
+        /// </summary>
+        public void MigrateToVersion1910()
+        {
+            try
+            {
+                // Get the new machine Identifier
+                var settings = new SettingsServiceV2<PlexSettings>(new SettingsJsonRepository(Db, new MemoryCacheProvider()));
+                var plex = settings.GetSettings();
+                if (!string.IsNullOrEmpty(plex.PlexAuthToken))
+                {
+                    var api = new PlexApi(new ApiRequest());
+                    var server = api.GetServer(plex.PlexAuthToken); // Get the server info
+                    plex.MachineIdentifier = server.Server.FirstOrDefault(x => x.AccessToken == plex.PlexAuthToken)?.MachineIdentifier;
+
+                    settings.SaveSettings(plex); // Save the new settings
+                }
+            }
+            catch (Exception e)
+            {
+                Log.Error(e);
+            }
+        }
     }
 }
diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs
index 5be4a868a..3c15d548e 100644
--- a/PlexRequests.Core/UserMapper.cs
+++ b/PlexRequests.Core/UserMapper.cs
@@ -99,7 +99,7 @@ namespace PlexRequests.Core
             return users.Any();
         }
 
-        private Guid? CreateUser(string username, string password, string[] claims = default(string[]), UserProperties properties = null)
+        public Guid? CreateUser(string username, string password, string[] claims = default(string[]), UserProperties properties = null)
         {
             var salt = PasswordHasher.GenerateSalt();
 
@@ -134,6 +134,13 @@ namespace PlexRequests.Core
             return CreateUser(username, password, new[] { UserClaims.User }, properties);
         }
 
+
+        public IEnumerable<string> GetAllClaims()
+        {
+            var properties = typeof(UserClaims).GetConstantsValues<string>();
+            return properties;
+        }
+
         public bool UpdatePassword(string username, string oldPassword, string newPassword)
         {
             var users = Repo.GetAll();
@@ -175,6 +182,8 @@ namespace PlexRequests.Core
 
     public interface ICustomUserMapper
     {
+        Guid? CreateUser(string username, string password, string[] claims, UserProperties props);
+        IEnumerable<string> GetAllClaims();
         IEnumerable<UsersModel> GetUsers();
         Task<IEnumerable<UsersModel>> GetUsersAsync();
         UsersModel GetUser(Guid userId);
diff --git a/PlexRequests.Helpers.Tests/PlexHelperTests.cs b/PlexRequests.Helpers.Tests/PlexHelperTests.cs
index 6f09d6c39..33a1bf1a3 100644
--- a/PlexRequests.Helpers.Tests/PlexHelperTests.cs
+++ b/PlexRequests.Helpers.Tests/PlexHelperTests.cs
@@ -61,6 +61,12 @@ namespace PlexRequests.Helpers.Tests
             return PlexHelper.GetSeasonNumberFromTitle(title);
         }
 
+        [TestCaseSource(nameof(MediaUrls))]
+        public string GetPlexMediaUrlTest(string machineId, string mediaId)
+        {
+            return PlexHelper.GetPlexMediaUrl(machineId, mediaId);
+        }
+
         private static IEnumerable<TestCaseData> PlexGuids
         {
             get
@@ -75,6 +81,15 @@ namespace PlexRequests.Helpers.Tests
             }
         }
 
+        private static IEnumerable<TestCaseData> MediaUrls
+        {
+            get
+            {
+                yield return new TestCaseData("abcd","99").Returns("https://app.plex.tv/web/app#!/server/abcd/details/%2Flibrary%2Fmetadata%2F99").SetName("Test 1");
+                yield return new TestCaseData("a54d1db669799308cd704b791f331eca6648b952", "51").Returns("https://app.plex.tv/web/app#!/server/a54d1db669799308cd704b791f331eca6648b952/details/%2Flibrary%2Fmetadata%2F51").SetName("Test 2");
+            }
+        }
+
         private static IEnumerable<TestCaseData> SeasonNumbers
         {
             get
diff --git a/PlexRequests.Helpers.Tests/TypeHelperTests.cs b/PlexRequests.Helpers.Tests/TypeHelperTests.cs
index 295b955ad..1d93c375f 100644
--- a/PlexRequests.Helpers.Tests/TypeHelperTests.cs
+++ b/PlexRequests.Helpers.Tests/TypeHelperTests.cs
@@ -26,7 +26,7 @@
 #endregion
 using System;
 using System.Collections.Generic;
-
+using System.Linq;
 using NUnit.Framework;
 
 using PlexRequests.Store;
@@ -42,6 +42,14 @@ namespace PlexRequests.Helpers.Tests
             return input.GetPropertyNames();
         }
 
+        [Test]
+        public void GetConstantsTest()
+        {
+            var consts = typeof(UserClaims).GetConstantsValues<string>();
+            Assert.That(consts.Contains("Admin"),Is.True);
+            Assert.That(consts.Contains("PowerUser"),Is.True);
+            Assert.That(consts.Contains("User"),Is.True);
+        }
 
         private static IEnumerable<TestCaseData> TypeData
         {
diff --git a/PlexRequests.Helpers/PlexHelper.cs b/PlexRequests.Helpers/PlexHelper.cs
index baafcd451..1e186ddba 100644
--- a/PlexRequests.Helpers/PlexHelper.cs
+++ b/PlexRequests.Helpers/PlexHelper.cs
@@ -95,6 +95,13 @@ namespace PlexRequests.Helpers
 
             return 0;
         }
+
+        public static string GetPlexMediaUrl(string machineId, string mediaId)
+        {
+            var url =
+                $"https://app.plex.tv/web/app#!/server/{machineId}/details/%2Flibrary%2Fmetadata%2F{mediaId}";
+            return url;
+        }
     }
 
     public class EpisodeModelHelper
diff --git a/PlexRequests.Helpers/TypeHelper.cs b/PlexRequests.Helpers/TypeHelper.cs
index 27a108bef..77eae6360 100644
--- a/PlexRequests.Helpers/TypeHelper.cs
+++ b/PlexRequests.Helpers/TypeHelper.cs
@@ -25,7 +25,9 @@
 //  ************************************************************************/
 #endregion
 using System;
+using System.Collections.Generic;
 using System.Linq;
+using System.Reflection;
 
 namespace PlexRequests.Helpers
 {
@@ -35,5 +37,19 @@ namespace PlexRequests.Helpers
         {
             return t.GetProperties().Select(x => x.Name).ToArray();
         }
+
+        public static IEnumerable<FieldInfo> GetConstants(this Type type)
+        {
+            var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
+
+            return fieldInfos.Where(fi => fi.IsLiteral && !fi.IsInitOnly);
+        }
+
+        public static IEnumerable<T> GetConstantsValues<T>(this Type type) where T : class
+        {
+            var fieldInfos = GetConstants(type);
+
+            return fieldInfos.Select(fi => fi.GetRawConstantValue() as T);
+        }
     }
 }
\ No newline at end of file
diff --git a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs
index 5b8ba361f..801af7509 100644
--- a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs
+++ b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs
@@ -42,6 +42,7 @@ using PlexRequests.Services.Interfaces;
 using PlexRequests.Helpers;
 using PlexRequests.Services.Jobs;
 using PlexRequests.Services.Models;
+using PlexRequests.Services.Notification;
 using PlexRequests.Store.Models;
 using PlexRequests.Store.Repository;
 
@@ -63,6 +64,11 @@ namespace PlexRequests.Services.Tests
         private Mock<IJobRecord> JobRec { get; set; }
         private Mock<IRepository<UsersToNotify>> NotifyUsers { get; set; }
         private Mock<IRepository<PlexEpisodes>> PlexEpisodes { get; set; }
+        private Mock<INotificationEngine> Engine
+        {
+            get;
+            set;
+        }
 
         [SetUp]
         public void Setup()
@@ -76,7 +82,8 @@ namespace PlexRequests.Services.Tests
             NotifyUsers = new Mock<IRepository<UsersToNotify>>();
             PlexEpisodes = new Mock<IRepository<PlexEpisodes>>();
             JobRec = new Mock<IJobRecord>();
-            Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object);
+            Engine = new Mock<INotificationEngine>();
+            Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object, Engine.Object);
 
         }
 
@@ -212,8 +219,7 @@ namespace PlexRequests.Services.Tests
                 new PlexEpisodes {EpisodeNumber = 1, ShowTitle = "The Flash",ProviderId = 23.ToString(), SeasonNumber = 1, EpisodeTitle = "Pilot"}
             };
             PlexEpisodes.Setup(x => x.Custom(It.IsAny<Func<IDbConnection, IEnumerable<PlexEpisodes>>>())).Returns(expected);
-            Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object);
-
+            
             var result = Checker.IsEpisodeAvailable(providerId, season, episode);
 
             return result;
@@ -242,6 +248,7 @@ namespace PlexRequests.Services.Tests
                 }
             });
             CacheMock.Setup(x => x.Get<List<PlexSearch>>(CacheKeys.PlexLibaries)).Returns(cachedMovies);
+            SettingsMock.Setup(x => x.GetSettings()).Returns(F.Create<PlexSettings>());
             var movies = Checker.GetPlexMovies();
 
             Assert.That(movies.Any(x => x.ProviderId == "1212"));
@@ -258,6 +265,7 @@ namespace PlexRequests.Services.Tests
                     new Directory1 {Type = "show", Title = "title1", Year = "2016", ProviderId = "1212", Seasons = new List<Directory1>()}
                 }
             });
+            SettingsMock.Setup(x => x.GetSettings()).Returns(F.Create<PlexSettings>());
             CacheMock.Setup(x => x.Get<List<PlexSearch>>(CacheKeys.PlexLibaries)).Returns(cachedTv);
             var movies = Checker.GetPlexTvShows();
 
@@ -268,8 +276,6 @@ namespace PlexRequests.Services.Tests
         public async Task GetAllPlexEpisodes()
         {
             PlexEpisodes.Setup(x => x.GetAllAsync()).ReturnsAsync(F.CreateMany<PlexEpisodes>().ToList());
-            Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object);
-
             var episodes = await Checker.GetEpisodes();
 
             Assert.That(episodes.Count(), Is.GreaterThan(0));
diff --git a/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs b/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs
index 8992e6545..d966e2b5f 100644
--- a/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs
+++ b/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs
@@ -42,6 +42,9 @@ namespace PlexRequests.Services.Interfaces
         List<PlexAlbum> GetPlexAlbums();
         bool IsAlbumAvailable(PlexAlbum[] plexAlbums, string title, string year, string artist);
         bool IsEpisodeAvailable(string theTvDbId, int season, int episode);
+        PlexAlbum GetAlbum(PlexAlbum[] plexAlbums, string title, string year, string artist);
+        PlexMovie GetMovie(PlexMovie[] plexMovies, string title, string year, string providerId = null);
+        PlexTvShow GetTvShow(PlexTvShow[] plexShows, string title, string year, string providerId = null, int[] seasons = null);
         /// <summary>
         /// Gets the episode's stored in the cache.
         /// </summary>
diff --git a/PlexRequests.Services/Interfaces/INotificationEngine.cs b/PlexRequests.Services/Interfaces/INotificationEngine.cs
new file mode 100644
index 000000000..ba53eb9bf
--- /dev/null
+++ b/PlexRequests.Services/Interfaces/INotificationEngine.cs
@@ -0,0 +1,39 @@
+#region Copyright
+// /************************************************************************
+//    Copyright (c) 2016 Jamie Rees
+//    File: INotificationEngine.cs
+//    Created By: Jamie Rees
+//   
+//    Permission is hereby granted, free of charge, to any person obtaining
+//    a copy of this software and associated documentation files (the
+//    "Software"), to deal in the Software without restriction, including
+//    without limitation the rights to use, copy, modify, merge, publish,
+//    distribute, sublicense, and/or sell copies of the Software, and to
+//    permit persons to whom the Software is furnished to do so, subject to
+//    the following conditions:
+//   
+//    The above copyright notice and this permission notice shall be
+//    included in all copies or substantial portions of the Software.
+//   
+//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//  ************************************************************************/
+#endregion
+
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using PlexRequests.Store;
+
+namespace PlexRequests.Services.Interfaces
+{
+    public interface INotificationEngine
+    {
+        Task NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey);
+        Task NotifyUsers(RequestedModel modelChanged, string apiKey);
+    }
+}
\ No newline at end of file
diff --git a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs
index e58a18c17..c8b46b32f 100644
--- a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs
+++ b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs
@@ -53,7 +53,7 @@ namespace PlexRequests.Services.Jobs
     public class PlexAvailabilityChecker : IJob, IAvailabilityChecker
     {
         public PlexAvailabilityChecker(ISettingsService<PlexSettings> plexSettings, IRequestService request, IPlexApi plex, ICacheProvider cache,
-            INotificationService notify, IJobRecord rec, IRepository<UsersToNotify> users, IRepository<PlexEpisodes> repo)
+            INotificationService notify, IJobRecord rec, IRepository<UsersToNotify> users, IRepository<PlexEpisodes> repo, INotificationEngine e)
         {
             Plex = plexSettings;
             RequestService = request;
@@ -63,6 +63,7 @@ namespace PlexRequests.Services.Jobs
             Job = rec;
             UserNotifyRepo = users;
             EpisodeRepo = repo;
+            NotificationEngine = e;
         }
 
         private ISettingsService<PlexSettings> Plex { get; }
@@ -74,6 +75,9 @@ namespace PlexRequests.Services.Jobs
         private INotificationService Notification { get; }
         private IJobRecord Job { get; }
         private IRepository<UsersToNotify> UserNotifyRepo { get; }
+        private INotificationEngine NotificationEngine { get; }
+
+
         public void CheckAndUpdateAll()
         {
             var plexSettings = Plex.GetSettings();
@@ -148,7 +152,7 @@ namespace PlexRequests.Services.Jobs
 
             if (modifiedModel.Any())
             {
-                NotifyUsers(modifiedModel, plexSettings.PlexAuthToken);
+                NotificationEngine.NotifyUsers(modifiedModel, plexSettings.PlexAuthToken);
                 RequestService.BatchUpdate(modifiedModel);
             }
 
@@ -158,6 +162,7 @@ namespace PlexRequests.Services.Jobs
 
         public List<PlexMovie> GetPlexMovies()
         {
+            var settings = Plex.GetSettings();
             var movies = new List<PlexMovie>();
             var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
             if (libs != null)
@@ -175,6 +180,7 @@ namespace PlexRequests.Services.Jobs
                         ReleaseYear = video.Year,
                         Title = video.Title,
                         ProviderId = video.ProviderId,
+                        Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, video.RatingKey)
                     }));
                 }
             }
@@ -182,6 +188,12 @@ namespace PlexRequests.Services.Jobs
         }
 
         public bool IsMovieAvailable(PlexMovie[] plexMovies, string title, string year, string providerId = null)
+        {
+            var movie = GetMovie(plexMovies, title, year, providerId);
+            return movie != null;
+        }
+
+        public PlexMovie GetMovie(PlexMovie[] plexMovies, string title, string year, string providerId = null)
         {
             var advanced = !string.IsNullOrEmpty(providerId);
             foreach (var movie in plexMovies)
@@ -191,20 +203,21 @@ namespace PlexRequests.Services.Jobs
                     if (!string.IsNullOrEmpty(movie.ProviderId) &&
                         movie.ProviderId.Equals(providerId, StringComparison.InvariantCultureIgnoreCase))
                     {
-                        return true;
+                        return movie;
                     }
                 }
                 if (movie.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) &&
                     movie.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase))
                 {
-                    return true;
+                    return movie;
                 }
             }
-            return false;
+            return null;
         }
 
         public List<PlexTvShow> GetPlexTvShows()
         {
+            var settings = Plex.GetSettings();
             var shows = new List<PlexTvShow>();
             var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
             if (libs != null)
@@ -224,7 +237,9 @@ namespace PlexRequests.Services.Jobs
                         Title = x.Title,
                         ReleaseYear = x.Year,
                         ProviderId = x.ProviderId,
-                        Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray()
+                        Seasons = x.Seasons?.Select(d => PlexHelper.GetSeasonNumberFromTitle(d.Title)).ToArray(),
+                        Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey)
+
                     }));
                 }
             }
@@ -232,6 +247,14 @@ namespace PlexRequests.Services.Jobs
         }
 
         public bool IsTvShowAvailable(PlexTvShow[] plexShows, string title, string year, string providerId = null, int[] seasons = null)
+        {
+            var show = GetTvShow(plexShows, title, year, providerId, seasons);
+            return show != null;
+        }
+
+
+        public PlexTvShow GetTvShow(PlexTvShow[] plexShows, string title, string year, string providerId = null,
+            int[] seasons = null)
         {
             var advanced = !string.IsNullOrEmpty(providerId);
             foreach (var show in plexShows)
@@ -242,23 +265,23 @@ namespace PlexRequests.Services.Jobs
                     {
                         if (seasons.Any(season => show.Seasons.Contains(season)))
                         {
-                            return true;
+                            return show;
                         }
-                        return false;
+                        return null;
                     }
                     if (!string.IsNullOrEmpty(show.ProviderId) &&
                         show.ProviderId.Equals(providerId, StringComparison.InvariantCultureIgnoreCase))
                     {
-                        return true;
+                        return show;
                     }
                 }
                 if (show.Title.Equals(title, StringComparison.CurrentCultureIgnoreCase) &&
                     show.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase))
                 {
-                    return true;
+                    return show;
                 }
             }
-            return false;
+            return null;
         }
 
         public bool IsEpisodeAvailable(string theTvDbId, int season, int episode)
@@ -328,6 +351,7 @@ namespace PlexRequests.Services.Jobs
 
         public List<PlexAlbum> GetPlexAlbums()
         {
+            var settings = Plex.GetSettings();
             var albums = new List<PlexAlbum>();
             var libs = Cache.Get<List<PlexSearch>>(CacheKeys.PlexLibaries);
             if (libs != null)
@@ -344,7 +368,8 @@ namespace PlexRequests.Services.Jobs
                     {
                         Title = x.Title,
                         ReleaseYear = x.Year,
-                        Artist = x.ParentTitle
+                        Artist = x.ParentTitle,
+                        Url = PlexHelper.GetPlexMediaUrl(settings.MachineIdentifier, x.RatingKey)
                     }));
                 }
             }
@@ -355,7 +380,13 @@ namespace PlexRequests.Services.Jobs
         {
             return plexAlbums.Any(x =>
                 x.Title.Contains(title) &&
-                //x.ReleaseYear.Equals(year, StringComparison.CurrentCultureIgnoreCase) &&
+                x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase));
+        }
+
+        public PlexAlbum GetAlbum(PlexAlbum[] plexAlbums, string title, string year, string artist)
+        {
+            return plexAlbums.FirstOrDefault(x =>
+                x.Title.Contains(title) &&
                 x.Artist.Equals(artist, StringComparison.CurrentCultureIgnoreCase));
         }
 
@@ -462,63 +493,6 @@ namespace PlexRequests.Services.Jobs
             return true;
         }
 
-        private void NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey)
-        {
-            try
-            {
-                var plexUser = PlexApi.GetUsers(apiKey);
-                var userAccount = PlexApi.GetAccount(apiKey);
-
-                var adminUsername = userAccount.Username ?? string.Empty;
-
-                var users = UserNotifyRepo.GetAll().ToList();
-                Log.Debug("Notifying Users Count {0}", users.Count);
-                foreach (var model in modelChanged)
-                {
-                    var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers);
-                    foreach (var user in selectedUsers)
-                    {
-                        Log.Info("Notifying user {0}", user);
-                        if (user == adminUsername)
-                        {
-                            Log.Info("This user is the Plex server owner");
-                            PublishUserNotification(userAccount.Username, userAccount.Email, model.Title);
-                            return;
-                        }
-
-                        var email = plexUser.User.FirstOrDefault(x => x.Username == user);
-                        if (email == null)
-                        {
-                            Log.Info("There is no email address for this Plex user, cannot send notification");
-                            // We do not have a plex user that requested this!
-                            continue;
-                        }
-
-                        Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
-                        PublishUserNotification(email.Username, email.Email, model.Title);
-                    }
-                }
-            }
-            catch (Exception e)
-            {
-                Log.Error(e);
-            }
-        }
-
-        private void PublishUserNotification(string username, string email, string title)
-        {
-            var notificationModel = new NotificationModel
-            {
-                User = username,
-                UserEmail = email,
-                NotificationType = NotificationType.RequestAvailable,
-                Title = title
-            };
-
-            // Send the notification to the user.
-            Notification.Publish(notificationModel);
-        }
-
         public void Execute(IJobExecutionContext context)
         {
             try
diff --git a/PlexRequests.Services/Models/PlexAlbum.cs b/PlexRequests.Services/Models/PlexAlbum.cs
index 5d2bd7254..09d4b2638 100644
--- a/PlexRequests.Services/Models/PlexAlbum.cs
+++ b/PlexRequests.Services/Models/PlexAlbum.cs
@@ -1,9 +1,10 @@
-namespace PlexRequests.Services.Models
-{
-    public class PlexAlbum
-    {
-        public string Title { get; set; }
-        public string Artist { get; set; }
-        public string ReleaseYear { get; set; }
-    }
-}
+namespace PlexRequests.Services.Models
+{
+    public class PlexAlbum
+    {
+        public string Title { get; set; }
+        public string Artist { get; set; }
+        public string ReleaseYear { get; set; }
+        public string Url { get; set; }
+    }
+}
diff --git a/PlexRequests.Services/Models/PlexMovie.cs b/PlexRequests.Services/Models/PlexMovie.cs
index 0149698ba..27eca9948 100644
--- a/PlexRequests.Services/Models/PlexMovie.cs
+++ b/PlexRequests.Services/Models/PlexMovie.cs
@@ -1,9 +1,10 @@
-namespace PlexRequests.Services.Models
-{
-    public class PlexMovie
-    {
-        public string Title { get; set; }
-        public string ReleaseYear { get; set; }
-        public string ProviderId { get; set; }
-    }
-}
+namespace PlexRequests.Services.Models
+{
+    public class PlexMovie
+    {
+        public string Title { get; set; }
+        public string ReleaseYear { get; set; }
+        public string ProviderId { get; set; }
+        public string Url { get; set; }
+    }
+}
diff --git a/PlexRequests.Services/Models/PlexTvShow.cs b/PlexRequests.Services/Models/PlexTvShow.cs
index 5ac629132..aecf6f088 100644
--- a/PlexRequests.Services/Models/PlexTvShow.cs
+++ b/PlexRequests.Services/Models/PlexTvShow.cs
@@ -6,5 +6,6 @@
         public string ReleaseYear { get; set; }
         public string ProviderId { get; set; }
         public int[] Seasons { get; set; }
+        public string Url { get; set; }
     }
 }
diff --git a/PlexRequests.Services/Notification/EmailMessageNotification.cs b/PlexRequests.Services/Notification/EmailMessageNotification.cs
index 3bbbc43cb..a81a15290 100644
--- a/PlexRequests.Services/Notification/EmailMessageNotification.cs
+++ b/PlexRequests.Services/Notification/EmailMessageNotification.cs
@@ -104,7 +104,14 @@ namespace PlexRequests.Services.Notification
 
         private bool ValidateConfiguration(EmailNotificationSettings settings)
         {
-            if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
+            if (settings.Authentication)
+            {
+                if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
+                {
+                    return false;
+                }              
+            }
+            if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
             {
                 return false;
             }
@@ -186,7 +193,7 @@ namespace PlexRequests.Services.Notification
                     {
                         client.Authenticate(settings.EmailUsername, settings.EmailPassword);
                     }
-
+                    Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated);
                     await client.SendAsync(message);
                     await client.DisconnectAsync(true);
                 }
diff --git a/PlexRequests.Services/Notification/NotificationEngine.cs b/PlexRequests.Services/Notification/NotificationEngine.cs
new file mode 100644
index 000000000..ccbe07fec
--- /dev/null
+++ b/PlexRequests.Services/Notification/NotificationEngine.cs
@@ -0,0 +1,156 @@
+#region Copyright
+// /************************************************************************
+//    Copyright (c) 2016 Jamie Rees
+//    File: NotificationEngine.cs
+//    Created By: Jamie Rees
+//   
+//    Permission is hereby granted, free of charge, to any person obtaining
+//    a copy of this software and associated documentation files (the
+//    "Software"), to deal in the Software without restriction, including
+//    without limitation the rights to use, copy, modify, merge, publish,
+//    distribute, sublicense, and/or sell copies of the Software, and to
+//    permit persons to whom the Software is furnished to do so, subject to
+//    the following conditions:
+//   
+//    The above copyright notice and this permission notice shall be
+//    included in all copies or substantial portions of the Software.
+//   
+//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//  ************************************************************************/
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using NLog;
+using NLog.Fluent;
+using PlexRequests.Api;
+using PlexRequests.Api.Interfaces;
+using PlexRequests.Core.Models;
+using PlexRequests.Services.Interfaces;
+using PlexRequests.Store;
+using PlexRequests.Store.Models;
+using PlexRequests.Store.Repository;
+
+namespace PlexRequests.Services.Notification
+{
+    public class NotificationEngine : INotificationEngine
+    {
+        public NotificationEngine(IPlexApi p, IRepository<UsersToNotify> repo, INotificationService service)
+        {
+            PlexApi = p;
+            UserNotifyRepo = repo;
+            Notification = service;
+        }
+
+        private IPlexApi PlexApi { get; }
+        private IRepository<UsersToNotify> UserNotifyRepo { get; }
+        private static Logger Log = LogManager.GetCurrentClassLogger();
+        private INotificationService Notification { get; }
+
+        public async Task NotifyUsers(IEnumerable<RequestedModel> modelChanged, string apiKey)
+        {
+            try
+            {
+                var plexUser = PlexApi.GetUsers(apiKey);
+                var userAccount = PlexApi.GetAccount(apiKey);
+
+                var adminUsername = userAccount.Username ?? string.Empty;
+
+                var users = UserNotifyRepo.GetAll().ToList();
+                Log.Debug("Notifying Users Count {0}", users.Count);
+                foreach (var model in modelChanged)
+                {
+                    var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers, StringComparer.CurrentCultureIgnoreCase);
+                    foreach (var user in selectedUsers)
+                    {
+                        Log.Info("Notifying user {0}", user);
+                        if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
+                        {
+                            Log.Info("This user is the Plex server owner");
+                            await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title);
+                            return;
+                        }
+
+                        var email = plexUser.User.FirstOrDefault(x => x.Username.Equals(user, StringComparison.CurrentCultureIgnoreCase));
+                        if (email == null)
+                        {
+                            Log.Info("There is no email address for this Plex user, cannot send notification");
+                            // We do not have a plex user that requested this!
+                            continue;
+                        }
+
+                        Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
+                        await PublishUserNotification(email.Username, email.Email, model.Title);
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                Log.Error(e);
+            }
+        }
+
+        public async Task NotifyUsers(RequestedModel model, string apiKey)
+        {
+            try
+            {
+                var plexUser = PlexApi.GetUsers(apiKey);
+                var userAccount = PlexApi.GetAccount(apiKey);
+
+                var adminUsername = userAccount.Username ?? string.Empty;
+
+                var users = UserNotifyRepo.GetAll().ToList();
+                Log.Debug("Notifying Users Count {0}", users.Count);
+
+                var selectedUsers = users.Select(x => x.Username).Intersect(model.RequestedUsers, StringComparer.CurrentCultureIgnoreCase);
+                foreach (var user in selectedUsers)
+                {
+                    Log.Info("Notifying user {0}", user);
+                    if (user.Equals(adminUsername, StringComparison.CurrentCultureIgnoreCase))
+                    {
+                        Log.Info("This user is the Plex server owner");
+                        await PublishUserNotification(userAccount.Username, userAccount.Email, model.Title);
+                        return;
+                    }
+
+                    var email = plexUser.User.FirstOrDefault(x => x.Username.Equals(user, StringComparison.CurrentCultureIgnoreCase));
+                    if (email == null)
+                    {
+                        Log.Info("There is no email address for this Plex user, cannot send notification");
+                        // We do not have a plex user that requested this!
+                        continue;
+                    }
+
+                    Log.Info("Sending notification to: {0} at: {1}, for title: {2}", email.Username, email.Email, model.Title);
+                    await PublishUserNotification(email.Username, email.Email, model.Title);
+                }
+            }
+            catch (Exception e)
+            {
+                Log.Error(e);
+            }
+        }
+
+        private async Task PublishUserNotification(string username, string email, string title)
+        {
+            var notificationModel = new NotificationModel
+            {
+                User = username,
+                UserEmail = email,
+                NotificationType = NotificationType.RequestAvailable,
+                Title = title
+            };
+
+            // Send the notification to the user.
+            await Notification.Publish(notificationModel);
+        }
+    }
+}
\ No newline at end of file
diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj
index df4d694c9..6aed947bc 100644
--- a/PlexRequests.Services/PlexRequests.Services.csproj
+++ b/PlexRequests.Services/PlexRequests.Services.csproj
@@ -75,6 +75,7 @@
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Interfaces\IJobRecord.cs" />
+    <Compile Include="Interfaces\INotificationEngine.cs" />
     <Compile Include="Jobs\JobRecord.cs" />
     <Compile Include="Jobs\JobNames.cs" />
     <Compile Include="Jobs\PlexEpisodeCacher.cs" />
@@ -97,6 +98,7 @@
     <Compile Include="Interfaces\INotification.cs" />
     <Compile Include="Interfaces\INotificationService.cs" />
     <Compile Include="Notification\EmailMessageNotification.cs" />
+    <Compile Include="Notification\NotificationEngine.cs" />
     <Compile Include="Notification\NotificationModel.cs" />
     <Compile Include="Notification\NotificationService.cs" />
     <Compile Include="Notification\PushoverNotification.cs" />
diff --git a/PlexRequests.UI.Tests/UserLoginModuleTests.cs b/PlexRequests.UI.Tests/UserLoginModuleTests.cs
index 16b138eea..22f57750e 100644
--- a/PlexRequests.UI.Tests/UserLoginModuleTests.cs
+++ b/PlexRequests.UI.Tests/UserLoginModuleTests.cs
@@ -72,9 +72,9 @@ namespace PlexRequests.UI.Tests
             LandingPageMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new LandingPageSettings());
             IAnalytics = new Mock<IAnalytics>();
             Linker = new Mock<IResourceLinker>();
-            Linker.Setup(x => x.BuildAbsoluteUri(It.IsAny<NancyContext>(), "SearchIndex", null)).Returns(new Uri("http://www.searchindex.com"));
-            Linker.Setup(x => x.BuildAbsoluteUri(It.IsAny<NancyContext>(), "LandingPageIndex", null)).Returns(new Uri("http://www.landingpage.com"));
-            Linker.Setup(x => x.BuildAbsoluteUri(It.IsAny<NancyContext>(), "UserLoginIndex", null)).Returns(new Uri("http://www.userloginindex.com"));
+            Linker.Setup(x => x.BuildRelativeUri(It.IsAny<NancyContext>(), "SearchIndex", null)).Returns(new Uri("http://www.searchindex.com"));
+            Linker.Setup(x => x.BuildRelativeUri(It.IsAny<NancyContext>(), "LandingPageIndex", null)).Returns(new Uri("http://www.landingpage.com"));
+            Linker.Setup(x => x.BuildRelativeUri(It.IsAny<NancyContext>(), "UserLoginIndex", null)).Returns(new Uri("http://www.userloginindex.com"));
             PlexSettingsMock = new Mock<ISettingsService<PlexSettings>>();
             PlexSettingsMock.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new PlexSettings() {PlexAuthToken = "abc"});
             Bootstrapper = new ConfigurableBootstrapper(with =>
diff --git a/PlexRequests.UI/Content/app/requests/requestsController.js b/PlexRequests.UI/Content/app/requests/requestsController.js
new file mode 100644
index 000000000..204dcf9a2
--- /dev/null
+++ b/PlexRequests.UI/Content/app/requests/requestsController.js
@@ -0,0 +1,51 @@
+(function () {
+    var controller = function($scope, requestsService) {
+
+        $scope.requests = [];
+        $scope.selectedTab = {};
+        $scope.currentPage = 1;
+        $scope.tabs = [];
+
+        $scope.plexSettings = {};
+        $scope.requestSettings = {};
+
+        // Search
+        $scope.searchTerm = "";
+
+
+        // Called on page load
+        $scope.init = function() {
+            // Get the settings
+            $scope.plexSettings = requestsService.getPlexRequestSettings(getBaseUrl());
+            $scope.requestSettings = requestsService.getRequestSettings(getBaseUrl());
+
+            if ($scope.plexSettings.SearchForMovies) {
+                $scope.selectedTab = "movies";
+
+                // Load the movie Requests
+                $scope.requests = requestsService.getRequests("movie", getBaseUrl());
+            }
+        };
+
+
+        $scope.changeTab = function(tab) {
+            // load the data from the tab
+            switch (tab) {
+                // Set the selected tab and load the appropriate data
+            }
+
+        };
+
+        $scope.search = function() {
+            $scope.requests = requestsService.getRequests
+        };
+
+    function getBaseUrl() {
+            return $('#baseUrl').val();
+        }
+
+
+    }
+
+    angular.module('PlexRequests').controller('requestsController', ["$scope", "requestsService", controller]);
+}());
\ No newline at end of file
diff --git a/PlexRequests.UI/Content/app/requests/requestsService.js b/PlexRequests.UI/Content/app/requests/requestsService.js
new file mode 100644
index 000000000..b702c0727
--- /dev/null
+++ b/PlexRequests.UI/Content/app/requests/requestsService.js
@@ -0,0 +1,49 @@
+(function () {
+
+    var requestsService = function ($http) {
+
+        $http.defaults.headers.common['Content-Type'] = 'application/json'; // Set default headers
+
+        var getRequests = function (type, baseUrl) {
+            switch (type) {
+                case "movie":
+                    return $http.get(createBaseUrl(baseUrl, "/requestsbeta/movies"));
+                case "tv":
+                    return $http.get(createBaseUrl(baseUrl, "/requestsbeta/tvshows"));
+                case "album":
+                    return $http.get(createBaseUrl(baseUrl, "/requestsbeta/albums"));
+            }
+            return null;
+        };
+
+        var getPlexRequestSettings = function (baseUrl) {
+            return $http.get(createBaseUrl(baseUrl, "/requestsbeta/plexrequestsettings"));
+        }
+
+        var getRequestsSettings = function (baseUrl) {
+            return $http.get(createBaseUrl(baseUrl, "/requestsbeta/requestsettings"));
+        }
+
+        var getRequestsSearch = function (type, baseUrl, searchTerm) {
+            switch (type) {
+                case "movie":
+                    return $http.get(createBaseUrl(baseUrl, "/requestsbeta/movies/"+ searchTerm));
+                case "tv":
+                    return $http.get(createBaseUrl(baseUrl, "/requestsbeta/tvshows/" + searchTerm));
+                case "album":
+                    return $http.get(createBaseUrl(baseUrl, "/requestsbeta/albums/" + searchTerm));
+            }
+            return null;
+        };
+
+        return {
+            getRequests: getRequests,
+            getRequestsSearch: getRequestsSearch,
+            getPlexRequestSettings: getPlexRequestSettings,
+            getRequestSettings: getRequestsSettings
+        };
+    }
+
+    angular.module('PlexRequests').factory('requestsService', ["$http", requestsService]);
+
+}());
\ No newline at end of file
diff --git a/PlexRequests.UI/Content/app/userManagement/userManagementController.js b/PlexRequests.UI/Content/app/userManagement/userManagementController.js
index e9678e539..88a85e7aa 100644
--- a/PlexRequests.UI/Content/app/userManagement/userManagementController.js
+++ b/PlexRequests.UI/Content/app/userManagement/userManagementController.js
@@ -4,49 +4,91 @@
 
         $scope.user = {}; // The local user
         $scope.users = []; // list of users
+        $scope.claims = []; // List of claims
 
-        $scope.selectedUser = {};
+        $scope.selectedUser = {}; // User on the right side
+        $scope.selectedClaims = {};
 
-        $scope.sortType = 'username';
+
+        $scope.sortType = "username";
         $scope.sortReverse = false;
-        $scope.searchTerm = '';
+        $scope.searchTerm = "";
+
 
         $scope.error = {
             error: false,
             errorMessage: ""
         };
 
+        // Select a user to populate on the right side
         $scope.selectUser = function (id) {
-            $scope.selectedUser = {};
             $scope.selectedUser = $scope.users.find(x => x.id === id);
         }
 
+        // Get all users in the system
         $scope.getUsers = function () {
             $scope.users = userManagementService.getUsers()
-            .then(function (data) {
-                $scope.users = data.data;
-            });
+                .then(function (data) {
+                    $scope.users = data.data;
+                });
         };
 
+        // Get the claims and populate the create dropdown
+        $scope.getClaims = function () {
+            userManagementService.getClaims()
+                .then(function (data) {
+                    $scope.claims = data.data;
+                });
+        }
+
+        // Create a user, do some validation too    
         $scope.addUser = function () {
+
             if (!$scope.user.username || !$scope.user.password) {
                 $scope.error.error = true;
                 $scope.error.errorMessage = "Please provide a correct username and password";
                 generateNotify($scope.error.errorMessage, 'warning');
                 return;
             }
-            userManagementService.addUser($scope.user).then(function (data) {
-                if (data.message) {
-                    $scope.error.error = true;
-                    $scope.error.errorMessage = data.message;
-                } else {
-                    $scope.users.push(data);
-                    $scope.user = {};
-                }
-            });
+
+            userManagementService.addUser($scope.user, $scope.selectedClaims)
+                .then(function (data) {
+                    if (data.message) {
+                        $scope.error.error = true;
+                        $scope.error.errorMessage = data.message;
+                    } else {
+                        $scope.users.push(data); // Push the new user into the array to update the DOM
+                        $scope.user = {};
+                        $scope.selectedClaims = {};
+                    }
+                });
         };
+
+        $scope.$watch('claims|filter:{selected:true}',
+            function (nv) {
+                $scope.selectedClaims = nv.map(function (claim) {
+                    return claim.name;
+                });
+            },
+            true);
+
+
+        $scope.updateUser = function () {
+
+        }
+
+        function getBaseUrl() {
+            return $('#baseUrl').val();
+        }
+
+
+        // On page load
+        $scope.init = function () {
+            $scope.getUsers();
+            $scope.getClaims();
+            return;
+        }
     }
 
     angular.module('PlexRequests').controller('userManagementController', ["$scope", "userManagementService", controller]);
-
 }());
\ No newline at end of file
diff --git a/PlexRequests.UI/Content/app/userManagement/userManagementService.js b/PlexRequests.UI/Content/app/userManagement/userManagementService.js
index fe7b7ef07..e1640e3ea 100644
--- a/PlexRequests.UI/Content/app/userManagement/userManagementService.js
+++ b/PlexRequests.UI/Content/app/userManagement/userManagementService.js
@@ -2,27 +2,32 @@
 
     var userManagementService = function ($http) {
 
-        $http.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded'; // Set default headers
+        $http.defaults.headers.common['Content-Type'] = 'application/json'; // Set default headers
 
         var getUsers = function () {
             return $http.get('/usermanagement/users');
         };
 
-        var addUser = function (user) {
-            if (!user) {
+        var addUser = function (user, claims) {
+            if (!user || claims.length === 0) {
                 return null;
             }
 
             return $http({
                 url: '/usermanagement/createuser',
                 method: "POST",
-                data: $.param(user)
+                data: { username: user.username, password: user.password, claims: claims, email: user.email }
             });
         }
 
+        var getClaims = function () {
+            return $http.get('/usermanagement/claims');
+        }
+
         return {
             getUsers: getUsers,
-            addUser: addUser
+            addUser: addUser,
+            getClaims: getClaims
         };
     }
 
diff --git a/PlexRequests.UI/Content/requests.js b/PlexRequests.UI/Content/requests.js
index 765db5ecb..678fa60fd 100644
--- a/PlexRequests.UI/Content/requests.js
+++ b/PlexRequests.UI/Content/requests.js
@@ -578,7 +578,7 @@ function tvLoad() {
             results.forEach(function (result) {
                 var ep = result.episodes;
                 ep.forEach(function (episode) {
-                    var foundItem = tvObject.find(x => x.seasonNumber === episode.seasonNumber);
+                    var foundItem = tvObject.find(function(x) { return x.seasonNumber === episode.seasonNumber });
                     if (!foundItem) {
                         var obj = { seasonNumber: episode.seasonNumber, episodes: [] }
                         tvObject.push(obj);
diff --git a/PlexRequests.UI/Content/search.js b/PlexRequests.UI/Content/search.js
index 37b41a95e..310ba27fe 100644
--- a/PlexRequests.UI/Content/search.js
+++ b/PlexRequests.UI/Content/search.js
@@ -5,6 +5,21 @@
         return opts.inverse(this);
 });
 
+Function.prototype.bind = function (parent) {
+    var f = this;
+    var args = [];
+
+    for (var a = 1; a < arguments.length; a++) {
+        args[args.length] = arguments[a];
+    }
+
+    var temp = function () {
+        return f.apply(parent, args);
+    }
+
+    return (temp);
+}
+
 
 
 $(function () {
@@ -56,7 +71,9 @@ $(function () {
         if (searchTimer) {
             clearTimeout(searchTimer);
         }
-        searchTimer = setTimeout(movieSearch, 800);
+        searchTimer = setTimeout(function () {
+            movieSearch();
+        }.bind(this), 800);
 
     });
 
@@ -75,7 +92,9 @@ $(function () {
         if (searchTimer) {
             clearTimeout(searchTimer);
         }
-        searchTimer = setTimeout(tvSearch, 400);
+        searchTimer = setTimeout(function () {
+            tvSearch();
+        }.bind(this), 800);
     });
 
     // Click TV dropdown option
@@ -116,7 +135,9 @@ $(function () {
         if (searchTimer) {
             clearTimeout(searchTimer);
         }
-        searchTimer = setTimeout(musicSearch, 400);
+        searchTimer = setTimeout(function () {
+            musicSearch();
+        }.bind(this), 800);
 
     });
 
@@ -423,7 +444,8 @@ $(function () {
             imdb: result.imdbId,
             requested: result.requested,
             approved: result.approved,
-            available: result.available
+            available: result.available,
+            url: result.plexUrl
         };
 
         return context;
@@ -444,7 +466,8 @@ $(function () {
             approved: result.approved,
             available: result.available,
             episodes: result.episodes,
-            tvFullyAvailable: result.tvFullyAvailable
+            tvFullyAvailable: result.tvFullyAvailable,
+            url: result.plexUrl
         };
         return context;
     }
@@ -464,7 +487,8 @@ $(function () {
             country: result.country,
             requested: result.requested,
             approved: result.approved,
-            available: result.available
+            available: result.available,
+            url: result.plexUrl
         };
 
         return context;
@@ -484,7 +508,7 @@ $(function () {
                 var $content = $("#seasonsBody");
                 $content.html("");
                 $('#selectedSeasonsId').val(id);
-                results.forEach(function(result) {
+                results.forEach(function (result) {
                     var context = buildSeasonsContext(result);
                     $content.append(seasonsTemplate(context));
                 });
@@ -503,7 +527,7 @@ $(function () {
         };
     });
 
-    $('#seasonsRequest').click(function(e) {
+    $('#seasonsRequest').click(function (e) {
         e.preventDefault();
         var tvId = $('#selectedSeasonsId').val();
         var url = createBaseUrl(base, '/search/seasons/');
@@ -522,7 +546,7 @@ $(function () {
 
         var $checkedSeasons = $('.selectedSeasons:checkbox:checked');
         $checkedSeasons.each(function (index, element) {
-            if (index < $checkedSeasons.length -1) {
+            if (index < $checkedSeasons.length - 1) {
                 seasonsParam = seasonsParam + element.id + ",";
             } else {
                 seasonsParam = seasonsParam + element.id;
@@ -536,7 +560,7 @@ $(function () {
         var url = $form.prop('action');
 
         sendRequestAjax(data, type, url, tvId);
-       
+
     });
 
     $('#episodesModal').on('show.bs.modal', function (event) {
@@ -560,7 +584,9 @@ $(function () {
                 results.forEach(function (result) {
                     var episodes = buildEpisodesView(result);
 
-                    if (!seenSeasons.find(x => x === episodes.season)) {
+                    if (!seenSeasons.find(function(x) {
+                         return x === episodes.season
+                    })) {
                         // Create the seasons heading
                         seenSeasons.push(episodes.season);
                         var context = buildSeasonsCount(result);
@@ -586,7 +612,7 @@ $(function () {
             loadingButton("episodesRequest", "primary");
             var tvId = $('#selectedEpisodeId').val();
 
-            
+
             var $form = $('#form' + tvId);
             var model = [];
 
@@ -623,7 +649,7 @@ $(function () {
                     }
 
                 },
-                error: function(e) {
+                error: function (e) {
                     console.log(e);
                     generateNotify("Something went wrong!", "danger");
                 }
diff --git a/PlexRequests.UI/Helpers/BaseUrlHelper.cs b/PlexRequests.UI/Helpers/BaseUrlHelper.cs
index 625d71380..2ce349ec0 100644
--- a/PlexRequests.UI/Helpers/BaseUrlHelper.cs
+++ b/PlexRequests.UI/Helpers/BaseUrlHelper.cs
@@ -82,9 +82,8 @@ namespace PlexRequests.UI.Helpers
                 $"<link rel=\"stylesheet\" href=\"{startUrl}/font-awesome.css\" type=\"text/css\"/>",
                 $"<link rel=\"stylesheet\" href=\"{startUrl}/pace.min.css\" type=\"text/css\"/>",
                 $"<link rel=\"stylesheet\" href=\"{startUrl}/awesome-bootstrap-checkbox.css\" type=\"text/css\"/>",
-                $"<link rel=\"stylesheet\" href=\"{startUrl}/base.css\" type=\"text/css\"/>",
-                $"<link rel=\"stylesheet\" href=\"{startUrl}/Themes/{settings.ThemeName}\" type=\"text/css\"/>",
-                $"<link rel=\"stylesheet\" href=\"{startUrl}/datepicker.min.css\" type=\"text/css\"/>",
+                $"<link rel=\"stylesheet\" href=\"{startUrl}/base.css?v={Assembly}\" type=\"text/css\"/>",
+                $"<link rel=\"stylesheet\" href=\"{startUrl}/Themes/{settings.ThemeName}?v={Assembly}\" type=\"text/css\"/>",
                 $"<link rel=\"stylesheet\" href=\"{startUrl}/tooltip/tooltipster.bundle.min.css\" type=\"text/css\"/>",
             };
 
@@ -95,11 +94,10 @@ namespace PlexRequests.UI.Helpers
                 $"<script src=\"{startUrl}/handlebars.min.js\"></script>",
                 $"<script src=\"{startUrl}/bootstrap.min.js\"></script>",
                 $"<script src=\"{startUrl}/bootstrap-notify.min.js\"></script>",
-                $"<script src=\"{startUrl}/site.js\"></script>",
+                $"<script src=\"{startUrl}/site.js?v={Assembly}\"></script>",
                 $"<script src=\"{startUrl}/pace.min.js\"></script>",
                 $"<script src=\"{startUrl}/jquery.mixitup.js\"></script>",
                 $"<script src=\"{startUrl}/moment.min.js\"></script>",
-                $"<script src=\"{startUrl}/bootstrap-datetimepicker.min.js\"></script>",
                 $"<script src=\"{startUrl}/tooltip/tooltipster.bundle.min.js\"></script>"
             };
 
@@ -118,6 +116,17 @@ namespace PlexRequests.UI.Helpers
             return helper.Raw(sb.ToString());
         }
 
+        public static IHtmlString LoadDateTimePickerAsset(this HtmlHelpers helper)
+        {
+            var content = GetBaseUrl();
+
+            var sb = new StringBuilder();
+            var startUrl = $"{content}/Content";
+            sb.AppendLine($"<link rel=\"stylesheet\" href=\"{startUrl}/datepicker.min.css\" type=\"text/css\"/>");
+            sb.AppendLine($"<script src=\"{startUrl}/bootstrap-datetimepicker.min.js\"></script>");
+
+            return helper.Raw(sb.ToString());
+        }
         public static IHtmlString LoadAngularAssets(this HtmlHelpers helper)
         {
             var sb = new StringBuilder();
@@ -135,7 +144,7 @@ namespace PlexRequests.UI.Helpers
             var startUrl = $"{content}/Content";
 
             sb.AppendLine($"<script src=\"{startUrl}/angular.min.js\"></script>"); // Load angular first
-            sb.AppendLine($"<script src=\"{startUrl}/app/app.js\"></script>");
+            sb.AppendLine($"<script src=\"{startUrl}/app/app.js?v={Assembly}\"></script>");
 
             return helper.Raw(sb.ToString());
         }
@@ -147,7 +156,7 @@ namespace PlexRequests.UI.Helpers
 
             var content = GetContentUrl(assetLocation);
 
-            sb.AppendLine($"<script src=\"{content}/Content/search.js\" type=\"text/javascript\"></script>");
+            sb.AppendLine($"<script src=\"{content}/Content/search.js?v={Assembly}\" type=\"text/javascript\"></script>");
 
             return helper.Raw(sb.ToString());
         }
@@ -159,7 +168,7 @@ namespace PlexRequests.UI.Helpers
 
             var content = GetContentUrl(assetLocation);
 
-            sb.AppendLine($"<script src=\"{content}/Content/requests.js\" type=\"text/javascript\"></script>");
+            sb.AppendLine($"<script src=\"{content}/Content/requests.js?v={Assembly}\" type=\"text/javascript\"></script>");
 
             return helper.Raw(sb.ToString());
         }
@@ -171,7 +180,7 @@ namespace PlexRequests.UI.Helpers
 
             var content = GetContentUrl(assetLocation);
 
-            sb.AppendLine($"<script src=\"{content}/Content/issues.js\" type=\"text/javascript\"></script>");
+            sb.AppendLine($"<script src=\"{content}/Content/issues.js?v={Assembly}\" type=\"text/javascript\"></script>");
 
             return helper.Raw(sb.ToString());
         }
@@ -183,7 +192,7 @@ namespace PlexRequests.UI.Helpers
 
             var content = GetContentUrl(assetLocation);
             
-            sb.AppendLine($"<script src=\"{content}/Content/wizard.js\" type=\"text/javascript\"></script>");
+            sb.AppendLine($"<script src=\"{content}/Content/wizard.js?v={Assembly}\" type=\"text/javascript\"></script>");
 
             return helper.Raw(sb.ToString());
         }
@@ -193,11 +202,25 @@ namespace PlexRequests.UI.Helpers
             var assetLocation = GetBaseUrl();
             var content = GetContentUrl(assetLocation);
 
-            var asset = $"<script src=\"{content}/Content/issue-details.js\" type=\"text/javascript\"></script>";
+            var asset = $"<script src=\"{content}/Content/issue-details.js?v={Assembly}\" type=\"text/javascript\"></script>";
 
             return helper.Raw(asset);
         }
 
+
+        public static IHtmlString LoadUserManagementAssets(this HtmlHelpers helper)
+        {
+            var assetLocation = GetBaseUrl();
+            var content = GetContentUrl(assetLocation);
+
+            var controller = $"<script src=\"{content}/Content/app/userManagement/userManagementController.js?v={Assembly}\" type=\"text/javascript\"></script>";
+            controller += $"<script src=\"{content}/Content/app/userManagement/userManagementService.js?v={Assembly}\" type=\"text/javascript\"></script>";
+
+
+            return helper.Raw(controller);
+        }
+
+
         public static IHtmlString LoadTableAssets(this HtmlHelpers helper)
         {
             var sb = new StringBuilder();
@@ -222,7 +245,7 @@ namespace PlexRequests.UI.Helpers
             var assetLocation = GetBaseUrl();
             var content = GetContentUrl(assetLocation);
 
-            var asset = $"<script src=\"{content}/Content/analytics.js\" type=\"text/javascript\"></script>";
+            var asset = $"<script src=\"{content}/Content/analytics.js?v={Assembly}\" type=\"text/javascript\"></script>";
 
             return helper.Raw(asset);
         }
diff --git a/PlexRequests.UI/Helpers/TvSender.cs b/PlexRequests.UI/Helpers/TvSender.cs
index 13a941512..05b2799ab 100644
--- a/PlexRequests.UI/Helpers/TvSender.cs
+++ b/PlexRequests.UI/Helpers/TvSender.cs
@@ -131,7 +131,7 @@ namespace PlexRequests.UI.Helpers
 
             var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile,
                 sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.SeasonCount, model.SeasonList, sonarrSettings.ApiKey,
-                sonarrSettings.FullUri);
+                sonarrSettings.FullUri, true, true);
 
             return result;
         }
diff --git a/PlexRequests.UI/Models/SearchViewModel.cs b/PlexRequests.UI/Models/SearchViewModel.cs
index 776b9d2b1..9c11d32ef 100644
--- a/PlexRequests.UI/Models/SearchViewModel.cs
+++ b/PlexRequests.UI/Models/SearchViewModel.cs
@@ -1,37 +1,38 @@
-#region Copyright
-// /************************************************************************
-//    Copyright (c) 2016 Jamie Rees
-//    File: SearchTvShowViewModel.cs
-//    Created By: Jamie Rees
-//   
-//    Permission is hereby granted, free of charge, to any person obtaining
-//    a copy of this software and associated documentation files (the
-//    "Software"), to deal in the Software without restriction, including
-//    without limitation the rights to use, copy, modify, merge, publish,
-//    distribute, sublicense, and/or sell copies of the Software, and to
-//    permit persons to whom the Software is furnished to do so, subject to
-//    the following conditions:
-//   
-//    The above copyright notice and this permission notice shall be
-//    included in all copies or substantial portions of the Software.
-//   
-//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//  ************************************************************************/
-#endregion
-
-
-namespace PlexRequests.UI.Models
-{
-    public class SearchViewModel
-    {
-        public bool Approved { get; set; }
-        public bool Requested { get; set; }
-        public bool Available { get; set; }
-    }
+#region Copyright
+// /************************************************************************
+//    Copyright (c) 2016 Jamie Rees
+//    File: SearchTvShowViewModel.cs
+//    Created By: Jamie Rees
+//   
+//    Permission is hereby granted, free of charge, to any person obtaining
+//    a copy of this software and associated documentation files (the
+//    "Software"), to deal in the Software without restriction, including
+//    without limitation the rights to use, copy, modify, merge, publish,
+//    distribute, sublicense, and/or sell copies of the Software, and to
+//    permit persons to whom the Software is furnished to do so, subject to
+//    the following conditions:
+//   
+//    The above copyright notice and this permission notice shall be
+//    included in all copies or substantial portions of the Software.
+//   
+//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//  ************************************************************************/
+#endregion
+
+
+namespace PlexRequests.UI.Models
+{
+    public class SearchViewModel
+    {
+        public bool Approved { get; set; }
+        public bool Requested { get; set; }
+        public bool Available { get; set; }
+        public string PlexUrl { get; set; }
+    }
 }
\ No newline at end of file
diff --git a/PlexRequests.UI/Models/SessionKeys.cs b/PlexRequests.UI/Models/SessionKeys.cs
index fefc7d8bd..766a84bf6 100644
--- a/PlexRequests.UI/Models/SessionKeys.cs
+++ b/PlexRequests.UI/Models/SessionKeys.cs
@@ -31,5 +31,6 @@ namespace PlexRequests.UI.Models
         public const string UsernameKey = "Username";
         public const string ClientDateTimeOffsetKey = "ClientDateTimeOffset";
         public const string UserWizardPlexAuth = nameof(UserWizardPlexAuth);
+        public const string UserWizardMachineId = nameof(UserWizardMachineId);
     }
 }
diff --git a/PlexRequests.UI/Models/UserManagementUsersViewModel.cs b/PlexRequests.UI/Models/UserManagementUsersViewModel.cs
index 57fcd1f5a..915f90075 100644
--- a/PlexRequests.UI/Models/UserManagementUsersViewModel.cs
+++ b/PlexRequests.UI/Models/UserManagementUsersViewModel.cs
@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using Newtonsoft.Json;
 
 namespace PlexRequests.UI.Models
 {
@@ -15,6 +16,7 @@ namespace PlexRequests.UI.Models
         public UserType Type { get; set; }
         public string EmailAddress { get; set; }
         public UserManagementPlexInformation PlexInfo { get; set; }
+        public string[] ClaimsArray { get; set; }
     }
 
     public class UserManagementPlexInformation
@@ -42,5 +44,18 @@ namespace PlexRequests.UI.Models
         PlexUser,
         LocalUser
     }
+
+    public class UserManagementCreateModel
+    {
+        [JsonProperty("username")]
+        public string Username { get; set; }
+        [JsonProperty("password")]
+        public string Password { get; set; }
+        [JsonProperty("claims")]
+        public string[] Claims { get; set; }
+
+        [JsonProperty("email")]
+        public string EmailAddress { get; set; }
+    }
 }
 
diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs
index 4d6ca7e43..a04edca3c 100644
--- a/PlexRequests.UI/Modules/AdminModule.cs
+++ b/PlexRequests.UI/Modules/AdminModule.cs
@@ -161,7 +161,7 @@ namespace PlexRequests.UI.Modules
             Post["/couchpotato"] = _ => SaveCouchPotato();
 
             Get["/plex"] = _ => Plex();
-            Post["/plex"] = _ => SavePlex();
+            Post["/plex", true] = async (x, ct) => await SavePlex();
 
             Get["/sonarr"] = _ => Sonarr();
             Post["/sonarr"] = _ => SaveSonarr();
@@ -170,13 +170,13 @@ namespace PlexRequests.UI.Modules
             Post["/sickrage"] = _ => SaveSickrage();
 
             Post["/sonarrprofiles"] = _ => GetSonarrQualityProfiles();
-            Post["/cpprofiles", true] = async (x,ct) => await GetCpProfiles();
+            Post["/cpprofiles", true] = async (x, ct) => await GetCpProfiles();
             Post["/cpapikey"] = x => GetCpApiKey();
 
             Get["/emailnotification"] = _ => EmailNotifications();
             Post["/emailnotification"] = _ => SaveEmailNotifications();
             Post["/testemailnotification"] = _ => TestEmailNotifications();
-            Get["/status", true] = async (x,ct) => await Status();
+            Get["/status", true] = async (x, ct) => await Status();
 
             Get["/pushbulletnotification"] = _ => PushbulletNotifications();
             Post["/pushbulletnotification"] = _ => SavePushbulletNotifications();
@@ -268,7 +268,7 @@ namespace PlexRequests.UI.Modules
                 Analytics.TrackEventAsync(Category.Admin, Action.Save, "CollectAnalyticData turned off", Username, CookieHelper.GetAnalyticClientId(Cookies));
             }
             var result = PrService.SaveSettings(model);
-            
+
             Analytics.TrackEventAsync(Category.Admin, Action.Save, "PlexRequestSettings", Username, CookieHelper.GetAnalyticClientId(Cookies));
             return Response.AsJson(result
                 ? new JsonResponseModel { Result = true }
@@ -377,7 +377,7 @@ namespace PlexRequests.UI.Modules
             return View["Plex", settings];
         }
 
-        private Response SavePlex()
+        private async Task<Response> SavePlex()
         {
             var plexSettings = this.Bind<PlexSettings>();
             var valid = this.Validate(plexSettings);
@@ -386,8 +386,11 @@ namespace PlexRequests.UI.Modules
                 return Response.AsJson(valid.SendJsonError());
             }
 
+            //Lookup identifier
+            var server = PlexApi.GetServer(plexSettings.PlexAuthToken);
+            plexSettings.MachineIdentifier = server.Server.FirstOrDefault(x => x.AccessToken == plexSettings.PlexAuthToken)?.MachineIdentifier;
 
-            var result = PlexService.SaveSettings(plexSettings);
+            var result = await PlexService.SaveSettingsAsync(plexSettings);
 
             return Response.AsJson(result
                 ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Plex!" }
@@ -517,7 +520,7 @@ namespace PlexRequests.UI.Modules
             {
                 if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
                 {
-                    return Response.AsJson(new JsonResponseModel {Result = false, Message = "SMTP Authentication is enabled, please specify a username and password"});
+                    return Response.AsJson(new JsonResponseModel { Result = false, Message = "SMTP Authentication is enabled, please specify a username and password" });
                 }
             }
 
@@ -542,7 +545,7 @@ namespace PlexRequests.UI.Modules
         {
             var checker = new StatusChecker();
             var status = await Cache.GetOrSetAsync(CacheKeys.LastestProductVersion, async () => await checker.GetStatus(), 30);
-            var md = new Markdown(new MarkdownOptions { AutoNewLines = true, AutoHyperlink = true});
+            var md = new Markdown(new MarkdownOptions { AutoNewLines = true, AutoHyperlink = true });
             status.ReleaseNotes = md.Transform(status.ReleaseNotes);
             return View["Status", status];
         }
@@ -711,7 +714,7 @@ namespace PlexRequests.UI.Modules
         private Response GetCpApiKey()
         {
             var settings = this.Bind<CouchPotatoSettings>();
-            
+
             if (string.IsNullOrEmpty(settings.Username) || string.IsNullOrEmpty(settings.Password))
             {
                 return Response.AsJson(new { Message = "Please enter a username and password to request the Api Key", Result = false });
@@ -938,12 +941,12 @@ namespace PlexRequests.UI.Modules
                 {
                     await LogsRepo.DeleteAsync(logEntity);
                 }
-                return Response.AsJson(new JsonResponseModel { Result = true, Message = "Logs cleared successfully."});
+                return Response.AsJson(new JsonResponseModel { Result = true, Message = "Logs cleared successfully." });
             }
             catch (Exception e)
             {
                 Log.Error(e);
-                return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });   
+                return Response.AsJson(new JsonResponseModel { Result = false, Message = e.Message });
             }
         }
     }
diff --git a/PlexRequests.UI/Modules/BaseModule.cs b/PlexRequests.UI/Modules/BaseModule.cs
index 1a205202b..a01253786 100644
--- a/PlexRequests.UI/Modules/BaseModule.cs
+++ b/PlexRequests.UI/Modules/BaseModule.cs
@@ -122,7 +122,7 @@ namespace PlexRequests.UI.Modules
         {
             get
             {
-                if (Context?.CurrentUser == null)
+                if (!LoggedIn)
                 {
                     return false;
                 }
@@ -130,6 +130,9 @@ namespace PlexRequests.UI.Modules
                 return claims.Contains(UserClaims.Admin) || claims.Contains(UserClaims.PowerUser);
             }
         }
+
+        protected bool LoggedIn => Context?.CurrentUser != null;
+
         protected string Culture { get; set; }
         protected const string CultureCookieName = "_culture";
         protected Response SetCookie()
diff --git a/PlexRequests.UI/Modules/IndexModule.cs b/PlexRequests.UI/Modules/IndexModule.cs
index 3d7023d39..fd13acb35 100644
--- a/PlexRequests.UI/Modules/IndexModule.cs
+++ b/PlexRequests.UI/Modules/IndexModule.cs
@@ -59,23 +59,23 @@ namespace PlexRequests.UI.Modules
                     if (!string.IsNullOrEmpty(Username))
                     {
                         // They are not logged in
-                        return Context.GetRedirect(Linker.BuildAbsoluteUri(Context, "LandingPageIndex").ToString());
+                        return Context.GetRedirect(Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString());
                     }
-                    return Context.GetRedirect(Linker.BuildAbsoluteUri(Context, "SearchIndex").ToString());
+                    return Context.GetRedirect(Linker.BuildRelativeUri(Context, "SearchIndex").ToString());
                 }
 
                 // After login
                 if (string.IsNullOrEmpty(Username))
                 {
                     // Not logged in yet
-                    return Context.GetRedirect(Linker.BuildAbsoluteUri(Context, "UserLoginIndex").ToString());
+                    return Context.GetRedirect(Linker.BuildRelativeUri(Context, "UserLoginIndex").ToString());
                 }
                 // Send them to landing
-                var landingUrl = Linker.BuildAbsoluteUri(Context, "LandingPageIndex").ToString();
+                var landingUrl = Linker.BuildRelativeUri(Context, "LandingPageIndex").ToString();
                 return Context.GetRedirect(landingUrl);
             }
 
-            return Context.GetRedirect(Linker.BuildAbsoluteUri(Context, "UserLoginIndex").ToString());
+            return Context.GetRedirect(Linker.BuildRelativeUri(Context, "UserLoginIndex").ToString());
         }
     }
 }
\ No newline at end of file
diff --git a/PlexRequests.UI/Modules/LandingPageModule.cs b/PlexRequests.UI/Modules/LandingPageModule.cs
index 92517f2f7..dccf69430 100644
--- a/PlexRequests.UI/Modules/LandingPageModule.cs
+++ b/PlexRequests.UI/Modules/LandingPageModule.cs
@@ -52,7 +52,7 @@ namespace PlexRequests.UI.Modules
                 var s = await LandingSettings.GetSettingsAsync();
                 if (!s.BeforeLogin && string.IsNullOrEmpty(Username)) //We are signed in
                 {
-                    var url = Linker.BuildAbsoluteUri(Context, "SearchIndex").ToString();
+                    var url = Linker.BuildRelativeUri(Context, "SearchIndex").ToString();
                     return Response.AsRedirect(url);
                 }
 
diff --git a/PlexRequests.UI/Modules/LoginModule.cs b/PlexRequests.UI/Modules/LoginModule.cs
index 8e6a9d39c..8c3bd550d 100644
--- a/PlexRequests.UI/Modules/LoginModule.cs
+++ b/PlexRequests.UI/Modules/LoginModule.cs
@@ -1,150 +1,154 @@
-#region Copyright
-// /************************************************************************
-//    Copyright (c) 2016 Jamie Rees
-//    File: LoginModule.cs
-//    Created By: Jamie Rees
-//   
-//    Permission is hereby granted, free of charge, to any person obtaining
-//    a copy of this software and associated documentation files (the
-//    "Software"), to deal in the Software without restriction, including
-//    without limitation the rights to use, copy, modify, merge, publish,
-//    distribute, sublicense, and/or sell copies of the Software, and to
-//    permit persons to whom the Software is furnished to do so, subject to
-//    the following conditions:
-//   
-//    The above copyright notice and this permission notice shall be
-//    included in all copies or substantial portions of the Software.
-//   
-//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//  ************************************************************************/
-#endregion
-using System;
-using System.Dynamic;
-
-using Nancy;
-using Nancy.Authentication.Forms;
-using Nancy.Extensions;
-using Nancy.Responses.Negotiation;
-using Nancy.Security;
-
-using PlexRequests.Core;
-using PlexRequests.Core.SettingModels;
-using PlexRequests.Helpers;
-using PlexRequests.UI.Models;
-
-namespace PlexRequests.UI.Modules
-{
-    public class LoginModule : BaseModule
-    {
-        public LoginModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m) : base(pr)
-        {
-            UserMapper = m;
-            Get["/login"] = _ =>
-            {
-                {
-                    dynamic model = new ExpandoObject();
-					model.Redirect = Request.Query.redirect.Value ?? string.Empty;
-                    model.Errored = Request.Query.error.HasValue;
-                    var adminCreated = UserMapper.DoUsersExist();
-                    model.AdminExists = adminCreated;
-                    return View["Index", model];
-                }
-
-            };
-
-            Get["/logout"] = x => this.LogoutAndRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/" : "~/");
-
-            Post["/login"] = x =>
-            {
-                var username = (string)Request.Form.Username;
-                var password = (string)Request.Form.Password;
-                var dtOffset = (int)Request.Form.DateTimeOffset;
-				var redirect = (string)Request.Form.Redirect;
-
-                var userId = UserMapper.ValidateUser(username, password);
-
-                if (userId == null)
-                {
-                    return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/login?error=true&username=" + username : "~/login?error=true&username=" + username);
-                }
-                DateTime? expiry = null;
-                if (Request.Form.RememberMe.HasValue)
-                {
-                    expiry = DateTime.Now.AddDays(7);
-                }
-                Session[SessionKeys.UsernameKey] = username;
-                Session[SessionKeys.ClientDateTimeOffsetKey] = dtOffset;
-				if(redirect.Contains("userlogin")){
-					redirect = !string.IsNullOrEmpty(BaseUrl) ? $"/{BaseUrl}/search" : "/search";
-				}
-				return this.LoginAndRedirect(userId.Value, expiry, redirect);
-            };
-
-            Get["/register"] = x =>
-            {
-                {
-                    dynamic model = new ExpandoObject();
-                    model.Errored = Request.Query.error.HasValue;
-
-                    return View["Register", model];
-                }
-            };
-
-            Post["/register"] = x =>
-            {
-                var username = (string)Request.Form.Username;
-                var exists = UserMapper.DoUsersExist();
-                if (exists)
-                {
-                    return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/register?error=true" : "~/register?error=true");
-                }
-                var userId = UserMapper.CreateAdmin(username, Request.Form.Password);
-                Session[SessionKeys.UsernameKey] = username;
-                return this.LoginAndRedirect((Guid)userId);
-            };
-
-            Get["/changepassword"] = _ => ChangePassword();
-            Post["/changepassword"] = _ => ChangePasswordPost();
-        }
-        private ICustomUserMapper UserMapper { get; }
-
-        private Negotiator ChangePassword()
-        {
-            this.RequiresAuthentication();
-            return View["ChangePassword"];
-        }
-
-        private Response ChangePasswordPost()
-        {
-            var username = Context.CurrentUser.UserName;
-            var oldPass = Request.Form.OldPassword;
-            var newPassword = Request.Form.NewPassword;
-            var newPasswordAgain = Request.Form.NewPasswordAgain;
-
-            if (string.IsNullOrEmpty(oldPass) || string.IsNullOrEmpty(newPassword) ||
-                string.IsNullOrEmpty(newPasswordAgain))
-            {
-                return Response.AsJson(new JsonResponseModel { Message = "Please fill in all fields", Result = false });
-            }
-
-            if (!newPassword.Equals(newPasswordAgain))
-            {
-                return Response.AsJson(new JsonResponseModel { Message = "The passwords do not match", Result = false });
-            }
-
-            var result = UserMapper.UpdatePassword(username, oldPass, newPassword);
-            if (result)
-            {
-                return Response.AsJson(new JsonResponseModel { Message = "Password has been changed!", Result = true });
-            }
-
-            return Response.AsJson(new JsonResponseModel { Message = "Could not update the password in the database", Result = false });
-        }
-    }
+#region Copyright
+// /************************************************************************
+//    Copyright (c) 2016 Jamie Rees
+//    File: LoginModule.cs
+//    Created By: Jamie Rees
+//   
+//    Permission is hereby granted, free of charge, to any person obtaining
+//    a copy of this software and associated documentation files (the
+//    "Software"), to deal in the Software without restriction, including
+//    without limitation the rights to use, copy, modify, merge, publish,
+//    distribute, sublicense, and/or sell copies of the Software, and to
+//    permit persons to whom the Software is furnished to do so, subject to
+//    the following conditions:
+//   
+//    The above copyright notice and this permission notice shall be
+//    included in all copies or substantial portions of the Software.
+//   
+//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//  ************************************************************************/
+#endregion
+using System;
+using System.Dynamic;
+
+using Nancy;
+using Nancy.Authentication.Forms;
+using Nancy.Extensions;
+using Nancy.Linker;
+using Nancy.Responses.Negotiation;
+using Nancy.Security;
+
+using PlexRequests.Core;
+using PlexRequests.Core.SettingModels;
+using PlexRequests.Helpers;
+using PlexRequests.UI.Models;
+
+namespace PlexRequests.UI.Modules
+{
+    public class LoginModule : BaseModule
+    {
+        public LoginModule(ISettingsService<PlexRequestSettings> pr, ICustomUserMapper m, IResourceLinker linker) : base(pr)
+        {
+            UserMapper = m;
+            Get["/login"] = _ =>
+            {
+                if (LoggedIn)
+                {
+                    var url = linker.BuildRelativeUri(Context, "SearchIndex");
+                    return Response.AsRedirect(url.ToString());
+                }
+                dynamic model = new ExpandoObject();
+                model.Redirect = Request.Query.redirect.Value ?? string.Empty;
+                model.Errored = Request.Query.error.HasValue;
+                var adminCreated = UserMapper.DoUsersExist();
+                model.AdminExists = adminCreated;
+                return View["Index", model];
+            };
+
+            Get["/logout"] = x => this.LogoutAndRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/" : "~/");
+
+            Post["/login"] = x =>
+            {
+                var username = (string)Request.Form.Username;
+                var password = (string)Request.Form.Password;
+                var dtOffset = (int)Request.Form.DateTimeOffset;
+                var redirect = (string)Request.Form.Redirect;
+
+                var userId = UserMapper.ValidateUser(username, password);
+
+                if (userId == null)
+                {
+                    return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/login?error=true&username=" + username : "~/login?error=true&username=" + username);
+                }
+                DateTime? expiry = null;
+                if (Request.Form.RememberMe.HasValue)
+                {
+                    expiry = DateTime.Now.AddDays(7);
+                }
+                Session[SessionKeys.UsernameKey] = username;
+                Session[SessionKeys.ClientDateTimeOffsetKey] = dtOffset;
+                if (redirect.Contains("userlogin"))
+                {
+                    redirect = !string.IsNullOrEmpty(BaseUrl) ? $"/{BaseUrl}/search" : "/search";
+                }
+                return this.LoginAndRedirect(userId.Value, expiry, redirect);
+            };
+
+            Get["/register"] = x =>
+            {
+                {
+                    dynamic model = new ExpandoObject();
+                    model.Errored = Request.Query.error.HasValue;
+
+                    return View["Register", model];
+                }
+            };
+
+            Post["/register"] = x =>
+            {
+                var username = (string)Request.Form.Username;
+                var exists = UserMapper.DoUsersExist();
+                if (exists)
+                {
+                    return Context.GetRedirect(!string.IsNullOrEmpty(BaseUrl) ? $"~/{BaseUrl}/register?error=true" : "~/register?error=true");
+                }
+                var userId = UserMapper.CreateAdmin(username, Request.Form.Password);
+                Session[SessionKeys.UsernameKey] = username;
+                return this.LoginAndRedirect((Guid)userId);
+            };
+
+            Get["/changepassword"] = _ => ChangePassword();
+            Post["/changepassword"] = _ => ChangePasswordPost();
+        }
+        private ICustomUserMapper UserMapper { get; }
+
+        private Negotiator ChangePassword()
+        {
+            this.RequiresAuthentication();
+            return View["ChangePassword"];
+        }
+
+        private Response ChangePasswordPost()
+        {
+            var username = Context.CurrentUser.UserName;
+            var oldPass = Request.Form.OldPassword;
+            var newPassword = Request.Form.NewPassword;
+            var newPasswordAgain = Request.Form.NewPasswordAgain;
+
+            if (string.IsNullOrEmpty(oldPass) || string.IsNullOrEmpty(newPassword) ||
+                string.IsNullOrEmpty(newPasswordAgain))
+            {
+                return Response.AsJson(new JsonResponseModel { Message = "Please fill in all fields", Result = false });
+            }
+
+            if (!newPassword.Equals(newPasswordAgain))
+            {
+                return Response.AsJson(new JsonResponseModel { Message = "The passwords do not match", Result = false });
+            }
+
+            var result = UserMapper.UpdatePassword(username, oldPass, newPassword);
+            if (result)
+            {
+                return Response.AsJson(new JsonResponseModel { Message = "Password has been changed!", Result = true });
+            }
+
+            return Response.AsJson(new JsonResponseModel { Message = "Could not update the password in the database", Result = false });
+        }
+    }
 }
\ No newline at end of file
diff --git a/PlexRequests.UI/Modules/RequestsBetaModule.cs b/PlexRequests.UI/Modules/RequestsBetaModule.cs
new file mode 100644
index 000000000..a8a1a6e3f
--- /dev/null
+++ b/PlexRequests.UI/Modules/RequestsBetaModule.cs
@@ -0,0 +1,453 @@
+#region Copyright
+// /************************************************************************
+//    Copyright (c) 2016 Jamie Rees
+//    File: RequestsModule.cs
+//    Created By: Jamie Rees
+//   
+//    Permission is hereby granted, free of charge, to any person obtaining
+//    a copy of this software and associated documentation files (the
+//    "Software"), to deal in the Software without restriction, including
+//    without limitation the rights to use, copy, modify, merge, publish,
+//    distribute, sublicense, and/or sell copies of the Software, and to
+//    permit persons to whom the Software is furnished to do so, subject to
+//    the following conditions:
+//   
+//    The above copyright notice and this permission notice shall be
+//    included in all copies or substantial portions of the Software.
+//   
+//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+//    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+//    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+//    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//  ************************************************************************/
+#endregion
+
+using System;
+using System.Linq;
+
+using Nancy;
+using Nancy.Responses.Negotiation;
+using Nancy.Security;
+
+using PlexRequests.Core;
+using PlexRequests.Core.SettingModels;
+using PlexRequests.Services.Interfaces;
+using PlexRequests.Services.Notification;
+using PlexRequests.Store;
+using PlexRequests.UI.Models;
+using PlexRequests.Helpers;
+using PlexRequests.UI.Helpers;
+using System.Collections.Generic;
+using PlexRequests.Api.Interfaces;
+using System.Threading.Tasks;
+
+using NLog;
+
+using PlexRequests.Core.Models;
+using PlexRequests.Helpers.Analytics;
+
+using Action = PlexRequests.Helpers.Analytics.Action;
+
+namespace PlexRequests.UI.Modules
+{
+    public class RequestsBetaModule : BaseAuthModule
+    {
+        public RequestsBetaModule(
+            IRequestService service,
+            ISettingsService<PlexRequestSettings> prSettings,
+            ISettingsService<RequestSettings> requestSettings,
+            ISettingsService<PlexSettings> plex,
+            INotificationService notify,
+            ISettingsService<SonarrSettings> sonarrSettings,
+            ISettingsService<SickRageSettings> sickRageSettings,
+            ISettingsService<CouchPotatoSettings> cpSettings,
+            ICouchPotatoApi cpApi,
+            ISonarrApi sonarrApi,
+            ISickRageApi sickRageApi,
+            ICacheProvider cache,
+            IAnalytics an) : base("requestsbeta", prSettings)
+        {
+            Service = service;
+            PrSettings = prSettings;
+            PlexSettings = plex;
+            NotificationService = notify;
+            SonarrSettings = sonarrSettings;
+            SickRageSettings = sickRageSettings;
+            CpSettings = cpSettings;
+            SonarrApi = sonarrApi;
+            SickRageApi = sickRageApi;
+            CpApi = cpApi;
+            Cache = cache;
+            Analytics = an;
+
+            Get["/"] = x => LoadRequests();
+            Get["/plexrequestsettings", true] = async (x, ct) => await GetPlexRequestSettings();
+            Get["/requestsettings", true] = async (x, ct) => await GetRequestSettings();
+            Get["/movies", true] = async (x, ct) => await GetMovies();
+            Get["/movies/{searchTerm}", true] = async (x, ct) => await GetMovies((string)x.searchTerm);
+
+
+            // Everything below is not being used in the beta page
+            Get["/tvshows", true] = async (c, ct) => await GetTvShows();
+            Get["/albums", true] = async (x, ct) => await GetAlbumRequests();
+            Post["/delete", true] = async (x, ct) => await DeleteRequest((int)Request.Form.id);
+            Post["/reportissue", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, (IssueState)(int)Request.Form.issue, null);
+            Post["/reportissuecomment", true] = async (x, ct) => await ReportIssue((int)Request.Form.requestId, IssueState.Other, (string)Request.Form.commentArea);
+
+            Post["/clearissues", true] = async (x, ct) => await ClearIssue((int)Request.Form.Id);
+
+            Post["/changeavailability", true] = async (x, ct) => await ChangeRequestAvailability((int)Request.Form.Id, (bool)Request.Form.Available);
+        }
+
+        private static Logger Log = LogManager.GetCurrentClassLogger();
+        private IRequestService Service { get; }
+        private IAnalytics Analytics { get; }
+        private INotificationService NotificationService { get; }
+        private ISettingsService<PlexRequestSettings> PrSettings { get; }
+        private ISettingsService<PlexSettings> PlexSettings { get; }
+        private ISettingsService<RequestSettings> RequestSettings { get; }
+        private ISettingsService<SonarrSettings> SonarrSettings { get; }
+        private ISettingsService<SickRageSettings> SickRageSettings { get; }
+        private ISettingsService<CouchPotatoSettings> CpSettings { get; }
+        private ISonarrApi SonarrApi { get; }
+        private ISickRageApi SickRageApi { get; }
+        private ICouchPotatoApi CpApi { get; }
+        private ICacheProvider Cache { get; }
+
+        private Negotiator LoadRequests()
+        {
+            return View["Index"];
+        }
+
+        private async Task<Response> GetPlexRequestSettings()
+        {
+            return Response.AsJson(await PrSettings.GetSettingsAsync());
+        }
+
+        private async Task<Response> GetRequestSettings()
+        {
+            return Response.AsJson(await RequestSettings.GetSettingsAsync());
+        }
+
+        private async Task<Response> GetMovies(string searchTerm = null, bool approved = false, bool notApproved = false,
+            bool available = false, bool notAvailable = false, bool released = false, bool notReleased = false)
+        {
+            var dbMovies = await FilterMovies(searchTerm, approved, notApproved, available, notAvailable, released, notReleased);
+            var qualities = await GetQualityProfiles();
+            var model = MapMoviesToView(dbMovies.ToList(), qualities);
+
+            return Response.AsJson(model);
+        }
+
+        private async Task<Response> GetTvShows()
+        {
+            var settingsTask = PrSettings.GetSettingsAsync();
+
+            var requests = await Service.GetAllAsync();
+            requests = requests.Where(x => x.Type == RequestType.TvShow);
+
+            var dbTv = requests;
+            var settings = await settingsTask;
+            if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
+            {
+                dbTv = dbTv.Where(x => x.UserHasRequested(Username)).ToList();
+            }
+
+            IEnumerable<QualityModel> qualities = new List<QualityModel>();
+            if (IsAdmin)
+            {
+                try
+                {
+                    var sonarrSettings = await SonarrSettings.GetSettingsAsync();
+                    if (sonarrSettings.Enabled)
+                    {
+                        var result = Cache.GetOrSetAsync(CacheKeys.SonarrQualityProfiles, async () =>
+                        {
+                            return await Task.Run(() => SonarrApi.GetProfiles(sonarrSettings.ApiKey, sonarrSettings.FullUri));
+                        });
+                        qualities = result.Result.Select(x => new QualityModel { Id = x.id.ToString(), Name = x.name }).ToList();
+                    }
+                    else
+                    {
+                        var sickRageSettings = await SickRageSettings.GetSettingsAsync();
+                        if (sickRageSettings.Enabled)
+                        {
+                            qualities = sickRageSettings.Qualities.Select(x => new QualityModel { Id = x.Key, Name = x.Value }).ToList();
+                        }
+                    }
+                }
+                catch (Exception e)
+                {
+                   Log.Info(e);
+                }
+
+            }
+
+            var viewModel = dbTv.Select(tv => new RequestViewModel
+            {
+                ProviderId = tv.ProviderId,
+                Type = tv.Type,
+                Status = tv.Status,
+                ImdbId = tv.ImdbId,
+                Id = tv.Id,
+                PosterPath = tv.PosterPath,
+                ReleaseDate = tv.ReleaseDate,
+                ReleaseDateTicks = tv.ReleaseDate.Ticks,
+                RequestedDate = tv.RequestedDate,
+                RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(tv.RequestedDate, DateTimeOffset).Ticks,
+                Released = DateTime.Now > tv.ReleaseDate,
+                Approved = tv.Available || tv.Approved,
+                Title = tv.Title,
+                Overview = tv.Overview,
+                RequestedUsers = IsAdmin ? tv.AllUsers.ToArray() : new string[] { },
+                ReleaseYear = tv.ReleaseDate.Year.ToString(),
+                Available = tv.Available,
+                Admin = IsAdmin,
+                IssueId = tv.IssueId,
+                TvSeriesRequestType = tv.SeasonsRequested,
+                Qualities = qualities.ToArray(),
+                Episodes = tv.Episodes.ToArray(),
+            }).ToList();
+
+            return Response.AsJson(viewModel);
+        }
+
+        private async Task<Response> GetAlbumRequests()
+        {
+            var settings = PrSettings.GetSettings();
+            var dbAlbum = await Service.GetAllAsync();
+            dbAlbum = dbAlbum.Where(x => x.Type == RequestType.Album);
+            if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
+            {
+                dbAlbum = dbAlbum.Where(x => x.UserHasRequested(Username));
+            }
+
+            var viewModel = dbAlbum.Select(album =>
+            {
+                return new RequestViewModel
+                {
+                    ProviderId = album.ProviderId,
+                    Type = album.Type,
+                    Status = album.Status,
+                    ImdbId = album.ImdbId,
+                    Id = album.Id,
+                    PosterPath = album.PosterPath,
+                    ReleaseDate = album.ReleaseDate,
+                    ReleaseDateTicks = album.ReleaseDate.Ticks,
+                    RequestedDate = album.RequestedDate,
+                    RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(album.RequestedDate, DateTimeOffset).Ticks,
+                    Released = DateTime.Now > album.ReleaseDate,
+                    Approved = album.Available || album.Approved,
+                    Title = album.Title,
+                    Overview = album.Overview,
+                    RequestedUsers = IsAdmin ? album.AllUsers.ToArray() : new string[] { },
+                    ReleaseYear = album.ReleaseDate.Year.ToString(),
+                    Available = album.Available,
+                    Admin = IsAdmin,
+                    IssueId = album.IssueId,
+                    TvSeriesRequestType = album.SeasonsRequested,
+                    MusicBrainzId = album.MusicBrainzId,
+                    ArtistName = album.ArtistName
+
+                };
+            }).ToList();
+
+            return Response.AsJson(viewModel);
+        }
+
+        private async Task<Response> DeleteRequest(int requestid)
+        {
+            this.RequiresClaims(UserClaims.Admin);
+            Analytics.TrackEventAsync(Category.Requests, Action.Delete, "Delete Request", Username, CookieHelper.GetAnalyticClientId(Cookies));
+
+            var currentEntity = await Service.GetAsync(requestid);
+            await Service.DeleteRequestAsync(currentEntity);
+            return Response.AsJson(new JsonResponseModel { Result = true });
+        }
+
+        /// <summary>
+        /// Reports the issue.
+        /// Comment can be null if the <c>IssueState != Other</c>
+        /// </summary>
+        /// <param name="requestId">The request identifier.</param>
+        /// <param name="issue">The issue.</param>
+        /// <param name="comment">The comment.</param>
+        /// <returns></returns>
+        private async Task<Response> ReportIssue(int requestId, IssueState issue, string comment)
+        {
+            var originalRequest = await Service.GetAsync(requestId);
+            if (originalRequest == null)
+            {
+                return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" });
+            }
+            originalRequest.Issues = issue;
+            originalRequest.OtherMessage = !string.IsNullOrEmpty(comment)
+                ? $"{Username} - {comment}"
+                : string.Empty;
+
+
+            var result = await Service.UpdateRequestAsync(originalRequest);
+
+            var model = new NotificationModel
+            {
+                User = Username,
+                NotificationType = NotificationType.Issue,
+                Title = originalRequest.Title,
+                DateTime = DateTime.Now,
+                Body = issue == IssueState.Other ? comment : issue.ToString().ToCamelCaseWords()
+            };
+            await NotificationService.Publish(model);
+
+            return Response.AsJson(result
+                ? new JsonResponseModel { Result = true }
+                : new JsonResponseModel { Result = false, Message = "Could not add issue, please try again or contact the administrator!" });
+        }
+
+        private async Task<Response> ClearIssue(int requestId)
+        {
+            this.RequiresClaims(UserClaims.Admin);
+
+            var originalRequest = await Service.GetAsync(requestId);
+            if (originalRequest == null)
+            {
+                return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to clear it!" });
+            }
+            originalRequest.Issues = IssueState.None;
+            originalRequest.OtherMessage = string.Empty;
+
+            var result = await Service.UpdateRequestAsync(originalRequest);
+            return Response.AsJson(result
+                                       ? new JsonResponseModel { Result = true }
+                                       : new JsonResponseModel { Result = false, Message = "Could not clear issue, please try again or check the logs" });
+        }
+
+        private async Task<Response> ChangeRequestAvailability(int requestId, bool available)
+        {
+            this.RequiresClaims(UserClaims.Admin);
+            Analytics.TrackEventAsync(Category.Requests, Action.Update, available ? "Make request available" : "Make request unavailable", Username, CookieHelper.GetAnalyticClientId(Cookies));
+            var originalRequest = await Service.GetAsync(requestId);
+            if (originalRequest == null)
+            {
+                return Response.AsJson(new JsonResponseModel { Result = false, Message = "Request does not exist to change the availability!" });
+            }
+
+            originalRequest.Available = available;
+
+            var result = await Service.UpdateRequestAsync(originalRequest);
+            return Response.AsJson(result
+                                       ? new { Result = true, Available = available, Message = string.Empty }
+                                       : new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" });
+        }
+
+        private List<RequestViewModel> MapMoviesToView(List<RequestedModel> dbMovies, List<QualityModel> qualities)
+        {
+            return dbMovies.Select(movie => new RequestViewModel
+            {
+                ProviderId = movie.ProviderId,
+                Type = movie.Type,
+                Status = movie.Status,
+                ImdbId = movie.ImdbId,
+                Id = movie.Id,
+                PosterPath = movie.PosterPath,
+                ReleaseDate = movie.ReleaseDate,
+                ReleaseDateTicks = movie.ReleaseDate.Ticks,
+                RequestedDate = movie.RequestedDate,
+                Released = DateTime.Now > movie.ReleaseDate,
+                RequestedDateTicks = DateTimeHelper.OffsetUTCDateTime(movie.RequestedDate, DateTimeOffset).Ticks,
+                Approved = movie.Available || movie.Approved,
+                Title = movie.Title,
+                Overview = movie.Overview,
+                RequestedUsers = IsAdmin ? movie.AllUsers.ToArray() : new string[] { },
+                ReleaseYear = movie.ReleaseDate.Year.ToString(),
+                Available = movie.Available,
+                Admin = IsAdmin,
+                IssueId = movie.IssueId,
+                Qualities = qualities.ToArray()
+            }).ToList();
+        }
+
+        private async Task<List<QualityModel>> GetQualityProfiles()
+        {
+            var qualities = new List<QualityModel>();
+            if (IsAdmin)
+            {
+                var cpSettings = CpSettings.GetSettings();
+                if (cpSettings.Enabled)
+                {
+                    try
+                    {
+                        var result = await Cache.GetOrSetAsync(CacheKeys.CouchPotatoQualityProfiles, async () =>
+                        {
+                            return await Task.Run(() => CpApi.GetProfiles(cpSettings.FullUri, cpSettings.ApiKey)).ConfigureAwait(false);
+                        });
+                        if (result != null)
+                        {
+                            qualities = result.list.Select(x => new QualityModel { Id = x._id, Name = x.label }).ToList();
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        Log.Info(e);
+                    }
+                }
+            }
+            return qualities;
+        }
+
+        private async Task<IEnumerable<RequestedModel>> FilterMovies(string searchTerm = null, bool approved = false, bool notApproved = false,
+            bool available = false, bool notAvailable = false, bool released = false, bool notReleased = false)
+        {
+            var settings = PrSettings.GetSettings();
+            var allRequests = await Service.GetAllAsync();
+            allRequests = allRequests.Where(x => x.Type == RequestType.Movie);
+
+            var dbMovies = allRequests;
+
+            if (settings.UsersCanViewOnlyOwnRequests && !IsAdmin)
+            {
+                dbMovies = dbMovies.Where(x => x.UserHasRequested(Username));
+            }
+
+            // Filter the movies on the search term
+            if (!string.IsNullOrEmpty(searchTerm))
+            {
+                dbMovies = dbMovies.Where(x => x.Title.Contains(searchTerm));
+            }
+
+            if (approved)
+            {
+                dbMovies = dbMovies.Where(x => x.Approved);
+            }
+
+            if (notApproved)
+            {
+                dbMovies = dbMovies.Where(x => !x.Approved);
+            }
+
+            if (available)
+            {
+                dbMovies = dbMovies.Where(x => x.Available);
+            }
+
+            if (notAvailable)
+            {
+                dbMovies = dbMovies.Where(x => !x.Available);
+            }
+
+            if (released)
+            {
+                dbMovies = dbMovies.Where(x => DateTime.Now > x.ReleaseDate);
+            }
+
+            if (notReleased)
+            {
+                dbMovies = dbMovies.Where(x => DateTime.Now < x.ReleaseDate);
+            }
+
+            return dbMovies;
+        }
+    }
+}
diff --git a/PlexRequests.UI/Modules/RequestsModule.cs b/PlexRequests.UI/Modules/RequestsModule.cs
index cb8ed6882..8a2a9821a 100644
--- a/PlexRequests.UI/Modules/RequestsModule.cs
+++ b/PlexRequests.UI/Modules/RequestsModule.cs
@@ -67,7 +67,8 @@ namespace PlexRequests.UI.Modules
             ISonarrApi sonarrApi,
             ISickRageApi sickRageApi,
             ICacheProvider cache,
-            IAnalytics an) : base("requests", prSettings)
+            IAnalytics an,
+            INotificationEngine engine) : base("requests", prSettings)
         {
             Service = service;
             PrSettings = prSettings;
@@ -81,6 +82,7 @@ namespace PlexRequests.UI.Modules
             CpApi = cpApi;
             Cache = cache;
             Analytics = an;
+            NotificationEngine = engine;
 
             Get["/", true] = async (x, ct) => await LoadRequests();
             Get["/movies", true] = async (x, ct) => await GetMovies();
@@ -108,6 +110,7 @@ namespace PlexRequests.UI.Modules
         private ISickRageApi SickRageApi { get; }
         private ICouchPotatoApi CpApi { get; }
         private ICacheProvider Cache { get; }
+        private INotificationEngine NotificationEngine { get; }
 
         private async Task<Negotiator> LoadRequests()
         {
@@ -376,6 +379,8 @@ namespace PlexRequests.UI.Modules
             originalRequest.Available = available;
 
             var result = await Service.UpdateRequestAsync(originalRequest);
+            var plexService = await PlexSettings.GetSettingsAsync();
+            await NotificationEngine.NotifyUsers(originalRequest, plexService.PlexAuthToken);
             return Response.AsJson(result
                                        ? new { Result = true, Available = available, Message = string.Empty }
                                        : new { Result = false, Available = false, Message = "Could not update the availability, please try again or check the logs" });
diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs
index 5f0fa4ed1..73f016f24 100644
--- a/PlexRequests.UI/Modules/SearchModule.cs
+++ b/PlexRequests.UI/Modules/SearchModule.cs
@@ -110,7 +110,7 @@ namespace PlexRequests.UI.Modules
 
             Get["movie/{searchTerm}", true] = async (x, ct) => await SearchMovie((string)x.searchTerm);
             Get["tv/{searchTerm}", true] = async (x, ct) => await SearchTvShow((string)x.searchTerm);
-            Get["music/{searchTerm}", true] = async (x, ct) => await SearchMusic((string)x.searchTerm);
+            Get["music/{searchTerm}", true] = async (x, ct) => await SearchAlbum((string)x.searchTerm);
             Get["music/coverArt/{id}"] = p => GetMusicBrainzCoverArt((string)p.id);
 
             Get["movie/upcoming", true] = async (x, ct) => await UpcomingMovies();
@@ -252,9 +252,11 @@ namespace PlexRequests.UI.Modules
                     VoteCount = movie.VoteCount
                 };
                 var canSee = CanUserSeeThisRequest(viewMovie.Id, settings.UsersCanViewOnlyOwnRequests, dbMovies);
-                if (Checker.IsMovieAvailable(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString()))
+                var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString());
+                if (plexMovie != null)
                 {
                     viewMovie.Available = true;
+                    viewMovie.PlexUrl = plexMovie.Url;
                 }
                 else if (dbMovies.ContainsKey(movie.Id) && canSee) // compare to the requests db
                 {
@@ -343,9 +345,12 @@ namespace PlexRequests.UI.Modules
                     providerId = viewT.Id.ToString();
                 }
 
-                if (Checker.IsTvShowAvailable(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4), providerId))
+                var plexShow = Checker.GetTvShow(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4),
+                    providerId);
+                if (plexShow != null)
                 {
                     viewT.Available = true;
+                    viewT.PlexUrl = plexShow.Url;
                 }
                 else if (t.show?.externals?.thetvdb != null)
                 {
@@ -371,7 +376,7 @@ namespace PlexRequests.UI.Modules
             return Response.AsJson(viewTv);
         }
 
-        private async Task<Response> SearchMusic(string searchTerm)
+        private async Task<Response> SearchAlbum(string searchTerm)
         {
             Analytics.TrackEventAsync(Category.Search, Action.Album, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies));
             var apiAlbums = new List<Release>();
@@ -405,9 +410,11 @@ namespace PlexRequests.UI.Modules
                 DateTime release;
                 DateTimeHelper.CustomParse(a.ReleaseEvents?.FirstOrDefault()?.date, out release);
                 var artist = a.ArtistCredit?.FirstOrDefault()?.artist;
-                if (Checker.IsAlbumAvailable(plexAlbums.ToArray(), a.title, release.ToString("yyyy"), artist?.name))
+                var plexAlbum = Checker.GetAlbum(plexAlbums.ToArray(), a.title, release.ToString("yyyy"), artist?.name);
+                if (plexAlbum != null)
                 {
                     viewA.Available = true;
+                    viewA.PlexUrl = plexAlbum.Url;
                 }
                 if (!string.IsNullOrEmpty(a.id) && dbAlbum.ContainsKey(a.id))
                 {
diff --git a/PlexRequests.UI/Modules/UserLoginModule.cs b/PlexRequests.UI/Modules/UserLoginModule.cs
index 61481faef..e30f4adc2 100644
--- a/PlexRequests.UI/Modules/UserLoginModule.cs
+++ b/PlexRequests.UI/Modules/UserLoginModule.cs
@@ -61,7 +61,17 @@ namespace PlexRequests.UI.Modules
             PlexSettings = plexSettings;
             Linker = linker;
 
-            Get["UserLoginIndex", "/", true] = async (x, ct) => await Index();
+            Get["UserLoginIndex", "/", true] = async (x, ct) =>
+            {
+                if (!string.IsNullOrEmpty(Username) || IsAdmin)
+                {
+                    var uri = Linker.BuildRelativeUri(Context, "SearchIndex");
+                    return Response.AsRedirect(uri.ToString()); 
+                }
+                var settings = await AuthService.GetSettingsAsync();
+                return View["Index", settings];
+            };
+
             Post["/", true] = async (x, ct) => await LoginUser();
             Get["/logout"] = x => Logout();
         }
@@ -75,12 +85,6 @@ namespace PlexRequests.UI.Modules
 
         private static Logger Log = LogManager.GetCurrentClassLogger();
 
-        public async Task<Negotiator> Index()
-        {
-            var settings = await AuthService.GetSettingsAsync();
-            return View["Index", settings];
-        }
-
         private async Task<Response> LoginUser()
         {
             var dateTimeOffset = Request.Form.DateTimeOffset;
@@ -89,7 +93,7 @@ namespace PlexRequests.UI.Modules
             if (string.IsNullOrWhiteSpace(username))
             {
                 Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
-                var uri = Linker.BuildAbsoluteUri(Context, "UserLoginIndex");
+                var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
                 return Response.AsRedirect(uri.ToString());  // TODO Check this
             }
 
@@ -102,7 +106,7 @@ namespace PlexRequests.UI.Modules
             {
                 Log.Debug("User is in denied list, not allowing them to authenticate");
                 Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
-                var uri = Linker.BuildAbsoluteUri(Context, "UserLoginIndex");
+                var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
                 return Response.AsRedirect(uri.ToString());  // TODO Check this
             }
 
@@ -161,7 +165,7 @@ namespace PlexRequests.UI.Modules
 
             if (!authenticated)
             {
-                var uri = Linker.BuildAbsoluteUri(Context, "UserLoginIndex");
+                var uri = Linker.BuildRelativeUri(Context, "UserLoginIndex");
                 Session["TempMessage"] = Resources.UI.UserLogin_IncorrectUserPass;
                 return Response.AsRedirect(uri.ToString()); // TODO Check this
             }
@@ -172,11 +176,11 @@ namespace PlexRequests.UI.Modules
             {
                 if (!landingSettings.BeforeLogin)
                 {
-                    var uri = Linker.BuildAbsoluteUri(Context, "LandingPageIndex");
+                    var uri = Linker.BuildRelativeUri(Context, "LandingPageIndex");
                     return Response.AsRedirect(uri.ToString());
                 }
             }
-            var retVal = Linker.BuildAbsoluteUri(Context, "SearchIndex");
+            var retVal = Linker.BuildRelativeUri(Context, "SearchIndex");
             return Response.AsRedirect(retVal.ToString()); // TODO Check this
         }
 
diff --git a/PlexRequests.UI/Modules/UserManagementModule.cs b/PlexRequests.UI/Modules/UserManagementModule.cs
index 9667dac6f..1e9909565 100644
--- a/PlexRequests.UI/Modules/UserManagementModule.cs
+++ b/PlexRequests.UI/Modules/UserManagementModule.cs
@@ -4,9 +4,10 @@ using System.Linq;
 using System.Threading.Tasks;
 
 using Nancy;
+using Nancy.Extensions;
 using Nancy.Responses.Negotiation;
 using Nancy.Security;
-
+using Newtonsoft.Json;
 using PlexRequests.Api.Interfaces;
 using PlexRequests.Core;
 using PlexRequests.Core.Models;
@@ -30,9 +31,10 @@ namespace PlexRequests.UI.Modules
             Get["/"] = x => Load();
 
             Get["/users", true] = async (x, ct) => await LoadUsers();
-            Post["/createuser"] = x => CreateUser(Request.Form["username"].ToString(), Request.Form["password"].ToString());
+            Post["/createuser"] = x => CreateUser();
             Get["/local/{id}"] = x => LocalDetails((Guid)x.id);
-            Get["/plex/{id}", true] = async (x,ct) => await PlexDetails(x.id);
+            Get["/plex/{id}", true] = async (x, ct) => await PlexDetails(x.id);
+            Get["/claims"] = x => GetClaims();
         }
 
         private ICustomUserMapper UserMapper { get; }
@@ -57,11 +59,12 @@ namespace PlexRequests.UI.Modules
 
                 model.Add(new UserManagementUsersViewModel
                 {
-                    Id= user.UserGuid,
+                    Id = user.UserGuid,
                     Claims = claimsString,
                     Username = user.UserName,
                     Type = UserType.LocalUser,
-                    EmailAddress = userProps.EmailAddress
+                    EmailAddress = userProps.EmailAddress,
+                    ClaimsArray = claims
                 });
             }
 
@@ -91,9 +94,17 @@ namespace PlexRequests.UI.Modules
             return Response.AsJson(model);
         }
 
-        private Response CreateUser(string username, string password)
+        private Response CreateUser()
         {
-            if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
+            var body = Request.Body.AsString();
+            if (string.IsNullOrEmpty(body))
+            {
+                return Response.AsJson(new JsonResponseModel { Result = false, Message = "Could not save user, invalid JSON body" });
+            }
+
+            var model = JsonConvert.DeserializeObject<UserManagementCreateModel>(body);
+
+            if (string.IsNullOrWhiteSpace(model.Username) || string.IsNullOrWhiteSpace(model.Password))
             {
                 return Response.AsJson(new JsonResponseModel
                 {
@@ -101,7 +112,7 @@ namespace PlexRequests.UI.Modules
                     Message = "Please enter in a valid Username and Password"
                 });
             }
-            var user = UserMapper.CreateRegularUser(username, password);
+            var user = UserMapper.CreateUser(model.Username, model.Password, model.Claims, new UserProperties { EmailAddress = model.EmailAddress });
             if (user.HasValue)
             {
                 return Response.AsJson(user);
@@ -139,6 +150,21 @@ namespace PlexRequests.UI.Modules
 
             return Nancy.Response.NoBody;
         }
+
+        /// <summary>
+        /// Returns all claims for the users.
+        /// </summary>
+        /// <returns></returns>
+        private Response GetClaims()
+        {
+            var retVal = new List<dynamic>();
+            var claims = UserMapper.GetAllClaims();
+            foreach (var c in claims)
+            {
+                retVal.Add(new { Name = c, Selected = false });
+            }
+            return Response.AsJson(retVal);
+        }
     }
 }
 
diff --git a/PlexRequests.UI/Modules/UserWizardModule.cs b/PlexRequests.UI/Modules/UserWizardModule.cs
index 253288929..1bfbbd679 100644
--- a/PlexRequests.UI/Modules/UserWizardModule.cs
+++ b/PlexRequests.UI/Modules/UserWizardModule.cs
@@ -34,7 +34,7 @@ using Nancy.Extensions;
 using Nancy.ModelBinding;
 using Nancy.Responses.Negotiation;
 using Nancy.Validation;
-
+using NLog;
 using PlexRequests.Api.Interfaces;
 using PlexRequests.Core;
 using PlexRequests.Core.SettingModels;
@@ -84,7 +84,9 @@ namespace PlexRequests.UI.Modules
         private ICustomUserMapper Mapper { get; }
         private IAnalytics Analytics { get; }
 
-     
+        private static Logger Log = LogManager.GetCurrentClassLogger();
+
+
         private Response PlexAuth()
         {
             var user = this.Bind<PlexAuth>();
@@ -103,9 +105,10 @@ namespace PlexRequests.UI.Modules
 
             // Set the auth token in the session so we can use it in the next form
             Session[SessionKeys.UserWizardPlexAuth] = model.user.authentication_token;
-
+            
             var servers = PlexApi.GetServer(model.user.authentication_token);
             var firstServer = servers.Server.FirstOrDefault();
+            
             return Response.AsJson(new { Result = true, firstServer?.Port, Ip = firstServer?.LocalAddresses, firstServer?.Scheme });
         }
 
@@ -119,6 +122,20 @@ namespace PlexRequests.UI.Modules
             }
             form.PlexAuthToken = Session[SessionKeys.UserWizardPlexAuth].ToString(); // Set the auth token from the previous form
 
+            // Get the machine ID from the settings (This could have changed)
+            try
+            {
+                var servers = PlexApi.GetServer(form.PlexAuthToken);
+                var firstServer = servers.Server.FirstOrDefault(x => x.AccessToken == form.PlexAuthToken);
+
+                Session[SessionKeys.UserWizardMachineId] = firstServer?.MachineIdentifier;
+            }
+            catch (Exception e)
+            {
+                // Probably bad settings, just continue
+                Log.Error(e);
+            }
+
             var result = await PlexSettings.SaveSettingsAsync(form);
             if (result)
             {
diff --git a/PlexRequests.UI/NinjectModules/ConfigurationModule.cs b/PlexRequests.UI/NinjectModules/ConfigurationModule.cs
index 5bb10dca3..ae55786e1 100644
--- a/PlexRequests.UI/NinjectModules/ConfigurationModule.cs
+++ b/PlexRequests.UI/NinjectModules/ConfigurationModule.cs
@@ -50,6 +50,7 @@ namespace PlexRequests.UI.NinjectModules
             Bind<ICustomUserMapper>().To<UserMapper>();
 
             Bind<INotificationService>().To<NotificationService>().InSingletonScope();
+            Bind<INotificationEngine>().To<NotificationEngine>();
         }
     }
 }
\ No newline at end of file
diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj
index d178bbc92..1779f378b 100644
--- a/PlexRequests.UI/PlexRequests.UI.csproj
+++ b/PlexRequests.UI/PlexRequests.UI.csproj
@@ -250,6 +250,7 @@
     <Compile Include="Modules\CultureModule.cs" />
     <Compile Include="Modules\IssuesModule.cs" />
     <Compile Include="Modules\LandingPageModule.cs" />
+    <Compile Include="Modules\RequestsBetaModule.cs" />
     <Compile Include="Modules\UpdateCheckerModule.cs" />
     <Compile Include="Modules\UserWizardModule.cs" />
     <Compile Include="NinjectModules\ApiModule.cs" />
@@ -282,6 +283,12 @@
     <Content Include="Content\app\app.js">
       <CopyToOutputDirectory>Always</CopyToOutputDirectory>
     </Content>
+    <Content Include="Content\app\requests\requestsController.js">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+    <Content Include="Content\app\requests\requestsService.js">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
     <Content Include="Content\awesome-bootstrap-checkbox.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
diff --git a/PlexRequests.UI/Resources/UI.resx b/PlexRequests.UI/Resources/UI.resx
index 58e8f71c8..4f23fce97 100644
--- a/PlexRequests.UI/Resources/UI.resx
+++ b/PlexRequests.UI/Resources/UI.resx
@@ -440,4 +440,7 @@
   <data name="Requests_ReleaseDate_Unavailable" xml:space="preserve">
     <value>There is no information available for the release date</value>
   </data>
+  <data name="Search_ViewInPlex" xml:space="preserve">
+    <value>View In Plex</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/PlexRequests.UI/Resources/UI1.Designer.cs b/PlexRequests.UI/Resources/UI1.Designer.cs
index 20ab447d7..6cd282c32 100644
--- a/PlexRequests.UI/Resources/UI1.Designer.cs
+++ b/PlexRequests.UI/Resources/UI1.Designer.cs
@@ -987,6 +987,15 @@ namespace PlexRequests.UI.Resources {
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to View In Plex.
+        /// </summary>
+        public static string Search_ViewInPlex {
+            get {
+                return ResourceManager.GetString("Search_ViewInPlex", resourceCulture);
+            }
+        }
+        
         /// <summary>
         ///   Looks up a localized string similar to You have reached your weekly request limit for Albums! Please contact your admin..
         /// </summary>
diff --git a/PlexRequests.UI/Validators/PlexRequestsValidator.cs b/PlexRequests.UI/Validators/PlexRequestsValidator.cs
index c82817185..7d61eba2c 100644
--- a/PlexRequests.UI/Validators/PlexRequestsValidator.cs
+++ b/PlexRequests.UI/Validators/PlexRequestsValidator.cs
@@ -24,6 +24,8 @@
 //    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //  ************************************************************************/
 #endregion
+
+using System;
 using FluentValidation;
 
 using PlexRequests.Core.SettingModels;
@@ -34,18 +36,18 @@ namespace PlexRequests.UI.Validators
     {
         public PlexRequestsValidator()
         {
-            RuleFor(x => x.BaseUrl).NotEqual("requests").WithMessage("You cannot use 'requests' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("admin").WithMessage("You cannot use 'admin' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("search").WithMessage("You cannot use 'search' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("issues").WithMessage("You cannot use 'issues' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("userlogin").WithMessage("You cannot use 'userlogin' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("login").WithMessage("You cannot use 'login' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("test").WithMessage("You cannot use 'test' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("approval").WithMessage("You cannot use 'approval' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("updatechecker").WithMessage("You cannot use 'updatechecker' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("usermanagement").WithMessage("You cannot use 'usermanagement' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("api").WithMessage("You cannot use 'api' as this is reserved by the application.");
-            RuleFor(x => x.BaseUrl).NotEqual("landing").WithMessage("You cannot use 'landing' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("requests",StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'requests' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("admin", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'admin' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("search", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'search' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("issues", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'issues' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("userlogin", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'userlogin' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("login", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'login' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("test", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'test' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("approval", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'approval' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("updatechecker", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'updatechecker' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("usermanagement", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'usermanagement' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("api", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'api' as this is reserved by the application.");
+            RuleFor(x => x.BaseUrl).NotEqual("landing", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'landing' as this is reserved by the application.");
         }
     }
 }
\ No newline at end of file
diff --git a/PlexRequests.UI/Views/Admin/LandingPage.cshtml b/PlexRequests.UI/Views/Admin/LandingPage.cshtml
index 9f6dcec6f..38d3cc6fa 100644
--- a/PlexRequests.UI/Views/Admin/LandingPage.cshtml
+++ b/PlexRequests.UI/Views/Admin/LandingPage.cshtml
@@ -1,7 +1,7 @@
 @using PlexRequests.UI.Helpers
 @Html.Partial("_Sidebar")
 @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase<PlexRequests.Core.SettingModels.LandingPageSettings>
-
+@Html.LoadDateTimePickerAsset()
 <div class="col-sm-8 col-sm-push-1">
     <form class="form-horizontal" method="POST" id="mainForm">
         <fieldset>
diff --git a/PlexRequests.UI/Views/Search/Index.cshtml b/PlexRequests.UI/Views/Search/Index.cshtml
index 17204abb1..950a7f06d 100644
--- a/PlexRequests.UI/Views/Search/Index.cshtml
+++ b/PlexRequests.UI/Views/Search/Index.cshtml
@@ -175,6 +175,8 @@
                 {{#if_eq type "movie"}}
                 {{#if_eq available true}}
                 <button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button>
+                <br />
+                <a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
                 {{else}}
                 {{#if_eq requested true}}
                 <button style="text-align: right" class="btn btn-primary-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Requested</button>
@@ -186,7 +188,8 @@
                 {{#if_eq type "tv"}}
                     {{#if_eq tvFullyAvailable true}}
                         @*//TODO Not used yet*@
-                        <button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button>
+                        <button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button><br />
+                <a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
                     {{else}}
                         <div class="dropdown">
                             <button id="{{id}}" class="btn btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
@@ -259,7 +262,8 @@
             <form method="POST" action="@url/search/request/{{type}}" id="form{{id}}">
                 <input name="{{type}}Id" type="text" value="{{id}}" hidden="hidden" />
                 {{#if_eq available true}}
-                <button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button>
+                <button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Available</button><br />
+                <a style="text-align: right" class="btn btn-sm btn-primary-outline" href="{{url}}" target="_blank"><i class="fa fa-eye"></i> @UI.Search_ViewInPlex</a>
                 {{else}}
                 {{#if_eq requested true}}
                 <button style="text-align: right" class="btn btn-success-outline disabled" disabled><i class="fa fa-check"></i> @UI.Search_Requested</button>
diff --git a/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml b/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml
index 1c274fedd..6692f6f74 100644
--- a/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml
+++ b/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml
@@ -78,6 +78,7 @@
                 <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-language" aria-hidden="true"><span class="caret"></span></i></a>
                 <ul class="dropdown-menu" role="menu">
                     <li><a href="@url/culture?l=en&u=@Context.Request.Path">@UI.Layout_English</a></li>
+                    <li><a href="@url/culture?l=fr&u=@Context.Request.Path">@UI.Layout_French</a></li>
                     <li><a href="@url/culture?l=nl&u=@Context.Request.Path">@UI.Layout_Dutch</a></li>
                     <li><a href="@url/culture?l=es&u=@Context.Request.Path">@UI.Layout_Spanish</a></li>
                     <li><a href="@url/culture?l=de&u=@Context.Request.Path">@UI.Layout_German</a></li>
diff --git a/PlexRequests.UI/Views/UserManagement/Index.cshtml b/PlexRequests.UI/Views/UserManagement/Index.cshtml
index 6a9a4318a..abdc5821b 100644
--- a/PlexRequests.UI/Views/UserManagement/Index.cshtml
+++ b/PlexRequests.UI/Views/UserManagement/Index.cshtml
@@ -1,9 +1,8 @@
 @using PlexRequests.UI.Helpers
 @inherits PlexRequests.UI.Helpers.AngularViewBase
 
-<script src="~/Content/app/userManagement/userManagementController.js"></script>
-<script src="~/Content/app/userManagement/userManagementService.js"></script>
-<div ng-controller="userManagementController" ng-init="getUsers()">
+@Html.LoadUserManagementAssets()
+<div ng-controller="userManagementController" ng-init="init()">
 
     <br />
     <br />
@@ -12,15 +11,26 @@
         <br>
         <br>
         <div ng-show="error.error" ng-bind="error.errorMessage"></div>
-        <form ng-submit="addUser()">
+        <form name="userform" ng-submit="addUser()" novalidate>
             <div class="form-group">
                 <input id="username" type="text" placeholder="user" ng-model="user.username" class="form-control form-control-custom" />
             </div>
             <div class="form-group">
                 <input id="password" type="password" placeholder="password" ng-model="user.password" class="form-control form-control-custom" />
             </div>
+            <div class="form-group">
+                <input id="email" type="email" placeholder="email address" ng-model="user.email" class="form-control form-control-custom" />
+            </div>
+
+            <div class="checkbox" ng-repeat="claim in claims">
+                <input id="claimCheckbox_{{$id}}" class="checkbox-custom" name="selectedClaims[]"
+                       ng-checked="claim.selected" ng-model="claim.selected" type="checkbox" value="claim" />
+                <label for="claimCheckbox_{{$id}}">{{claim.name}}</label>
+            </div>
+
             <input type="submit" class="btn btn-success-outline" value="Add" />
         </form>
+
         <form>
             <div class="form-group">
                 <div class="input-group">
@@ -33,6 +43,7 @@
                 </div>
             </div>
         </form>
+
         <table class="table table-striped table-hover table-responsive table-condensed">
             <thead>
                 <tr>
@@ -98,9 +109,27 @@
         <div>
             <strong>User Type: </strong><span ng-bind="selectedUser.type === 1 ? 'Local User' : 'Plex User'"></span>
         </div>
-    </div>
+    <br/>
+    <br/>
+        <div ng-show="selectedUser.type === 1">
+            <!--Edit-->
+            
+            <strong>Modify Roles:</strong>
+            <!--Load all claims-->
+                <div class="checkbox" ng-repeat="claim in claims">
+                    <input id="claimCheckboxEdit_{{$id}}" class="checkbox-custom" name="selectedClaims[]" ng-checked="@*//TODO: Need to figure our how to preselect them*@" ng-model="claim.selected" type="checkbox" value="claim" />
+                    <label for="claimCheckboxEdit_{{$id}}">{{claim.name}}</label>
+                </div>
+        
+
+ 
+            
+            
+            <button ng-click="updateUser()" class="btn btn-primary-outline">Update</button>
+        </div>
 
 
+    </div> <!-- End of user side menu -->
 
 
 </div>
\ No newline at end of file
diff --git a/appveyor.yml b/appveyor.yml
index 5370cbdcd..1285d7e4b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -3,9 +3,9 @@ configuration: Release
 assembly_info:
   patch: true
   file: '**\AssemblyInfo.*'
-  assembly_version: '1.9.0'
+  assembly_version: '1.9.1'
   assembly_file_version: '{version}'
-  assembly_informational_version: '1.9.0' 
+  assembly_informational_version: '1.9.1' 
 before_build:
 - cmd: appveyor-retry nuget restore
 build: