From 396bc5588abcdb2b7f5ed4daddd139e53bbf7c09 Mon Sep 17 00:00:00 2001 From: "Jamie.Rees" Date: Tue, 25 Apr 2017 16:26:56 +0100 Subject: [PATCH] Lots more work :( --- Ombi/Ombi.Core.Tests/MovieEngineTests.cs | 16 -- Ombi/Ombi.Core.Tests/Ombi.Core.Tests.csproj | 21 -- .../Ombi.Core/Engine/Interfaces/BaseEngine.cs | 1 - Ombi/Ombi.Core/Engine/RequestEngine.cs | 48 ++-- Ombi/Ombi.Core/Ombi.Core.csproj | 2 + .../Ombi.DependencyInjection/IocExtensions.cs | 7 +- .../Ombi.DependencyInjection.csproj | 2 + Ombi/Ombi.Helpers/Ombi.Helpers.csproj | 6 - .../EmailNotification.cs | 223 ++++++++++++++++++ .../Ombi.Notifications.Email.csproj | 18 ++ Ombi/Ombi.Notifications.Emil/Class1.cs | 8 + .../Ombi.Notifications.Emil.csproj | 7 + .../EmailBasicTemplate.cs | 34 +++ .../IEmailBasicTemplate.cs | 8 + .../Ombi.Notifications.Templates.csproj | 7 + .../Templates/BasicTemplate.html | 189 +++++++++++++++ Ombi/Ombi.Notifications/BaseNotification.cs | 91 +++++++ .../Interfaces/INotification.cs | 21 ++ .../Interfaces/INotificationService.cs | 18 ++ .../Models/NotificationMessage.cs | 13 + .../Models/NotificationModel.cs | 17 ++ .../Ombi.Notifications/NotificationService.cs | 78 ++++++ Ombi/Ombi.Notifications/NotificationType.cs | 16 ++ .../Ombi.Notifications.csproj | 12 + Ombi/Ombi.Schedule/IJobSetup.cs | 7 + Ombi/Ombi.Schedule/ITestJob.cs | 11 - Ombi/Ombi.Schedule/JobSetup.cs | 20 ++ Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs | 7 + Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs | 5 +- Ombi/Ombi.Schedule/Ombi.Schedule.csproj | 1 + Ombi/Ombi.Schedule/TestJob.cs | 17 -- Ombi/Ombi.Settings/Ombi.Settings.csproj | 17 ++ .../Settings/ISettingsResolver.cs | 0 .../Settings/ISettingsService.cs | 0 .../Settings/Models/External/EmbySettings.cs | 0 .../Models/External/ExternalSettings.cs | 2 +- .../Settings/Models/External/PlexSettings.cs | 0 .../Models/External/SonarrSettings.cs | 0 .../Settings/Models/LandingPageSettings.cs | 2 +- .../EmailNotificationSettings.cs | 14 ++ .../Settings/Models/OmbiSettings.cs | 2 +- .../Settings/Models/Settings.cs | 2 +- .../Settings/SettingsResolver.cs | 0 .../Settings/SettingsService.cs | 5 +- Ombi/Ombi.Store/Context/IOmbiContext.cs | 2 + Ombi/Ombi.Store/Context/OmbiContext.cs | 7 + .../Repository/SettingsJsonRepository.cs | 1 + Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj | 24 ++ .../UserIdentityManagerTests.cs | 32 +++ Ombi/Ombi.sln | 36 +++ Ombi/Ombi/Controllers/SettingsController.cs | 1 + Ombi/Ombi/Ombi.csproj | 4 + Ombi/Ombi/Startup.cs | 9 +- Ombi/Ombi/wwwroot/app/app.module.ts | 5 +- Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts | 1 + .../app/landingpage/landingpage.component.css | 5 + .../landingpage/landingpage.component.html | 26 ++ .../app/landingpage/landingpage.component.ts | 27 +++ .../wwwroot/app/services/settings.service.ts | 27 ++- .../landingpage/landingpage.component.html | 4 +- appveyor.yml | 2 +- 61 files changed, 1066 insertions(+), 122 deletions(-) delete mode 100644 Ombi/Ombi.Core.Tests/MovieEngineTests.cs delete mode 100644 Ombi/Ombi.Core.Tests/Ombi.Core.Tests.csproj create mode 100644 Ombi/Ombi.Notifications.Email/EmailNotification.cs create mode 100644 Ombi/Ombi.Notifications.Email/Ombi.Notifications.Email.csproj create mode 100644 Ombi/Ombi.Notifications.Emil/Class1.cs create mode 100644 Ombi/Ombi.Notifications.Emil/Ombi.Notifications.Emil.csproj create mode 100644 Ombi/Ombi.Notifications.Templates/EmailBasicTemplate.cs create mode 100644 Ombi/Ombi.Notifications.Templates/IEmailBasicTemplate.cs create mode 100644 Ombi/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj create mode 100644 Ombi/Ombi.Notifications.Templates/Templates/BasicTemplate.html create mode 100644 Ombi/Ombi.Notifications/BaseNotification.cs create mode 100644 Ombi/Ombi.Notifications/Interfaces/INotification.cs create mode 100644 Ombi/Ombi.Notifications/Interfaces/INotificationService.cs create mode 100644 Ombi/Ombi.Notifications/Models/NotificationMessage.cs create mode 100644 Ombi/Ombi.Notifications/Models/NotificationModel.cs create mode 100644 Ombi/Ombi.Notifications/NotificationService.cs create mode 100644 Ombi/Ombi.Notifications/NotificationType.cs create mode 100644 Ombi/Ombi.Notifications/Ombi.Notifications.csproj create mode 100644 Ombi/Ombi.Schedule/IJobSetup.cs delete mode 100644 Ombi/Ombi.Schedule/ITestJob.cs create mode 100644 Ombi/Ombi.Schedule/JobSetup.cs create mode 100644 Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs delete mode 100644 Ombi/Ombi.Schedule/TestJob.cs create mode 100644 Ombi/Ombi.Settings/Ombi.Settings.csproj rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/ISettingsResolver.cs (100%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/ISettingsService.cs (100%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/External/EmbySettings.cs (100%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/External/ExternalSettings.cs (89%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/External/PlexSettings.cs (100%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/External/SonarrSettings.cs (100%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/LandingPageSettings.cs (87%) create mode 100644 Ombi/Ombi.Settings/Settings/Models/Notifications/EmailNotificationSettings.cs rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/OmbiSettings.cs (79%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/Models/Settings.cs (63%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/SettingsResolver.cs (100%) rename Ombi/{Ombi.Core => Ombi.Settings}/Settings/SettingsService.cs (97%) create mode 100644 Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj create mode 100644 Ombi/Ombi.Tests.Core/UserIdentityManagerTests.cs create mode 100644 Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.css create mode 100644 Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.html create mode 100644 Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.ts diff --git a/Ombi/Ombi.Core.Tests/MovieEngineTests.cs b/Ombi/Ombi.Core.Tests/MovieEngineTests.cs deleted file mode 100644 index 8050b65f1..000000000 --- a/Ombi/Ombi.Core.Tests/MovieEngineTests.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Ombi.Core.Tests -{ - [TestFixture] - public class MovieEngineTests - { - - [Test] - public void Test() - { - Assert.IsTrue(true); - } - } -} diff --git a/Ombi/Ombi.Core.Tests/Ombi.Core.Tests.csproj b/Ombi/Ombi.Core.Tests/Ombi.Core.Tests.csproj deleted file mode 100644 index a3b14d63f..000000000 --- a/Ombi/Ombi.Core.Tests/Ombi.Core.Tests.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - Library - - - - netstandard1.6 - - Exe - library - - - - - - - - - - \ No newline at end of file diff --git a/Ombi/Ombi.Core/Engine/Interfaces/BaseEngine.cs b/Ombi/Ombi.Core/Engine/Interfaces/BaseEngine.cs index f5f0fb605..2c3a26938 100644 --- a/Ombi/Ombi.Core/Engine/Interfaces/BaseEngine.cs +++ b/Ombi/Ombi.Core/Engine/Interfaces/BaseEngine.cs @@ -1,6 +1,5 @@ using System.Security.Principal; using Ombi.Core.Claims; -using Ombi.Core.Settings.Models; using Ombi.Store.Entities; namespace Ombi.Core.Engine.Interfaces diff --git a/Ombi/Ombi.Core/Engine/RequestEngine.cs b/Ombi/Ombi.Core/Engine/RequestEngine.cs index ecc4834e4..c7d9d001c 100644 --- a/Ombi/Ombi.Core/Engine/RequestEngine.cs +++ b/Ombi/Ombi.Core/Engine/RequestEngine.cs @@ -4,22 +4,27 @@ using System.Globalization; using System.Linq; using System.Security.Principal; using System.Threading.Tasks; +using Hangfire; using Ombi.Api.TheMovieDb; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; using Ombi.Core.Requests.Models; using Ombi.Store.Entities; using Ombi.Helpers; +using Ombi.Notifications; +using Ombi.Notifications.Models; namespace Ombi.Core.Engine { public class RequestEngine : BaseMediaEngine, IRequestEngine { - public RequestEngine(IMovieDbApi movieApi, IRequestService requestService, IPrincipal user) : base(user, requestService) + public RequestEngine(IMovieDbApi movieApi, IRequestService requestService, IPrincipal user, INotificationService notificationService) : base(user, requestService) { MovieApi = movieApi; + NotificationService = notificationService; } private IMovieDbApi MovieApi { get; } + private INotificationService NotificationService { get; } public async Task RequestMovie(SearchMovieViewModel model) { var movieInfo = await MovieApi.GetMovieInformation(model.Id); @@ -133,16 +138,16 @@ namespace Ombi.Core.Engine { //Log.Fatal(e); //await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message); - - //await NotificationService.Publish(new NotificationModel - //{ - // DateTime = DateTime.Now, - // User = Username, - // RequestType = RequestType.Movie, - // Title = model.Title, - // NotificationType = NotificationType.ItemAddedToFaultQueue - //}); - + var notification = new NotificationModel + { + DateTime = DateTime.Now, + User = Username, + RequestType = RequestType.Movie, + Title = model.Title, + NotificationType = NotificationType.ItemAddedToFaultQueue + }; + BackgroundJob.Enqueue(() => NotificationService.Publish(notification).Wait()); + //return Response.AsJson(new JsonResponseModel //{ // Result = true, @@ -160,16 +165,17 @@ namespace Ombi.Core.Engine if (ShouldSendNotification(model.Type)) { - // var notificationModel = new NotificationModel - // { - // Title = model.Title, - // User = Username, - // DateTime = DateTime.Now, - // NotificationType = NotificationType.NewRequest, - // RequestType = model.Type, - // ImgSrc = model.Type == RequestType.Movie ? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}" : model.PosterPath - // }; - // await NotificationService.Publish(notificationModel); + var notificationModel = new NotificationModel + { + Title = model.Title, + User = Username, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = model.Type, + ImgSrc = model.Type == RequestType.Movie ? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}" : model.PosterPath + }; + + BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel).Wait()); } //var limit = await RequestLimitRepo.GetAllAsync(); diff --git a/Ombi/Ombi.Core/Ombi.Core.csproj b/Ombi/Ombi.Core/Ombi.Core.csproj index 890c99c07..1c50dc498 100644 --- a/Ombi/Ombi.Core/Ombi.Core.csproj +++ b/Ombi/Ombi.Core/Ombi.Core.csproj @@ -18,6 +18,8 @@ + + diff --git a/Ombi/Ombi.DependencyInjection/IocExtensions.cs b/Ombi/Ombi.DependencyInjection/IocExtensions.cs index 3b9444c04..9a2428187 100644 --- a/Ombi/Ombi.DependencyInjection/IocExtensions.cs +++ b/Ombi/Ombi.DependencyInjection/IocExtensions.cs @@ -17,7 +17,10 @@ using Ombi.Core.IdentityResolver; using Ombi.Core.Models.Requests; using Ombi.Core.Requests.Models; using Ombi.Core.Settings; +using Ombi.Notifications; using Ombi.Schedule; +using Ombi.Schedule.Jobs; +using Ombi.Settings.Settings; using Ombi.Store.Context; using Ombi.Store.Repository; using Ombi.TheMovieDbApi; @@ -71,13 +74,15 @@ namespace Ombi.DependencyInjection public static IServiceCollection RegisterServices(this IServiceCollection services) { services.AddTransient(); + services.AddSingleton(); return services; } public static IServiceCollection RegisterJobs(this IServiceCollection services) { - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); return services; } diff --git a/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index e623ef6d1..fa95e654a 100644 --- a/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/Ombi/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -16,7 +16,9 @@ + + \ No newline at end of file diff --git a/Ombi/Ombi.Helpers/Ombi.Helpers.csproj b/Ombi/Ombi.Helpers/Ombi.Helpers.csproj index 3fc3d4752..3f353740a 100644 --- a/Ombi/Ombi.Helpers/Ombi.Helpers.csproj +++ b/Ombi/Ombi.Helpers/Ombi.Helpers.csproj @@ -10,10 +10,4 @@ - - - ..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1\System.IdentityModel.dll - - - \ No newline at end of file diff --git a/Ombi/Ombi.Notifications.Email/EmailNotification.cs b/Ombi/Ombi.Notifications.Email/EmailNotification.cs new file mode 100644 index 000000000..9f633320c --- /dev/null +++ b/Ombi/Ombi.Notifications.Email/EmailNotification.cs @@ -0,0 +1,223 @@ +using System; +using System.Threading.Tasks; +using MailKit.Net.Smtp; +using MimeKit; +using Ombi.Core.Models.Requests; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models.Notifications; +using Ombi.Notifications.Models; +using Ombi.Notifications.Templates; + +namespace Ombi.Notifications.Email +{ + public class EmailNotification : BaseNotification + { + public EmailNotification(ISettingsService settings) : base(settings) + { + } + + public override string NotificationName => nameof(EmailNotification); + + protected override bool ValidateConfiguration(EmailNotificationSettings settings) + { + if (settings.Authentication) + { + if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword)) + { + return false; + } + } + if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString())) + { + return false; + } + + return true; + } + + protected override async Task NewRequest(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + $"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!", + $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime:f}", + model.ImgSrc); + + + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!", + To = settings.RecipientEmail, + }; + + message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime:f}"); + + await Send(message, settings); + } + + protected override async Task Issue(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + $"Ombi: New issue for {model.Title}!", + $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!", + model.ImgSrc); + + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: New issue for {model.Title}!", + To = settings.RecipientEmail, + }; + + message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!"); + + await Send(message, settings); + } + + protected override async Task AddedToRequestQueue(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + "Ombi: A request could not be added.", + $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying", + model.ImgSrc); + + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: A request could not be added", + To = settings.RecipientEmail, + }; + + message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying"); + + + await Send(message, settings); + } + + protected override async Task RequestDeclined(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + "Ombi: Your request has been declined", + $"Hello! Your request for {model.Title} has been declined, Sorry!", + model.ImgSrc); + + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: Your request has been declined", + To = model.UserEmail, + }; + + message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been declined, Sorry!"); + + + await Send(message, settings); + } + + protected override async Task RequestApproved(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + "Ombi: Your request has been approved!", + $"Hello! Your request for {model.Title} has been approved!", + model.ImgSrc); + + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: Your request has been approved!", + To = model.UserEmail, + }; + + message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been approved!"); + + await Send(message, settings); + } + + protected override async Task AvailableRequest(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + $"Ombi: {model.Title} is now available!", + $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)", + model.ImgSrc); + + + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: {model.Title} is now available!", + To = model.UserEmail, + }; + + message.Other.Add("PlainTextBody", $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)"); + + await Send(message, settings); + } + + protected override async Task Send(NotificationMessage model, EmailNotificationSettings settings) + { + try + { + var body = new BodyBuilder + { + HtmlBody = model.Message, + TextBody = model.Other["PlainTextBody"] + }; + + var message = new MimeMessage + { + Body = body.ToMessageBody(), + Subject = model.Subject + }; + message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender)); + message.To.Add(new MailboxAddress(model.To, model.To)); + + using (var client = new SmtpClient()) + { + client.Connect(settings.EmailHost, settings.EmailPort); // Let MailKit figure out the correct SecureSocketOptions. + + // Note: since we don't have an OAuth2 token, disable + // the XOAUTH2 authentication mechanism. + client.AuthenticationMechanisms.Remove("XOAUTH2"); + + if (settings.Authentication) + { + client.Authenticate(settings.EmailUsername, settings.EmailPassword); + } + //Log.Info("sending message to {0} \r\n from: {1}\r\n Are we authenticated: {2}", message.To, message.From, client.IsAuthenticated); + await client.SendAsync(message); + await client.DisconnectAsync(true); + } + } + catch (Exception e) + { + //Log.Error(e); + throw new InvalidOperationException(e.Message); + } + } + + protected override async Task Test(NotificationModel model, EmailNotificationSettings settings) + { + var email = new EmailBasicTemplate(); + var html = email.LoadTemplate( + "Test Message", + "This is just a test! Success!", + model.ImgSrc); + var message = new NotificationMessage + { + Message = html, + Subject = $"Ombi: Test", + To = settings.RecipientEmail, + }; + + message.Other.Add("PlainTextBody", "This is just a test! Success!"); + + await Send(message, settings); + } + } +} diff --git a/Ombi/Ombi.Notifications.Email/Ombi.Notifications.Email.csproj b/Ombi/Ombi.Notifications.Email/Ombi.Notifications.Email.csproj new file mode 100644 index 000000000..90d57b9cb --- /dev/null +++ b/Ombi/Ombi.Notifications.Email/Ombi.Notifications.Email.csproj @@ -0,0 +1,18 @@ + + + + netstandard1.6 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ombi/Ombi.Notifications.Emil/Class1.cs b/Ombi/Ombi.Notifications.Emil/Class1.cs new file mode 100644 index 000000000..614f80a27 --- /dev/null +++ b/Ombi/Ombi.Notifications.Emil/Class1.cs @@ -0,0 +1,8 @@ +using System; + +namespace Ombi.Notifications.Emil +{ + public class Class1 + { + } +} diff --git a/Ombi/Ombi.Notifications.Emil/Ombi.Notifications.Emil.csproj b/Ombi/Ombi.Notifications.Emil/Ombi.Notifications.Emil.csproj new file mode 100644 index 000000000..954020d10 --- /dev/null +++ b/Ombi/Ombi.Notifications.Emil/Ombi.Notifications.Emil.csproj @@ -0,0 +1,7 @@ + + + + netstandard1.4 + + + \ No newline at end of file diff --git a/Ombi/Ombi.Notifications.Templates/EmailBasicTemplate.cs b/Ombi/Ombi.Notifications.Templates/EmailBasicTemplate.cs new file mode 100644 index 000000000..36ecf40e5 --- /dev/null +++ b/Ombi/Ombi.Notifications.Templates/EmailBasicTemplate.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using System.Text; + +namespace Ombi.Notifications.Templates +{ + public class EmailBasicTemplate : IEmailBasicTemplate + { + public string TemplateLocation => Path.Combine(Directory.GetCurrentDirectory(), "Templates","BasicTemplate.html"); + + private const string SubjectKey = "{@SUBJECT}"; + private const string BodyKey = "{@BODY}"; + private const string ImgSrc = "{@IMGSRC}"; + private const string DateKey = "{@DATENOW}"; + + public string LoadTemplate(string subject, string body, string imgSrc) + { + try + { + var sb = new StringBuilder(File.ReadAllText(TemplateLocation)); + sb.Replace(SubjectKey, subject); + sb.Replace(BodyKey, body); + sb.Replace(ImgSrc, imgSrc); + sb.Replace(DateKey, DateTime.Now.ToString("f")); + + return sb.ToString(); + } + catch (Exception e) + { + return string.Empty; + } + } + } +} diff --git a/Ombi/Ombi.Notifications.Templates/IEmailBasicTemplate.cs b/Ombi/Ombi.Notifications.Templates/IEmailBasicTemplate.cs new file mode 100644 index 000000000..88f3a2fff --- /dev/null +++ b/Ombi/Ombi.Notifications.Templates/IEmailBasicTemplate.cs @@ -0,0 +1,8 @@ +namespace Ombi.Notifications.Templates +{ + public interface IEmailBasicTemplate + { + string LoadTemplate(string subject, string body, string imgSrc); + string TemplateLocation { get; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj b/Ombi/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj new file mode 100644 index 000000000..9eca54729 --- /dev/null +++ b/Ombi/Ombi.Notifications.Templates/Ombi.Notifications.Templates.csproj @@ -0,0 +1,7 @@ + + + + netstandard1.4 + + + \ No newline at end of file diff --git a/Ombi/Ombi.Notifications.Templates/Templates/BasicTemplate.html b/Ombi/Ombi.Notifications.Templates/Templates/BasicTemplate.html new file mode 100644 index 000000000..036c7cc21 --- /dev/null +++ b/Ombi/Ombi.Notifications.Templates/Templates/BasicTemplate.html @@ -0,0 +1,189 @@ + + + + + + Ombi + + + + + + + + + +
  +
+ + + + + + + + + + + +
+ + + + + + + + + + +
+ +
+

Hi there!

+

{@SUBJECT}

+

{@BODY}

+ +
+ +
+
+ + + + + + +
+
 
+ + \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/BaseNotification.cs b/Ombi/Ombi.Notifications/BaseNotification.cs new file mode 100644 index 000000000..86fd18bcf --- /dev/null +++ b/Ombi/Ombi.Notifications/BaseNotification.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading.Tasks; +using Ombi.Core.Settings; +using Ombi.Core.Settings.Models; +using Ombi.Notifications.Models; + +namespace Ombi.Notifications +{ + public abstract class BaseNotification : INotification where T : Settings.Settings.Models.Settings, new() + { + protected BaseNotification(ISettingsService settings) + { + Settings = settings; + } + + protected ISettingsService Settings { get; } + public abstract string NotificationName { get; } + + public async Task NotifyAsync(NotificationModel model) + { + var configuration = GetConfiguration(); + await NotifyAsync(model, configuration); + } + + public async Task NotifyAsync(NotificationModel model, Settings.Settings.Models.Settings settings) + { + if (settings == null) await NotifyAsync(model); + + var notificationSettings = (T)settings; + + if (!ValidateConfiguration(notificationSettings)) + { + return; + } + + try + { + switch (model.NotificationType) + { + case NotificationType.NewRequest: + await NewRequest(model, notificationSettings); + break; + case NotificationType.Issue: + await Issue(model, notificationSettings); + break; + case NotificationType.RequestAvailable: + await AvailableRequest(model, notificationSettings); + break; + case NotificationType.RequestApproved: + await RequestApproved(model, notificationSettings); + break; + case NotificationType.AdminNote: + throw new NotImplementedException(); + + case NotificationType.Test: + await Test(model, notificationSettings); + break; + case NotificationType.RequestDeclined: + await RequestDeclined(model, notificationSettings); + break; + case NotificationType.ItemAddedToFaultQueue: + await AddedToRequestQueue(model, notificationSettings); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + catch (NotImplementedException) + { + // Do nothing, it's not implimented meaning it might not be ready or even used + } + } + + private T GetConfiguration() + { + var settings = Settings.GetSettings(); + return settings; + } + + + protected abstract bool ValidateConfiguration(T settings); + protected abstract Task NewRequest(NotificationModel model, T settings); + protected abstract Task Issue(NotificationModel model, T settings); + protected abstract Task AddedToRequestQueue(NotificationModel model, T settings); + protected abstract Task RequestDeclined(NotificationModel model, T settings); + protected abstract Task RequestApproved(NotificationModel model, T settings); + protected abstract Task AvailableRequest(NotificationModel model, T settings); + protected abstract Task Send(NotificationMessage model, T settings); + protected abstract Task Test(NotificationModel model, T settings); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/Interfaces/INotification.cs b/Ombi/Ombi.Notifications/Interfaces/INotification.cs new file mode 100644 index 000000000..441373da6 --- /dev/null +++ b/Ombi/Ombi.Notifications/Interfaces/INotification.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Ombi.Core.Settings.Models; +using Ombi.Notifications.Models; + +namespace Ombi.Notifications +{ + public interface INotification + { + string NotificationName { get; } + + Task NotifyAsync(NotificationModel model); + + /// + /// Sends a notification to the user, this is usually for testing the settings. + /// + /// The model. + /// The settings. + /// + Task NotifyAsync(NotificationModel model, Settings.Settings.Models.Settings settings); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/Interfaces/INotificationService.cs b/Ombi/Ombi.Notifications/Interfaces/INotificationService.cs new file mode 100644 index 000000000..9001b299a --- /dev/null +++ b/Ombi/Ombi.Notifications/Interfaces/INotificationService.cs @@ -0,0 +1,18 @@ +using System.Collections.Concurrent; +using System.Threading.Tasks; +using Ombi.Core.Settings.Models; +using Ombi.Notifications.Models; + +namespace Ombi.Notifications +{ + public interface INotificationService + { + ConcurrentDictionary Observers { get; } + + Task Publish(NotificationModel model); + Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings); + Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type); + void Subscribe(INotification notification); + void UnSubscribe(INotification notification); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/Models/NotificationMessage.cs b/Ombi/Ombi.Notifications/Models/NotificationMessage.cs new file mode 100644 index 000000000..f14604d3f --- /dev/null +++ b/Ombi/Ombi.Notifications/Models/NotificationMessage.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Ombi.Notifications.Models +{ + public class NotificationMessage + { + public string Subject { get; set; } + public string Message { get; set; } + public string To { get; set; } + + public Dictionary Other { get; set; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/Models/NotificationModel.cs b/Ombi/Ombi.Notifications/Models/NotificationModel.cs new file mode 100644 index 000000000..80479c018 --- /dev/null +++ b/Ombi/Ombi.Notifications/Models/NotificationModel.cs @@ -0,0 +1,17 @@ +using System; +using Ombi.Store.Entities; + +namespace Ombi.Notifications.Models +{ + public class NotificationModel + { + public string Title { get; set; } + public string Body { get; set; } + public DateTime DateTime { get; set; } + public NotificationType NotificationType { get; set; } + public string User { get; set; } + public string UserEmail { get; set; } + public RequestType RequestType { get; set; } + public string ImgSrc { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/NotificationService.cs b/Ombi/Ombi.Notifications/NotificationService.cs new file mode 100644 index 000000000..f73361108 --- /dev/null +++ b/Ombi/Ombi.Notifications/NotificationService.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Core.Settings.Models; +using Ombi.Notifications.Models; + +namespace Ombi.Notifications +{ + public class NotificationService : INotificationService + { + public ConcurrentDictionary Observers { get; } = new ConcurrentDictionary(); + + /// + /// Sends a notification to the user. This one is used in normal notification scenarios + /// + /// The model. + /// + public async Task Publish(NotificationModel model) + { + var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model)); + + await Task.WhenAll(notificationTasks).ConfigureAwait(false); + } + + /// + /// Sends a notification to the user, this is usually for testing the settings. + /// + /// The model. + /// The settings. + /// + public async Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings) + { + var notificationTasks = Observers.Values.Select(notification => NotifyAsync(notification, model, settings)); + + await Task.WhenAll(notificationTasks).ConfigureAwait(false); + } + + public void Subscribe(INotification notification) + { + Observers.TryAdd(notification.NotificationName, notification); + } + + public void UnSubscribe(INotification notification) + { + Observers.TryRemove(notification.NotificationName, out notification); + } + + private async Task NotifyAsync(INotification notification, NotificationModel model) + { + try + { + await notification.NotifyAsync(model).ConfigureAwait(false); + } + catch (Exception ex) + { + } + + } + + private async Task NotifyAsync(INotification notification, NotificationModel model, Settings.Settings.Models.Settings settings) + { + try + { + await notification.NotifyAsync(model, settings).ConfigureAwait(false); + } + catch (Exception ex) + { + throw new InvalidOperationException(ex.Message); + } + } + + public async Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type) + { + await type.NotifyAsync(model, settings); + } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Notifications/NotificationType.cs b/Ombi/Ombi.Notifications/NotificationType.cs new file mode 100644 index 000000000..7954f703b --- /dev/null +++ b/Ombi/Ombi.Notifications/NotificationType.cs @@ -0,0 +1,16 @@ +using System; + +namespace Ombi.Notifications +{ + public enum NotificationType + { + NewRequest, + Issue, + RequestAvailable, + RequestApproved, + AdminNote, + Test, + RequestDeclined, + ItemAddedToFaultQueue + } +} diff --git a/Ombi/Ombi.Notifications/Ombi.Notifications.csproj b/Ombi/Ombi.Notifications/Ombi.Notifications.csproj new file mode 100644 index 000000000..d687e46fd --- /dev/null +++ b/Ombi/Ombi.Notifications/Ombi.Notifications.csproj @@ -0,0 +1,12 @@ + + + + netstandard1.6 + + + + + + + + \ No newline at end of file diff --git a/Ombi/Ombi.Schedule/IJobSetup.cs b/Ombi/Ombi.Schedule/IJobSetup.cs new file mode 100644 index 000000000..4dbc2e718 --- /dev/null +++ b/Ombi/Ombi.Schedule/IJobSetup.cs @@ -0,0 +1,7 @@ +namespace Ombi.Schedule +{ + public interface IJobSetup + { + void Setup(); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Schedule/ITestJob.cs b/Ombi/Ombi.Schedule/ITestJob.cs deleted file mode 100644 index 16b18a92c..000000000 --- a/Ombi/Ombi.Schedule/ITestJob.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Hangfire.RecurringJobExtensions; -using Hangfire.Server; - -namespace Ombi.Schedule -{ - public interface ITestJob - { - [RecurringJob("*/1 * * * *")] - void Test(PerformContext context); - } -} \ No newline at end of file diff --git a/Ombi/Ombi.Schedule/JobSetup.cs b/Ombi/Ombi.Schedule/JobSetup.cs new file mode 100644 index 000000000..04e222cc0 --- /dev/null +++ b/Ombi/Ombi.Schedule/JobSetup.cs @@ -0,0 +1,20 @@ +using System; +using Hangfire; +using Ombi.Schedule.Jobs; + +namespace Ombi.Schedule +{ + public class JobSetup : IJobSetup + { + public JobSetup(IPlexContentCacher cacher) + { + Cacher = cacher; + } + + private IPlexContentCacher Cacher { get; } + public void Setup() + { + RecurringJob.AddOrUpdate(() => Cacher.CacheContent(), Cron.Hourly, TimeZoneInfo.Utc, "Cacher"); + } + } +} diff --git a/Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs b/Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs new file mode 100644 index 000000000..44a3780f4 --- /dev/null +++ b/Ombi/Ombi.Schedule/Jobs/IPlexContentCacher.cs @@ -0,0 +1,7 @@ +namespace Ombi.Schedule.Jobs +{ + public interface IPlexContentCacher + { + void CacheContent(); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs b/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs index 092d27e7f..4b090a4c4 100644 --- a/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs +++ b/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs @@ -32,11 +32,10 @@ using Ombi.Api.Plex; using Ombi.Api.Plex.Models; using Ombi.Core.Settings; using Ombi.Core.Settings.Models.External; -using Ombi.Helpers; namespace Ombi.Schedule.Jobs { - public partial class PlexContentCacher + public partial class PlexContentCacher : IPlexContentCacher { public PlexContentCacher(ISettingsService plex, IPlexApi plexApi) { @@ -132,7 +131,7 @@ namespace Ombi.Schedule.Jobs { if (plex.Enable) { - if (plex?.Ip == null || plex?.PlexAuthToken == null) + if (string.IsNullOrEmpty(plex?.Ip) || string.IsNullOrEmpty(plex?.PlexAuthToken)) { return false; } diff --git a/Ombi/Ombi.Schedule/Ombi.Schedule.csproj b/Ombi/Ombi.Schedule/Ombi.Schedule.csproj index 5d12d2c36..1961233c0 100644 --- a/Ombi/Ombi.Schedule/Ombi.Schedule.csproj +++ b/Ombi/Ombi.Schedule/Ombi.Schedule.csproj @@ -14,6 +14,7 @@ + \ No newline at end of file diff --git a/Ombi/Ombi.Schedule/TestJob.cs b/Ombi/Ombi.Schedule/TestJob.cs deleted file mode 100644 index 66d9e22f8..000000000 --- a/Ombi/Ombi.Schedule/TestJob.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Diagnostics; -using Hangfire; -using Hangfire.RecurringJobExtensions; -using Hangfire.Server; - -namespace Ombi.Schedule -{ - public class TestJob : ITestJob - { - [RecurringJob("*/1 * * * *")] - public void Test(PerformContext context) - { - Debug.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss} TestJob1 Running ..."); - } - } -} diff --git a/Ombi/Ombi.Settings/Ombi.Settings.csproj b/Ombi/Ombi.Settings/Ombi.Settings.csproj new file mode 100644 index 000000000..4c984dffd --- /dev/null +++ b/Ombi/Ombi.Settings/Ombi.Settings.csproj @@ -0,0 +1,17 @@ + + + + netstandard1.6 + + + + + + + + + + + + + \ No newline at end of file diff --git a/Ombi/Ombi.Core/Settings/ISettingsResolver.cs b/Ombi/Ombi.Settings/Settings/ISettingsResolver.cs similarity index 100% rename from Ombi/Ombi.Core/Settings/ISettingsResolver.cs rename to Ombi/Ombi.Settings/Settings/ISettingsResolver.cs diff --git a/Ombi/Ombi.Core/Settings/ISettingsService.cs b/Ombi/Ombi.Settings/Settings/ISettingsService.cs similarity index 100% rename from Ombi/Ombi.Core/Settings/ISettingsService.cs rename to Ombi/Ombi.Settings/Settings/ISettingsService.cs diff --git a/Ombi/Ombi.Core/Settings/Models/External/EmbySettings.cs b/Ombi/Ombi.Settings/Settings/Models/External/EmbySettings.cs similarity index 100% rename from Ombi/Ombi.Core/Settings/Models/External/EmbySettings.cs rename to Ombi/Ombi.Settings/Settings/Models/External/EmbySettings.cs diff --git a/Ombi/Ombi.Core/Settings/Models/External/ExternalSettings.cs b/Ombi/Ombi.Settings/Settings/Models/External/ExternalSettings.cs similarity index 89% rename from Ombi/Ombi.Core/Settings/Models/External/ExternalSettings.cs rename to Ombi/Ombi.Settings/Settings/Models/External/ExternalSettings.cs index 5470a20e8..6504a32ba 100644 --- a/Ombi/Ombi.Core/Settings/Models/External/ExternalSettings.cs +++ b/Ombi/Ombi.Settings/Settings/Models/External/ExternalSettings.cs @@ -4,7 +4,7 @@ using Ombi.Helpers; namespace Ombi.Core.Settings.Models.External { - public abstract class ExternalSettings : Settings + public abstract class ExternalSettings : Ombi.Settings.Settings.Models.Settings { public bool Ssl { get; set; } public string SubDir { get; set; } diff --git a/Ombi/Ombi.Core/Settings/Models/External/PlexSettings.cs b/Ombi/Ombi.Settings/Settings/Models/External/PlexSettings.cs similarity index 100% rename from Ombi/Ombi.Core/Settings/Models/External/PlexSettings.cs rename to Ombi/Ombi.Settings/Settings/Models/External/PlexSettings.cs diff --git a/Ombi/Ombi.Core/Settings/Models/External/SonarrSettings.cs b/Ombi/Ombi.Settings/Settings/Models/External/SonarrSettings.cs similarity index 100% rename from Ombi/Ombi.Core/Settings/Models/External/SonarrSettings.cs rename to Ombi/Ombi.Settings/Settings/Models/External/SonarrSettings.cs diff --git a/Ombi/Ombi.Core/Settings/Models/LandingPageSettings.cs b/Ombi/Ombi.Settings/Settings/Models/LandingPageSettings.cs similarity index 87% rename from Ombi/Ombi.Core/Settings/Models/LandingPageSettings.cs rename to Ombi/Ombi.Settings/Settings/Models/LandingPageSettings.cs index a26d0a6ba..b44ed1fce 100644 --- a/Ombi/Ombi.Core/Settings/Models/LandingPageSettings.cs +++ b/Ombi/Ombi.Settings/Settings/Models/LandingPageSettings.cs @@ -3,7 +3,7 @@ using Newtonsoft.Json; namespace Ombi.Core.Settings.Models { - public class LandingPageSettings : Settings + public class LandingPageSettings : Ombi.Settings.Settings.Models.Settings { public bool Enabled { get; set; } public bool BeforeLogin { get; set; } diff --git a/Ombi/Ombi.Settings/Settings/Models/Notifications/EmailNotificationSettings.cs b/Ombi/Ombi.Settings/Settings/Models/Notifications/EmailNotificationSettings.cs new file mode 100644 index 000000000..331475d31 --- /dev/null +++ b/Ombi/Ombi.Settings/Settings/Models/Notifications/EmailNotificationSettings.cs @@ -0,0 +1,14 @@ +namespace Ombi.Core.Settings.Models.Notifications +{ + public sealed class EmailNotificationSettings : Ombi.Settings.Settings.Models.Settings + { + public bool Enabled { get; set; } + public string EmailHost { get; set; } + public string EmailPassword { get; set; } + public int EmailPort { get; set; } + public string EmailSender { get; set; } + public string EmailUsername { get; set; } + public bool Authentication { get; set; } + public string RecipientEmail { get; set; } + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Core/Settings/Models/OmbiSettings.cs b/Ombi/Ombi.Settings/Settings/Models/OmbiSettings.cs similarity index 79% rename from Ombi/Ombi.Core/Settings/Models/OmbiSettings.cs rename to Ombi/Ombi.Settings/Settings/Models/OmbiSettings.cs index 6294a43a4..ccfa1b198 100644 --- a/Ombi/Ombi.Core/Settings/Models/OmbiSettings.cs +++ b/Ombi/Ombi.Settings/Settings/Models/OmbiSettings.cs @@ -1,6 +1,6 @@ namespace Ombi.Core.Settings.Models { - public class OmbiSettings : Settings + public class OmbiSettings : Ombi.Settings.Settings.Models.Settings { public int Port { get; set; } //public string BaseUrl { get; set; } diff --git a/Ombi/Ombi.Core/Settings/Models/Settings.cs b/Ombi/Ombi.Settings/Settings/Models/Settings.cs similarity index 63% rename from Ombi/Ombi.Core/Settings/Models/Settings.cs rename to Ombi/Ombi.Settings/Settings/Models/Settings.cs index d24438223..7ecff345c 100644 --- a/Ombi/Ombi.Core/Settings/Models/Settings.cs +++ b/Ombi/Ombi.Settings/Settings/Models/Settings.cs @@ -1,4 +1,4 @@ -namespace Ombi.Core.Settings.Models +namespace Ombi.Settings.Settings.Models { public class Settings { diff --git a/Ombi/Ombi.Core/Settings/SettingsResolver.cs b/Ombi/Ombi.Settings/Settings/SettingsResolver.cs similarity index 100% rename from Ombi/Ombi.Core/Settings/SettingsResolver.cs rename to Ombi/Ombi.Settings/Settings/SettingsResolver.cs diff --git a/Ombi/Ombi.Core/Settings/SettingsService.cs b/Ombi/Ombi.Settings/Settings/SettingsService.cs similarity index 97% rename from Ombi/Ombi.Core/Settings/SettingsService.cs rename to Ombi/Ombi.Settings/Settings/SettingsService.cs index 552325e24..daa23145d 100644 --- a/Ombi/Ombi.Core/Settings/SettingsService.cs +++ b/Ombi/Ombi.Settings/Settings/SettingsService.cs @@ -1,13 +1,14 @@ using System.Threading.Tasks; using Newtonsoft.Json; +using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Store.Entities; using Ombi.Store.Repository; -namespace Ombi.Core.Settings +namespace Ombi.Settings.Settings { public class SettingsServiceV2 : ISettingsService - where T : Models.Settings, new() + where T : Ombi.Settings.Settings.Models.Settings, new() { public SettingsServiceV2(ISettingsRepository repo) diff --git a/Ombi/Ombi.Store/Context/IOmbiContext.cs b/Ombi/Ombi.Store/Context/IOmbiContext.cs index c176a3f99..c6592c9da 100644 --- a/Ombi/Ombi.Store/Context/IOmbiContext.cs +++ b/Ombi/Ombi.Store/Context/IOmbiContext.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Ombi.Store.Entities; namespace Ombi.Store.Context @@ -13,5 +14,6 @@ namespace Ombi.Store.Context DbSet Requests { get; set; } DbSet Settings { get; set; } DbSet Users { get; set; } + EntityEntry Entry(GlobalSettings settings); } } \ No newline at end of file diff --git a/Ombi/Ombi.Store/Context/OmbiContext.cs b/Ombi/Ombi.Store/Context/OmbiContext.cs index 57daca0cd..b6c72264b 100644 --- a/Ombi/Ombi.Store/Context/OmbiContext.cs +++ b/Ombi/Ombi.Store/Context/OmbiContext.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; using Ombi.Store.Entities; namespace Ombi.Store.Context @@ -13,10 +14,16 @@ namespace Ombi.Store.Context _created = true; Database.EnsureCreated(); Database.Migrate(); + + } public DbSet Requests { get; set; } public DbSet Settings { get; set; } public DbSet Users { get; set; } + public EntityEntry Entry(GlobalSettings settings) + { + return Entry(settings); + } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { diff --git a/Ombi/Ombi.Store/Repository/SettingsJsonRepository.cs b/Ombi/Ombi.Store/Repository/SettingsJsonRepository.cs index e5e0da498..cd6be08b9 100644 --- a/Ombi/Ombi.Store/Repository/SettingsJsonRepository.cs +++ b/Ombi/Ombi.Store/Repository/SettingsJsonRepository.cs @@ -76,6 +76,7 @@ namespace Ombi.Store.Repository public void Update(GlobalSettings entity) { + Db.Entry(entity).State = EntityState.Modified; Db.SaveChanges(); } } diff --git a/Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj b/Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj new file mode 100644 index 000000000..245cb749e --- /dev/null +++ b/Ombi/Ombi.Tests.Core/Ombi.Tests.Core.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp1.1 + + + + + + + + + + + + + + + + + + + + diff --git a/Ombi/Ombi.Tests.Core/UserIdentityManagerTests.cs b/Ombi/Ombi.Tests.Core/UserIdentityManagerTests.cs new file mode 100644 index 000000000..a9edae9b5 --- /dev/null +++ b/Ombi/Ombi.Tests.Core/UserIdentityManagerTests.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using Moq; +using Ombi.Core.IdentityResolver; +using Ombi.Core.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Xunit; + +namespace Ombi.Tests.Core +{ + public class UserIdentityManagerTests + { + public UserIdentityManagerTests() + { + UserRepo = new Mock(); + var mapper = new Mock(); + Manager = new UserIdentityManager(UserRepo.Object, mapper.Object); + } + private Mock UserRepo { get; } + private IUserIdentityManager Manager { get; } + + [Fact] + public async Task CredentialsValid() + { + UserRepo.Setup(x => x.GetUser("ABC")).ReturnsAsync(() => new User{Password = "4tDKIbNCZ0pMxNzSSjVbT6mG88o52x9jOixPEwQS9rg=", Salt = new byte[]{30,80,214,127,185,134,75,86,80,177,0,242,202,161,219,246}}); + + Assert.True(await Manager.CredentialsValid("ABC", "ABC")); + } + } +} diff --git a/Ombi/Ombi.sln b/Ombi/Ombi.sln index d7c88b370..b1659baca 100644 --- a/Ombi/Ombi.sln +++ b/Ombi/Ombi.sln @@ -44,6 +44,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{6F42AB98 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.TvMaze", "Ombi.Api.TvMaze\Ombi.Api.TvMaze.csproj", "{0E8EF835-E4F0-4EE5-A2B6-678DEE973721}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications", "Ombi.Notifications\Ombi.Notifications.csproj", "{E6EE2830-E4AC-4F2E-AD93-2C9305605761}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Notifications", "Notifications", "{EA30DD15-6280-4687-B370-2956EC2E54E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Email", "Ombi.Notifications.Email\Ombi.Notifications.Email.csproj", "{72DB97D7-2D60-4B96-8C57-6C0E20E892EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notifications.Templates", "Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj", "{6EE01B17-0966-4E11-8BC1-A5318A92AB1D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Tests.Core", "Ombi.Tests.Core\Ombi.Tests.Core.csproj", "{627A27A7-8879-4851-8140-38F8F5ADD6CD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Settings", "Ombi.Settings\Ombi.Settings.csproj", "{AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -102,6 +114,26 @@ Global {0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Debug|Any CPU.Build.0 = Debug|Any CPU {0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.ActiveCfg = Release|Any CPU {0E8EF835-E4F0-4EE5-A2B6-678DEE973721}.Release|Any CPU.Build.0 = Release|Any CPU + {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E6EE2830-E4AC-4F2E-AD93-2C9305605761}.Release|Any CPU.Build.0 = Release|Any CPU + {72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72DB97D7-2D60-4B96-8C57-6C0E20E892EB}.Release|Any CPU.Build.0 = Release|Any CPU + {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EE01B17-0966-4E11-8BC1-A5318A92AB1D}.Release|Any CPU.Build.0 = Release|Any CPU + {627A27A7-8879-4851-8140-38F8F5ADD6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {627A27A7-8879-4851-8140-38F8F5ADD6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {627A27A7-8879-4851-8140-38F8F5ADD6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {627A27A7-8879-4851-8140-38F8F5ADD6CD}.Release|Any CPU.Build.0 = Release|Any CPU + {AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE3AA23D-5B66-42AF-B44E-B9B4D8856C6F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -115,5 +147,9 @@ Global {08FF107D-31E1-470D-AF86-E09B015CEE06} = {9293CA11-360A-4C20-A674-B9E794431BF5} {CFB5E008-D0D0-43C0-AA06-89E49D17F384} = {9293CA11-360A-4C20-A674-B9E794431BF5} {0E8EF835-E4F0-4EE5-A2B6-678DEE973721} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {E6EE2830-E4AC-4F2E-AD93-2C9305605761} = {EA30DD15-6280-4687-B370-2956EC2E54E5} + {72DB97D7-2D60-4B96-8C57-6C0E20E892EB} = {EA30DD15-6280-4687-B370-2956EC2E54E5} + {6EE01B17-0966-4E11-8BC1-A5318A92AB1D} = {EA30DD15-6280-4687-B370-2956EC2E54E5} + {627A27A7-8879-4851-8140-38F8F5ADD6CD} = {6F42AB98-9196-44C4-B888-D5E409F415A1} EndGlobalSection EndGlobal diff --git a/Ombi/Ombi/Controllers/SettingsController.cs b/Ombi/Ombi/Controllers/SettingsController.cs index e513c8e49..deae8fec1 100644 --- a/Ombi/Ombi/Controllers/SettingsController.cs +++ b/Ombi/Ombi/Controllers/SettingsController.cs @@ -56,6 +56,7 @@ namespace Ombi.Controllers } [HttpGet("landingpage")] + [AllowAnonymous] public async Task LandingPageSettings() { return await Get(); diff --git a/Ombi/Ombi/Ombi.csproj b/Ombi/Ombi/Ombi.csproj index 6e5bddc50..1b36335d5 100644 --- a/Ombi/Ombi/Ombi.csproj +++ b/Ombi/Ombi/Ombi.csproj @@ -42,6 +42,7 @@ + @@ -57,6 +58,9 @@ + + PreserveNewest + PreserveNewest diff --git a/Ombi/Ombi/Startup.cs b/Ombi/Ombi/Startup.cs index 6bd14cde8..f739443fb 100644 --- a/Ombi/Ombi/Startup.cs +++ b/Ombi/Ombi/Startup.cs @@ -47,17 +47,15 @@ namespace Ombi services.RegisterDependencies(); // Ioc and EF services.AddSingleton(); - //services.AddTransient(new InjectionFactory(u => HttpContext.Current.User)); services.AddScoped(sp => sp.GetService().HttpContext.User); services.AddHangfire(x => { x.UseMemoryStorage(new MemoryStorageOptions()); - //using RecurringJobAttribute to build RecurringJob automatically. - x.UseRecurringJob(typeof(ITestJob)); //x.UseActivator(new IoCJobActivator(services.BuildServiceProvider())); }); + } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -79,6 +77,11 @@ namespace Ombi app.UseHangfireServer(); app.UseHangfireDashboard(); + + // Setup the scheduler + var jobSetup = (IJobSetup)app.ApplicationServices.GetService(typeof(IJobSetup)); + jobSetup.Setup(); + ConfigureAuth(app); var provider = new FileExtensionContentTypeProvider(); diff --git a/Ombi/Ombi/wwwroot/app/app.module.ts b/Ombi/Ombi/wwwroot/app/app.module.ts index 7d2d5fdf9..587f3a216 100644 --- a/Ombi/Ombi/wwwroot/app/app.module.ts +++ b/Ombi/Ombi/wwwroot/app/app.module.ts @@ -14,6 +14,7 @@ import { SearchComponent } from './search/search.component'; import { MovieSearchComponent } from './search/moviesearch.component'; import { RequestComponent } from './requests/request.component'; import { LoginComponent } from './login/login.component'; +import { LandingPageComponent } from './landingpage/landingpage.component'; import { PageNotFoundComponent } from './errors/not-found.component'; // Services @@ -42,6 +43,7 @@ const routes: Routes = [ { path: 'search', component: SearchComponent, canActivate: [AuthGuard] }, { path: 'requests', component: RequestComponent, canActivate: [AuthGuard] }, { path: 'login', component: LoginComponent }, + { path: 'landingpage', component: LandingPageComponent }, ]; @NgModule({ @@ -66,7 +68,8 @@ const routes: Routes = [ SearchComponent, RequestComponent, LoginComponent, - MovieSearchComponent + MovieSearchComponent, + LandingPageComponent ], providers: [ SearchService, diff --git a/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts b/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts index f3aa4e39c..0adcdfef7 100644 --- a/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts +++ b/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts @@ -42,6 +42,7 @@ export interface ILandingPageSettings extends ISettings { enabled: boolean, beforeLogin: boolean, afterLogin: boolean, + noticeEnabled: boolean, noticeText: string, noticeBackgroundColor: string, timeLimit: boolean, diff --git a/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.css b/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.css new file mode 100644 index 000000000..36425ca75 --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.css @@ -0,0 +1,5 @@ +.landing-box { + height: 150px; + background: #333333 !important; + border-radius: 2% +} diff --git a/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.html b/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.html new file mode 100644 index 000000000..269b353da --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.html @@ -0,0 +1,26 @@ +
+ +

Hey! Welcome back to {{websiteName}}

+ +

{{settings.noticeText}}

+
+
+ Request Something +
+
+ +
+
+ View all your requests +
+
+ +
+
+ Report an issue +
+
+ + + +
diff --git a/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.ts b/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.ts new file mode 100644 index 000000000..1d7a544db --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/landingpage/landingpage.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { SettingsService } from '../services/settings.service'; +import { ILandingPageSettings } from '../interfaces/ISettings'; + +@Component({ + selector: 'ombi', + moduleId: module.id, + templateUrl: './landingpage.component.html', + styleUrls: ['./landingpage.component.css'] +}) +export class LandingPageComponent implements OnInit { + + constructor(private settingsService: SettingsService) { } + + websiteName: string; + landingPageSettings: ILandingPageSettings; + + ngOnInit(): void { + this.settingsService.getLandingPage().subscribe(x => { + this.landingPageSettings = x; + console.log(x); + }); + this.websiteName = "Ombi"; + } + + +} \ No newline at end of file diff --git a/Ombi/Ombi/wwwroot/app/services/settings.service.ts b/Ombi/Ombi/wwwroot/app/services/settings.service.ts index 1327aa8f5..742c3b073 100644 --- a/Ombi/Ombi/wwwroot/app/services/settings.service.ts +++ b/Ombi/Ombi/wwwroot/app/services/settings.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { AuthHttp } from 'angular2-jwt'; +import { Http } from '@angular/http'; import { Observable } from 'rxjs/Rx'; import { ServiceAuthHelpers } from './service.helpers'; @@ -7,47 +8,49 @@ import { IOmbiSettings, IEmbySettings, IPlexSettings, ISonarrSettings,ILandingPa @Injectable() export class SettingsService extends ServiceAuthHelpers { - constructor(http: AuthHttp) { - super(http, '/api/v1/Settings/'); + constructor(public httpAuth: AuthHttp, private nonAuthHttp: Http) { + super(httpAuth, '/api/v1/Settings/'); } getOmbi(): Observable { - return this.http.get(`${this.url}/Ombi/`).map(this.extractData); + return this.httpAuth.get(`${this.url}/Ombi/`).map(this.extractData); } saveOmbi(settings: IOmbiSettings): Observable { - return this.http.post(`${this.url}/Ombi/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); + return this.httpAuth.post(`${this.url}/Ombi/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); } getEmby(): Observable { - return this.http.get(`${this.url}/Emby/`).map(this.extractData); + return this.httpAuth.get(`${this.url}/Emby/`).map(this.extractData); } saveEmby(settings: IEmbySettings): Observable { - return this.http.post(`${this.url}/Emby/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); + return this.httpAuth.post(`${this.url}/Emby/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); } getPlex(): Observable { - return this.http.get(`${this.url}/Plex/`).map(this.extractData); + return this.httpAuth.get(`${this.url}/Plex/`).map(this.extractData); } savePlex(settings: IPlexSettings): Observable { - return this.http.post(`${this.url}/Plex/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); + return this.httpAuth.post(`${this.url}/Plex/`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); } getSonarr(): Observable { - return this.http.get(`${this.url}/Sonarr`).map(this.extractData); + return this.httpAuth.get(`${this.url}/Sonarr`).map(this.extractData); } saveSonarr(settings: ISonarrSettings): Observable { - return this.http.post(`${this.url}/Sonarr`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); + return this.httpAuth.post(`${this.url}/Sonarr`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); } + + // Using http since we need it not to be authenticated to get the landing page settings getLandingPage(): Observable { - return this.http.get(`${this.url}/LandingPage`).map(this.extractData); + return this.nonAuthHttp.get(`${this.url}/LandingPage`).map(this.extractData); } saveLandingPage(settings: ILandingPageSettings): Observable { - return this.http.post(`${this.url}/LandingPage`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); + return this.httpAuth.post(`${this.url}/LandingPage`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData); } } \ No newline at end of file diff --git a/Ombi/Ombi/wwwroot/app/settings/landingpage/landingpage.component.html b/Ombi/Ombi/wwwroot/app/settings/landingpage/landingpage.component.html index 236b899d1..95a01b01f 100644 --- a/Ombi/Ombi/wwwroot/app/settings/landingpage/landingpage.component.html +++ b/Ombi/Ombi/wwwroot/app/settings/landingpage/landingpage.component.html @@ -1,11 +1,11 @@ -
+
Landing Page Configuration
- +
diff --git a/appveyor.yml b/appveyor.yml index 69f0d1d28..e0c0516df 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,12 +14,12 @@ before_build: - appveyor-retry dotnet restore - appveyor-retry npm install bower -g - appveyor-retry npm install -g gulp -#- appveyor-retry npm install -g typescript - appveyor-retry npm install - appveyor-retry bower install - gulp publish build_script: - dotnet build +- dotnet xunit after_build: - dotnet publish -c Release -r win10-x64 - dotnet publish -c Release -r osx.10.12-x64