diff --git a/PlexRequests.Api.Interfaces/ISlackApi.cs b/PlexRequests.Api.Interfaces/ISlackApi.cs new file mode 100644 index 000000000..812419f69 --- /dev/null +++ b/PlexRequests.Api.Interfaces/ISlackApi.cs @@ -0,0 +1,37 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: ISlackApi.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.Threading.Tasks; + +using PlexRequests.Api.Models.Notifications; + +namespace PlexRequests.Api.Interfaces +{ + public interface ISlackApi + { + Task PushAsync(string team, string token, string service, SlackNotificationBody message); + } +} \ 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 3b3321ec6..8512ca932 100644 --- a/PlexRequests.Api.Interfaces/PlexRequests.Api.Interfaces.csproj +++ b/PlexRequests.Api.Interfaces/PlexRequests.Api.Interfaces.csproj @@ -51,6 +51,7 @@ + diff --git a/PlexRequests.Api.Models/Notifications/SlackNotificationBody.cs b/PlexRequests.Api.Models/Notifications/SlackNotificationBody.cs new file mode 100644 index 000000000..a92b06d4f --- /dev/null +++ b/PlexRequests.Api.Models/Notifications/SlackNotificationBody.cs @@ -0,0 +1,56 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SlackNotificationBody.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 SlackNotificationBody + { + [JsonConstructor] + public SlackNotificationBody() + { + username = "Plex Requests"; + } + + [JsonIgnore] + private string _username; + public string username + { + get { return _username; } + set + { + if (!string.IsNullOrEmpty(value)) + _username = value; + } + } + public string channel { get; set; } + public string text { get; set; } + + public string icon_url { get; set; } + public string icon_emoji { 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 efd7d834d..8e670e858 100644 --- a/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj +++ b/PlexRequests.Api.Models/PlexRequests.Api.Models.csproj @@ -58,6 +58,7 @@ + diff --git a/PlexRequests.Api/ApiRequest.cs b/PlexRequests.Api/ApiRequest.cs index b5ffa8c76..c1395c561 100644 --- a/PlexRequests.Api/ApiRequest.cs +++ b/PlexRequests.Api/ApiRequest.cs @@ -72,7 +72,7 @@ namespace PlexRequests.Api return response.Data; } - + public IRestResponse Execute(IRestRequest request, Uri baseUri) { var client = new RestClient { BaseUrl = baseUri }; @@ -109,7 +109,6 @@ namespace PlexRequests.Api public T ExecuteJson(IRestRequest request, Uri baseUri) where T : new() { var client = new RestClient { BaseUrl = baseUri }; - var response = client.Execute(request); Log.Trace("Api Content Response:"); Log.Trace(response.Content); diff --git a/PlexRequests.Api/PlexRequests.Api.csproj b/PlexRequests.Api/PlexRequests.Api.csproj index b1f9e1391..d627e0ac9 100644 --- a/PlexRequests.Api/PlexRequests.Api.csproj +++ b/PlexRequests.Api/PlexRequests.Api.csproj @@ -71,6 +71,7 @@ MockApiData.resx + diff --git a/PlexRequests.Api/SlackApi.cs b/PlexRequests.Api/SlackApi.cs new file mode 100644 index 000000000..929b475f1 --- /dev/null +++ b/PlexRequests.Api/SlackApi.cs @@ -0,0 +1,62 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: PlexApi.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.Threading.Tasks; + +using PlexRequests.Api.Interfaces; +using PlexRequests.Api.Models.Notifications; + +using RestSharp; + +namespace PlexRequests.Api +{ + public class SlackApi : ISlackApi + { + public async Task PushAsync(string team, string token, string service, SlackNotificationBody message) + { + var request = new RestRequest + { + Method = Method.POST, + Resource = "/services/{team}/{service}/{token}" + }; + + request.AddUrlSegment("team", team); + request.AddUrlSegment("service", service); + request.AddUrlSegment("token", token); + request.AddJsonBody(message); + + var api = new ApiRequest(); + return await Task.Run( + () => + { + var result = api.Execute(request, new Uri("https://hooks.slack.com/")); + return result.Content; + }); + } + } +} + diff --git a/PlexRequests.Core/PlexRequests.Core.csproj b/PlexRequests.Core/PlexRequests.Core.csproj index 8a66e8d43..0b454381e 100644 --- a/PlexRequests.Core/PlexRequests.Core.csproj +++ b/PlexRequests.Core/PlexRequests.Core.csproj @@ -73,6 +73,7 @@ + diff --git a/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs b/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs new file mode 100644 index 000000000..2b412ed3e --- /dev/null +++ b/PlexRequests.Core/SettingModels/SlackNotificationSettings.cs @@ -0,0 +1,37 @@ +using System; + +using Newtonsoft.Json; + +namespace PlexRequests.Core.SettingModels +{ + public class SlackNotificationSettings : Settings + { + public bool Enabled { get; set; } + public string WebhookUrl { get; set; } + public string Channel { get; set; } + public string Username { get; set; } + + [JsonIgnore] + public string Team => SplitWebUrl(3); + + [JsonIgnore] + public string Service => SplitWebUrl(4); + + [JsonIgnore] + public string Token => SplitWebUrl(5); + + private string SplitWebUrl(int index) + { + if (!WebhookUrl.StartsWith("http", StringComparison.InvariantCulture)) + { + WebhookUrl = "https://" + WebhookUrl; + } + var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + return split.Length < index + ? string.Empty + : split[index]; + } + + } +} \ No newline at end of file diff --git a/PlexRequests.Services/Notification/PushbulletNotification.cs b/PlexRequests.Services/Notification/PushbulletNotification.cs index f4d6f802e..70f318331 100644 --- a/PlexRequests.Services/Notification/PushbulletNotification.cs +++ b/PlexRequests.Services/Notification/PushbulletNotification.cs @@ -77,7 +77,7 @@ namespace PlexRequests.Services.Notification case NotificationType.AdminNote: break; case NotificationType.Test: - await PushTestAsync(model, pushSettings); + await PushTestAsync(pushSettings); break; default: throw new ArgumentOutOfRangeException(); @@ -116,7 +116,7 @@ namespace PlexRequests.Services.Notification await Push(settings, message, pushTitle); } - private async Task PushTestAsync(NotificationModel model, PushbulletNotificationSettings settings) + private async Task PushTestAsync(PushbulletNotificationSettings settings) { var message = "This is just a test! Success!"; var pushTitle = "Plex Requests: Test Message!"; diff --git a/PlexRequests.Services/Notification/SlackNotification.cs b/PlexRequests.Services/Notification/SlackNotification.cs new file mode 100644 index 000000000..5da0c156a --- /dev/null +++ b/PlexRequests.Services/Notification/SlackNotification.cs @@ -0,0 +1,155 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SlackNotification.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.Threading.Tasks; + +using NLog; + +using PlexRequests.Api.Interfaces; +using PlexRequests.Api.Models.Notifications; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Services.Interfaces; + +namespace PlexRequests.Services.Notification +{ + public class SlackNotification : INotification + { + public SlackNotification(ISlackApi api, ISettingsService sn) + { + Api = api; + Settings = sn; + } + + public string NotificationName => "SlackNotification"; + + private ISlackApi Api { get; } + private ISettingsService Settings { get; } + private static Logger Log = LogManager.GetCurrentClassLogger(); + + + public async Task NotifyAsync(NotificationModel model) + { + var settings = Settings.GetSettings(); + + await NotifyAsync(model, settings); + } + + public async Task NotifyAsync(NotificationModel model, Settings settings) + { + if (settings == null) await NotifyAsync(model); + + var pushSettings = (SlackNotificationSettings)settings; + if (!ValidateConfiguration(pushSettings)) + { + Log.Error("Settings for Slack was not correct, we cannot push a notification"); + return; + } + + switch (model.NotificationType) + { + case NotificationType.NewRequest: + await PushNewRequestAsync(model, pushSettings); + break; + case NotificationType.Issue: + await PushIssueAsync(model, pushSettings); + break; + case NotificationType.RequestAvailable: + break; + case NotificationType.RequestApproved: + break; + case NotificationType.AdminNote: + break; + case NotificationType.Test: + await PushTest(pushSettings); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private async Task PushNewRequestAsync(NotificationModel model, SlackNotificationSettings settings) + { + var message = $"{model.Title} has been requested by user: {model.User}"; + await Push(settings, message); + } + + private async Task PushIssueAsync(NotificationModel model, SlackNotificationSettings settings) + { + var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}"; + await Push(settings, message); + } + + private async Task PushTest(SlackNotificationSettings settings) + { + var message = $"This is a test from Plex Requests, if you can see this then we have successfully pushed a notification!"; + await Push(settings, message); + } + + private async Task Push(SlackNotificationSettings config, string message) + { + try + { + var notification = new SlackNotificationBody { username = config.Username, channel = config.Channel ?? string.Empty, text = message }; + + var result = await Api.PushAsync(config.Team, config.Token, config.Service, notification); + if (!result.Equals("ok")) + { + Log.Error("Slack returned a message that was not 'ok', the notification did not get pushed"); + Log.Error($"Message that slack returned: {result}"); + } + } + catch (Exception e) + { + Log.Error(e); + } + } + + private bool ValidateConfiguration(SlackNotificationSettings settings) + { + if (!settings.Enabled) + { + return false; + } + if (string.IsNullOrEmpty(settings.WebhookUrl)) + { + return false; + } + try + { + var a = settings.Team; + var b = settings.Service; + var c = settings.Token; + } + catch (IndexOutOfRangeException) + { + return false; + } + return true; + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services/PlexRequests.Services.csproj b/PlexRequests.Services/PlexRequests.Services.csproj index 607bc20f9..257fc5b7e 100644 --- a/PlexRequests.Services/PlexRequests.Services.csproj +++ b/PlexRequests.Services/PlexRequests.Services.csproj @@ -93,6 +93,7 @@ + diff --git a/PlexRequests.UI/Bootstrapper.cs b/PlexRequests.UI/Bootstrapper.cs index 5f95ea9ba..4eef3be38 100644 --- a/PlexRequests.UI/Bootstrapper.cs +++ b/PlexRequests.UI/Bootstrapper.cs @@ -83,6 +83,7 @@ namespace PlexRequests.UI container.Register, SettingsServiceV2>(); container.Register, SettingsServiceV2>(); container.Register, SettingsServiceV2>(); + container.Register, SettingsServiceV2>(); // Repo's container.Register, GenericRepository>(); @@ -108,6 +109,7 @@ namespace PlexRequests.UI container.Register(); container.Register(); container.Register(); + container.Register(); // NotificationService container.Register().AsSingleton(); @@ -193,6 +195,13 @@ namespace PlexRequests.UI { notificationService.Subscribe(new PushoverNotification(container.Resolve(), pushoverService)); } + + var slackService = container.Resolve>(); + var slackSettings = slackService.GetSettings(); + if (slackSettings.Enabled) + { + notificationService.Subscribe(new SlackNotification(container.Resolve(), slackService)); + } } protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context) diff --git a/PlexRequests.UI/Modules/AdminNotificationsModule.cs b/PlexRequests.UI/Modules/AdminNotificationsModule.cs new file mode 100644 index 000000000..26565bc95 --- /dev/null +++ b/PlexRequests.UI/Modules/AdminNotificationsModule.cs @@ -0,0 +1,130 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: AdminNotificationsModule.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System; + +using Nancy; +using Nancy.ModelBinding; +using Nancy.Responses.Negotiation; +using Nancy.Security; +using Nancy.Validation; + +using NLog; + +using PlexRequests.Api.Interfaces; +using PlexRequests.Core; +using PlexRequests.Core.SettingModels; +using PlexRequests.Helpers; +using PlexRequests.Services.Interfaces; +using PlexRequests.Services.Notification; +using PlexRequests.UI.Helpers; +using PlexRequests.UI.Models; + +namespace PlexRequests.UI.Modules +{ + public class AdminNotificationsModule : BaseModule + { + public AdminNotificationsModule(ISettingsService prService, ISettingsService slackSettings, + INotificationService notify, ISlackApi slackApi) : base("admin", prService) + { + this.RequiresClaims(UserClaims.Admin); + + SlackSettings = slackSettings; + NotificationService = notify; + SlackApi = slackApi; + + Post["/testslacknotification"] = _ => TestSlackNotification(); + + Get["/slacknotification"] = _ => SlackNotifications(); + Post["/slacknotification"] = _ => SaveSlackNotifications(); + } + private ISettingsService SlackSettings { get; } + private INotificationService NotificationService { get; } + private ISlackApi SlackApi { get; } + + private static Logger Log = LogManager.GetCurrentClassLogger(); + + private Response TestSlackNotification() + { + var settings = this.BindAndValidate(); + if (!ModelValidationResult.IsValid) + { + return Response.AsJson(ModelValidationResult.SendJsonError()); + } + var notificationModel = new NotificationModel + { + NotificationType = NotificationType.Test, + DateTime = DateTime.Now + }; + try + { + NotificationService.Subscribe(new SlackNotification(SlackApi,SlackSettings)); + settings.Enabled = true; + NotificationService.Publish(notificationModel, settings); + Log.Info("Sent slack notification test"); + } + catch (Exception e) + { + Log.Error(e,"Failed to subscribe and publish test Slack Notification"); + } + finally + { + NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings)); + } + return Response.AsJson(new JsonResponseModel { Result = true, Message = "Successfully sent a test Slack Notification! If you do not receive it please check the logs." }); + } + + private Negotiator SlackNotifications() + { + var settings = SlackSettings.GetSettings(); + return View["SlackNotifications", settings]; + } + + private Response SaveSlackNotifications() + { + var settings = this.BindAndValidate(); + if (!ModelValidationResult.IsValid) + { + return Response.AsJson(ModelValidationResult.SendJsonError()); + } + + var result = SlackSettings.SaveSettings(settings); + if (settings.Enabled) + { + NotificationService.Subscribe(new SlackNotification(SlackApi, SlackSettings)); + } + else + { + NotificationService.UnSubscribe(new SlackNotification(SlackApi, SlackSettings)); + } + + Log.Info("Saved slack settings, result: {0}", result); + return Response.AsJson(result + ? new JsonResponseModel { Result = true, Message = "Successfully Updated the Settings for Slack Notifications!" } + : new JsonResponseModel { Result = false, Message = "Could not update the settings, take a look at the logs." }); + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/PlexRequests.UI.csproj b/PlexRequests.UI/PlexRequests.UI.csproj index 50c045ad1..0abadf609 100644 --- a/PlexRequests.UI/PlexRequests.UI.csproj +++ b/PlexRequests.UI/PlexRequests.UI.csproj @@ -175,6 +175,7 @@ + @@ -186,6 +187,7 @@ + @@ -479,6 +481,9 @@ Always + + Always + web.config diff --git a/PlexRequests.UI/Validators/SlackSettingsValidator.cs b/PlexRequests.UI/Validators/SlackSettingsValidator.cs new file mode 100644 index 000000000..c3c477514 --- /dev/null +++ b/PlexRequests.UI/Validators/SlackSettingsValidator.cs @@ -0,0 +1,40 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: SlackSettingsValidator.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 SlackSettingsValidator : AbstractValidator + { + public SlackSettingsValidator() + { + RuleFor(request => request.WebhookUrl).NotEmpty().WithMessage("You must specify a Webhook Url"); + } + } +} \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/Authentication.cshtml b/PlexRequests.UI/Views/Admin/Authentication.cshtml index badd56f05..d25094b27 100644 --- a/PlexRequests.UI/Views/Admin/Authentication.cshtml +++ b/PlexRequests.UI/Views/Admin/Authentication.cshtml @@ -58,7 +58,7 @@
- +

diff --git a/PlexRequests.UI/Views/Admin/SlackNotifications.cshtml b/PlexRequests.UI/Views/Admin/SlackNotifications.cshtml new file mode 100644 index 000000000..f24d62691 --- /dev/null +++ b/PlexRequests.UI/Views/Admin/SlackNotifications.cshtml @@ -0,0 +1,119 @@ +@using PlexRequests.UI.Helpers +@Html.Partial("_Sidebar") + +
+
+
+ Slack Notifications + +
+
+ + @if (Model.Enabled) + { + + } + else + { + + } + +
+
+ +
+ + This is the full webhook url. + Slack > Settings > Add app or integration > Build > Make a Custom Integration > Incoming Webhooks > Add Incoming Webhook. You will then have a Webhook Url +
+ +
+
+ +
+ + You can override the default channel here +
+ +
+
+ +
+ + You can override the default username (Plex Requests) here +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml index 04dc3457a..9b7fee6cc 100644 --- a/PlexRequests.UI/Views/Admin/_Sidebar.cshtml +++ b/PlexRequests.UI/Views/Admin/_Sidebar.cshtml @@ -11,6 +11,7 @@ @Html.GetSidebarUrl(Context, "/admin/emailnotification", "Email Notifications") @Html.GetSidebarUrl(Context, "/admin/pushbulletnotification", "Pushbullet Notifications") @Html.GetSidebarUrl(Context, "/admin/pushovernotification", "Pushover Notifications") + @Html.GetSidebarUrl(Context, "/admin/slacknotification", "Slack Notifications") @Html.GetSidebarUrl(Context, "/admin/logs", "Logs") @Html.GetSidebarUrl(Context, "/admin/status", "Status")
diff --git a/PlexRequests.UI/Views/Login/Index.cshtml b/PlexRequests.UI/Views/Login/Index.cshtml index bbdf57068..f74d44d21 100644 --- a/PlexRequests.UI/Views/Login/Index.cshtml +++ b/PlexRequests.UI/Views/Login/Index.cshtml @@ -8,7 +8,7 @@ } }
- Username + Username
Password
diff --git a/PlexRequests.UI/Views/Login/Register.cshtml b/PlexRequests.UI/Views/Login/Register.cshtml index c53fa0193..e41ab1ef6 100644 --- a/PlexRequests.UI/Views/Login/Register.cshtml +++ b/PlexRequests.UI/Views/Login/Register.cshtml @@ -1,5 +1,5 @@  - Username + Username
Password
diff --git a/PlexRequests.UI/Views/UserLogin/Index.cshtml b/PlexRequests.UI/Views/UserLogin/Index.cshtml index ebef5b3cb..eaece9a55 100644 --- a/PlexRequests.UI/Views/UserLogin/Index.cshtml +++ b/PlexRequests.UI/Views/UserLogin/Index.cshtml @@ -13,7 +13,7 @@
- +