diff --git a/.gitattributes b/.gitattributes index 210f21789..4bf8b8c28 100644 --- a/.gitattributes +++ b/.gitattributes @@ -66,6 +66,3 @@ PlexRequests.UI/Content/* linguist-vendored PlexRequests.UI/Content/* linguist-vendored base.scss linguist-vendored=false -requests-1.7.js linguist-vendored=false -search-1.7.js linguist-vendored=false -site-1.7.js linguist-vendored=false diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a25cfb512..09b8fe865 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,6 +3,8 @@ If this is a bug report please make sure you have filled the following in: #### Plex Requests.Net Version: +#### update Branch: + #### Operating System: @@ -19,4 +21,4 @@ Logs go here (Please make sure you remove any personal information from the logs #### Reproduction Steps: -Please include any steps to reproduce the issue, this the request that is causing the problem etc. \ No newline at end of file +Please include any steps to reproduce the issue, this the request that is causing the problem etc. diff --git a/PlexRequests.Api.Interfaces/IPlexApi.cs b/PlexRequests.Api.Interfaces/IPlexApi.cs index 3588b0ecd..bfd42ae50 100644 --- a/PlexRequests.Api.Interfaces/IPlexApi.cs +++ b/PlexRequests.Api.Interfaces/IPlexApi.cs @@ -44,6 +44,7 @@ namespace PlexRequests.Api.Interfaces PlexSearch GetAllEpisodes(string authToken, Uri host, string section, int startPage, int returnCount); PlexServer GetServer(string authToken); PlexSeasonMetadata GetSeasons(string authToken, Uri plexFullHost, string ratingKey); - RecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId); + RecentlyAddedModelOld RecentlyAddedOld(string authToken, Uri plexFullHost, string sectionId); + PlexRecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId); } } \ No newline at end of file diff --git a/PlexRequests.Api.Models/Plex/RecentlyAddedModel.cs b/PlexRequests.Api.Models/Plex/RecentlyAddedModelOld.cs similarity index 58% rename from PlexRequests.Api.Models/Plex/RecentlyAddedModel.cs rename to PlexRequests.Api.Models/Plex/RecentlyAddedModelOld.cs index 136f48a81..01b64a4f0 100644 --- a/PlexRequests.Api.Models/Plex/RecentlyAddedModel.cs +++ b/PlexRequests.Api.Models/Plex/RecentlyAddedModelOld.cs @@ -1,7 +1,7 @@ #region Copyright // /************************************************************************ // Copyright (c) 2016 Jamie Rees -// File: RecentlyAddedModel.cs +// File: RecentlyAddedModelOld.cs // Created By: Jamie Rees // // Permission is hereby granted, free of charge, to any person obtaining @@ -110,7 +110,7 @@ namespace PlexRequests.Api.Models.Plex public string tag { get; set; } } - public class RecentlyAddedModel + public class RecentlyAddedModelOld { public string _elementType { get; set; } public string allowSync { get; set; } @@ -130,4 +130,112 @@ namespace PlexRequests.Api.Models.Plex public string viewMode { get; set; } public List _children { get; set; } } + + + // 1.3 and forward! + public class PartRecentlyAdded + { + public int id { get; set; } + public string key { get; set; } + public int duration { get; set; } + public string file { get; set; } + public double size { get; set; } + public string audioProfile { get; set; } + public string container { get; set; } + public string videoProfile { get; set; } + public string deepAnalysisVersion { get; set; } + public string requiredBandwidths { get; set; } + } + + public class Medium + { + public string videoResolution { get; set; } + public int id { get; set; } + public int duration { get; set; } + public int bitrate { get; set; } + public int width { get; set; } + public int height { get; set; } + public double aspectRatio { get; set; } + public int audioChannels { get; set; } + public string audioCodec { get; set; } + public string videoCodec { get; set; } + public string container { get; set; } + public string videoFrameRate { get; set; } + public string audioProfile { get; set; } + public string videoProfile { get; set; } + public List Part { get; set; } + } + + public class DirectorRecentlyAdded + { + public string tag { get; set; } + } + + public class WriterRecentlyAdded + { + public string tag { get; set; } + } + + public class Metadata + { + public string ratingKey { get; set; } + public string key { get; set; } + public string parentRatingKey { get; set; } + public string grandparentRatingKey { get; set; } + public string type { get; set; } + public string title { get; set; } + public string titleSort { get; set; } + public string grandparentKey { get; set; } + public string parentKey { get; set; } + public string grandparentTitle { get; set; } + public string contentRating { get; set; } + public string summary { get; set; } + public int index { get; set; } + public int parentIndex { get; set; } + public int year { get; set; } + public string thumb { get; set; } + public string art { get; set; } + public string parentThumb { get; set; } + public string grandparentThumb { get; set; } + public string grandparentArt { get; set; } + public string grandparentTheme { get; set; } + public int duration { get; set; } + public string originallyAvailableAt { get; set; } + public int addedAt { get; set; } + public int updatedAt { get; set; } + public List Media { get; set; } + public List Director { get; set; } + public List Writer { get; set; } + public int? viewCount { get; set; } + public int? lastViewedAt { get; set; } + public double? rating { get; set; } + } + + public class MediaContainer + { + public double size { get; set; } + public double totalSize { get; set; } + public bool allowSync { get; set; } + public string art { get; set; } + public string identifier { get; set; } + public int librarySectionID { get; set; } + public string librarySectionTitle { get; set; } + public string librarySectionUUID { get; set; } + public string mediaTagPrefix { get; set; } + public int mediaTagVersion { get; set; } + public bool mixedParents { get; set; } + public bool nocache { get; set; } + public int offset { get; set; } + public string thumb { get; set; } + public string title1 { get; set; } + public string title2 { get; set; } + public string viewGroup { get; set; } + public int viewMode { get; set; } + public List Metadata { get; set; } + } + + public class PlexRecentlyAddedModel + { + public MediaContainer MediaContainer { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj index a1638df6f..1d29ec759 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -73,7 +73,7 @@ - + diff --git a/PlexRequests.Api/ApiRequest.cs b/PlexRequests.Api/ApiRequest.cs index 8cb023ea6..8bf1a2721 100644 --- a/PlexRequests.Api/ApiRequest.cs +++ b/PlexRequests.Api/ApiRequest.cs @@ -104,8 +104,7 @@ namespace PlexRequests.Api } var result = DeserializeXml(response.Content); - return result; - } + return result;} public T ExecuteJson(IRestRequest request, Uri baseUri) where T : new() { diff --git a/PlexRequests.Api/CouchPotatoApi.cs b/PlexRequests.Api/CouchPotatoApi.cs index e4382ff22..9523d7284 100644 --- a/PlexRequests.Api/CouchPotatoApi.cs +++ b/PlexRequests.Api/CouchPotatoApi.cs @@ -65,9 +65,7 @@ namespace PlexRequests.Api var obj = RetryHandler.Execute(() => Api.ExecuteJson(request, baseUrl), (exception, timespan) => Log.Error(exception, "Exception when calling AddMovie for CP, Retrying {0}", timespan), new[] { - TimeSpan.FromSeconds (2), - TimeSpan.FromSeconds(5), - TimeSpan.FromSeconds(10)}); + TimeSpan.FromSeconds (2)}); if (obj.Count > 0) diff --git a/PlexRequests.Api/PlexApi.cs b/PlexRequests.Api/PlexApi.cs index 2e03ad274..b20971910 100644 --- a/PlexRequests.Api/PlexApi.cs +++ b/PlexRequests.Api/PlexApi.cs @@ -346,7 +346,7 @@ namespace PlexRequests.Api return servers; } - public RecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId) + public RecentlyAddedModelOld RecentlyAddedOld(string authToken, Uri plexFullHost, string sectionId) { var request = new RestRequest { @@ -360,7 +360,7 @@ namespace PlexRequests.Api try { - var lib = RetryHandler.Execute(() => Api.ExecuteJson(request, plexFullHost), + var lib = RetryHandler.Execute(() => Api.ExecuteJson(request, plexFullHost), (exception, timespan) => Log.Error(exception, "Exception when calling RecentlyAddedModel for Plex, Retrying {0}", timespan), new[] { TimeSpan.FromSeconds (5), TimeSpan.FromSeconds(10), @@ -372,7 +372,37 @@ namespace PlexRequests.Api catch (Exception e) { Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAddedModel"); - return new RecentlyAddedModel(); + return new RecentlyAddedModelOld(); + } + } + + public PlexRecentlyAddedModel RecentlyAdded(string authToken, Uri plexFullHost, string sectionId) + { + var request = new RestRequest + { + Method = Method.GET, + Resource = "library/sections/{sectionId}/recentlyAdded" + }; + + request.AddUrlSegment("sectionId", sectionId); + AddHeaders(ref request, authToken, true); + AddLimitHeaders(ref request, 0, 25); + + try + { + var lib = RetryHandler.Execute(() => Api.ExecuteJson(request, plexFullHost), + (exception, timespan) => Log.Error(exception, "Exception when calling PlexRecentlyAddedModel for Plex, Retrying {0}", timespan), new[] { + TimeSpan.FromSeconds (5), + TimeSpan.FromSeconds(10), + TimeSpan.FromSeconds(30) + }); + + return lib; + } + catch (Exception e) + { + Log.Error(e, "There has been a API Exception when attempting to get the Plex RecentlyAddedModel"); + return new PlexRecentlyAddedModel(); } } diff --git a/PlexRequests.Api/RetryHandler.cs b/PlexRequests.Api/RetryHandler.cs index d3ffb1912..f385b6c5c 100644 --- a/PlexRequests.Api/RetryHandler.cs +++ b/PlexRequests.Api/RetryHandler.cs @@ -33,7 +33,7 @@ namespace PlexRequests.Api { public static class RetryHandler { - private static readonly TimeSpan[] DefaultRetryTime = { TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) }; + private static readonly TimeSpan[] DefaultRetryTime = { TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5) }; public static T Execute(Func action, TimeSpan[] timeSpan = null) { diff --git a/PlexRequests.Api/SonarrApi.cs b/PlexRequests.Api/SonarrApi.cs index 961f04ea2..10811f10f 100644 --- a/PlexRequests.Api/SonarrApi.cs +++ b/PlexRequests.Api/SonarrApi.cs @@ -117,8 +117,7 @@ namespace PlexRequests.Api try { var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] { - TimeSpan.FromSeconds (1), - TimeSpan.FromSeconds(2), + TimeSpan.FromSeconds (2) }); result = policy.Execute(() => Api.ExecuteJson(request, baseUrl)); @@ -186,8 +185,7 @@ namespace PlexRequests.Api try { var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling AddSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] { - TimeSpan.FromSeconds (1), - TimeSpan.FromSeconds(2), + TimeSpan.FromSeconds (2) }); result = policy.Execute(() => Api.ExecuteJson(request, baseUrl)); @@ -228,8 +226,8 @@ namespace PlexRequests.Api { var policy = RetryHandler.RetryAndWaitPolicy((exception, timespan) => Log.Error(exception, "Exception when calling GetSeries for Sonarr, Retrying {0}", timespan), new TimeSpan[] { TimeSpan.FromSeconds (5), - TimeSpan.FromSeconds(10), - TimeSpan.FromSeconds(30) + TimeSpan.FromSeconds(5), + TimeSpan.FromSeconds(5) }); return policy.Execute(() => Api.ExecuteJson>(request, baseUrl)); diff --git a/PlexRequests.Api/TheMovieDbApi.cs b/PlexRequests.Api/TheMovieDbApi.cs index 0e860d58a..eba5e115e 100644 --- a/PlexRequests.Api/TheMovieDbApi.cs +++ b/PlexRequests.Api/TheMovieDbApi.cs @@ -47,25 +47,18 @@ namespace PlexRequests.Api public async Task> SearchMovie(string searchTerm) { var results = await Client.SearchMovie(searchTerm); - return results.Results; - } - - [Obsolete("Should use TvMaze for TV")] - public async Task> SearchTv(string searchTerm) - { - var results = await Client.SearchTvShow(searchTerm); - return results.Results; + return results?.Results ?? new List(); } public async Task> GetCurrentPlayingMovies() { var movies = await Client.GetMovieList(MovieListType.NowPlaying); - return movies.Results; + return movies?.Results ?? new List(); } public async Task> GetUpcomingMovies() { var movies = await Client.GetMovieList(MovieListType.Upcoming); - return movies.Results; + return movies?.Results ?? new List(); } public async Task GetMovieInformation(int tmdbId) @@ -77,14 +70,7 @@ namespace PlexRequests.Api public async Task GetMovieInformation(string imdbId) { var movies = await Client.GetMovie(imdbId); - return movies; - } - - [Obsolete("Should use TvMaze for TV")] - public async Task GetTvShowInformation(int tmdbId) - { - var show = await Client.GetTvShow(tmdbId); - return show; + return movies ?? new Movie(); } } } diff --git a/PlexRequests.Core.Migration/MigrationRunner.cs b/PlexRequests.Core.Migration/MigrationRunner.cs index c8935031a..d53ab8d3d 100644 --- a/PlexRequests.Core.Migration/MigrationRunner.cs +++ b/PlexRequests.Core.Migration/MigrationRunner.cs @@ -22,42 +22,31 @@ namespace PlexRequests.Core.Migration public void MigrateToLatest() { var con = Db.DbConnection(); - var versions = GetMigrations().OrderBy(x => x.Key); - - var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault(); - if (dbVersion == null) - { - dbVersion = new TableCreation.VersionInfo { Version = 0 }; - } + var versions = GetMigrations(); + + var dbVersion = con.GetVersionInfo().OrderByDescending(x => x.Version).FirstOrDefault() ?? + new TableCreation.VersionInfo { Version = 0 }; foreach (var v in versions) { +#if !DEBUG if (v.Value.Version > dbVersion.Version) { - // Assuming only one constructor - var ctor = v.Key.GetConstructors().FirstOrDefault(); - var dependencies = new List(); +#endif + // Assuming only one constructor + var ctor = v.Key.GetConstructors().FirstOrDefault(); + var dependencies = ctor.GetParameters().Select(param => Kernel.Get(param.ParameterType)).ToList(); - foreach (var param in ctor.GetParameters()) - { - Console.WriteLine(string.Format( - "Param {0} is named {1} and is of type {2}", - param.Position, param.Name, param.ParameterType)); + var method = v.Key.GetMethod("Start"); + if (method != null) + { + var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null); + var parametersArray = new object[] { Db.DbConnection() }; - var dep = Kernel.Get(param.ParameterType); - dependencies.Add(dep); - } - - var method = v.Key.GetMethod("Start"); - if (method != null) - { - object result = null; - var classInstance = Activator.CreateInstance(v.Key, dependencies.Any() ? dependencies.ToArray() : null); - - var parametersArray = new object[] { Db.DbConnection() }; - - method.Invoke(classInstance, parametersArray); - } + method.Invoke(classInstance, parametersArray); } +#if !DEBUG + } +#endif } } diff --git a/PlexRequests.Core.Migration/Migrations/Version1100.cs b/PlexRequests.Core.Migration/Migrations/Version1100.cs new file mode 100644 index 000000000..7e23cbdbe --- /dev/null +++ b/PlexRequests.Core.Migration/Migrations/Version1100.cs @@ -0,0 +1,278 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Version1100.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.Data; +using NLog; +using System.Linq; +using PlexRequests.Api.Interfaces; +using PlexRequests.Core.SettingModels; +using PlexRequests.Core.Users; +using PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; +using PlexRequests.Store; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; + +namespace PlexRequests.Core.Migration.Migrations +{ + [Migration(11000, "v1.10.0.0")] + public class Version1100 : BaseMigration, IMigration + { + public Version1100(IUserRepository userRepo, IRequestService requestService, ISettingsService log, IPlexApi plexApi, ISettingsService plexService, + IPlexUserRepository plexusers, ISettingsService prSettings, ISettingsService umSettings, + ISettingsService sjs, IRepository usersToNotify) + { + UserRepo = userRepo; + RequestService = requestService; + Log = log; + PlexApi = plexApi; + PlexSettings = plexService; + PlexUsers = plexusers; + PlexRequestSettings = prSettings; + UserManagementSettings = umSettings; + ScheduledJobSettings = sjs; + UserNotifyRepo = usersToNotify; + } + public int Version => 11000; + private IUserRepository UserRepo { get; } + private IRequestService RequestService { get; } + private ISettingsService Log { get; } + private IPlexApi PlexApi { get; } + private ISettingsService PlexSettings { get; } + private IPlexUserRepository PlexUsers { get; } + private ISettingsService PlexRequestSettings { get; } + private ISettingsService UserManagementSettings { get; } + private ISettingsService ScheduledJobSettings { get; } + private IRepository UserNotifyRepo { get; } + + public void Start(IDbConnection con) + { + UpdateDb(con); + + // Update the current admin permissions set + + PopulateDefaultUserManagementSettings(); + UpdateAdmin(); + ResetLogLevel(); + UpdatePlexUsers(); + UpdateScheduledJobs(); + MigrateUserNotifications(); + + UpdateSchema(con, Version); + } + + private void MigrateUserNotifications() + { + var usersToNotify = UserNotifyRepo.GetAll(); + var plexUsers = PlexUsers.GetAll().ToList(); + var users = UserRepo.GetAll().ToList(); + + if (usersToNotify == null) + { + return; + } + + foreach (var u in usersToNotify) + { + var selectedPlexUser = plexUsers.FirstOrDefault(x => x.Username.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase)); + if (selectedPlexUser != null) + { + selectedPlexUser.Features += (int)Features.RequestAddedNotification; + PlexUsers.Update(selectedPlexUser); + } + + var selectedLocalUser = + users.FirstOrDefault(x => x.UserName.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase)); + if (selectedLocalUser != null) + { + selectedLocalUser.Features += (int)Features.RequestAddedNotification; + UserRepo.Update(selectedLocalUser); + } + + } + } + + private void UpdateScheduledJobs() + { + var settings = ScheduledJobSettings.GetSettings(); + + settings.PlexUserChecker = 24; + settings.PlexContentCacher = 60; + + ScheduledJobSettings.SaveSettings(settings); + } + + private void PopulateDefaultUserManagementSettings() + { + var plexRequestSettings = PlexRequestSettings.GetSettings(); + + UserManagementSettings.SaveSettings(new UserManagementSettings + { + AutoApproveMovies = !plexRequestSettings.RequireMovieApproval, + RequestTvShows = plexRequestSettings.SearchForTvShows, + RequestMusic = plexRequestSettings.SearchForMusic, + RequestMovies = plexRequestSettings.SearchForMovies, + AutoApproveMusic = !plexRequestSettings.RequireMusicApproval, + AutoApproveTvShows = !plexRequestSettings.RequireTvShowApproval + }); + } + + private void UpdatePlexUsers() + { + var settings = PlexSettings.GetSettings(); + if (string.IsNullOrEmpty(settings.PlexAuthToken)) + { + return; + } + var plexUsers = PlexApi.GetUsers(settings.PlexAuthToken); + + if (plexUsers?.User == null) + { + return; + } + + var prSettings = PlexRequestSettings.GetSettings(); + + var dbUsers = PlexUsers.GetAll().ToList(); + foreach (var user in plexUsers.User) + { + if (dbUsers.FirstOrDefault(x => x.PlexUserId == user.Id) != null) + { + continue; + } + + int permissions = 0; + if (prSettings.SearchForMovies) + { + permissions = (int)Permissions.RequestMovie; + } + if (prSettings.SearchForTvShows) + { + permissions += (int)Permissions.RequestTvShow; + } + if (prSettings.SearchForMusic) + { + permissions += (int)Permissions.RequestMusic; + } + if (!prSettings.RequireMovieApproval) + { + permissions += (int)Permissions.AutoApproveMovie; + } + if (!prSettings.RequireTvShowApproval) + { + permissions += (int)Permissions.AutoApproveTv; + } + if (!prSettings.RequireMusicApproval) + { + permissions += (int)Permissions.AutoApproveAlbum; + } + + // Add report Issues + + permissions += (int)Permissions.ReportIssue; + + var m = new PlexUsers + { + PlexUserId = user.Id, + Permissions = permissions, + Features = 0, + UserAlias = string.Empty, + EmailAddress = user.Email, + Username = user.Username, + LoginId = Guid.NewGuid().ToString() + }; + + PlexUsers.Insert(m); + } + + } + + private void ResetLogLevel() + { + var logSettings = Log.GetSettings(); + logSettings.Level = LogLevel.Error.Ordinal; + Log.SaveSettings(logSettings); + + LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level)); + } + + private void UpdateDb(IDbConnection con) + { + // Create the two new columns + con.AlterTable("Users", "ADD", "Permissions", true, "INTEGER"); + con.AlterTable("Users", "ADD", "Features", true, "INTEGER"); + + con.AlterTable("PlexUsers", "ADD", "Permissions", true, "INTEGER"); + con.AlterTable("PlexUsers", "ADD", "Features", true, "INTEGER"); + con.AlterTable("PlexUsers", "ADD", "Username", true, "VARCHAR(100)"); + con.AlterTable("PlexUsers", "ADD", "EmailAddress", true, "VARCHAR(100)"); + con.AlterTable("PlexUsers", "ADD", "LoginId", true, "VARCHAR(100)"); + + //https://image.tmdb.org/t/p/w150/https://image.tmdb.org/t/p/w150//aqhAqttDq7zgsTaBHtCD8wmTk6k.jpg + + // UI = https://image.tmdb.org/t/p/w150/{{posterPath}} + // Update old invalid posters + var allRequests = RequestService.GetAll(); + if (allRequests == null) + { + return; + } + var requestedModels = allRequests as RequestedModel[] ?? allRequests.ToArray(); + foreach (var req in requestedModels) + { + if (req.PosterPath.Contains("https://image.tmdb.org/t/p/w150/")) + { + var newImg = req.PosterPath.Replace("https://image.tmdb.org/t/p/w150/", string.Empty); + req.PosterPath = newImg; + } + } + RequestService.BatchUpdate(requestedModels); + } + + private void UpdateAdmin() + { + var users = UserRepo.GetAll().ToList(); + + foreach (var user in users) + { + user.Permissions = (int) + (Permissions.Administrator + | Permissions.ReportIssue + | Permissions.RequestMusic + | Permissions.RequestTvShow + | Permissions.RequestMovie + | Permissions.AutoApproveAlbum + | Permissions.AutoApproveMovie + | Permissions.AutoApproveTv); + } + + UserRepo.UpdateAll(users); + } + } +} diff --git a/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj b/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj index a92ae74d5..0b582b68b 100644 --- a/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj +++ b/PlexRequests.Core.Migration/PlexRequests.Core.Migration.csproj @@ -45,6 +45,10 @@ ..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll + + ..\packages\NLog.4.3.11\lib\net45\NLog.dll + True + ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll True @@ -65,14 +69,27 @@ + + + {95834072-A675-415D-AA8F-877C91623810} + PlexRequests.Api.Interfaces + + + {CB37A5F8-6DFC-4554-99D3-A42B502E4591} + PlexRequests.Api.Models + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} PlexRequests.Core + + {1252336D-42A3-482A-804C-836E60173DFA} + PlexRequests.Helpers + {92433867-2B7B-477B-A566-96C382427525} PlexRequests.Store diff --git a/PlexRequests.Core.Migration/packages.config b/PlexRequests.Core.Migration/packages.config index dfdfc5ae9..6d547be19 100644 --- a/PlexRequests.Core.Migration/packages.config +++ b/PlexRequests.Core.Migration/packages.config @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj b/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj index 0e6f4d3e0..5002e3c21 100644 --- a/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj +++ b/PlexRequests.Core.Tests/PlexRequests.Core.Tests.csproj @@ -60,7 +60,6 @@ - diff --git a/PlexRequests.Core/CacheKeys.cs b/PlexRequests.Core/CacheKeys.cs index 0f718f1f1..e40dd9efd 100644 --- a/PlexRequests.Core/CacheKeys.cs +++ b/PlexRequests.Core/CacheKeys.cs @@ -30,7 +30,7 @@ namespace PlexRequests.Core { public struct TimeFrameMinutes { - public const int SchedulerCaching = 60; + public const int SchedulerCaching = 120; } public const string PlexLibaries = nameof(PlexLibaries); diff --git a/PlexRequests.UI/Helpers/HeadphonesSender.cs b/PlexRequests.Core/HeadphonesSender.cs similarity index 99% rename from PlexRequests.UI/Helpers/HeadphonesSender.cs rename to PlexRequests.Core/HeadphonesSender.cs index 29c428612..19e438be2 100644 --- a/PlexRequests.UI/Helpers/HeadphonesSender.cs +++ b/PlexRequests.Core/HeadphonesSender.cs @@ -24,18 +24,16 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion + using System.Linq; using System.Threading; using System.Threading.Tasks; - using NLog; - using PlexRequests.Api.Interfaces; -using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Store; -namespace PlexRequests.UI.Helpers +namespace PlexRequests.Core { public class HeadphonesSender { diff --git a/PlexRequests.Core/ISecurityExtensions.cs b/PlexRequests.Core/ISecurityExtensions.cs new file mode 100644 index 000000000..10db3ccfe --- /dev/null +++ b/PlexRequests.Core/ISecurityExtensions.cs @@ -0,0 +1,34 @@ +using System; +using Nancy; +using Nancy.Security; +using PlexRequests.Helpers.Permissions; + +namespace PlexRequests.Core +{ + public interface ISecurityExtensions + { + Response AdminLoginRedirect(Permissions perm, NancyContext context); + Response AdminLoginRedirect(NancyContext context, params Permissions[] perm); + bool DoesNotHavePermissions(Permissions perm, IUserIdentity currentUser); + + Response HasAnyPermissionsRedirect(NancyContext context, string routeName, HttpStatusCode code, + params Permissions[] perm); + bool DoesNotHavePermissions(int perm, IUserIdentity currentUser); + Func ForbiddenIfNot(Func test); + bool HasAnyPermissions(IUserIdentity user, params Permissions[] perm); + bool HasPermissions(IUserIdentity user, Permissions perm); + Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code); + Func HttpStatusCodeIfNot(HttpStatusCode statusCode, Func test); + bool IsLoggedIn(NancyContext context); + bool IsNormalUser(IUserIdentity user); + bool IsPlexUser(IUserIdentity user); + bool HasPermissions(string userName, Permissions perm); + + /// + /// Gets the username this could be the alias! We should always use this method when getting the username + /// + /// The username. + /// null if we cannot find a user + string GetUsername(string username); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/IStatusChecker.cs b/PlexRequests.Core/IStatusChecker.cs new file mode 100644 index 000000000..eec365b68 --- /dev/null +++ b/PlexRequests.Core/IStatusChecker.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Octokit; +using PlexRequests.Core.Models; + +namespace PlexRequests.Core +{ + public interface IStatusChecker + { + Task GetStatus(); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Models/NotificationType.cs b/PlexRequests.Core/Models/NotificationType.cs index a01d153dd..eace7b018 100644 --- a/PlexRequests.Core/Models/NotificationType.cs +++ b/PlexRequests.Core/Models/NotificationType.cs @@ -34,6 +34,7 @@ namespace PlexRequests.Core.Models RequestApproved, AdminNote, Test, - + RequestDeclined, + ItemAddedToFaultQueue } } diff --git a/PlexRequests.Core/Models/StatusModel.cs b/PlexRequests.Core/Models/StatusModel.cs index 9cbcf644d..ee55ab373 100644 --- a/PlexRequests.Core/Models/StatusModel.cs +++ b/PlexRequests.Core/Models/StatusModel.cs @@ -28,7 +28,8 @@ namespace PlexRequests.Core.Models { public class StatusModel { - public string Version { get; set; } + public string CurrentVersion { get; set; } + public string NewVersion { get; set; } public bool UpdateAvailable { get; set; } public string UpdateUri { get; set; } public string DownloadUri { get; set; } diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj index 7fdb4b375..f2f79067b 100644 --- a/PlexRequests.Core/PlexRequests.Core.csproj +++ b/PlexRequests.Core/PlexRequests.Core.csproj @@ -39,9 +39,17 @@ ..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll True + + ..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll + True + ..\Assemblies\Mono.Data.Sqlite.dll + + ..\packages\Nancy.Linker.0.3.1\lib\net40-Client\Nancy.Linker.dll + True + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True @@ -54,7 +62,12 @@ ..\packages\Quartz.2.3.3\lib\net40\Quartz.dll True + + ..\packages\RestSharp.105.2.3\lib\net45\RestSharp.dll + True + + @@ -81,7 +94,10 @@ + + + @@ -99,6 +115,9 @@ + + + @@ -119,12 +138,22 @@ + + - + + + + + + + + + diff --git a/PlexRequests.Core/Queue/ITransientFaultQueue.cs b/PlexRequests.Core/Queue/ITransientFaultQueue.cs new file mode 100644 index 000000000..ee5ea05d0 --- /dev/null +++ b/PlexRequests.Core/Queue/ITransientFaultQueue.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using PlexRequests.Store; +using PlexRequests.Store.Models; + +namespace PlexRequests.Core.Queue +{ + public interface ITransientFaultQueue + { + void Dequeue(); + Task DequeueAsync(); + IEnumerable GetQueue(); + Task> GetQueueAsync(); + void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType); + Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType, string message = null); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Queue/TransientFaultQueue.cs b/PlexRequests.Core/Queue/TransientFaultQueue.cs new file mode 100644 index 000000000..2be15bfa5 --- /dev/null +++ b/PlexRequests.Core/Queue/TransientFaultQueue.cs @@ -0,0 +1,130 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: TransientFaultQueue.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.Linq; +using System.Threading.Tasks; +using Dapper; +using PlexRequests.Helpers; +using PlexRequests.Store; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; + +namespace PlexRequests.Core.Queue +{ + public class TransientFaultQueue : ITransientFaultQueue + { + public TransientFaultQueue(IRepository queue) + { + RequestQueue = queue; + } + + private IRepository RequestQueue { get; } + + + public void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType) + { + //Ensure there is not a duplicate queued item + var existingItem = RequestQueue.Custom( + connection => + { + connection.Open(); + var result = connection.Query("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id }); + + return result; + }).FirstOrDefault(); + + if (existingItem != null) + { + // It's already in the queue + return; + } + + var queue = new RequestQueue + { + Type = type, + Content = ByteConverterHelper.ReturnBytes(request), + PrimaryIdentifier = id, + FaultType = faultType + }; + RequestQueue.Insert(queue); + } + + public async Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType, string description = null) + { + //Ensure there is not a duplicate queued item + var existingItem = await RequestQueue.CustomAsync(async connection => + { + connection.Open(); + var result = await connection.QueryAsync("select * from RequestFaultQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id }); + + return result; + }); + + if (existingItem.FirstOrDefault() != null) + { + // It's already in the queue + return; + } + + var queue = new RequestQueue + { + Type = type, + Content = ByteConverterHelper.ReturnBytes(request), + PrimaryIdentifier = id, + FaultType = faultType, + Message = description ?? string.Empty + }; + await RequestQueue.InsertAsync(queue); + } + + public IEnumerable GetQueue() + { + var items = RequestQueue.GetAll(); + + + return items; + } + + public async Task> GetQueueAsync() + { + var items = RequestQueue.GetAllAsync(); + + return await items; + } + + public void Dequeue() + { + RequestQueue.DeleteAll("RequestQueue"); + } + + public async Task DequeueAsync() + { + await RequestQueue.DeleteAllAsync("RequestQueue"); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/SecurityExtensions.cs b/PlexRequests.Core/SecurityExtensions.cs new file mode 100644 index 000000000..38972cf22 --- /dev/null +++ b/PlexRequests.Core/SecurityExtensions.cs @@ -0,0 +1,302 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SecurityExtensions.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 Nancy; +using Nancy.Linker; +using Nancy.Responses; +using Nancy.Security; +using PlexRequests.Core.Models; +using PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; +using PlexRequests.Store.Repository; +using ISecurityExtensions = PlexRequests.Core.ISecurityExtensions; + +namespace PlexRequests.Core +{ + public class SecurityExtensions : ISecurityExtensions + { + public SecurityExtensions(IUserRepository userRepository, IResourceLinker linker, IPlexUserRepository plexUsers) + { + UserRepository = userRepository; + Linker = linker; + PlexUsers = plexUsers; + } + + private IUserRepository UserRepository { get; } + private IResourceLinker Linker { get; } + private IPlexUserRepository PlexUsers { get; } + + public bool IsLoggedIn(NancyContext context) + { + var userName = context.Request.Session[SessionKeys.UsernameKey]; + var realUser = false; + var plexUser = userName != null; + + if (context.CurrentUser?.IsAuthenticated() ?? false) + { + realUser = true; + } + + return realUser || plexUser; + } + + public bool IsPlexUser(IUserIdentity user) + { + if (user == null) + { + return false; + } + var plexUser = PlexUsers.GetUserByUsername(user.UserName); + return plexUser != null; + } + + public bool IsNormalUser(IUserIdentity user) + { + if (user == null) + { + return false; + } + var dbUser = UserRepository.GetUserByUsername(user.UserName); + + return dbUser != null; + } + + /// + /// Gets the username this could be the alias! We should always use this method when getting the username + /// + /// The username. + /// null if we cannot find a user + public string GetUsername(string username) + { + var plexUser = PlexUsers.GetUserByUsername(username); + if (plexUser != null) + { + if (!string.IsNullOrEmpty(plexUser.UserAlias)) + { + return plexUser.UserAlias; + } + else + { + return plexUser.Username; + } + } + + var dbUser = UserRepository.GetUserByUsername(username); + if (dbUser != null) + { + var userProps = ByteConverterHelper.ReturnObject(dbUser.UserProperties); + if (!string.IsNullOrEmpty(userProps.UserAlias)) + { + return userProps.UserAlias; + } + else + { + return dbUser.UserName; + } + } + return null; + } + + + /// + /// Creates a hook to be used in a pipeline before a route handler to ensure + /// that the request was made by an authenticated user does not have the claims. + /// + /// Claims the authenticated user needs to have + /// Hook that returns an Unauthorized response if the user is not + /// authenticated or does have the claims, null otherwise + private Func DoesNotHavePermissions(int perm) + { + return ForbiddenIfNot(ctx => + { + var permissions = GetPermissions(ctx.CurrentUser); + var result = permissions.HasFlag((Permissions)perm); + return !result; + }); + } + + public bool DoesNotHavePermissions(int perm, IUserIdentity currentUser) + { + return DoesNotHavePermissions((Permissions)perm, currentUser); + } + + public bool DoesNotHavePermissions(Permissions perm, IUserIdentity currentUser) + { + var permissions = GetPermissions(currentUser); + var result = permissions.HasFlag(perm); + return !result; + } + + public bool HasPermissions(IUserIdentity user, Permissions perm) + { + var permissions = GetPermissions(user); + return permissions.HasFlag(perm); + } + public bool HasPermissions(string userName, Permissions perm) + { + var permissions = GetPermissions(userName); + return permissions.HasFlag(perm); + } + + public bool HasAnyPermissions(IUserIdentity user, params Permissions[] perm) + { + var permissions = GetPermissions(user); + + foreach (var p in perm) + { + var result = permissions.HasFlag(p); + if (result) + { + return true; + } + } + + return false; + } + + public Response HasPermissionsRedirect(Permissions perm, NancyContext context, string routeName, HttpStatusCode code) + { + var url = Linker.BuildRelativeUri(context, routeName); + + var response = ForbiddenIfNot(ctx => + { + var permissions = GetPermissions(ctx.CurrentUser); + var result = permissions.HasFlag(perm); + return result; + }); + + var r = response(context); + return r.StatusCode == code + ? new RedirectResponse($"{url.ToString()}?redirect={context.Request.Path}") + : null; + } + public Response HasAnyPermissionsRedirect(NancyContext context, string routeName, HttpStatusCode code, params Permissions[] perm) + { + var url = Linker.BuildRelativeUri(context, routeName); + + var response = ForbiddenIfNot(ctx => + { + var permissions = GetPermissions(ctx.CurrentUser); + var hasPermission = false; + foreach (var p in perm) + { + var result = permissions.HasFlag(p); + if (result) + { + hasPermission = true; + } + } + return hasPermission; + }); + + var r = response(context); + return r.StatusCode == code + ? new RedirectResponse(url.ToString()) + : null; + } + + + public Response AdminLoginRedirect(Permissions perm, NancyContext context) + { + // This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code). + return HasPermissionsRedirect(perm, context, "LocalLogin", HttpStatusCode.Forbidden); + } + + public Response AdminLoginRedirect(NancyContext context, params Permissions[] perm) + { + // This will redirect us to the Login Page if we don't have the correct permission passed in (e.g. Admin with Http 403 status code). + return HasAnyPermissionsRedirect(context, "LocalLogin", HttpStatusCode.Forbidden, perm); + } + + // BELOW IS A COPY FROM THE SecurityHooks CLASS! + + /// + /// Creates a hook to be used in a pipeline before a route handler to ensure that + /// the request satisfies a specific test. + /// + /// Test that must return true for the request to continue + /// Hook that returns an Forbidden response if the test fails, null otherwise + public Func ForbiddenIfNot(Func test) + { + return HttpStatusCodeIfNot(HttpStatusCode.Forbidden, test); + } + + /// + /// Creates a hook to be used in a pipeline before a route handler to ensure that + /// the request satisfies a specific test. + /// + /// HttpStatusCode to use for the response + /// Test that must return true for the request to continue + /// Hook that returns a response with a specific HttpStatusCode if the test fails, null otherwise + public Func HttpStatusCodeIfNot(HttpStatusCode statusCode, Func test) + { + return ctx => + { + Response response = new Response + { + StatusCode = HttpStatusCode.OK + }; + if (!test(ctx)) + { + response = new Response + { + StatusCode = statusCode + }; + } + return response; + }; + } + + private Permissions GetPermissions(IUserIdentity user) + { + return GetPermissions(user?.UserName); + } + + private Permissions GetPermissions(string userName) + { + if (string.IsNullOrEmpty(userName)) return 0; + + var dbUser = UserRepository.GetUserByUsername(userName); + + if (dbUser != null) + { + var permissions = (Permissions)dbUser.Permissions; + return permissions; + } + + var plexUser = PlexUsers.GetUserByUsername(userName); + if (plexUser != null) + { + var permissions = (Permissions)plexUser.Permissions; + return permissions; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs index 5c7e6ddf5..f4aec5a96 100644 --- a/PlexRequests.Core/SettingModels/PlexRequestSettings.cs +++ b/PlexRequests.Core/SettingModels/PlexRequestSettings.cs @@ -44,15 +44,20 @@ namespace PlexRequests.Core.SettingModels public bool SearchForMovies { get; set; } public bool SearchForTvShows { get; set; } public bool SearchForMusic { get; set; } + [Obsolete("Use the user management settings")] public bool RequireMovieApproval { get; set; } + [Obsolete("Use the user management settings")] public bool RequireTvShowApproval { get; set; } + [Obsolete("Use the user management settings")] public bool RequireMusicApproval { get; set; } + + [Obsolete("Use the user management settings")] public bool UsersCanViewOnlyOwnRequests { get; set; } + [Obsolete("Use the user management settings")] public bool UsersCanViewOnlyOwnIssues { get; set; } public int MovieWeeklyRequestLimit { get; set; } public int TvWeeklyRequestLimit { get; set; } public int AlbumWeeklyRequestLimit { get; set; } - public string NoApprovalUsers { get; set; } public bool CollectAnalyticData { get; set; } public bool IgnoreNotifyForAutoApprovedRequests { get; set; } public bool Wizard { get; set; } @@ -69,26 +74,5 @@ namespace PlexRequests.Core.SettingModels public string ThemeName { get; set; } public string ApiKey { get; set; } - - [JsonIgnore] - public List ApprovalWhiteList - { - get - { - var users = new List(); - if (string.IsNullOrEmpty(NoApprovalUsers)) - { - return users; - } - - var splitUsers = NoApprovalUsers.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var user in splitUsers) - { - if (!string.IsNullOrWhiteSpace(user)) - users.Add(user.Trim()); - } - return users; - } - } } } diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index 234dc5774..d0e6b05e1 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -42,5 +42,8 @@ namespace PlexRequests.Core.SettingModels [Obsolete("We use the CRON job now")] public int RecentlyAdded { get; set; } public string RecentlyAddedCron { get; set; } + public int FaultQueueHandler { get; set; } + public int PlexContentCacher { get; set; } + public int PlexUserChecker { get; set; } } } \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/SystemSettings.cs b/PlexRequests.Core/SettingModels/SystemSettings.cs new file mode 100644 index 000000000..8d387f9e7 --- /dev/null +++ b/PlexRequests.Core/SettingModels/SystemSettings.cs @@ -0,0 +1,63 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SystemSettings.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.ComponentModel; +using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; +using PlexRequests.Core.Models; + +namespace PlexRequests.Core.SettingModels +{ + public class SystemSettings : Settings + { + public Branches Branch { get; set; } + public string Version { get; set; } + public StatusModel Status { get; set; } + [JsonIgnore] + public List BranchDropdown { get; set; } + } + + public class BranchDropdown + { + public bool Selected { get; set; } + public string Name { get; set; } + public Branches Value { get; set; } + } + + public enum Branches + { + [Display(Name = "Stable")] + Stable, + + [Display(Name = "Early Access Preview")] + EarlyAccessPreview, + + [Display(Name = "Development")] + Dev, + } +} \ No newline at end of file diff --git a/PlexRequests.Core/SettingModels/UserManagementSettings.cs b/PlexRequests.Core/SettingModels/UserManagementSettings.cs new file mode 100644 index 000000000..768a2167c --- /dev/null +++ b/PlexRequests.Core/SettingModels/UserManagementSettings.cs @@ -0,0 +1,45 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserManagementSettings.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.Core.SettingModels +{ + public class UserManagementSettings : Settings + { + public bool RequestMovies { get; set; } + public bool RequestTvShows { get; set; } + public bool RequestMusic { get; set; } + public bool AutoApproveMovies { get; set; } + public bool AutoApproveTvShows { get; set; } + public bool AutoApproveMusic { get; set; } + public bool ReportIssues { get; set; } + public bool UsersCanViewOnlyOwnRequests { get; set; } + public bool UsersCanViewOnlyOwnIssues { get; set; } + + // Features + public bool RecentlyAddedNotification { get; set; } + public bool RecentlyAddedNewsletter { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Setup.cs b/PlexRequests.Core/Setup.cs index 56f3ab95e..e43809a16 100644 --- a/PlexRequests.Core/Setup.cs +++ b/PlexRequests.Core/Setup.cs @@ -50,7 +50,7 @@ namespace PlexRequests.Core Db = new DbConfiguration(new SqliteFactory()); var created = Db.CheckDb(); TableCreation.CreateTables(Db.DbConnection()); - + if (created) { CreateDefaultSettingsPage(urlBase); @@ -61,65 +61,35 @@ namespace PlexRequests.Core TableCreation.Vacuum(Db.DbConnection()); } - - // The below code is obsolete, we should use PlexRequests.Core.Migrations.MigrationRunner - var version = CheckSchema(); - if (version > 0) - { - if (version > 1899 && version <= 1900) - { - MigrateToVersion1900(); - } - - if(version > 1899 && version <= 1910) - { - MigrateToVersion1910(); - } - } + // Add the new 'running' item into the scheduled jobs so we can check if the cachers are running + Db.DbConnection().AlterTable("ScheduledJobs", "ADD", "Running", true, "INTEGER"); return Db.DbConnection().ConnectionString; } - public static string ConnectionString => Db.DbConnection().ConnectionString; - - - private int CheckSchema() - { - var productVersion = AssemblyHelper.GetProductVersion(); - var trimStatus = new Regex("[^0-9]", RegexOptions.Compiled).Replace(productVersion, string.Empty).PadRight(4, '0'); - var version = int.Parse(trimStatus); - - var connection = Db.DbConnection(); - var schema = connection.GetSchemaVersion(); - if (schema == null) - { - connection.CreateSchema(version); // Set the default. - schema = connection.GetSchemaVersion(); - } - if (version > schema.SchemaVersion) - { - Db.DbConnection().UpdateSchemaVersion(version); - schema = connection.GetSchemaVersion(); - } - version = schema.SchemaVersion; - - return version; - } - private void CreateDefaultSettingsPage(string baseUrl) { + var defaultUserSettings = new UserManagementSettings + { + RequestMovies = true, + RequestTvShows = true, + ReportIssues = true, + + }; var defaultSettings = new PlexRequestSettings { - RequireTvShowApproval = true, - RequireMovieApproval = true, SearchForMovies = true, SearchForTvShows = true, BaseUrl = baseUrl ?? string.Empty, CollectAnalyticData = true, }; - var s = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); + var ctor = new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider()); + var s = new SettingsServiceV2(ctor); s.SaveSettings(defaultSettings); + var userSettings = new SettingsServiceV2(ctor); + userSettings.SaveSettings(defaultUserSettings); + var cron = (Quartz.Impl.Triggers.CronTriggerImpl)CronScheduleBuilder.WeeklyOnDayAndHourAndMinute(DayOfWeek.Friday, 7, 0).Build(); var scheduled = new ScheduledJobsSettings @@ -148,7 +118,6 @@ namespace PlexRequests.Core Task.Run(() => { CacheSonarrQualityProfiles(mc); }); Task.Run(() => { CacheCouchPotatoQualityProfiles(mc); }); // we don't need to cache sickrage profiles, those are static - // TODO: cache headphones profiles? } catch (Exception) { @@ -156,12 +125,12 @@ namespace PlexRequests.Core } } - private void CacheSonarrQualityProfiles(MemoryCacheProvider cacheProvider) + private void CacheSonarrQualityProfiles(ICacheProvider cacheProvider) { try { Log.Info("Executing GetSettings call to Sonarr for quality profiles"); - var sonarrSettingsService = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); + var sonarrSettingsService = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider)); var sonarrSettings = sonarrSettingsService.GetSettings(); if (sonarrSettings.Enabled) { @@ -178,12 +147,12 @@ namespace PlexRequests.Core } } - private void CacheCouchPotatoQualityProfiles(MemoryCacheProvider cacheProvider) + private void CacheCouchPotatoQualityProfiles(ICacheProvider cacheProvider) { try { Log.Info("Executing GetSettings call to CouchPotato for quality profiles"); - var cpSettingsService = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), new MemoryCacheProvider())); + var cpSettingsService = new SettingsServiceV2(new SettingsJsonRepository(new DbConfiguration(new SqliteFactory()), cacheProvider)); var cpSettings = cpSettingsService.GetSettings(); if (cpSettings.Enabled) { @@ -199,102 +168,5 @@ namespace PlexRequests.Core Log.Error(ex, "Failed to cache CouchPotato quality profiles!"); } } - - - /// - /// Migrates to version 1.9. - /// Move the Plex auth token to the new field. - /// Reconfigure the log level - /// Set the wizard flag to true if we already have settings - /// - public void MigrateToVersion1900() - { - // Need to change the Plex Token location - var authSettings = new SettingsServiceV2(new SettingsJsonRepository(Db, new MemoryCacheProvider())); - var auth = authSettings.GetSettings(); - var plexSettings = new SettingsServiceV2(new SettingsJsonRepository(Db, new MemoryCacheProvider())); - - if (auth != null) - { - //If we have an authToken we do not need to go through the setup - if (!string.IsNullOrEmpty(auth.OldPlexAuthToken)) - { - var prServuce = new SettingsServiceV2(new SettingsJsonRepository(Db, new MemoryCacheProvider())); - var settings = prServuce.GetSettings(); - settings.Wizard = true; - prServuce.SaveSettings(settings); - } - - // Clear out the old token and save it to the new field - var currentSettings = plexSettings.GetSettings(); - if (!string.IsNullOrEmpty(auth.OldPlexAuthToken)) - { - currentSettings.PlexAuthToken = auth.OldPlexAuthToken; - plexSettings.SaveSettings(currentSettings); - - // Clear out the old value - auth.OldPlexAuthToken = string.Empty; - authSettings.SaveSettings(auth); - } - - } - - - // Set the log level - try - { - var settingsService = new SettingsServiceV2(new SettingsJsonRepository(Db, new MemoryCacheProvider())); - var logSettings = settingsService.GetSettings(); - logSettings.Level = LogLevel.Error.Ordinal; - settingsService.SaveSettings(logSettings); - - LoggingHelper.ReconfigureLogLevel(LogLevel.FromOrdinal(logSettings.Level)); - } - catch (Exception e) - { - Log.Error(e); - } - - - // Enable analytics; - try - { - - var prSettings = new SettingsServiceV2(new SettingsJsonRepository(Db, new MemoryCacheProvider())); - var settings = prSettings.GetSettings(); - settings.CollectAnalyticData = true; - var updated = prSettings.SaveSettings(settings); - - } - catch (Exception e) - { - Log.Error(e); - } - } - - /// - /// Migrates to version1910. - /// - public void MigrateToVersion1910() - { - try - { - // Get the new machine Identifier - var settings = new SettingsServiceV2(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/StatusChecker.cs b/PlexRequests.Core/StatusChecker.cs deleted file mode 100644 index bf9f0485e..000000000 --- a/PlexRequests.Core/StatusChecker.cs +++ /dev/null @@ -1,83 +0,0 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: StatusChecker.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 System.Threading.Tasks; - -using Octokit; - -using PlexRequests.Core.Models; -using PlexRequests.Helpers; - -namespace PlexRequests.Core -{ - public class StatusChecker - { - public StatusChecker() - { - Git = new GitHubClient(new ProductHeaderValue("PlexRequests-StatusChecker")); - } - private IGitHubClient Git { get; } - private const string Owner = "tidusjar"; - private const string RepoName = "PlexRequests.Net"; - - public async Task GetLatestRelease() - { - var releases = await Git.Repository.Release.GetAll(Owner, RepoName); - return releases.FirstOrDefault(); - } - - public async Task GetStatus() - { - var assemblyVersion = AssemblyHelper.GetProductVersion(); - var model = new StatusModel - { - Version = assemblyVersion, - }; - - var latestRelease = await GetLatestRelease(); - if (latestRelease == null) - { - return new StatusModel { Version = "Unknown" }; - } - var latestVersionArray = latestRelease.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries); - var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty; - - if (!latestVersion.Equals(assemblyVersion, StringComparison.InvariantCultureIgnoreCase)) - { - model.UpdateAvailable = true; - model.UpdateUri = latestRelease.HtmlUrl; - } - - model.ReleaseNotes = latestRelease.Body; - model.DownloadUri = latestRelease.Assets[0].BrowserDownloadUrl; - model.ReleaseTitle = latestRelease.Name; - - return model; - } - } -} \ No newline at end of file diff --git a/PlexRequests.Core/StatusChecker/AppveyorArtifactResult.cs b/PlexRequests.Core/StatusChecker/AppveyorArtifactResult.cs new file mode 100644 index 000000000..9e67262d6 --- /dev/null +++ b/PlexRequests.Core/StatusChecker/AppveyorArtifactResult.cs @@ -0,0 +1,35 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: AppveyorArtifactResult.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.Core.StatusChecker +{ + public class AppveyorArtifactResult + { + public string fileName { get; set; } + public string type { get; set; } + public int size { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/StatusChecker/AppveyorBranchResult.cs b/PlexRequests.Core/StatusChecker/AppveyorBranchResult.cs new file mode 100644 index 000000000..766e5a804 --- /dev/null +++ b/PlexRequests.Core/StatusChecker/AppveyorBranchResult.cs @@ -0,0 +1,138 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: AppveyorBranchResult.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; + +namespace PlexRequests.Core.StatusChecker +{ + public class NuGetFeed + { + public string id { get; set; } + public string name { get; set; } + public bool publishingEnabled { get; set; } + public string created { get; set; } + } + + public class AccessRightDefinition + { + public string name { get; set; } + public string description { get; set; } + } + + public class AccessRight + { + public string name { get; set; } + public bool allowed { get; set; } + } + + public class RoleAce + { + public int roleId { get; set; } + public string name { get; set; } + public bool isAdmin { get; set; } + public List accessRights { get; set; } + } + + public class SecurityDescriptor + { + public List accessRightDefinitions { get; set; } + public List roleAces { get; set; } + } + + public class Project + { + public int projectId { get; set; } + public int accountId { get; set; } + public string accountName { get; set; } + public List builds { get; set; } + public string name { get; set; } + public string slug { get; set; } + public string repositoryType { get; set; } + public string repositoryScm { get; set; } + public string repositoryName { get; set; } + public string repositoryBranch { get; set; } + public bool isPrivate { get; set; } + public bool skipBranchesWithoutAppveyorYml { get; set; } + public bool enableSecureVariablesInPullRequests { get; set; } + public bool enableSecureVariablesInPullRequestsFromSameRepo { get; set; } + public bool enableDeploymentInPullRequests { get; set; } + public bool rollingBuilds { get; set; } + public bool alwaysBuildClosedPullRequests { get; set; } + public string tags { get; set; } + public NuGetFeed nuGetFeed { get; set; } + public SecurityDescriptor securityDescriptor { get; set; } + public string created { get; set; } + public string updated { get; set; } + } + + public class Job + { + public string jobId { get; set; } + public string name { get; set; } + public bool allowFailure { get; set; } + public int messagesCount { get; set; } + public int compilationMessagesCount { get; set; } + public int compilationErrorsCount { get; set; } + public int compilationWarningsCount { get; set; } + public int testsCount { get; set; } + public int passedTestsCount { get; set; } + public int failedTestsCount { get; set; } + public int artifactsCount { get; set; } + public string status { get; set; } + public string started { get; set; } + public string finished { get; set; } + public string created { get; set; } + public string updated { get; set; } + } + + public class Build + { + public int buildId { get; set; } + public List jobs { get; set; } + public int buildNumber { get; set; } + public string version { get; set; } + public string message { get; set; } + public string branch { get; set; } + public bool isTag { get; set; } + public string commitId { get; set; } + public string authorName { get; set; } + public string committerName { get; set; } + public string committed { get; set; } + public List messages { get; set; } + public string status { get; set; } + public string started { get; set; } + public string finished { get; set; } + public string created { get; set; } + public string updated { get; set; } + } + + public class AppveyorBranchResult + { + public Project project { get; set; } + public Build build { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/StatusChecker/StatusChecker.cs b/PlexRequests.Core/StatusChecker/StatusChecker.cs new file mode 100644 index 000000000..94e33b6b6 --- /dev/null +++ b/PlexRequests.Core/StatusChecker/StatusChecker.cs @@ -0,0 +1,183 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: StatusChecker.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 Octokit; +using PlexRequests.Api; +using PlexRequests.Core.Models; +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers; +using RestSharp; + +namespace PlexRequests.Core.StatusChecker +{ + public class StatusChecker : IStatusChecker + { + public StatusChecker(ISettingsService ss) + { + SystemSettings = ss; + Git = new GitHubClient(new ProductHeaderValue("PlexRequests-StatusChecker")); + } + + private ISettingsService SystemSettings { get; } + + private IGitHubClient Git { get; } + private const string Owner = "tidusjar"; + private const string RepoName = "PlexRequests.Net"; + private const string AppveyorApiUrl = "https://ci.appveyor.com/api"; + + private const string Api = + "48Ku58C0794nBrXra8IxWav+dc6NqgkRw+PZB3/bQwbt/D0IrnJQkgtjzo0bd6nkooLMKsC8M+Ab7jyBO+ROjY14VRuxffpDopX9r0iG/fjBl6mZVvqkm+VTDNstDtzp"; + + public async Task GetStatus() + { + var settings = await SystemSettings.GetSettingsAsync(); + var stable = settings.Branch == Branches.Stable; + + if (!stable) + { + // Early Access Preview Releases + return GetAppveyorRelease(settings.Branch); + } + + // Stable releases + return await GetLatestGithubRelease(); + } + + private async Task GetLatestGithubRelease() + { + var assemblyVersion = AssemblyHelper.GetProductVersion(); + var model = new StatusModel + { + CurrentVersion = assemblyVersion, + }; + + var releases = await Git.Repository.Release.GetAll(Owner, RepoName); + var latestRelease = releases.FirstOrDefault(); + + if (latestRelease == null) + { + return new StatusModel { NewVersion = "Unknown" }; + } + var latestVersionArray = latestRelease.Name.Split(new[] { 'v' }, StringSplitOptions.RemoveEmptyEntries); + var latestVersion = latestVersionArray.Length > 1 ? latestVersionArray[1] : string.Empty; + + if (!latestVersion.Equals(assemblyVersion, StringComparison.InvariantCultureIgnoreCase)) + { + model.UpdateAvailable = true; + model.UpdateUri = latestRelease.HtmlUrl; + model.NewVersion = latestVersion; + } + + model.ReleaseNotes = latestRelease.Body; + model.DownloadUri = latestRelease.Assets[0].BrowserDownloadUrl; + model.ReleaseTitle = latestRelease.Name; + + return model; + } + + private StatusModel GetAppveyorRelease(Branches branch) + { + var request = new ApiRequest(); + + // Get latest EAP Build + var eapBranchRequest = new RestRequest + { + Method = Method.GET + }; + + + switch (branch) + { + case Branches.Dev: + eapBranchRequest.Resource = "/projects/tidusjar/requestplex/branch/dev"; + break; + case Branches.EarlyAccessPreview: + eapBranchRequest.Resource = "/projects/tidusjar/requestplex/branch/EAP"; + break; + } + + var api = StringCipher.Decrypt(Api,"Appveyor"); + eapBranchRequest.AddHeader("Authorization", $"Bearer {api}"); + eapBranchRequest.AddHeader("Content-Type", "application/json"); + + var branchResult = request.ExecuteJson(eapBranchRequest, new Uri(AppveyorApiUrl)); + + var jobId = branchResult.build.jobs.FirstOrDefault()?.jobId ?? string.Empty; + + if (string.IsNullOrEmpty(jobId)) + { + return new StatusModel {UpdateAvailable = false}; + } + + // Get artifacts from the EAP Build + var eapAtrifactRequest = new RestRequest + { + Resource = $"/buildjobs/{jobId}/artifacts", + Method = Method.GET + }; + eapAtrifactRequest.AddHeader("Authorization", $"Bearer {api}"); + eapAtrifactRequest.AddHeader("Content-Type", "application/json"); + + var artifactResults = request.ExecuteJson>(eapAtrifactRequest, new Uri(AppveyorApiUrl)); + + var artifactResult = artifactResults.FirstOrDefault(); + + if (artifactResult == null) + { + return new StatusModel + { + UpdateAvailable = false + }; + } + var downloadLink = $"{AppveyorApiUrl}/buildjobs/{jobId}/artifacts/{artifactResult.fileName}"; + + var branchDisplay = EnumHelper.GetDisplayValue(branch); + var fileversion = AssemblyHelper.GetFileVersion(); + + var model = new StatusModel + { + DownloadUri = downloadLink, + ReleaseNotes = $"{branchDisplay} (See recent commits for details)", + ReleaseTitle = $"Plex Requests {branchDisplay}", + NewVersion = branchResult.build.version, + UpdateUri = downloadLink, + CurrentVersion = fileversion + }; + + if (!fileversion.Equals(branchResult.build.version, StringComparison.CurrentCultureIgnoreCase)) + { + model.UpdateAvailable = true; + } + + return model; + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Helpers/TvSender.cs b/PlexRequests.Core/TvSender.cs similarity index 99% rename from PlexRequests.UI/Helpers/TvSender.cs rename to PlexRequests.Core/TvSender.cs index b67311d64..7ec462582 100644 --- a/PlexRequests.UI/Helpers/TvSender.cs +++ b/PlexRequests.Core/TvSender.cs @@ -24,18 +24,19 @@ // 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 PlexRequests.Api.Interfaces; using PlexRequests.Api.Models.SickRage; using PlexRequests.Api.Models.Sonarr; using PlexRequests.Core.SettingModels; using PlexRequests.Store; -using System.Linq; -using System.Threading.Tasks; -namespace PlexRequests.UI.Helpers +namespace PlexRequests.Core { public class TvSender { diff --git a/PlexRequests.UI/Helpers/TvSenderOld.cs b/PlexRequests.Core/TvSenderOld.cs similarity index 99% rename from PlexRequests.UI/Helpers/TvSenderOld.cs rename to PlexRequests.Core/TvSenderOld.cs index ced1c81dd..b603272b1 100644 --- a/PlexRequests.UI/Helpers/TvSenderOld.cs +++ b/PlexRequests.Core/TvSenderOld.cs @@ -36,7 +36,7 @@ using PlexRequests.Api.Models.Sonarr; using PlexRequests.Core.SettingModels; using PlexRequests.Store; -namespace PlexRequests.UI.Helpers +namespace PlexRequests.Core { public class TvSenderOld { diff --git a/PlexRequests.Core/UserMapper.cs b/PlexRequests.Core/UserMapper.cs index 24e44999f..50215093d 100644 --- a/PlexRequests.Core/UserMapper.cs +++ b/PlexRequests.Core/UserMapper.cs @@ -36,6 +36,7 @@ using Nancy.Security; using PlexRequests.Core.Models; using PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; using PlexRequests.Store; using PlexRequests.Store.Repository; @@ -60,7 +61,6 @@ namespace PlexRequests.Core return new UserIdentity { UserName = user.UserName, - Claims = ByteConverterHelper.ReturnObject(user.Claims) }; } @@ -99,7 +99,7 @@ namespace PlexRequests.Core return users.Any(); } - public Guid? CreateUser(string username, string password, string[] claims = default(string[]), UserProperties properties = null) + public Guid? CreateUser(string username, string password, UserProperties properties = null) { var salt = PasswordHasher.GenerateSalt(); @@ -109,7 +109,7 @@ namespace PlexRequests.Core UserGuid = Guid.NewGuid().ToString(), Salt = salt, Hash = PasswordHasher.ComputeHash(password, salt), - Claims = ByteConverterHelper.ReturnBytes(claims), + Claims = new byte[] {0}, UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()), }; Repo.Insert(userModel); @@ -118,32 +118,33 @@ namespace PlexRequests.Core return new Guid(userRecord.UserGuid); } + public Guid? CreateUser(string username, string password, int permissions, int features, UserProperties properties = null) + { + var salt = PasswordHasher.GenerateSalt(); + + var userModel = new UsersModel + { + UserName = username, + UserGuid = Guid.NewGuid().ToString(), + Salt = salt, + Hash = PasswordHasher.ComputeHash(password, salt), + UserProperties = ByteConverterHelper.ReturnBytes(properties ?? new UserProperties()), + Permissions = permissions, + Features = features, + Claims = new byte[] {0} + }; + Repo.Insert(userModel); + var userRecord = Repo.Get(userModel.UserGuid); + + return new Guid(userRecord.UserGuid); + } + public void DeleteUser(string userId) { var user = Repo.Get(userId); Repo.Delete(user); } - public Guid? CreateAdmin(string username, string password, UserProperties properties = null) - { - return CreateUser(username, password, new[] { UserClaims.RegularUser, UserClaims.PowerUser, UserClaims.Admin }, properties); - } - - public Guid? CreatePowerUser(string username, string password, UserProperties properties = null) - { - return CreateUser(username, password, new[] { UserClaims.RegularUser, UserClaims.PowerUser }, properties); - } - - public Guid? CreateRegularUser(string username, string password, UserProperties properties = null) - { - return CreateUser(username, password, new[] { UserClaims.RegularUser }, properties); - } - - public IEnumerable GetAllClaims() - { - var properties = typeof(UserClaims).GetConstantsValues(); - return properties; - } public bool UpdatePassword(string username, string oldPassword, string newPassword) { @@ -186,8 +187,8 @@ namespace PlexRequests.Core public interface ICustomUserMapper { - Guid? CreateUser(string username, string password, string[] claims, UserProperties props); - IEnumerable GetAllClaims(); + Guid? CreateUser(string username, string password, int permissions, int features, + UserProperties properties = null); IEnumerable GetUsers(); Task> GetUsersAsync(); UsersModel GetUser(Guid userId); @@ -195,9 +196,6 @@ namespace PlexRequests.Core bool DoUsersExist(); Guid? ValidateUser(string username, string password); bool UpdatePassword(string username, string oldPassword, string newPassword); - Guid? CreateAdmin(string username, string password, UserProperties properties = null); - Guid? CreatePowerUser(string username, string password, UserProperties properties = null); - Guid? CreateRegularUser(string username, string password, UserProperties properties = null); void DeleteUser(string userId); } } diff --git a/PlexRequests.Core/Users/IUserHelper.cs b/PlexRequests.Core/Users/IUserHelper.cs new file mode 100644 index 000000000..492574d47 --- /dev/null +++ b/PlexRequests.Core/Users/IUserHelper.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using PlexRequests.Helpers.Permissions; + +namespace PlexRequests.Core.Users +{ + public interface IUserHelper + { + IEnumerable GetUsers(); + IEnumerable GetUsersWithPermission(Permissions permission); + IEnumerable GetUsersWithFeature(Features feature); + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Users/UserHelper.cs b/PlexRequests.Core/Users/UserHelper.cs new file mode 100644 index 000000000..1654e0906 --- /dev/null +++ b/PlexRequests.Core/Users/UserHelper.cs @@ -0,0 +1,159 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserHelper.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.Linq; +using PlexRequests.Core.Models; +using PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; +using PlexRequests.Store.Repository; + +namespace PlexRequests.Core.Users +{ + public class UserHelper : IUserHelper + { + public UserHelper(IUserRepository userRepository, IPlexUserRepository plexUsers, ISecurityExtensions security) + { + LocalUserRepository = userRepository; + PlexUserRepository = plexUsers; + Security = security; + } + + private IUserRepository LocalUserRepository { get; } + private IPlexUserRepository PlexUserRepository { get; } + private ISecurityExtensions Security { get; } + + + public IEnumerable GetUsers() + { + var model = new List(); + + var localUsers = LocalUserRepository.GetAll(); + var plexUsers = PlexUserRepository.GetAll(); + + foreach (var user in localUsers) + { + var props = ByteConverterHelper.ReturnObject(user.UserProperties); + model.Add(new UserHelperModel + { + Type = UserType.LocalUser, + Username = user.UserName, + UserAlias = props.UserAlias, + EmailAddress = props.EmailAddress, + Permissions = (Permissions)user.Permissions + }); + } + + model.AddRange(plexUsers.Select(user => new UserHelperModel + { + Type = UserType.LocalUser, + Username = user.Username, + UserAlias = user.UserAlias, + EmailAddress = user.EmailAddress, + Permissions = (Permissions)user.Permissions + })); + + return model; + } + + public IEnumerable GetUsersWithPermission(Permissions permission) + { + var model = new List(); + + var localUsers = LocalUserRepository.GetAll().ToList(); + var plexUsers = PlexUserRepository.GetAll().ToList(); + + var filteredLocal = localUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission)); + var filteredPlex = plexUsers.Where(x => ((Permissions)x.Permissions).HasFlag(permission)); + + + foreach (var user in filteredLocal) + { + var props = ByteConverterHelper.ReturnObject(user.UserProperties); + model.Add(new UserHelperModel + { + Type = UserType.LocalUser, + Username = user.UserName, + UserAlias = props.UserAlias, + EmailAddress = props.EmailAddress, + Permissions = (Permissions)user.Permissions, + Features = (Features)user.Features + }); + } + + model.AddRange(filteredPlex.Select(user => new UserHelperModel + { + Type = UserType.LocalUser, + Username = user.Username, + UserAlias = user.UserAlias, + EmailAddress = user.EmailAddress, + Permissions = (Permissions)user.Permissions, + Features = (Features)user.Features + })); + + return model; + } + + public IEnumerable GetUsersWithFeature(Features features) + { + var model = new List(); + + var localUsers = LocalUserRepository.GetAll().ToList(); + var plexUsers = PlexUserRepository.GetAll().ToList(); + + var filteredLocal = localUsers.Where(x => ((Features)x.Features).HasFlag(features)); + var filteredPlex = plexUsers.Where(x => ((Features)x.Features).HasFlag(features)); + + + foreach (var user in filteredLocal) + { + var props = ByteConverterHelper.ReturnObject(user.UserProperties); + model.Add(new UserHelperModel + { + Type = UserType.LocalUser, + Username = user.UserName, + UserAlias = props.UserAlias, + EmailAddress = props.EmailAddress, + Permissions = (Permissions)user.Permissions, + Features = (Features)user.Features + }); + } + + model.AddRange(filteredPlex.Select(user => new UserHelperModel + { + Type = UserType.LocalUser, + Username = user.Username, + UserAlias = user.UserAlias, + EmailAddress = user.EmailAddress, + Permissions = (Permissions)user.Permissions, + Features = (Features)user.Features + })); + + return model; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Users/UserHelperModel.cs b/PlexRequests.Core/Users/UserHelperModel.cs new file mode 100644 index 000000000..5d66fc066 --- /dev/null +++ b/PlexRequests.Core/Users/UserHelperModel.cs @@ -0,0 +1,42 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: UserHelperModel.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 PlexRequests.Helpers; +using PlexRequests.Helpers.Permissions; + +namespace PlexRequests.Core.Users +{ + public class UserHelperModel + { + public string Username { get; set; } + public string UserAlias { get; set; } + public Permissions Permissions { get; set; } + public Features Features { get; set; } + public string EmailAddress { get; set; } + public UserType Type { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/Users/UserManagementHelper.cs b/PlexRequests.Core/Users/UserManagementHelper.cs new file mode 100644 index 000000000..1188a7717 --- /dev/null +++ b/PlexRequests.Core/Users/UserManagementHelper.cs @@ -0,0 +1,70 @@ +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers.Permissions; + +namespace PlexRequests.Core.Users +{ + public static class UserManagementHelper + { + + public static int GetPermissions(UserManagementSettings settings) + { + var permission = 0; + + if (settings.AutoApproveMovies) + { + permission += (int)Permissions.AutoApproveMovie; + } + if (settings.AutoApproveMusic) + { + permission += (int)Permissions.AutoApproveAlbum; + } + if (settings.AutoApproveTvShows) + { + permission += (int)Permissions.AutoApproveTv; + } + if (settings.RequestMovies) + { + permission += (int)Permissions.RequestMovie; + } + if (settings.RequestMusic) + { + permission += (int)Permissions.RequestMusic; + } + if (settings.RequestTvShows) + { + permission += (int)Permissions.RequestTvShow; + } + if (settings.ReportIssues) + { + permission += (int)Permissions.ReportIssue; + } + if (settings.UsersCanViewOnlyOwnRequests) + { + permission += (int)Permissions.UsersCanViewOnlyOwnRequests; + } + if (settings.UsersCanViewOnlyOwnIssues) + { + permission += (int)Permissions.UsersCanViewOnlyOwnIssues; + } + + + return permission; + } + + public static int GetFeatures(UserManagementSettings settings) + { + var features = 0; + + if (settings.RecentlyAddedNewsletter) + { + features += (int)Features.Newsletter; + } + if (settings.RecentlyAddedNotification) + { + features += (int)Features.RequestAddedNotification; + } + + return features; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core/packages.config b/PlexRequests.Core/packages.config index 6f3ca4a6c..e343a209e 100644 --- a/PlexRequests.Core/packages.config +++ b/PlexRequests.Core/packages.config @@ -2,11 +2,14 @@ + + + \ No newline at end of file diff --git a/PlexRequests.Helpers/Analytics/Analytics.cs b/PlexRequests.Helpers/Analytics/Analytics.cs index 764295fa3..ed7eed4fd 100644 --- a/PlexRequests.Helpers/Analytics/Analytics.cs +++ b/PlexRequests.Helpers/Analytics/Analytics.cs @@ -54,7 +54,7 @@ namespace PlexRequests.Helpers.Analytics Track(HitType.@event, username, cat, act, label, clientId, value); } - public async Task TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null) + public async void TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null) { var cat = category.ToString(); var act = action.ToString(); @@ -146,7 +146,7 @@ namespace PlexRequests.Helpers.Analytics request.Method = RequestMethod; // set the Content-Length header to the correct value request.ContentLength = Encoding.UTF8.GetByteCount(postDataString); - +#if !DEBUG // write the request body to the request using (var writer = new StreamWriter(request.GetRequestStream())) { @@ -165,7 +165,10 @@ namespace PlexRequests.Helpers.Analytics { Log.Error(ex, "Analytics tracking failed"); } +#endif } + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously private async Task SendRequestAsync(string postDataString) { var request = (HttpWebRequest)WebRequest.Create(AnalyticsUri); @@ -173,6 +176,7 @@ namespace PlexRequests.Helpers.Analytics // set the Content-Length header to the correct value request.ContentLength = Encoding.UTF8.GetByteCount(postDataString); +#if !DEBUG // write the request body to the request using (var writer = new StreamWriter(await request.GetRequestStreamAsync())) { @@ -191,7 +195,9 @@ namespace PlexRequests.Helpers.Analytics { Log.Error(ex, "Analytics tracking failed"); } +#endif } +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously private Dictionary BuildRequestData(HitType type, string username, string category, string action, string clientId, string label, int? value, string exceptionDescription, int? fatal) { diff --git a/PlexRequests.Helpers/Analytics/Category.cs b/PlexRequests.Helpers/Analytics/Category.cs index 33d0cecf1..e15173672 100644 --- a/PlexRequests.Helpers/Analytics/Category.cs +++ b/PlexRequests.Helpers/Analytics/Category.cs @@ -38,6 +38,7 @@ namespace PlexRequests.Helpers.Analytics Issues, UserLogin, Services, - Navbar + Navbar, + UserManagement } } \ No newline at end of file diff --git a/PlexRequests.Helpers/Analytics/IAnalytics.cs b/PlexRequests.Helpers/Analytics/IAnalytics.cs index d21dd18d1..b808c392a 100644 --- a/PlexRequests.Helpers/Analytics/IAnalytics.cs +++ b/PlexRequests.Helpers/Analytics/IAnalytics.cs @@ -51,7 +51,7 @@ namespace PlexRequests.Helpers.Analytics /// The client identifier. /// The value. /// - Task TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null); + void TrackEventAsync(Category category, Action action, string label, string username, string clientId, int? value = null); /// /// Tracks the page view. diff --git a/PlexRequests.Helpers/AssemblyHelper.cs b/PlexRequests.Helpers/AssemblyHelper.cs index ef342cc41..abdcfeeaf 100644 --- a/PlexRequests.Helpers/AssemblyHelper.cs +++ b/PlexRequests.Helpers/AssemblyHelper.cs @@ -45,5 +45,13 @@ namespace PlexRequests.Helpers var retVersion = fvi.ProductVersion; return retVersion; } + + public static string GetFileVersion() + { + var assembly = Assembly.GetExecutingAssembly(); + var fvi = FileVersionInfo.GetVersionInfo(assembly.Location); + var retVersion = fvi.FileVersion; + return retVersion; + } } } \ No newline at end of file diff --git a/PlexRequests.Helpers/EnumExtensions.cs b/PlexRequests.Helpers/EnumExtensions.cs new file mode 100644 index 000000000..72e1fcf68 --- /dev/null +++ b/PlexRequests.Helpers/EnumExtensions.cs @@ -0,0 +1,54 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EnumExtensions.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; + +namespace PlexRequests.Helpers +{ + public static class EnumExtensions + { + public static IEnumerable GetUniqueFlags(this Enum flags) + { + ulong flag = 1; + foreach (var value in Enum.GetValues(flags.GetType()).Cast()) + { + var bits = Convert.ToUInt64(value); + while (flag < bits) + { + flag <<= 1; + } + + if (flag == bits && flags.HasFlag(value)) + { + yield return value; + } + } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/EnumHelper.cs b/PlexRequests.Helpers/EnumHelper.cs new file mode 100644 index 000000000..387f867e9 --- /dev/null +++ b/PlexRequests.Helpers/EnumHelper.cs @@ -0,0 +1,117 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EnumHelper.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.ComponentModel.DataAnnotations; +using System.Linq; +using System.Reflection; + +namespace PlexRequests.Helpers +{ + public static class EnumHelper + { + public static IList GetValues(Enum value) + { + return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => (T) Enum.Parse(value.GetType(), fi.Name, false)).ToList(); + } + + public static T Parse(string value) + { + return (T)Enum.Parse(typeof(T), value, true); + } + + public static IList GetNames(Enum value) + { + return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList(); + } + + public static IList GetDisplayValues(Enum value) + { + return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList(); + } + + private static string LookupResource(Type resourceManagerProvider, string resourceKey) + { + foreach (var staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) + { + if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager)) + { + System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null); + return resourceManager.GetString(resourceKey); + } + } + + return resourceKey; // Fallback with the key name + } + + public static string GetDisplayValue(T value) + { + var fieldInfo = value.GetType().GetField(value.ToString()); + + var descriptionAttributes = fieldInfo.GetCustomAttributes( + typeof(DisplayAttribute), false) as DisplayAttribute[]; + + if (descriptionAttributes[0].ResourceType != null) + return LookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name); + + if (descriptionAttributes == null) return string.Empty; + return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString(); + } + + public static T GetValueFromName(string name) + { + var type = typeof(T); + if (!type.IsEnum) throw new InvalidOperationException(); + + foreach (var field in type.GetFields()) + { + var attribute = Attribute.GetCustomAttribute(field, + typeof(DisplayAttribute)) as DisplayAttribute; + if (attribute != null) + { + if (attribute.Name == name) + { + return (T)field.GetValue(null); + } + } + else + { + if (field.Name == name) + return (T)field.GetValue(null); + } + } + + throw new ArgumentOutOfRangeException(nameof(name)); + } + + public static int All() + { + return Enum.GetValues(typeof(T)).Cast().Sum(); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Core.Tests/StatusCheckerTests.cs b/PlexRequests.Helpers/Permissions/Features.cs similarity index 78% rename from PlexRequests.Core.Tests/StatusCheckerTests.cs rename to PlexRequests.Helpers/Permissions/Features.cs index 38b9b4674..24a945ee3 100644 --- a/PlexRequests.Core.Tests/StatusCheckerTests.cs +++ b/PlexRequests.Helpers/Permissions/Features.cs @@ -1,7 +1,7 @@ #region Copyright // /************************************************************************ // Copyright (c) 2016 Jamie Rees -// File: AuthenticationSettingsTests.cs +// File: Features.cs // Created By: Jamie Rees // // Permission is hereby granted, free of charge, to any person obtaining @@ -26,21 +26,17 @@ #endregion using System; -using NUnit.Framework; +using System.ComponentModel.DataAnnotations; -namespace PlexRequests.Core.Tests +namespace PlexRequests.Helpers.Permissions { - [TestFixture] - public class StatusCheckerTests + [Flags] + public enum Features { - [Test] - [Ignore("API Limit")] - public void CheckStatusTest() - { - var checker = new StatusChecker(); - var status = checker.GetStatus(); + [Display(Name = "Newsletter")] + Newsletter = 1, + [Display(Name = "Request Added Notification")] + RequestAddedNotification = 2, - Assert.That(status, Is.Not.Null); - } } -} +} \ No newline at end of file diff --git a/PlexRequests.Helpers/Permissions/Permissions.cs b/PlexRequests.Helpers/Permissions/Permissions.cs new file mode 100644 index 000000000..57112ee7e --- /dev/null +++ b/PlexRequests.Helpers/Permissions/Permissions.cs @@ -0,0 +1,78 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: Permissions.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.ComponentModel.DataAnnotations; + +namespace PlexRequests.Helpers.Permissions +{ + [Flags] + //// + //// NOTE if any are added, make sure we change the UserManagementHelper + //// + public enum Permissions + { + [Display(Name = "Access Administration Settings")] + Administrator = 1, + + [Display(Name = "Request Movie")] + RequestMovie = 2, + + [Display(Name = "Request TV Show")] + RequestTvShow = 4, + + [Display(Name = "Request Music")] + RequestMusic = 8, + + [Display(Name = "Report Issue")] + ReportIssue = 16, + + [Display(Name = "Read Only User")] + ReadOnlyUser = 32, + + [Display(Name = "Auto Approve Movie Requests")] + AutoApproveMovie = 64, + + [Display(Name = "Auto Approve TV Show Requests")] + AutoApproveTv = 128, + + [Display(Name = "Auto Approve Album Requests")] + AutoApproveAlbum = 256, + + [Display(Name = "Manage Requests")] + ManageRequests = 512, + + [Display(Name = "Users can only view their own requests")] + UsersCanViewOnlyOwnRequests = 1024, + + [Display(Name = "Users can only view their own issues")] + UsersCanViewOnlyOwnIssues = 2048, + + [Display(Name = "Bypass the request limit")] + BypassRequestLimit = 4096 + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/PlexRequests.Helpers.csproj b/PlexRequests.Helpers/PlexRequests.Helpers.csproj index 0ffffb4c8..5563d5da5 100644 --- a/PlexRequests.Helpers/PlexRequests.Helpers.csproj +++ b/PlexRequests.Helpers/PlexRequests.Helpers.csproj @@ -39,6 +39,10 @@ ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll True + + ..\packages\Ninject.3.2.0.0\lib\net45-full\Ninject.dll + True + ..\packages\NLog.4.3.6\lib\net45\NLog.dll True @@ -48,6 +52,7 @@ True + @@ -69,6 +74,7 @@ + @@ -78,9 +84,13 @@ + + + + diff --git a/PlexRequests.UI/Models/SessionKeys.cs b/PlexRequests.Helpers/SessionKeys.cs similarity index 98% rename from PlexRequests.UI/Models/SessionKeys.cs rename to PlexRequests.Helpers/SessionKeys.cs index 766a84bf6..fe43a909d 100644 --- a/PlexRequests.UI/Models/SessionKeys.cs +++ b/PlexRequests.Helpers/SessionKeys.cs @@ -24,7 +24,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // ************************************************************************/ #endregion -namespace PlexRequests.UI.Models +namespace PlexRequests.Helpers { public class SessionKeys { diff --git a/PlexRequests.Helpers/packages.config b/PlexRequests.Helpers/packages.config index 97b45cbe0..db4648bf2 100644 --- a/PlexRequests.Helpers/packages.config +++ b/PlexRequests.Helpers/packages.config @@ -2,6 +2,7 @@ + \ No newline at end of file diff --git a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs index 801af7509..1097bfb67 100644 --- a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs +++ b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs @@ -1,284 +1,284 @@ -#region Copyright -// /************************************************************************ -// Copyright (c) 2016 Jamie Rees -// File: PlexAvailabilityCheckerTests.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.Data; -using System.Linq; -using System.Threading.Tasks; +//#region Copyright +//// /************************************************************************ +//// Copyright (c) 2016 Jamie Rees +//// File: PlexAvailabilityCheckerTests.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.Data; +//using System.Linq; +//using System.Threading.Tasks; -using Moq; +//using Moq; -using NUnit.Framework; +//using NUnit.Framework; -using PlexRequests.Api.Interfaces; -using PlexRequests.Api.Models.Plex; -using PlexRequests.Core; -using PlexRequests.Core.SettingModels; -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; +//using PlexRequests.Api.Interfaces; +//using PlexRequests.Api.Models.Plex; +//using PlexRequests.Core; +//using PlexRequests.Core.SettingModels; +//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; -using Ploeh.AutoFixture; +//using Ploeh.AutoFixture; -namespace PlexRequests.Services.Tests -{ - [TestFixture] - public class PlexAvailabilityCheckerTests - { - public IAvailabilityChecker Checker { get; set; } - private Fixture F { get; set; } = new Fixture(); - private Mock> SettingsMock { get; set; } - private Mock> AuthMock { get; set; } - private Mock RequestMock { get; set; } - private Mock PlexMock { get; set; } - private Mock CacheMock { get; set; } - private Mock NotificationMock { get; set; } - private Mock JobRec { get; set; } - private Mock> NotifyUsers { get; set; } - private Mock> PlexEpisodes { get; set; } - private Mock Engine - { - get; - set; - } +//namespace PlexRequests.Services.Tests +//{ +// [TestFixture] +// public class PlexAvailabilityCheckerTests +// { +// public IAvailabilityChecker Checker { get; set; } +// private Fixture F { get; set; } = new Fixture(); +// private Mock> SettingsMock { get; set; } +// private Mock> AuthMock { get; set; } +// private Mock RequestMock { get; set; } +// private Mock PlexMock { get; set; } +// private Mock CacheMock { get; set; } +// private Mock NotificationMock { get; set; } +// private Mock JobRec { get; set; } +// private Mock> NotifyUsers { get; set; } +// private Mock> PlexEpisodes { get; set; } +// private Mock Engine +// { +// get; +// set; +// } - [SetUp] - public void Setup() - { - SettingsMock = new Mock>(); - AuthMock = new Mock>(); - RequestMock = new Mock(); - PlexMock = new Mock(); - NotificationMock = new Mock(); - CacheMock = new Mock(); - NotifyUsers = new Mock>(); - PlexEpisodes = new Mock>(); - JobRec = new Mock(); - Engine = new Mock(); - Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object, Engine.Object); +// [SetUp] +// public void Setup() +// { +// SettingsMock = new Mock>(); +// AuthMock = new Mock>(); +// RequestMock = new Mock(); +// PlexMock = new Mock(); +// NotificationMock = new Mock(); +// CacheMock = new Mock(); +// NotifyUsers = new Mock>(); +// PlexEpisodes = new Mock>(); +// JobRec = new Mock(); +// Engine = new Mock(); +// Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object, Engine.Object); - } +// } - [Test] - public void InvalidSettings() - { - Checker.CheckAndUpdateAll(); - PlexMock.Verify(x => x.GetLibrary(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); - PlexMock.Verify(x => x.GetAccount(It.IsAny()), Times.Never); - PlexMock.Verify(x => x.GetLibrarySections(It.IsAny(), It.IsAny()), Times.Never); - PlexMock.Verify(x => x.GetStatus(It.IsAny(), It.IsAny()), Times.Never); - PlexMock.Verify(x => x.GetUsers(It.IsAny()), Times.Never); - } +// [Test] +// public void InvalidSettings() +// { +// Checker.CheckAndUpdateAll(); +// PlexMock.Verify(x => x.GetLibrary(It.IsAny(), It.IsAny(), It.IsAny()), Times.Never); +// PlexMock.Verify(x => x.GetAccount(It.IsAny()), Times.Never); +// PlexMock.Verify(x => x.GetLibrarySections(It.IsAny(), It.IsAny()), Times.Never); +// PlexMock.Verify(x => x.GetStatus(It.IsAny(), It.IsAny()), Times.Never); +// PlexMock.Verify(x => x.GetUsers(It.IsAny()), Times.Never); +// } - [TestCaseSource(nameof(IsMovieAvailableTestData))] - public bool IsMovieAvailableTest(string title, string year) - { - var movies = new List - { - new PlexMovie {Title = title, ProviderId = null, ReleaseYear = year} - }; - var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011"); +// [TestCaseSource(nameof(IsMovieAvailableTestData))] +// public bool IsMovieAvailableTest(string title, string year) +// { +// var movies = new List +// { +// new PlexMovie {Title = title, ProviderId = null, ReleaseYear = year} +// }; +// var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011"); - return result; - } +// return result; +// } - private static IEnumerable IsMovieAvailableTestData - { - get - { - yield return new TestCaseData("title", "2011").Returns(true).SetName("IsMovieAvailable True"); - yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsMovieAvailable False different title"); - yield return new TestCaseData("title", "2001").Returns(false).SetName("IsMovieAvailable False different year"); - } - } +// private static IEnumerable IsMovieAvailableTestData +// { +// get +// { +// yield return new TestCaseData("title", "2011").Returns(true).SetName("IsMovieAvailable True"); +// yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsMovieAvailable False different title"); +// yield return new TestCaseData("title", "2001").Returns(false).SetName("IsMovieAvailable False different year"); +// } +// } - [TestCaseSource(nameof(IsMovieAvailableAdvancedTestData))] - public bool IsMovieAvailableAdvancedTest(string title, string year, string providerId) - { - var movies = new List - { - new PlexMovie {Title = title, ProviderId = providerId, ReleaseYear = year } - }; - var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011", 9999.ToString()); +// [TestCaseSource(nameof(IsMovieAvailableAdvancedTestData))] +// public bool IsMovieAvailableAdvancedTest(string title, string year, string providerId) +// { +// var movies = new List +// { +// new PlexMovie {Title = title, ProviderId = providerId, ReleaseYear = year } +// }; +// var result = Checker.IsMovieAvailable(movies.ToArray(), "title", "2011", 9999.ToString()); - return result; - } +// return result; +// } - private static IEnumerable IsMovieAvailableAdvancedTestData - { - get - { - yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsMovieAvailable True"); - yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsMovieAvailable False different title"); - yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsMovieAvailable False different year"); - yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsMovieAvailable False different providerID"); - } - } +// private static IEnumerable IsMovieAvailableAdvancedTestData +// { +// get +// { +// yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsMovieAvailable True"); +// yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsMovieAvailable False different title"); +// yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsMovieAvailable False different year"); +// yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsMovieAvailable False different providerID"); +// } +// } - [TestCaseSource(nameof(IsTvAvailableTestData))] - public bool IsTvAvailableTest(string title, string year) - { - var tv = new List - { - new PlexTvShow {Title = title, ProviderId = null, ReleaseYear = year} - }; - var result = Checker.IsTvShowAvailable(tv.ToArray(), "title", "2011"); +// [TestCaseSource(nameof(IsTvAvailableTestData))] +// public bool IsTvAvailableTest(string title, string year) +// { +// var tv = new List +// { +// new PlexTvShow {Title = title, ProviderId = null, ReleaseYear = year} +// }; +// var result = Checker.IsTvShowAvailable(tv.ToArray(), "title", "2011"); - return result; - } +// return result; +// } - private static IEnumerable IsTvAvailableTestData - { - get - { - yield return new TestCaseData("title", "2011").Returns(true).SetName("IsTvAvailable True"); - yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsTvAvailable False different title"); - yield return new TestCaseData("title", "2001").Returns(false).SetName("IsTvAvailable False different year"); - } - } +// private static IEnumerable IsTvAvailableTestData +// { +// get +// { +// yield return new TestCaseData("title", "2011").Returns(true).SetName("IsTvAvailable True"); +// yield return new TestCaseData("title2", "2011").Returns(false).SetName("IsTvAvailable False different title"); +// yield return new TestCaseData("title", "2001").Returns(false).SetName("IsTvAvailable False different year"); +// } +// } - [TestCaseSource(nameof(IsTvAvailableAdvancedTestData))] - public bool IsTvAvailableAdvancedTest(string title, string year, string providerId) - { - var movies = new List - { - new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year } - }; - var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString()); +// [TestCaseSource(nameof(IsTvAvailableAdvancedTestData))] +// public bool IsTvAvailableAdvancedTest(string title, string year, string providerId) +// { +// var movies = new List +// { +// new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year } +// }; +// var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString()); - return result; - } +// return result; +// } - private static IEnumerable IsTvAvailableAdvancedTestData - { - get - { - yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsTvAvailable True"); - yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsTvAvailable False different title"); - yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsTvAvailable False different year"); - yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsTvAvailable False different providerID"); - } - } +// private static IEnumerable IsTvAvailableAdvancedTestData +// { +// get +// { +// yield return new TestCaseData("title", "2011", "9999").Returns(true).SetName("Advanced IsTvAvailable True"); +// yield return new TestCaseData("title2", "2011", "99929").Returns(false).SetName("Advanced IsTvAvailable False different title"); +// yield return new TestCaseData("title", "2001", "99939").Returns(false).SetName("Advanced IsTvAvailable False different year"); +// yield return new TestCaseData("title", "2001", "44445").Returns(false).SetName("Advanced IsTvAvailable False different providerID"); +// } +// } - [TestCaseSource(nameof(IsTvAvailableAdvancedSeasonsTestData))] - public bool IsTvAvailableAdvancedSeasonsTest(string title, string year, string providerId, int[] seasons) - { - var movies = new List - { - new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year , Seasons = seasons} - }; - var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString(), new[] { 1, 2, 3 }); +// [TestCaseSource(nameof(IsTvAvailableAdvancedSeasonsTestData))] +// public bool IsTvAvailableAdvancedSeasonsTest(string title, string year, string providerId, int[] seasons) +// { +// var movies = new List +// { +// new PlexTvShow {Title = title, ProviderId = providerId, ReleaseYear = year , Seasons = seasons} +// }; +// var result = Checker.IsTvShowAvailable(movies.ToArray(), "title", "2011", 9999.ToString(), new[] { 1, 2, 3 }); - return result; - } +// return result; +// } - private static IEnumerable IsTvAvailableAdvancedSeasonsTestData - { - get - { - yield return new TestCaseData("title", "2011", "9999", new[] { 1, 2, 3 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable True"); - yield return new TestCaseData("title2", "2011", "99929", new[] { 5, 6 }).Returns(false).SetName("Advanced IsTvSeasonsAvailable False no seasons"); - yield return new TestCaseData("title2", "2011", "9999", new[] { 1, 6 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable true one season"); - } - } +// private static IEnumerable IsTvAvailableAdvancedSeasonsTestData +// { +// get +// { +// yield return new TestCaseData("title", "2011", "9999", new[] { 1, 2, 3 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable True"); +// yield return new TestCaseData("title2", "2011", "99929", new[] { 5, 6 }).Returns(false).SetName("Advanced IsTvSeasonsAvailable False no seasons"); +// yield return new TestCaseData("title2", "2011", "9999", new[] { 1, 6 }).Returns(true).SetName("Advanced IsTvSeasonsAvailable true one season"); +// } +// } - [TestCaseSource(nameof(IsEpisodeAvailableTestData))] - public bool IsEpisodeAvailableTest(string providerId, int season, int episode) - { - var expected = new List - { - new PlexEpisodes {EpisodeNumber = 1, ShowTitle = "The Flash",ProviderId = 23.ToString(), SeasonNumber = 1, EpisodeTitle = "Pilot"} - }; - PlexEpisodes.Setup(x => x.Custom(It.IsAny>>())).Returns(expected); +// [TestCaseSource(nameof(IsEpisodeAvailableTestData))] +// public bool IsEpisodeAvailableTest(string providerId, int season, int episode) +// { +// var expected = new List +// { +// new PlexEpisodes {EpisodeNumber = 1, ShowTitle = "The Flash",ProviderId = 23.ToString(), SeasonNumber = 1, EpisodeTitle = "Pilot"} +// }; +// PlexEpisodes.Setup(x => x.Custom(It.IsAny>>())).Returns(expected); - var result = Checker.IsEpisodeAvailable(providerId, season, episode); +// var result = Checker.IsEpisodeAvailable(providerId, season, episode); - return result; - } +// return result; +// } - private static IEnumerable IsEpisodeAvailableTestData - { - get - { - yield return new TestCaseData("23", 1, 1).Returns(true).SetName("IsEpisodeAvailable True S01E01"); - yield return new TestCaseData("23", 1, 2).Returns(false).SetName("IsEpisodeAvailable False S01E02"); - yield return new TestCaseData("23", 99, 99).Returns(false).SetName("IsEpisodeAvailable False S99E99"); - yield return new TestCaseData("230", 99, 99).Returns(false).SetName("IsEpisodeAvailable False Incorrect ProviderId"); - } - } +// private static IEnumerable IsEpisodeAvailableTestData +// { +// get +// { +// yield return new TestCaseData("23", 1, 1).Returns(true).SetName("IsEpisodeAvailable True S01E01"); +// yield return new TestCaseData("23", 1, 2).Returns(false).SetName("IsEpisodeAvailable False S01E02"); +// yield return new TestCaseData("23", 99, 99).Returns(false).SetName("IsEpisodeAvailable False S99E99"); +// yield return new TestCaseData("230", 99, 99).Returns(false).SetName("IsEpisodeAvailable False Incorrect ProviderId"); +// } +// } - [Test] - public void GetPlexMoviesTests() - { - var cachedMovies = F.Build().Without(x => x.Directory).CreateMany().ToList(); - cachedMovies.Add(new PlexSearch - { - Video = new List + + + @@ -210,14 +213,10 @@ - - - - @@ -232,6 +231,7 @@ + @@ -244,6 +244,9 @@ + + + @@ -257,8 +260,7 @@ - - + @@ -287,6 +289,12 @@ Always + + PreserveNewest + + + PreserveNewest + Always @@ -296,6 +304,18 @@ Always + + PreserveNewest + + + PreserveNewest + + + Always + + + PreserveNewest + PreserveNewest @@ -323,8 +343,7 @@ - - + @@ -338,6 +357,9 @@ + + PreserveNewest + datepicker.scss @@ -351,6 +373,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -660,7 +685,7 @@ Always - + Always @@ -681,7 +706,7 @@ Always - + Always @@ -726,6 +751,12 @@ PreserveNewest + + Always + + + Always + web.config @@ -744,6 +775,7 @@ PublicResXFileCodeGenerator UI1.Designer.cs + Designer diff --git a/PlexRequests.UI/Program.cs b/PlexRequests.UI/Program.cs index d71a3976c..2153aca13 100644 --- a/PlexRequests.UI/Program.cs +++ b/PlexRequests.UI/Program.cs @@ -59,11 +59,15 @@ namespace PlexRequests.UI var baseUrl = result.MapResult( o => o.BaseUrl, e => string.Empty); - + var port = result.MapResult( x => x.Port, e => -1); + var listenerPrefix = result.MapResult( + x => x.ListenerPrefix, + e => string.Empty); + var updated = result.MapResult(x => x.Updated, e => UpdateValue.None); CheckUpdate(updated); @@ -81,7 +85,12 @@ namespace PlexRequests.UI if (port == -1 || port == 3579) port = GetStartupPort(); - var options = new StartOptions(Debugger.IsAttached ? $"http://localhost:{port}" : $"http://+:{port}") + if (string.IsNullOrEmpty(listenerPrefix)) + { + listenerPrefix = "+"; + } + + var options = new StartOptions(Debugger.IsAttached ? $"http://localhost:{port}" : $"http://{listenerPrefix}:{port}") { ServerFactory = "Microsoft.Owin.Host.HttpListener" }; diff --git a/PlexRequests.UI/Resources/UI.da.resx b/PlexRequests.UI/Resources/UI.da.resx index 14e0154ac..5937e76be 100644 --- a/PlexRequests.UI/Resources/UI.da.resx +++ b/PlexRequests.UI/Resources/UI.da.resx @@ -459,4 +459,34 @@ Der er ingen oplysninger om udgivelsesdatoen + + Udsigt I Plex + + + Doner til Bibliotek vedligeholder + + + Tilgængelig på + + + Movie status + + + Ikke anmodet endnu + + + Afventer godkendelse + + + Behandler forespørgsel + + + Anmodning afvist! + + + Tv-show status! + + + En baggrund proces kører i øjeblikket, så der kan være nogle uventede problemer. Dette bør ikke tage for lang tid. + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.de.resx b/PlexRequests.UI/Resources/UI.de.resx index 039e49e46..fa6f71547 100644 --- a/PlexRequests.UI/Resources/UI.de.resx +++ b/PlexRequests.UI/Resources/UI.de.resx @@ -459,4 +459,34 @@ Es gibt noch keine Informationen für diesen Release-Termin - + + Ansicht In Plex + + + Spenden zur Bibliothek Maintainer + + + Verfügbar auf Plex + + + Film-Status! + + + Noch nicht heraus! + + + Genehmigung ausstehend + + + Die Verarbeitung Anfrage + + + Anfrage verweigert. + + + TV-Show-Status + + + Ein Hintergrundprozess gerade läuft, so könnte es einige unerwartete Verhalten sein. Dies sollte nicht zu lange dauern. + + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.es.resx b/PlexRequests.UI/Resources/UI.es.resx index a1706e45d..e7908b3f5 100644 --- a/PlexRequests.UI/Resources/UI.es.resx +++ b/PlexRequests.UI/Resources/UI.es.resx @@ -459,4 +459,31 @@ No hay información disponible para la fecha de lanzamiento + + Donar a la biblioteca Mantenedor + + + Disponible en Plex + + + estado de película + + + No solicitado + + + PENDIENTES DE APROBACIÓN + + + solicitud de procesamiento + + + Solicitud denegada + + + estado de programa de televisión + + + Un proceso en segundo plano se está ejecutando actualmente, por lo que podría ser un comportamiento inesperado. Esto no debería tomar mucho tiempo. + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.fr.resx b/PlexRequests.UI/Resources/UI.fr.resx index e4a54012d..5767fb4d2 100644 --- a/PlexRequests.UI/Resources/UI.fr.resx +++ b/PlexRequests.UI/Resources/UI.fr.resx @@ -459,4 +459,34 @@ Il n'y a pas d'information disponible pour la date de sortie + + Voir Dans Plex + + + Faire un don à la bibliothèque mainteneur! + + + Disponible sur Plex + + + le statut de film + + + Pas encore demandé + + + En attente de validation + + + Traitement de la demande + + + Requête refusée + + + TV show status + + + Un processus d'arrière-plan est en cours d'exécution, de sorte qu'il pourrait y avoir des comportements inattendus. Cela ne devrait pas prendre trop de temps. + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.it.resx b/PlexRequests.UI/Resources/UI.it.resx index fe73d4586..febcf378a 100644 --- a/PlexRequests.UI/Resources/UI.it.resx +++ b/PlexRequests.UI/Resources/UI.it.resx @@ -459,4 +459,34 @@ Non ci sono informazioni disponibili per la data di uscita + + Guarda In Plex + + + Donare alla libreria Maintainer + + + Disponibile su Plex + + + lo stato di film + + + Non ancora richiesto + + + Approvazione in sospeso + + + Elaborazione richiesta + + + Richiesta negata + + + Show televisivo di stato + + + Un processo in background è in esecuzione, quindi ci potrebbero essere alcuni comportamenti imprevisti. Questo non dovrebbe richiedere troppo tempo. + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.nl.resx b/PlexRequests.UI/Resources/UI.nl.resx index 9ddc8688e..e64511a14 100644 --- a/PlexRequests.UI/Resources/UI.nl.resx +++ b/PlexRequests.UI/Resources/UI.nl.resx @@ -459,4 +459,34 @@ Er is geen informatie beschikbaar voor de release datum + + Bekijk In Plex + + + Doneer aan Library Maintainer + + + Beschikbaar vanaf + + + Movie-status! + + + Nog niet gevraagd + + + Wacht op goedkeuring + + + Verwerking verzoek... + + + Aanvraag afgewezen + + + TV-show-status + + + Een achtergrond taak is momenteel actief, dus er misschien een onverwachte gedrag. Dit moet niet te lang duren. + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.pt.resx b/PlexRequests.UI/Resources/UI.pt.resx index 712b779c4..efedc0856 100644 --- a/PlexRequests.UI/Resources/UI.pt.resx +++ b/PlexRequests.UI/Resources/UI.pt.resx @@ -459,4 +459,31 @@ Não há informações disponíveis para a data de lançamento + + Ver Em Plex + + + Doações para Biblioteca Mantenedor + + + Disponível em Plex + + + status de filme + + + Não solicitada ainda + + + B – P/ Aprovação + + + Processando solicitação... + + + Solicitação negada. + + + Programa de TV status + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI.resx b/PlexRequests.UI/Resources/UI.resx index ced499673..a4433781f 100644 --- a/PlexRequests.UI/Resources/UI.resx +++ b/PlexRequests.UI/Resources/UI.resx @@ -467,4 +467,10 @@ TV show status - \ No newline at end of file + + Currently we are indexing all of the available tv shows and movies on the Plex server, so there might be some unexpected behavior. This shouldn't take too long. + + + User Management + + diff --git a/PlexRequests.UI/Resources/UI.sv.resx b/PlexRequests.UI/Resources/UI.sv.resx index 8b3708153..924f2cc70 100644 --- a/PlexRequests.UI/Resources/UI.sv.resx +++ b/PlexRequests.UI/Resources/UI.sv.resx @@ -459,4 +459,34 @@ Det finns ingen information tillgänglig för release datum + + Vy I Plex + + + Donera till bibliotek Ansvarig + + + Tillgänglig den + + + Film status + + + Inte Begärd ännu + + + Väntar på godkännande + + + Bearbetning förfrågan + + + Förfrågan nekad + + + Visa status + + + En bakgrundsprocess är igång, så det kan finnas några oväntade beteende. Detta bör inte ta alltför lång tid. + \ No newline at end of file diff --git a/PlexRequests.UI/Resources/UI1.Designer.cs b/PlexRequests.UI/Resources/UI1.Designer.cs index 548dafff4..a5cf28b1f 100644 --- a/PlexRequests.UI/Resources/UI1.Designer.cs +++ b/PlexRequests.UI/Resources/UI1.Designer.cs @@ -222,6 +222,15 @@ namespace PlexRequests.UI.Resources { } } + /// + /// Looks up a localized string similar to A background process is currently running, so there might be some unexpected behavior. This shouldn't take too long.. + /// + public static string Layout_CacherRunning { + get { + return ResourceManager.GetString("Layout_CacherRunning", resourceCulture); + } + } + /// /// Looks up a localized string similar to Change Password. /// @@ -393,6 +402,15 @@ namespace PlexRequests.UI.Resources { } } + /// + /// Looks up a localized string similar to User Management. + /// + public static string Layout_Usermanagement { + get { + return ResourceManager.GetString("Layout_Usermanagement", resourceCulture); + } + } + /// /// Looks up a localized string similar to Welcome. /// diff --git a/PlexRequests.UI/Start/StartupOptions.cs b/PlexRequests.UI/Start/StartupOptions.cs index 0f3dd7d97..f7d479186 100644 --- a/PlexRequests.UI/Start/StartupOptions.cs +++ b/PlexRequests.UI/Start/StartupOptions.cs @@ -59,5 +59,15 @@ namespace PlexRequests.UI.Start [Option('u', "updated", Required = false, HelpText = "This should only be used by the internal application")] public UpdateValue Updated { get; set; } + + /// + /// Gets or sets a value indicating whether this is updated. + /// + /// + /// true if updated; otherwise, false. + /// + [Option('l', "listenerprefix", Required = false, HelpText = "To change the prefix for the listener")] + public string ListenerPrefix { get; set; } + } } \ No newline at end of file diff --git a/PlexRequests.UI/Startup.cs b/PlexRequests.UI/Startup.cs index 1917ccc53..e4d4bf107 100644 --- a/PlexRequests.UI/Startup.cs +++ b/PlexRequests.UI/Startup.cs @@ -32,8 +32,11 @@ using Ninject.Planning.Bindings.Resolvers; using NLog; using Owin; +using PlexRequests.Core; using PlexRequests.Core.Migration; using PlexRequests.Services.Jobs; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; using PlexRequests.UI.Helpers; using PlexRequests.UI.Jobs; using PlexRequests.UI.NinjectModules; @@ -57,7 +60,7 @@ namespace PlexRequests.UI Debug.WriteLine("Modules found finished."); - var kernel = new StandardKernel(modules); + var kernel = new StandardKernel(new NinjectSettings { InjectNonPublic = true }, modules); Debug.WriteLine("Created Kernel and Injected Modules"); Debug.WriteLine("Added Contravariant Binder"); @@ -75,10 +78,18 @@ namespace PlexRequests.UI Debug.WriteLine("Settings up Scheduler"); var scheduler = new Scheduler(); - scheduler.StartScheduler(); - //var c = kernel.Get(); - //c.Test(); + + // Reset any jobs running + var jobSettings = kernel.Get>(); + var all = jobSettings.GetAll(); + foreach (var scheduledJobse in all) + { + scheduledJobse.Running = false; + jobSettings.Update(scheduledJobse); + } + scheduler.StartScheduler(); + diff --git a/PlexRequests.UI/Validators/PlexRequestsValidator.cs b/PlexRequests.UI/Validators/PlexRequestsValidator.cs index 7d61eba2c..481364fd9 100644 --- a/PlexRequests.UI/Validators/PlexRequestsValidator.cs +++ b/PlexRequests.UI/Validators/PlexRequestsValidator.cs @@ -44,7 +44,7 @@ namespace PlexRequests.UI.Validators 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("layout", StringComparer.CurrentCultureIgnoreCase).WithMessage("You cannot use 'layout' 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."); diff --git a/PlexRequests.UI/Views/Admin/Authentication.cshtml b/PlexRequests.UI/Views/Admin/Authentication.cshtml index 74c5070f2..d8040b3ba 100644 --- a/PlexRequests.UI/Views/Admin/Authentication.cshtml +++ b/PlexRequests.UI/Views/Admin/Authentication.cshtml @@ -1,13 +1,17 @@ @using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @{ var baseUrl = Html.GetBaseUrl(); var formAction = "/admin/authentication"; + + var usermanagement = "/usermanagement"; if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) { formAction = "/" + baseUrl.ToHtmlString() + formAction; + usermanagement = "/" + baseUrl.ToHtmlString() + usermanagement; } + }
@@ -50,18 +54,10 @@
+ User Management
-

Current users that are allowed to authenticate:

- -
- -
-
- -
- -
-
+
+

A comma separated list of users that you do not want to login.

diff --git a/PlexRequests.UI/Views/Admin/CouchPotato.cshtml b/PlexRequests.UI/Views/Admin/CouchPotato.cshtml index 176ea202c..2c24670ba 100644 --- a/PlexRequests.UI/Views/Admin/CouchPotato.cshtml +++ b/PlexRequests.UI/Views/Admin/CouchPotato.cshtml @@ -1,6 +1,6 @@ @using PlexRequests.UI.Helpers @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @{ int port; if (Model.Port == 0) diff --git a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml index 2a7f788b7..ff71da313 100644 --- a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml +++ b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml @@ -2,7 +2,7 @@ @using PlexRequests.Core.Models @using PlexRequests.UI.Helpers @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @{ int port; if (Model.EmailPort == 0) diff --git a/PlexRequests.UI/Views/Admin/Headphones.cshtml b/PlexRequests.UI/Views/Admin/Headphones.cshtml index 1938bdceb..1b11c3500 100644 --- a/PlexRequests.UI/Views/Admin/Headphones.cshtml +++ b/PlexRequests.UI/Views/Admin/Headphones.cshtml @@ -1,5 +1,5 @@ @using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @{ int port; if (Model.Port == 0) diff --git a/PlexRequests.UI/Views/Admin/LandingPage.cshtml b/PlexRequests.UI/Views/Admin/LandingPage.cshtml index 9861ab792..c002b292e 100644 --- a/PlexRequests.UI/Views/Admin/LandingPage.cshtml +++ b/PlexRequests.UI/Views/Admin/LandingPage.cshtml @@ -1,5 +1,5 @@ @using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase @Html.LoadDateTimePickerAsset()
diff --git a/PlexRequests.UI/Views/Admin/Logs.cshtml b/PlexRequests.UI/Views/Admin/Logs.cshtml index 233b703c2..a28aee738 100644 --- a/PlexRequests.UI/Views/Admin/Logs.cshtml +++ b/PlexRequests.UI/Views/Admin/Logs.cshtml @@ -1,5 +1,5 @@ @using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @Html.LoadTableAssets() @{ @@ -21,7 +21,7 @@
- - @*
- -
- -
- - This is your Plex data directory location, if we cannot manually find it then you need to specify the location! See Here. - -
-
-
- -
-
*@
@@ -110,6 +95,13 @@
+
+ +
+ +
+
+
diff --git a/PlexRequests.UI/Views/Admin/PushbulletNotifications.cshtml b/PlexRequests.UI/Views/Admin/PushbulletNotifications.cshtml index 57d64329d..36d8dae24 100644 --- a/PlexRequests.UI/Views/Admin/PushbulletNotifications.cshtml +++ b/PlexRequests.UI/Views/Admin/PushbulletNotifications.cshtml @@ -1,5 +1,5 @@ @using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar")
diff --git a/PlexRequests.UI/Views/Admin/PushoverNotifications.cshtml b/PlexRequests.UI/Views/Admin/PushoverNotifications.cshtml index 545812ce3..bd4d6de81 100644 --- a/PlexRequests.UI/Views/Admin/PushoverNotifications.cshtml +++ b/PlexRequests.UI/Views/Admin/PushoverNotifications.cshtml @@ -1,5 +1,5 @@ @using PlexRequests.UI.Helpers -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar")
diff --git a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml index 2173ecf77..772eb828c 100644 --- a/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml +++ b/PlexRequests.UI/Views/Admin/SchedulerSettings.cshtml @@ -1,6 +1,6 @@ @using PlexRequests.UI.Helpers @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar")
@@ -32,6 +32,16 @@
+
+ + +
+ +
+ + +
+
diff --git a/PlexRequests.UI/Views/Admin/Settings.cshtml b/PlexRequests.UI/Views/Admin/Settings.cshtml index acebf8e31..2951b5162 100644 --- a/PlexRequests.UI/Views/Admin/Settings.cshtml +++ b/PlexRequests.UI/Views/Admin/Settings.cshtml @@ -1,6 +1,6 @@ @using PlexRequests.UI.Helpers @inherits Nancy.ViewEngines.Razor.NancyRazorViewBase -@Html.Partial("_Sidebar") +@Html.Partial("Shared/Partial/_Sidebar") @{ int port; if (Model.Port == 0) @@ -57,11 +57,17 @@
- + +
+ +
+
+
+
@@ -74,20 +80,9 @@
-
-
+ + @Html.Checkbox(Model.SearchForMovies,"SearchForMovies","Search for Movies") - @if (Model.SearchForMovies) - { - - } - else - { - - } - -
-
@@ -117,82 +112,7 @@
-
-
- @if (Model.RequireMovieApproval) - { - - } - else - { - - } -
-
-
-
- - @if (Model.RequireTvShowApproval) - { - - } - else - { - - } - - -
-
-
-
- - @if (Model.RequireMusicApproval) - { - - } - else - { - - } - - -
-
- -
-
- - @if (Model.UsersCanViewOnlyOwnRequests) - { - - - } - else - { - - } - - -
-
- -
-
- - @if (Model.UsersCanViewOnlyOwnIssues) - { - - - } - else - { - - } -
-
-
@@ -278,38 +198,25 @@
- - - - -

A comma separated list of users whose requests do not require approval (These users also do not have a request limit).

+

If the request limits are set to 0 then no request limit is applied.

- +
- +
- -

If the request limits are set to 0 then no request limit is applied.

-
- -
- +
+ +
+ +
-
- -
- -
- -
-
@@ -320,7 +227,7 @@
-
+
@@ -333,6 +240,11 @@ \ No newline at end of file diff --git a/PlexRequests.UI/Views/FaultQueue/RequestFaultQueue.cshtml b/PlexRequests.UI/Views/FaultQueue/RequestFaultQueue.cshtml new file mode 100644 index 000000000..bf44e6626 --- /dev/null +++ b/PlexRequests.UI/Views/FaultQueue/RequestFaultQueue.cshtml @@ -0,0 +1,109 @@ +@using PlexRequests.UI.Helpers +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase> +@Html.Partial("Shared/Partial/_Sidebar") +
+
+ Release Fault Queue + + + + + + + + + + + + + + @foreach (var m in Model) + { + + + + + + + + } + +
+ Request Title + + Type + + Fault Type + + LastRetry + + Error Description +
+ @m.Title + + @m.Type + + @m.FaultType + + @m.LastRetry + + @m.Message +
+ +
+
+ +@**@ \ No newline at end of file diff --git a/PlexRequests.UI/Views/Issues/Details.cshtml b/PlexRequests.UI/Views/Issues/Details.cshtml index 6bdd4f4ce..c1c8c684f 100644 --- a/PlexRequests.UI/Views/Issues/Details.cshtml +++ b/PlexRequests.UI/Views/Issues/Details.cshtml @@ -1,6 +1,7 @@ @using System.Linq @using PlexRequests.Core.Models @using PlexRequests.Helpers +@using PlexRequests.Helpers.Permissions @using PlexRequests.UI.Helpers @{ var baseUrl = Html.GetBaseUrl(); @@ -10,16 +11,8 @@ formAction = "/" + baseUrl.ToHtmlString(); } - var isAdmin = false; + var isAdmin = Html.HasAnyPermission(true, Permissions.Administrator, Permissions.ManageRequests); - if (Context.CurrentUser != null) - { - var claims = Context.CurrentUser.Claims.ToList(); - if (claims.Contains("Admin") || claims.Contains("PowerUser")) - { - isAdmin = true; - } - } }

Details

diff --git a/PlexRequests.UI/Views/Requests/Index.cshtml b/PlexRequests.UI/Views/Requests/Index.cshtml index 8909be59b..d42203059 100644 --- a/PlexRequests.UI/Views/Requests/Index.cshtml +++ b/PlexRequests.UI/Views/Requests/Index.cshtml @@ -1,19 +1,22 @@ @using Nancy.Security @using Nancy.Security +@using PlexRequests.Helpers.Permissions @using PlexRequests.UI.Helpers @using PlexRequests.UI.Resources @{ var baseUrl = Html.GetBaseUrl(); var formAction = string.Empty; + var isAdmin = Html.HasAnyPermission(true, Permissions.Administrator, Permissions.ManageRequests); if (!string.IsNullOrEmpty(baseUrl.ToHtmlString())) { formAction = "/" + baseUrl.ToHtmlString(); } }
+

@UI.Requests_Title

@UI.Requests_Paragraph

-
+
-
+
@@ -38,38 +41,59 @@
- @if (Context.CurrentUser.IsAuthenticated()) //TODO replace with IsAdmin + @if (isAdmin) { @if (Model.SearchForMovies) - { - - - } + { + + + } @if (Model.SearchForTvShows) - { - - - } + { + + + } @if (Model.SearchForMusic) - { - - - } + { + + + } }
@if (Model.SearchForMovies) - { + {
-
-
+
+
@@ -102,12 +126,12 @@ } @if (Model.SearchForTvShows) - { + {
-
-
+
+
@@ -115,12 +139,12 @@ } @if (Model.SearchForMusic) - { + {
-
-
+
+
@@ -168,38 +192,20 @@

{{title}} ({{year}})

-
- {{#if_eq type "tv"}} - @UI.Search_TV_Show_Status: - {{else}} - @UI.Search_Movie_Status: - {{/if_eq}} - {{status}} -
- -
- Request status: - {{#if available}} - @UI.Search_Available_on_plex - {{else}} - {{#if approved}} - @UI.Search_Processing_Request - {{else if denied}} - @UI.Search_Request_denied - {{#if deniedReason}} - - {{/if}} - {{else}} - @UI.Search_Pending_approval - {{/if}} - {{/if}} -

+ {{#if_eq type "tv"}} + @UI.Search_TV_Show_Status: + {{else}} + @UI.Search_Movie_Status: + {{/if_eq}} + {{status}} {{#if denied}}
Denied: - + {{#if deniedReason}} + + {{/if}}
{{/if}} @@ -208,7 +214,26 @@ {{else}}
@UI.Requests_ReleaseDate: {{releaseDate}}
{{/if_eq}} -
+ {{#unless denied}} +
+ @UI.Common_Approved: + {{#if_eq approved false}} + + {{/if_eq}} + {{#if_eq approved true}} + + {{/if_eq}} +
+ {{/unless}} +
+ @UI.Requests_Available + {{#if_eq available false}} + + {{/if_eq}} + {{#if_eq available true}} + + {{/if_eq}} +
{{#if_eq type "tv"}} {{#if episodes}} diff --git a/PlexRequests.UI/Views/Search/Index.cshtml b/PlexRequests.UI/Views/Search/Index.cshtml index 6ea284a4e..64446a264 100644 --- a/PlexRequests.UI/Views/Search/Index.cshtml +++ b/PlexRequests.UI/Views/Search/Index.cshtml @@ -111,33 +111,6 @@
} - -
-
-
-
-
- - - -
-
-
- - -
-
-
-
- -
-
-
- -
-
-
- \ No newline at end of file diff --git a/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml b/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml index 293b86626..cf7b42dc1 100644 --- a/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml +++ b/PlexRequests.UI/Views/Shared/Partial/_Navbar.cshtml @@ -2,6 +2,7 @@ @using Nancy.Session @using Nancy; @using PlexRequests.Core.SettingModels +@using PlexRequests.Helpers @using PlexRequests.UI.Helpers @using PlexRequests.UI.Models @using PlexRequests.UI.Resources @@ -35,23 +36,27 @@ @Html.GetNavbarUrl(Context, "/search", UI.Layout_Search, "search") @Html.GetNavbarUrl(Context, "/requests", UI.Layout_Requests, "plus-circle") @Html.GetNavbarUrl(Context, "/issues", UI.Layout_Issues, "exclamation", "") - @if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin + @if (Html.IsAdmin()) + { + @Html.GetNavbarUrl(Context, "/usermanagement", UI.Layout_Usermanagement, "users") + } + @*@if (Context.CurrentUser.IsAuthenticated()) // TODO replace with IsAdmin*@ + @if (Html.IsAdmin()) {
  • }
    @@ -104,16 +120,18 @@ var donateLink = $("#customDonateHref"); var donationText = $("#donationText"); donateLink.attr("href", result.url); - if(result.message) { - donationText.text(result.message); + if (result.message) { + donationText.text(result.message); } } }, - error: function(xhr, status, error) { + error: function (xhr, status, error) { console.log("error " + error); $("#customDonate").hide(); } }); + + diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Shared/Partial/_Sidebar.cshtml similarity index 88% rename from PlexRequests.UI/Views/Admin/_Sidebar.cshtml rename to PlexRequests.UI/Views/Shared/Partial/_Sidebar.cshtml index d65cd9848..595a2590e 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Shared/Partial/_Sidebar.cshtml @@ -5,6 +5,7 @@ @Html.GetSidebarUrl(Context, "/admin", "Plex Request") @Html.GetSidebarUrl(Context, "/admin/landingpage", "Landing Page") @Html.GetSidebarUrl(Context, "/admin/authentication", "Authentication") + @Html.GetSidebarUrl(Context, "/admin/usermanagementsettings", "User Management Settings") @Html.GetSidebarUrl(Context, "/admin/plex", "Plex") @Html.GetSidebarUrl(Context, "/admin/couchpotato", "CouchPotato") @Html.GetSidebarUrl(Context, "/admin/sonarr", "Sonarr") @@ -18,5 +19,6 @@ @Html.GetSidebarUrl(Context, "/admin/logs", "Logs") @Html.GetSidebarUrl(Context, "/admin/status", "Status") @Html.GetSidebarUrl(Context, "/admin/scheduledjobs", "Scheduled Jobs") + @Html.GetSidebarUrl(Context, "/admin/faultqueue", "Request Fault Queue")
    \ No newline at end of file diff --git a/PlexRequests.UI/Views/Shared/_Layout.cshtml b/PlexRequests.UI/Views/Shared/_Layout.cshtml index 86eae181c..fcd6f8d37 100644 --- a/PlexRequests.UI/Views/Shared/_Layout.cshtml +++ b/PlexRequests.UI/Views/Shared/_Layout.cshtml @@ -16,8 +16,6 @@
    - - @*@MiniProfiler.RenderIncludes()*@ @Html.GetInformationalVersion() diff --git a/PlexRequests.UI/Views/SystemStatus/Status.cshtml b/PlexRequests.UI/Views/SystemStatus/Status.cshtml new file mode 100644 index 000000000..6b1c65403 --- /dev/null +++ b/PlexRequests.UI/Views/SystemStatus/Status.cshtml @@ -0,0 +1,129 @@ +@using PlexRequests.UI.Helpers +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase +@Html.Partial("Shared/Partial/_Sidebar") + +
    +
    + Status + + +
    + + +
    + + @if (Model.Status.UpdateAvailable) + { +
    + + +
    + } + +
    + +
    + +
    + +
    +
    + +
    + + +
    + + @if (Model.Status.UpdateAvailable) + { + +
    + + } + else + { + + } + +
    + + @if (Model.Status.UpdateAvailable) + { +

    + @Model.Status.ReleaseTitle +

    +
    + + @Html.Raw(Model.Status.ReleaseNotes) + } + + +
    +
    + + \ No newline at end of file diff --git a/PlexRequests.UI/Views/UserLogin/Index.cshtml b/PlexRequests.UI/Views/UserLogin/Index.cshtml index 1dd021d7d..ce03d5e45 100644 --- a/PlexRequests.UI/Views/UserLogin/Index.cshtml +++ b/PlexRequests.UI/Views/UserLogin/Index.cshtml @@ -53,38 +53,9 @@ { - generateNotify('@nullableError',"warning"); + generateNotify('@nullableError', "warning"); } - - @*$('#loginBtn').click(function (e) { - e.preventDefault(); - var $form = $("#loginForm"); - var formData = $form.serialize(); - var dtOffset = new Date().getTimezoneOffset(); - formData += ('&DateTimeOffset=' + dtOffset); - - $.ajax({ - type: $form.prop("method"), - url: $form.prop("action"), - data: formData, - dataType: "json", - success: function (response) { - console.log(response); - if (response.result === true) { - location.replace(response.message); - - } else { - generateNotify(response.message, "warning"); - } - }, - error: function (e) { - console.log(e); - generateNotify("@UI.Javascript_SomethingWentWrong", "danger"); - } - }); - });*@ - }); \ No newline at end of file diff --git a/PlexRequests.UI/Views/UserManagement/Index.cshtml b/PlexRequests.UI/Views/UserManagement/Index.cshtml index 495dc29d5..f9eab94a4 100644 --- a/PlexRequests.UI/Views/UserManagement/Index.cshtml +++ b/PlexRequests.UI/Views/UserManagement/Index.cshtml @@ -1,55 +1,13 @@ -@using PlexRequests.UI.Helpers -@inherits PlexRequests.UI.Helpers.AngularViewBase - +@inherits PlexRequests.UI.Helpers.AngularViewBase +@using PlexRequests.UI.Helpers @Html.LoadUserManagementAssets() + +
    - - - +
    @@ -58,110 +16,14 @@

    -
    -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    +
    +
    +
    + + -
    - - -
    - -
    - -
    -
    -
    -
    - -
    - - - -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    - - Username - - - - - - Alias - - - - - - Email - - - - - Roles - - - User Type - - - - - - Last Logged In - - - -
    - {{u.username}} - - {{u.alias}} - - {{u.emailAddress}} - - {{u.claims}} - - {{u.type === 1 ? 'Local User' : 'Plex User'}} - - Details/Edit -
    +
    diff --git a/PlexRequests.UI/Views/UserManagementSettings/UserManagementSettings.cshtml b/PlexRequests.UI/Views/UserManagementSettings/UserManagementSettings.cshtml new file mode 100644 index 000000000..0334e86a1 --- /dev/null +++ b/PlexRequests.UI/Views/UserManagementSettings/UserManagementSettings.cshtml @@ -0,0 +1,69 @@ +@using PlexRequests.UI.Helpers +@inherits Nancy.ViewEngines.Razor.NancyRazorViewBase +@Html.Partial("Shared/Partial/_Sidebar") + +
    +
    +
    + User Management Settings + + Here you can manage the default permissions and features that your users get + + +

    Permissions

    + @Html.Checkbox(Model.RequestMovies, "RequestMovies", "Request Movies") + @Html.Checkbox(Model.RequestTvShows, "RequestTvShows", "Request TV Shows") + @Html.Checkbox(Model.RequestMusic, "RequestMusic", "Request Music") + @Html.Checkbox(Model.AutoApproveMovies, "AutoApproveMovies", "Auto Approve Movie Requests") + @Html.Checkbox(Model.AutoApproveTvShows, "AutoApproveTvShows", "Auto Approve TV Show Requests") + @Html.Checkbox(Model.AutoApproveMusic, "AutoApproveMusic", "Auto Approve Music Requests") + @Html.Checkbox(Model.ReportIssues, "ReportIssues", "Report Issues") + @Html.Checkbox(Model.UsersCanViewOnlyOwnIssues, "UsersCanViewOnlyOwnIssues", "Users can only view their own issues") + @Html.Checkbox(Model.UsersCanViewOnlyOwnRequests, "UsersCanViewOnlyOwnRequests", "Users can only view their own requests") + + + +

    Features

    + @Html.Checkbox(Model.RecentlyAddedNewsletter, "RecentlyAddedNewsletter", "Recently Added Newsletter") + @Html.Checkbox(Model.RecentlyAddedNotification, "RecentlyAddedNotification", "Recently Added Notifications") + +
    +
    +
    +
    + +
    +
    +
    +
    +
    + + \ No newline at end of file diff --git a/PlexRequests.UI/Views/UserWizard/Index.cshtml b/PlexRequests.UI/Views/UserWizard/Index.cshtml index 60f1f095e..cf9365e55 100644 --- a/PlexRequests.UI/Views/UserWizard/Index.cshtml +++ b/PlexRequests.UI/Views/UserWizard/Index.cshtml @@ -104,22 +104,7 @@
    -
    -
    - -
    -
    -
    -
    - -
    -
    -
    -
    - -
    -
    - +
    Next @@ -155,7 +140,7 @@