From aa5304b8dda9d69522d8fee2eb0dc5f61fa16069 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 17 Mar 2016 08:54:40 +0000 Subject: [PATCH 01/10] Added validation to the Email settings, also increased the availability checker from 2 minutes to 5 --- .../EmailNotificationSettings.cs | 1 - .../AvailabilityUpdateService.cs | 4 +- .../Interfaces/IIntervals.cs | 2 - PlexRequests.Services/UpdateInterval.cs | 4 +- PlexRequests.UI/Modules/AdminModule.cs | 5 ++ PlexRequests.UI/PlexRequests.UI.csproj | 1 + .../EmailNotificationSettingsValidator.cs | 46 ++++++++++++++++ .../Views/Admin/EmailNotifications.cshtml | 52 +++++++++++++------ 8 files changed, 90 insertions(+), 25 deletions(-) create mode 100644 PlexRequests.UI/Validators/EmailNotificationSettingsValidator.cs diff --git a/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs b/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs index 059f8c628..3c81d83cf 100644 --- a/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs +++ b/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs @@ -4,7 +4,6 @@ { public string EmailHost { get; set; } public int EmailPort { get; set; } - public bool EmailAuthentication { get; set; } public string RecipientEmail { get; set; } public string EmailUsername { get; set; } public string EmailPassword { get; set; } diff --git a/PlexRequests.Services/AvailabilityUpdateService.cs b/PlexRequests.Services/AvailabilityUpdateService.cs index cbdaeea53..c32573d7a 100644 --- a/PlexRequests.Services/AvailabilityUpdateService.cs +++ b/PlexRequests.Services/AvailabilityUpdateService.cs @@ -26,8 +26,6 @@ #endregion using System; using System.Reactive.Linq; -using System.Runtime.Remoting.Messaging; -using System.Threading.Tasks; using System.Web.Hosting; using FluentScheduler; @@ -76,7 +74,7 @@ namespace PlexRequests.Services public void Stop(bool immediate) { - throw new System.NotImplementedException(); + HostingEnvironment.UnregisterObject(this); } } diff --git a/PlexRequests.Services/Interfaces/IIntervals.cs b/PlexRequests.Services/Interfaces/IIntervals.cs index 260017689..09e13df08 100644 --- a/PlexRequests.Services/Interfaces/IIntervals.cs +++ b/PlexRequests.Services/Interfaces/IIntervals.cs @@ -30,8 +30,6 @@ namespace PlexRequests.Services.Interfaces { public interface IIntervals { - TimeSpan CriticalNotification { get; } // notification interval for critical load - TimeSpan Measurement { get; } // how often to measure TimeSpan Notification { get; } // notification interval for high load } } \ No newline at end of file diff --git a/PlexRequests.Services/UpdateInterval.cs b/PlexRequests.Services/UpdateInterval.cs index 64c65610a..876b9e379 100644 --- a/PlexRequests.Services/UpdateInterval.cs +++ b/PlexRequests.Services/UpdateInterval.cs @@ -32,9 +32,7 @@ namespace PlexRequests.Services { public class UpdateInterval : IIntervals { - public TimeSpan Measurement => TimeSpan.FromSeconds(1); - public TimeSpan CriticalNotification { get; } - public TimeSpan Notification => TimeSpan.FromMinutes(2); + public TimeSpan Notification => TimeSpan.FromMinutes(5); } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index cba488a80..69a543708 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -294,6 +294,11 @@ namespace PlexRequests.UI.Modules private Response SaveEmailNotifications() { var settings = this.Bind(); + var valid = this.Validate(settings); + if (!valid.IsValid) + { + return Response.AsJson(valid.SendJsonError()); + } Log.Trace(settings.DumpJson()); var result = EmailService.SaveSettings(settings); diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index ec6b7bfdf..c60df4f92 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -161,6 +161,7 @@ + diff --git a/PlexRequests.UI/Validators/EmailNotificationSettingsValidator.cs b/PlexRequests.UI/Validators/EmailNotificationSettingsValidator.cs new file mode 100644 index 000000000..be5c106e7 --- /dev/null +++ b/PlexRequests.UI/Validators/EmailNotificationSettingsValidator.cs @@ -0,0 +1,46 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SonarrValidator.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 FluentValidation; + +using PlexRequests.Core.SettingModels; + +namespace PlexRequests.UI.Validators +{ + public class EmailNotificationSettingsValidator : AbstractValidator + { + public EmailNotificationSettingsValidator() + { + RuleFor(request => request.EmailHost).NotEmpty().WithMessage("You must specify a Host name."); + RuleFor(request => request.EmailPort).NotEmpty().WithMessage("You must specify a Port."); + RuleFor(request => request.RecipientEmail).NotEmpty().WithMessage("You must specify a Recipient."); + RuleFor(request => request.RecipientEmail).EmailAddress().WithMessage("You must specify a valid Recipient."); + RuleFor(request => request.EmailUsername).EmailAddress().WithMessage("You must specify a valid Username."); + RuleFor(request => request.EmailUsername).NotEmpty().WithMessage("You must specify a Username."); + RuleFor(request => request.EmailPassword).NotEmpty().WithMessage("You must specify a valid password."); + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml index 90c17038d..405a96e25 100644 --- a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml +++ b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml @@ -52,21 +52,6 @@ -
-
- -
-
-
@@ -84,10 +69,45 @@
- +
+ \ No newline at end of file From b9a886d5dc0873cf163dd49432e2273ad6e6ab54 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 17 Mar 2016 13:32:58 +0000 Subject: [PATCH 02/10] Added first implimentation of the Notification Service #8 Added tests to cover the notification service --- .../EmailNotificationSettings.cs | 1 + .../NotificationServiceTests.cs | 116 ++++++++++++++++++ .../PlexRequests.Services.Tests.csproj | 1 + .../Notification/EmailMessageNotification.cs | 108 ++++++++++++++++ .../Notification/INotification.cs | 46 +++++++ .../Notification/NotificationService.cs | 78 ++++++++++++ .../PlexRequests.Services.csproj | 3 + PlexRequests.UI/Bootstrapper.cs | 18 ++- PlexRequests.UI/Modules/AdminModule.cs | 8 +- PlexRequests.UI/Modules/SearchModule.cs | 20 ++- .../Views/Admin/EmailNotifications.cshtml | 14 +++ PlexRequests.UI/Views/Admin/_Sidebar.cshtml | 4 +- README.md | 3 + 13 files changed, 410 insertions(+), 10 deletions(-) create mode 100644 PlexRequests.Services.Tests/NotificationServiceTests.cs create mode 100644 PlexRequests.Services/Notification/EmailMessageNotification.cs create mode 100644 PlexRequests.Services/Notification/INotification.cs create mode 100644 PlexRequests.Services/Notification/NotificationService.cs diff --git a/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs b/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs index 3c81d83cf..e725b0f0c 100644 --- a/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs +++ b/PlexRequests.Core/SettingModels/EmailNotificationSettings.cs @@ -4,6 +4,7 @@ { public string EmailHost { get; set; } public int EmailPort { get; set; } + public bool Ssl { get; set; } public string RecipientEmail { get; set; } public string EmailUsername { get; set; } public string EmailPassword { get; set; } diff --git a/PlexRequests.Services.Tests/NotificationServiceTests.cs b/PlexRequests.Services.Tests/NotificationServiceTests.cs new file mode 100644 index 000000000..ce5bf23ae --- /dev/null +++ b/PlexRequests.Services.Tests/NotificationServiceTests.cs @@ -0,0 +1,116 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: NotificationServiceTests.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 Moq; + +using NUnit.Framework; + +using PlexRequests.Services.Notification; + +namespace PlexRequests.Services.Tests +{ + [TestFixture] + public class NotificationServiceTests + { + + [Test] + public void SubscribeNewNotifier() + { + var notificationMock = new Mock(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock.Object); + + Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + } + + [Test] + public void SubscribeExistingNotifier() + { + var notificationMock1 = new Mock(); + var notificationMock2 = new Mock(); + notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1"); + notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock1.Object); + + Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + + NotificationService.Subscribe(notificationMock2.Object); + + Assert.That(NotificationService.Observers["Notification1"], Is.Not.Null); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + } + + [Test] + public void UnSubscribeMissingNotifier() + { + var notificationMock = new Mock(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.UnSubscribe(notificationMock.Object); + + Assert.That(NotificationService.Observers.Count, Is.EqualTo(0)); + } + + [Test] + public void UnSubscribeNotifier() + { + var notificationMock = new Mock(); + notificationMock.SetupGet(x => x.NotificationName).Returns("Notification1"); + NotificationService.Subscribe(notificationMock.Object); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(1)); + + NotificationService.UnSubscribe(notificationMock.Object); + Assert.That(NotificationService.Observers.Count, Is.EqualTo(0)); + } + + [Test] + public void PublishWithNoObservers() + { + Assert.DoesNotThrow( + () => + { NotificationService.Publish(string.Empty, string.Empty); }); + } + + [Test] + public void PublishAllNotifiers() + { + var notificationMock1 = new Mock(); + var notificationMock2 = new Mock(); + notificationMock1.SetupGet(x => x.NotificationName).Returns("Notification1"); + notificationMock2.SetupGet(x => x.NotificationName).Returns("Notification2"); + NotificationService.Subscribe(notificationMock1.Object); + NotificationService.Subscribe(notificationMock2.Object); + + Assert.That(NotificationService.Observers.Count, Is.EqualTo(2)); + + NotificationService.Publish("a","b"); + + notificationMock1.Verify(x => x.Notify("a","b"), Times.Once); + notificationMock2.Verify(x => x.Notify("a","b"), Times.Once); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj b/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj index 175b4f0f8..5fc159440 100644 --- a/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj +++ b/PlexRequests.Services.Tests/PlexRequests.Services.Tests.csproj @@ -55,6 +55,7 @@ + diff --git a/PlexRequests.Services/Notification/EmailMessageNotification.cs b/PlexRequests.Services/Notification/EmailMessageNotification.cs new file mode 100644 index 000000000..2c5c84158 --- /dev/null +++ b/PlexRequests.Services/Notification/EmailMessageNotification.cs @@ -0,0 +1,108 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: EmailMessageNotification.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.Net; +using System.Net.Mail; + +using NLog; + +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; + +namespace PlexRequests.Services.Notification +{ + public class EmailMessageNotification : INotification + { + public EmailMessageNotification(ISettingsService settings) + { + EmailNotificationSettings = settings; + } + + private static Logger Log = LogManager.GetCurrentClassLogger(); + private ISettingsService EmailNotificationSettings { get; } + public string NotificationName => "EmailMessageNotification"; + public bool Notify(string title, string requester) + { + var configuration = GetConfiguration(); + if (!ValidateConfiguration(configuration)) + { + return false; + } + + var message = new MailMessage + { + IsBodyHtml = true, + To = { new MailAddress(configuration.RecipientEmail) }, + Body = $"User {requester} has requested {title}!", + From = new MailAddress(configuration.EmailUsername), + Subject = $"New Request for {title}!" + }; + + try + { + using (var smtp = new SmtpClient(configuration.EmailHost, configuration.EmailPort)) + { + smtp.Credentials = new NetworkCredential(configuration.EmailUsername, configuration.EmailPassword); + smtp.EnableSsl = configuration.Ssl; + smtp.Send(message); + return true; + } + } + catch (SmtpException smtp) + { + Log.Fatal(smtp); + } + catch (Exception e) + { + Log.Fatal(e); + } + return false; + } + + private EmailNotificationSettings GetConfiguration() + { + var settings = EmailNotificationSettings.GetSettings(); + return settings; + } + + private bool ValidateConfiguration(EmailNotificationSettings settings) + { + if (!settings.Enabled) + { + return false; + } + if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.EmailUsername) + || string.IsNullOrEmpty(settings.EmailPassword) || string.IsNullOrEmpty(settings.RecipientEmail) + || string.IsNullOrEmpty(settings.EmailPort.ToString())) + { + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Notification/INotification.cs b/PlexRequests.Services/Notification/INotification.cs new file mode 100644 index 000000000..6b85bd178 --- /dev/null +++ b/PlexRequests.Services/Notification/INotification.cs @@ -0,0 +1,46 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: INotification.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.Services.Notification +{ + public interface INotification + { + /// + /// Gets the name of the notification. + /// + /// + /// The name of the notification. + /// + string NotificationName { get; } + /// + /// Notifies the specified title. + /// + /// The title. + /// The requester. + /// + bool Notify(string title, string requester); + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Notification/NotificationService.cs b/PlexRequests.Services/Notification/NotificationService.cs new file mode 100644 index 000000000..4afba12bf --- /dev/null +++ b/PlexRequests.Services/Notification/NotificationService.cs @@ -0,0 +1,78 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: NotificationService.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System.Collections.Generic; +using System.Threading; + +namespace PlexRequests.Services.Notification +{ + public static class NotificationService + { + public static Dictionary Observers { get; } + + static NotificationService() + { + Observers = new Dictionary(); + } + + public static void Publish(string title, string requester) + { + foreach (var observer in Observers) + { + var notification = observer.Value; + + new Thread(() => + { + Thread.CurrentThread.IsBackground = true; + notification.Notify(title, requester); + }).Start(); + } + } + + public static void Subscribe(INotification notification) + { + INotification notificationValue; + if (Observers.TryGetValue(notification.NotificationName, out notificationValue)) + { + // Observer already exists + return; + } + + Observers[notification.NotificationName] = notification; + } + + public static void UnSubscribe(INotification notification) + { + INotification notificationValue; + if (!Observers.TryGetValue(notification.NotificationName, out notificationValue)) + { + // Observer doesn't exists + return; + } + Observers.Remove(notification.NotificationName); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index 3a18c5a04..ef5bc886b 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -77,6 +77,9 @@ + + + diff --git a/PlexRequests.UI/Bootstrapper.cs b/PlexRequests.UI/Bootstrapper.cs index 372117541..c6cc21598 100644 --- a/PlexRequests.UI/Bootstrapper.cs +++ b/PlexRequests.UI/Bootstrapper.cs @@ -44,6 +44,7 @@ using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Services; using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Notification; using PlexRequests.Store; using PlexRequests.Store.Repository; using PlexRequests.UI.Jobs; @@ -86,19 +87,20 @@ namespace PlexRequests.UI container.Register(); + + SubscribeAllObservers(container); base.ConfigureRequestContainer(container, context); } protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines) { - TaskManager.TaskFactory = new Jobs.PlexTaskFactory(); + TaskManager.TaskFactory = new PlexTaskFactory(); TaskManager.Initialize(new PlexRegistry()); CookieBasedSessions.Enable(pipelines, CryptographyConfiguration.Default); StaticConfiguration.DisableErrorTraces = false; - base.ApplicationStartup(container, pipelines); // Enable forms auth @@ -109,8 +111,20 @@ namespace PlexRequests.UI }; FormsAuthentication.Enable(pipelines, formsAuthConfiguration); + } + protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration { Password = @"password" }; + + private void SubscribeAllObservers(TinyIoCContainer container) + { + var emailSettingsService = container.Resolve>(); + var emailSettings = emailSettingsService.GetSettings(); + if (emailSettings.Enabled) + { + NotificationService.Subscribe(new EmailMessageNotification(emailSettingsService)); + } + } } } \ No newline at end of file diff --git a/PlexRequests.UI/Modules/AdminModule.cs b/PlexRequests.UI/Modules/AdminModule.cs index 69a543708..457922db2 100644 --- a/PlexRequests.UI/Modules/AdminModule.cs +++ b/PlexRequests.UI/Modules/AdminModule.cs @@ -40,6 +40,7 @@ using PlexRequests.Api.Interfaces; using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; +using PlexRequests.Services.Notification; using PlexRequests.UI.Helpers; using PlexRequests.UI.Models; @@ -302,8 +303,13 @@ namespace PlexRequests.UI.Modules Log.Trace(settings.DumpJson()); var result = EmailService.SaveSettings(settings); + + NotificationService.Subscribe(new EmailMessageNotification(EmailService)); + Log.Info("Saved email settings, result: {0}", result); - return Context.GetRedirect("~/admin/emailnotification"); + return Response.AsJson(result + ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Email Notifications!" } + : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); } private Negotiator Status() diff --git a/PlexRequests.UI/Modules/SearchModule.cs b/PlexRequests.UI/Modules/SearchModule.cs index cf9c81531..e96a5e753 100644 --- a/PlexRequests.UI/Modules/SearchModule.cs +++ b/PlexRequests.UI/Modules/SearchModule.cs @@ -37,6 +37,7 @@ using PlexRequests.Core; using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Notification; using PlexRequests.Store; using PlexRequests.UI.Models; @@ -235,7 +236,8 @@ namespace PlexRequests.UI.Modules { Log.Debug("Adding movie to database requests"); var id = RequestService.AddRequest(movieId, model); - //BackgroundJob.Enqueue(() => Checker.CheckAndUpdate(model.Title, (int)id)); + + NotificationService.Publish(model.Title, model.RequestedBy); return Response.AsJson(new JsonResponseModel { Result = true }); } @@ -291,7 +293,6 @@ namespace PlexRequests.UI.Modules LatestTv = latest }; - RequestService.AddRequest(showId, model); var settings = PrService.GetSettings(); if (!settings.RequireApproval) @@ -302,11 +303,20 @@ namespace PlexRequests.UI.Modules var result = SonarrApi.AddSeries(model.ProviderId, model.Title, qualityProfile, sonarrSettings.SeasonFolders, sonarrSettings.RootPath, model.LatestTv, sonarrSettings.ApiKey, sonarrSettings.FullUri); - Log.Info("Added series {0} to Sonarr, Result: {1}", model.Title, result); - Log.Trace("Model sent to Sonarr: "); - Log.Trace(model.DumpJson()); + if (result != null) + { + model.Approved = true; + Log.Debug("Adding tv to database requests (No approval required)"); + RequestService.AddRequest(showId, model); + + return Response.AsJson(new JsonResponseModel { Result = true }); + } + return Response.AsJson(new JsonResponseModel { Result = false, Message = "Something went wrong adding the movie to CouchPotato! Please check your settings." }); } + RequestService.AddRequest(showId, model); + NotificationService.Publish(model.Title, model.RequestedBy); + return Response.AsJson(new { Result = true }); } private string GetTvDbAuthToken(TheTvDbApi api) diff --git a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml index 405a96e25..6e0543aea 100644 --- a/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml +++ b/PlexRequests.UI/Views/Admin/EmailNotifications.cshtml @@ -29,6 +29,20 @@
+
+
+ +
+
diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml index c2b594da8..88726ee09 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml @@ -46,14 +46,14 @@ } @*Sickbeard Settings*@ - @*@if (Context.Request.Path == "/admin/emailnotification") + @if (Context.Request.Path == "/admin/emailnotification") { Email Notifications } else { Email Notifications - }*@ + } @if (Context.Request.Path == "/admin/status") { diff --git a/README.md b/README.md index 000cac51c..4ee857e4d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,9 @@ [![Gitter](https://badges.gitter.im/tidusjar/PlexRequest.NET.svg)](https://gitter.im/tidusjar/PlexRequests.Net?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Build status](https://ci.appveyor.com/api/projects/status/hgj8j6lcea7j0yhn?svg=true)](https://ci.appveyor.com/project/tidusjar/requestplex) [![Linux Status](https://travis-ci.org/tidusjar/PlexRequests.Net.svg)](https://travis-ci.org/tidusjar/PlexRequests.Net) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/tidusjar/plexrequests.net.svg)](http://isitmaintained.com/project/tidusjar/plexrequests.net "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/tidusjar/plexrequests.net.svg)](http://isitmaintained.com/project/tidusjar/plexrequests.net "Percentage of issues still open") +[![Github All Releases](https://img.shields.io/github/downloads/tidusjar/PlexRequests.net/total.svg)]() This is based off [Plex Requests by lokenx](https://github.com/lokenx/plexrequests-meteor) so big props to that guy! I wanted to write a similar application in .Net! From f046c5d39b5c651383cdcb1533263fee4afa69ae Mon Sep 17 00:00:00 2001 From: tidusjar Date: Thu, 17 Mar 2016 14:34:55 +0000 Subject: [PATCH 03/10] Added Pushbullet notifications #8 --- PlexRequests.Api.Interfaces/IPushbulletApi.cs | 43 +++++++ .../PlexRequests.Api.Interfaces.csproj | 1 + .../Movie/CouchPotatoAdd.cs | 112 +++++++++--------- .../Notifications/PushbulletPush.cs | 38 ++++++ .../Notifications/PushbulletResponse.cs | 48 ++++++++ .../PlexRequests.Api.Models.csproj | 9 ++ PlexRequests.Api.Models/packages.config | 4 + PlexRequests.Api/PlexRequests.Api.csproj | 1 + PlexRequests.Api/PushbulletApi.cs | 63 ++++++++++ PlexRequests.Api/app.config | 8 +- PlexRequests.Core/PlexRequests.Core.csproj | 1 + .../PushBulletNotificationSettings.cs | 9 ++ .../Notification/NotificationService.cs | 12 ++ .../Notification/PushbulletNotification.cs | 80 +++++++++++++ .../PlexRequests.Services.csproj | 1 + PlexRequests.UI/Bootstrapper.cs | 9 ++ PlexRequests.UI/Modules/AdminModule.cs | 38 +++++- PlexRequests.UI/PlexRequests.UI.csproj | 4 + .../Validators/PushbulletSettingsValidator.cs | 40 +++++++ .../Admin/PushbulletNotifications.cshtml | 74 ++++++++++++ 20 files changed, 534 insertions(+), 61 deletions(-) create mode 100644 PlexRequests.Api.Interfaces/IPushbulletApi.cs create mode 100644 PlexRequests.Api.Models/Notifications/PushbulletPush.cs create mode 100644 PlexRequests.Api.Models/Notifications/PushbulletResponse.cs create mode 100644 PlexRequests.Api.Models/packages.config create mode 100644 PlexRequests.Api/PushbulletApi.cs create mode 100644 PlexRequests.Core/SettingModels/PushBulletNotificationSettings.cs create mode 100644 PlexRequests.Services/Notification/PushbulletNotification.cs create mode 100644 PlexRequests.UI/Validators/PushbulletSettingsValidator.cs create mode 100644 PlexRequests.UI/Views/Admin/PushbulletNotifications.cshtml diff --git a/PlexRequests.Api.Interfaces/IPushbulletApi.cs b/PlexRequests.Api.Interfaces/IPushbulletApi.cs new file mode 100644 index 000000000..647280aaf --- /dev/null +++ b/PlexRequests.Api.Interfaces/IPushbulletApi.cs @@ -0,0 +1,43 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: IPushbulletApi.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.Api.Models.Notifications; + +namespace PlexRequests.Api.Interfaces +{ + public interface IPushbulletApi + { + /// + /// Pushes the specified message. + /// + /// The access token. + /// The title. + /// The message. + /// The device identifier. + /// + PushbulletResponse Push(string accessToken, string title, string message, string deviceIdentifier = default(string)); + } +} \ No newline at end of file diff --git a/PlexRequests.Api.Interfaces/PlexRequests.Api.Interfaces.csproj b/PlexRequests.Api.Interfaces/PlexRequests.Api.Interfaces.csproj index 42a2cfe0c..b76996490 100644 --- a/PlexRequests.Api.Interfaces/PlexRequests.Api.Interfaces.csproj +++ b/PlexRequests.Api.Interfaces/PlexRequests.Api.Interfaces.csproj @@ -48,6 +48,7 @@ + diff --git a/PlexRequests.Api.Models/Movie/CouchPotatoAdd.cs b/PlexRequests.Api.Models/Movie/CouchPotatoAdd.cs index 2e98a415f..5a2e228b9 100644 --- a/PlexRequests.Api.Models/Movie/CouchPotatoAdd.cs +++ b/PlexRequests.Api.Models/Movie/CouchPotatoAdd.cs @@ -19,65 +19,65 @@ namespace PlexRequests.Api.Models.Movie -public class Images -{ - public List disc_art { get; set; } - public List poster { get; set; } - public List extra_thumbs { get; set; } - public List poster_original { get; set; } - public List landscape { get; set; } - public string[] actors { get; set; } - public List backdrop_original { get; set; } - public List clear_art { get; set; } - public List logo { get; set; } - public List banner { get; set; } - public List backdrop { get; set; } - public List extra_fanart { get; set; } -} + public class Images + { + public List disc_art { get; set; } + public List poster { get; set; } + public List extra_thumbs { get; set; } + public List poster_original { get; set; } + public List landscape { get; set; } + public string[] actors { get; set; } + public List backdrop_original { get; set; } + public List clear_art { get; set; } + public List logo { get; set; } + public List banner { get; set; } + public List backdrop { get; set; } + public List extra_fanart { get; set; } + } -public class Info -{ - public Rating rating { get; set; } - public List genres { get; set; } - public int tmdb_id { get; set; } - public string plot { get; set; } - public string tagline { get; set; } - public string original_title { get; set; } - public string[] actor_roles { get; set; } - public bool via_imdb { get; set; } - public string mpaa { get; set; } - public bool via_tmdb { get; set; } - public List directors { get; set; } - public List titles { get; set; } - public string imdb { get; set; } - public int year { get; set; } - public Images images { get; set; } - public List actors { get; set; } - public List writers { get; set; } - public int runtime { get; set; } - public string type { get; set; } - public string released { get; set; } -} + public class Info + { + public Rating rating { get; set; } + public List genres { get; set; } + public int tmdb_id { get; set; } + public string plot { get; set; } + public string tagline { get; set; } + public string original_title { get; set; } + public string[] actor_roles { get; set; } + public bool via_imdb { get; set; } + public string mpaa { get; set; } + public bool via_tmdb { get; set; } + public List directors { get; set; } + public List titles { get; set; } + public string imdb { get; set; } + public int year { get; set; } + public Images images { get; set; } + public List actors { get; set; } + public List writers { get; set; } + public int runtime { get; set; } + public string type { get; set; } + public string released { get; set; } + } -public class Identifiers -{ - public string imdb { get; set; } -} + public class Identifiers + { + public string imdb { get; set; } + } -public class Movie -{ - public string status { get; set; } - public Info info { get; set; } - public string _t { get; set; } - public List releases { get; set; } - public string title { get; set; } - public string _rev { get; set; } - public string profile_id { get; set; } - public string _id { get; set; } - public object category_id { get; set; } - public string type { get; set; } - public Identifiers identifiers { get; set; } -} + public class Movie + { + public string status { get; set; } + public Info info { get; set; } + public string _t { get; set; } + public List releases { get; set; } + public string title { get; set; } + public string _rev { get; set; } + public string profile_id { get; set; } + public string _id { get; set; } + public object category_id { get; set; } + public string type { get; set; } + public Identifiers identifiers { get; set; } + } } diff --git a/PlexRequests.Api.Models/Notifications/PushbulletPush.cs b/PlexRequests.Api.Models/Notifications/PushbulletPush.cs new file mode 100644 index 000000000..d2f63efd1 --- /dev/null +++ b/PlexRequests.Api.Models/Notifications/PushbulletPush.cs @@ -0,0 +1,38 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PushbulletPush.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 Newtonsoft.Json; + +namespace PlexRequests.Api.Models.Notifications +{ + public class PushbulletPush + { + public string body { get; set; } + public string title { get; set; } + public string type { get; set; } + public string device_iden { get; set; } + } +} \ No newline at end of file diff --git a/PlexRequests.Api.Models/Notifications/PushbulletResponse.cs b/PlexRequests.Api.Models/Notifications/PushbulletResponse.cs new file mode 100644 index 000000000..eda3c41c7 --- /dev/null +++ b/PlexRequests.Api.Models/Notifications/PushbulletResponse.cs @@ -0,0 +1,48 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PushbulletResponse.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.Api.Models.Notifications +{ + public class PushbulletResponse + { + public bool active { get; set; } + public string iden { get; set; } + public double created { get; set; } + public double modified { get; set; } + public string type { get; set; } + public bool dismissed { get; set; } + public string direction { get; set; } + public string sender_iden { get; set; } + public string sender_email { get; set; } + public string sender_email_normalized { get; set; } + public string sender_name { get; set; } + public string receiver_iden { get; set; } + public string receiver_email { get; set; } + public string receiver_email_normalized { get; set; } + public string title { get; set; } + public string body { 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 2255291fe..402d1192a 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -31,6 +31,10 @@ 4 + + ..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll + True + @@ -43,6 +47,8 @@ + + @@ -58,6 +64,9 @@ + + +