mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-12 16:22:55 -07:00
More on #865 TODO, Find out whats going on with the notifications and why exceptions are being thrown.
Bascailly custom notification messages are almost done
This commit is contained in:
parent
5e6032ecba
commit
f193471b6c
46 changed files with 888 additions and 457 deletions
|
@ -16,6 +16,8 @@ using System.Security.Principal;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Store;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -136,10 +138,10 @@ namespace Ombi.Core.Engine
|
|||
{
|
||||
//Log.Fatal(e);
|
||||
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
|
||||
var notification = new NotificationModel
|
||||
var notification = new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
User = Username,
|
||||
RequestedUser = Username,
|
||||
RequestType = RequestType.Movie,
|
||||
Title = model.Title,
|
||||
NotificationType = NotificationType.ItemAddedToFaultQueue
|
||||
|
@ -208,10 +210,10 @@ namespace Ombi.Core.Engine
|
|||
|
||||
if (ShouldSendNotification(model.Type))
|
||||
{
|
||||
var notificationModel = new NotificationModel
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
Title = model.Title,
|
||||
User = Username,
|
||||
RequestedUser = Username,
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.NewRequest,
|
||||
RequestType = model.Type,
|
||||
|
|
|
@ -15,6 +15,8 @@ using System.Linq;
|
|||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Store;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
|
@ -272,17 +274,20 @@ 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.PosterPath
|
||||
};
|
||||
var n = new NotificationOptions();
|
||||
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel).Wait());
|
||||
n.Title = model.Title;
|
||||
n.RequestedUser = Username;
|
||||
n.DateTime = DateTime.Now;
|
||||
n.NotificationType = NotificationType.NewRequest;
|
||||
n.RequestType = model.Type;
|
||||
n.ImgSrc = model.PosterPath;
|
||||
|
||||
|
||||
BackgroundJob.Enqueue(() =>
|
||||
|
||||
NotificationService.Publish(n).Wait()
|
||||
);
|
||||
}
|
||||
|
||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||
|
|
|
@ -4,24 +4,6 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public static class RequestTypeDisplay
|
||||
{
|
||||
public static string GetString(this RequestType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case RequestType.Movie:
|
||||
return "Movie";
|
||||
|
||||
case RequestType.TvShow:
|
||||
return "TV Show";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum IssueState
|
||||
{
|
||||
None = 99,
|
||||
|
|
21
src/Ombi.Core/Models/UI/EmailNotificationsViewModel.cs
Normal file
21
src/Ombi.Core/Models/UI/EmailNotificationsViewModel.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Models.Notifications
|
||||
{
|
||||
/// <summary>
|
||||
/// The view model for the notification settings page
|
||||
/// </summary>
|
||||
/// <seealso cref="Ombi.Settings.Settings.Models.Notifications.EmailNotificationSettings" />
|
||||
public class EmailNotificationsViewModel : EmailNotificationSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the notification templates.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The notification templates.
|
||||
/// </value>
|
||||
public List<NotificationTemplates> NotificationTemplates { get; set; }
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ using Ombi.Core.Engine;
|
|||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.IdentityResolver;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Core.Requests.Models;
|
||||
using Ombi.Core.Rule;
|
||||
using Ombi.Core.Settings;
|
||||
|
@ -75,6 +76,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IUserRepository, UserRepository>();
|
||||
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
||||
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
|
||||
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
|
||||
}
|
||||
public static void RegisterServices(this IServiceCollection services)
|
||||
|
|
12
src/Ombi.Helpers/NotificationAgent.cs
Normal file
12
src/Ombi.Helpers/NotificationAgent.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ombi.Helpers
|
||||
{
|
||||
public enum NotificationAgent
|
||||
{
|
||||
Email,
|
||||
Discord,
|
||||
Pushbullet,
|
||||
Pushover,
|
||||
Telegram,
|
||||
Slack
|
||||
}
|
||||
}
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
namespace Ombi.Helpers
|
||||
{
|
||||
public enum NotificationType
|
||||
{
|
|
@ -1,19 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="6.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api\Ombi.Api.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||
<ProjectReference Include="..\Ombi\Ombi.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,15 +0,0 @@
|
|||
using System;
|
||||
using AutoMapper;
|
||||
|
||||
namespace Ombi.Mapping.Maps
|
||||
{
|
||||
public class OmbiProfile : Profile
|
||||
{
|
||||
public OmbiProfile()
|
||||
{
|
||||
// Add as many of these lines as you need to map your objects
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
14
src/Ombi.Mapping/Profiles/SettingsProfile.cs
Normal file
14
src/Ombi.Mapping/Profiles/SettingsProfile.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using AutoMapper;
|
||||
using Ombi.Models.Notifications;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
|
||||
namespace Ombi.Mapping.Profiles
|
||||
{
|
||||
public class SettingsProfile : Profile
|
||||
{
|
||||
public SettingsProfile()
|
||||
{
|
||||
CreateMap<EmailNotificationsViewModel, EmailNotificationSettings>().ReverseMap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,227 +0,0 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MailKit.Net.Smtp;
|
||||
using MimeKit;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Notifications.Templates;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
|
||||
namespace Ombi.Notifications.Email
|
||||
{
|
||||
public class EmailNotification : BaseNotification<EmailNotificationSettings>
|
||||
{
|
||||
public EmailNotification(ISettingsService<EmailNotificationSettings> settings) : base(settings)
|
||||
{
|
||||
}
|
||||
|
||||
public override string NotificationName => nameof(EmailNotification);
|
||||
|
||||
protected override bool ValidateConfiguration(EmailNotificationSettings settings)
|
||||
{
|
||||
if (!settings.Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Helpers\Ombi.Helpers.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,18 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.6</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="1.16.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Core\Ombi.Core.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications\Ombi.Notifications.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -4,15 +4,15 @@ using Microsoft.Extensions.Logging;
|
|||
using Ombi.Api.Discord;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Notification.Agents
|
||||
namespace Ombi.Notifications.Agents
|
||||
{
|
||||
public class DiscordNotification : BaseNotification<DiscordNotificationSettings>
|
||||
{
|
||||
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn, ILogger<DiscordNotification> log) : base(sn)
|
||||
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn, ILogger<DiscordNotification> log, INotificationTemplatesRepository r) : base(sn, r)
|
||||
{
|
||||
Api = api;
|
||||
Logger = log;
|
||||
|
@ -45,9 +45,9 @@ namespace Ombi.Notification.Agents
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override async Task NewRequest(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task NewRequest(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"{model.Title} has been requested by user: {model.User}";
|
||||
var message = $"{model.Title} has been requested by user: {model.RequestedUser}";
|
||||
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
|
@ -56,9 +56,9 @@ namespace Ombi.Notification.Agents
|
|||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task Issue(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task Issue(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"A new issue: {model.Body} has been reported by user: {model.User} for the title: {model.Title}";
|
||||
var message = $"A new issue: {model.Body} has been reported by user: {model.RequestedUser} for the title: {model.Title}";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
|
@ -66,9 +66,9 @@ namespace Ombi.Notification.Agents
|
|||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task AddedToRequestQueue(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"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";
|
||||
var message = $"Hello! The user '{model.RequestedUser}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
|
||||
var notification = new NotificationMessage
|
||||
{
|
||||
Message = message,
|
||||
|
@ -76,7 +76,7 @@ namespace Ombi.Notification.Agents
|
|||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task RequestDeclined(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task RequestDeclined(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! Your request for {model.Title} has been declined, Sorry!";
|
||||
var notification = new NotificationMessage
|
||||
|
@ -86,7 +86,7 @@ namespace Ombi.Notification.Agents
|
|||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task RequestApproved(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! The request for {model.Title} has now been approved!";
|
||||
var notification = new NotificationMessage
|
||||
|
@ -96,7 +96,7 @@ namespace Ombi.Notification.Agents
|
|||
await Send(notification, settings);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task AvailableRequest(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"Hello! The request for {model.Title} is now available!";
|
||||
var notification = new NotificationMessage
|
||||
|
@ -118,7 +118,7 @@ namespace Ombi.Notification.Agents
|
|||
}
|
||||
}
|
||||
|
||||
protected override async Task Test(NotificationModel model, DiscordNotificationSettings settings)
|
||||
protected override async Task Test(NotificationOptions model, DiscordNotificationSettings settings)
|
||||
{
|
||||
var message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!";
|
||||
var notification = new NotificationMessage
|
|
@ -2,17 +2,18 @@
|
|||
using System.Threading.Tasks;
|
||||
using MailKit.Net.Smtp;
|
||||
using MimeKit;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Notifications.Templates;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Notifications.Email
|
||||
namespace Ombi.Notifications.Agents
|
||||
{
|
||||
public class EmailNotification : BaseNotification<EmailNotificationSettings>
|
||||
{
|
||||
public EmailNotification(ISettingsService<EmailNotificationSettings> settings) : base(settings)
|
||||
public EmailNotification(ISettingsService<EmailNotificationSettings> settings, INotificationTemplatesRepository r) : base(settings, r)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -26,12 +27,12 @@ namespace Ombi.Notifications.Email
|
|||
}
|
||||
if (settings.Authentication)
|
||||
{
|
||||
if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
|
||||
if (string.IsNullOrEmpty(settings.Username) || string.IsNullOrEmpty(settings.Password))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(settings.EmailHost) || string.IsNullOrEmpty(settings.RecipientEmail) || string.IsNullOrEmpty(settings.EmailPort.ToString()))
|
||||
if (string.IsNullOrEmpty(settings.Host) || string.IsNullOrEmpty(settings.AdminEmail) || string.IsNullOrEmpty(settings.Port.ToString()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -39,69 +40,78 @@ namespace Ombi.Notifications.Email
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override async Task NewRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||
private async Task<NotificationMessageContent> LoadTemplate(NotificationType type, NotificationOptions model)
|
||||
{
|
||||
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, type);
|
||||
// Need to do the parsing
|
||||
var resolver = new NotificationMessageResolver();
|
||||
return resolver.ParseMessage(template, new NotificationMessageCurlys(model.RequestedUser, model.Title, DateTime.Now.ToString("D"),
|
||||
model.NotificationType.ToString(), null));
|
||||
|
||||
}
|
||||
|
||||
protected override async Task NewRequest(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var template = await LoadTemplate(NotificationType.NewRequest, model);
|
||||
|
||||
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 html = email.LoadTemplate(template.Subject, template.Message, model.ImgSrc);
|
||||
|
||||
|
||||
var message = new NotificationMessage
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
|
||||
To = settings.RecipientEmail,
|
||||
Subject = $"Ombi: New {model.RequestType} request for {model.Title}!",
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
|
||||
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}");
|
||||
message.Other.Add("PlainTextBody", $"Hello! The user '{model.RequestedUser}' has requested the {model.RequestType} '{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)
|
||||
protected override async Task Issue(NotificationOptions 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}!",
|
||||
$"Hello! The user '{model.RequestedUser}' 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,
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
|
||||
message.Other.Add("PlainTextBody", $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!");
|
||||
message.Other.Add("PlainTextBody", $"Hello! The user '{model.RequestedUser}' 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)
|
||||
protected override async Task AddedToRequestQueue(NotificationOptions 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",
|
||||
$"Hello! The user '{model.RequestedUser}' 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,
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
|
||||
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");
|
||||
message.Other.Add("PlainTextBody", $"Hello! The user '{model.RequestedUser}' 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)
|
||||
protected override async Task RequestDeclined(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(
|
||||
|
@ -122,7 +132,7 @@ namespace Ombi.Notifications.Email
|
|||
await Send(message, settings);
|
||||
}
|
||||
|
||||
protected override async Task RequestApproved(NotificationModel model, EmailNotificationSettings settings)
|
||||
protected override async Task RequestApproved(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(
|
||||
|
@ -142,7 +152,7 @@ namespace Ombi.Notifications.Email
|
|||
await Send(message, settings);
|
||||
}
|
||||
|
||||
protected override async Task AvailableRequest(NotificationModel model, EmailNotificationSettings settings)
|
||||
protected override async Task AvailableRequest(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(
|
||||
|
@ -178,12 +188,12 @@ namespace Ombi.Notifications.Email
|
|||
Body = body.ToMessageBody(),
|
||||
Subject = model.Subject
|
||||
};
|
||||
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
|
||||
message.From.Add(new MailboxAddress(settings.Sender, settings.Sender));
|
||||
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.
|
||||
client.Connect(settings.Host, settings.Port); // Let MailKit figure out the correct SecureSocketOptions.
|
||||
|
||||
// Note: since we don't have an OAuth2 token, disable
|
||||
// the XOAUTH2 authentication mechanism.
|
||||
|
@ -191,7 +201,7 @@ namespace Ombi.Notifications.Email
|
|||
|
||||
if (settings.Authentication)
|
||||
{
|
||||
client.Authenticate(settings.EmailUsername, settings.EmailPassword);
|
||||
client.Authenticate(settings.Username, settings.Password);
|
||||
}
|
||||
//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);
|
||||
|
@ -205,7 +215,7 @@ namespace Ombi.Notifications.Email
|
|||
}
|
||||
}
|
||||
|
||||
protected override async Task Test(NotificationModel model, EmailNotificationSettings settings)
|
||||
protected override async Task Test(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(
|
||||
|
@ -216,7 +226,7 @@ namespace Ombi.Notifications.Email
|
|||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: Test",
|
||||
To = settings.RecipientEmail,
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
|
||||
message.Other.Add("PlainTextBody", "This is just a test! Success!");
|
|
@ -1,28 +1,32 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
{
|
||||
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
|
||||
{
|
||||
protected BaseNotification(ISettingsService<T> settings)
|
||||
protected BaseNotification(ISettingsService<T> settings, INotificationTemplatesRepository templateRepo)
|
||||
{
|
||||
Settings = settings;
|
||||
TemplateRepository = templateRepo;
|
||||
}
|
||||
|
||||
protected ISettingsService<T> Settings { get; }
|
||||
protected INotificationTemplatesRepository TemplateRepository { get; }
|
||||
public abstract string NotificationName { get; }
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model)
|
||||
public async Task NotifyAsync(NotificationOptions model)
|
||||
{
|
||||
var configuration = GetConfiguration();
|
||||
await NotifyAsync(model, configuration);
|
||||
}
|
||||
|
||||
public async Task NotifyAsync(NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||
public async Task NotifyAsync(NotificationOptions model, Settings.Settings.Models.Settings settings)
|
||||
{
|
||||
if (settings == null) await NotifyAsync(model);
|
||||
|
||||
|
@ -79,13 +83,13 @@ namespace Ombi.Notifications
|
|||
|
||||
|
||||
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 NewRequest(NotificationOptions model, T settings);
|
||||
protected abstract Task Issue(NotificationOptions model, T settings);
|
||||
protected abstract Task AddedToRequestQueue(NotificationOptions model, T settings);
|
||||
protected abstract Task RequestDeclined(NotificationOptions model, T settings);
|
||||
protected abstract Task RequestApproved(NotificationOptions model, T settings);
|
||||
protected abstract Task AvailableRequest(NotificationOptions model, T settings);
|
||||
protected abstract Task Send(NotificationMessage model, T settings);
|
||||
protected abstract Task Test(NotificationModel model, T settings);
|
||||
protected abstract Task Test(NotificationOptions model, T settings);
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace Ombi.Notifications
|
|||
{
|
||||
string NotificationName { get; }
|
||||
|
||||
Task NotifyAsync(NotificationModel model);
|
||||
Task NotifyAsync(NotificationOptions model);
|
||||
|
||||
/// <summary>
|
||||
/// Sends a notification to the user, this is usually for testing the settings.
|
||||
|
@ -16,6 +16,6 @@ namespace Ombi.Notifications
|
|||
/// <param name="model">The model.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <returns></returns>
|
||||
Task NotifyAsync(NotificationModel model, Settings.Settings.Models.Settings settings);
|
||||
Task NotifyAsync(NotificationOptions model, Settings.Settings.Models.Settings settings);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Settings.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Notifications;
|
||||
using Ombi.Notifications.Models;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
namespace Ombi.Core.Notifications
|
||||
{
|
||||
public interface INotificationService
|
||||
{
|
||||
Task Publish(NotificationModel model);
|
||||
Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings);
|
||||
Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type);
|
||||
Task Publish(NotificationOptions model);
|
||||
Task Publish(NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings);
|
||||
Task PublishTest(NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings, INotification type);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,17 @@
|
|||
using System;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Notifications.Models
|
||||
{
|
||||
public class NotificationModel
|
||||
public class NotificationOptions
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Body { get; set; }
|
||||
public DateTime DateTime { get; set; }
|
||||
public DateTime DateTime { get; set; } = DateTime.Now;
|
||||
public NotificationType NotificationType { get; set; }
|
||||
public string User { get; set; }
|
||||
public string RequestedUser { get; set; }
|
||||
public string UserEmail { get; set; }
|
||||
public RequestType RequestType { get; set; }
|
||||
public string ImgSrc { get; set; }
|
145
src/Ombi.Notifications/NotificationMessageResolver.cs
Normal file
145
src/Ombi.Notifications/NotificationMessageResolver.cs
Normal file
|
@ -0,0 +1,145 @@
|
|||
using System.Collections.Generic;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
{
|
||||
public class NotificationMessageContent
|
||||
{
|
||||
public string Subject { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
public class NotificationMessageCurlys
|
||||
{
|
||||
public NotificationMessageCurlys(string requestedUser, string title, string requestedDateTime, string type, string issue)
|
||||
{
|
||||
RequestedUser = requestedUser;
|
||||
Title = title;
|
||||
RequestedDate = requestedDateTime;
|
||||
Type = type;
|
||||
Issue = issue;
|
||||
}
|
||||
private string RequestedUser { get; }
|
||||
private string Title { get; }
|
||||
private string RequestedDate { get; }
|
||||
private string Type { get; }
|
||||
private string Issue { get; }
|
||||
|
||||
public Dictionary<string, string> Curlys => new Dictionary<string, string>
|
||||
{
|
||||
{nameof(RequestedUser), RequestedUser },
|
||||
{nameof(Title), Title },
|
||||
{nameof(RequestedDate), RequestedDate },
|
||||
{nameof(Type), Type },
|
||||
{nameof(Issue), Issue }
|
||||
};
|
||||
}
|
||||
|
||||
public class NotificationMessageResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// The start character '{'
|
||||
/// </summary>
|
||||
private const char StartChar = (char)123;
|
||||
/// <summary>
|
||||
/// The end character '}'
|
||||
/// </summary>
|
||||
private const char EndChar = (char)125;
|
||||
|
||||
/// <summary>
|
||||
/// Parses the message.
|
||||
/// </summary>
|
||||
/// <param name="notification">The notification.</param>
|
||||
/// <param name="c">The c.</param>
|
||||
/// <returns></returns>
|
||||
public NotificationMessageContent ParseMessage(NotificationTemplates notification, NotificationMessageCurlys c)
|
||||
{
|
||||
return Resolve(notification.Message, notification.Subject, c.Curlys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the specified message curly fields.
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <returns></returns>
|
||||
private NotificationMessageContent Resolve(string body, string subject, IReadOnlyDictionary<string, string> parameters)
|
||||
{
|
||||
// Find the fields
|
||||
var bodyFields = FindCurlyFields(body);
|
||||
var subjectFields = FindCurlyFields(subject);
|
||||
|
||||
body = ReplaceFields(bodyFields, parameters, body);
|
||||
subject = ReplaceFields(subjectFields, parameters, subject);
|
||||
|
||||
return new NotificationMessageContent { Message = body ?? string.Empty, Subject = subject ?? string.Empty };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the curly fields.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<string> FindCurlyFields(string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
var insideCurly = false;
|
||||
var fields = new List<string>();
|
||||
var currentWord = string.Empty;
|
||||
var chars = message.ToCharArray();
|
||||
|
||||
foreach (var c in chars)
|
||||
{
|
||||
if (char.IsWhiteSpace(c))
|
||||
{
|
||||
currentWord = string.Empty;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == StartChar) // Start of curly '{'
|
||||
{
|
||||
insideCurly = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == EndChar) // End of curly '}'
|
||||
{
|
||||
fields.Add(currentWord); // We have finished the curly, add the word into the list
|
||||
currentWord = string.Empty;
|
||||
insideCurly = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insideCurly)
|
||||
{
|
||||
currentWord += c.ToString(); // Add the character onto the word.
|
||||
}
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the fields.
|
||||
/// </summary>
|
||||
/// <param name="fields">The fields.</param>
|
||||
/// <param name="parameters">The parameters.</param>
|
||||
/// <param name="mainText">The main text.</param>
|
||||
/// <returns></returns>
|
||||
private string ReplaceFields(IEnumerable<string> fields, IReadOnlyDictionary<string, string> parameters, string mainText)
|
||||
{
|
||||
foreach (var field in fields)
|
||||
{
|
||||
string outString;
|
||||
if (parameters.TryGetValue(field, out outString))
|
||||
{
|
||||
mainText = mainText.Replace($"{{{field}}}", outString);
|
||||
}
|
||||
}
|
||||
return mainText;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
|
||||
|
@ -16,13 +17,13 @@ namespace Ombi.Notifications
|
|||
Log = log;
|
||||
NotificationAgents = new List<INotification>();
|
||||
|
||||
var baseSearchType = typeof(BaseNotification<>).FullName;
|
||||
var baseSearchType = typeof(BaseNotification<>).Name;
|
||||
|
||||
var ass = typeof(NotificationService).GetTypeInfo().Assembly;
|
||||
|
||||
foreach (var ti in ass.DefinedTypes)
|
||||
{
|
||||
if (ti?.BaseType?.FullName == baseSearchType)
|
||||
if (ti?.BaseType?.Name == baseSearchType)
|
||||
{
|
||||
var type = ti?.AsType();
|
||||
var ctors = type.GetConstructors();
|
||||
|
@ -48,7 +49,7 @@ namespace Ombi.Notifications
|
|||
/// </summary>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
public async Task Publish(NotificationModel model)
|
||||
public async Task Publish(NotificationOptions model)
|
||||
{
|
||||
var notificationTasks = NotificationAgents.Select(notification => NotifyAsync(notification, model));
|
||||
|
||||
|
@ -61,7 +62,7 @@ namespace Ombi.Notifications
|
|||
/// <param name="model">The model.</param>
|
||||
/// <param name="settings">The settings.</param>
|
||||
/// <returns></returns>
|
||||
public async Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||
public async Task Publish(NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings)
|
||||
{
|
||||
var notificationTasks = NotificationAgents.Select(notification => NotifyAsync(notification, model, settings));
|
||||
|
||||
|
@ -69,7 +70,7 @@ namespace Ombi.Notifications
|
|||
}
|
||||
|
||||
|
||||
private async Task NotifyAsync(INotification notification, NotificationModel model)
|
||||
private async Task NotifyAsync(INotification notification, NotificationOptions model)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -82,7 +83,7 @@ namespace Ombi.Notifications
|
|||
|
||||
}
|
||||
|
||||
private async Task NotifyAsync(INotification notification, NotificationModel model, Settings.Settings.Models.Settings settings)
|
||||
private async Task NotifyAsync(INotification notification, NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -94,7 +95,7 @@ namespace Ombi.Notifications
|
|||
}
|
||||
}
|
||||
|
||||
public async Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type)
|
||||
public async Task PublishTest(NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings, INotification type)
|
||||
{
|
||||
await type.NotifyAsync(model, settings);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
namespace Ombi.Settings.Settings.Models.Notifications
|
||||
{
|
||||
public sealed class EmailNotificationSettings : Settings
|
||||
public class EmailNotificationSettings : 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 string Host { get; set; }
|
||||
public string Password { get; set; }
|
||||
public int Port { get; set; }
|
||||
public string Sender { get; set; }
|
||||
public string Username { get; set; }
|
||||
public bool Authentication { get; set; }
|
||||
public string RecipientEmail { get; set; }
|
||||
public string AdminEmail { get; set; }
|
||||
}
|
||||
}
|
|
@ -21,5 +21,6 @@ namespace Ombi.Store.Context
|
|||
EntityEntry<T> Entry<T>(T entry) where T : class;
|
||||
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;
|
||||
DbSet<TEntity> Set<TEntity>() where TEntity : class;
|
||||
DbSet<NotificationTemplates> NotificationTemplates { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Context
|
||||
{
|
||||
public class OmbiContext : DbContext, IOmbiContext
|
||||
public sealed class OmbiContext : DbContext, IOmbiContext
|
||||
{
|
||||
private static bool _created;
|
||||
public OmbiContext()
|
||||
|
@ -26,6 +30,9 @@ namespace Ombi.Store.Context
|
|||
// Run Script
|
||||
|
||||
Database.ExecuteSqlCommand(file, 0);
|
||||
|
||||
// Add the notifcation templates
|
||||
AddAllTemplates();
|
||||
}
|
||||
|
||||
public DbSet<RequestBlobs> Requests { get; set; }
|
||||
|
@ -33,10 +40,93 @@ namespace Ombi.Store.Context
|
|||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<PlexContent> PlexContent { get; set; }
|
||||
public DbSet<RadarrCache> RadarrCache { get; set; }
|
||||
public DbSet<NotificationTemplates> NotificationTemplates { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
optionsBuilder.UseSqlite("Data Source=Ombi.db");
|
||||
}
|
||||
|
||||
|
||||
private void AddAllTemplates()
|
||||
{
|
||||
// Check if templates exist
|
||||
var templates = NotificationTemplates.ToList();
|
||||
if (templates.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var allAgents = Enum.GetValues(typeof(NotificationAgent)).Cast<NotificationAgent>().ToList();
|
||||
var allTypes = Enum.GetValues(typeof(NotificationType)).Cast<NotificationType>().ToList();
|
||||
|
||||
foreach (var agent in allAgents)
|
||||
{
|
||||
foreach (var notificationType in allTypes)
|
||||
{
|
||||
var notificationToAdd = new NotificationTemplates();
|
||||
switch (notificationType)
|
||||
{
|
||||
case NotificationType.NewRequest:
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
NotificationType = notificationType,
|
||||
Message = "Hello! The user '{requestedUser}' has requested the {Type} '{Title}'! Please log in to approve this request. Request Date: {RequestedDate}",
|
||||
Subject = "Ombi: New {Type} request for {Title}!",
|
||||
Agent = agent,
|
||||
};
|
||||
break;
|
||||
case NotificationType.Issue:
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
NotificationType = notificationType,
|
||||
Message = "Hello! The user '{requestedUser}' has reported a new issue for the title {Title}! </br> {Issue}",
|
||||
Subject = "Ombi: New issue for {Title}!",
|
||||
Agent = agent,
|
||||
};
|
||||
break;
|
||||
case NotificationType.RequestAvailable:
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
NotificationType = notificationType,
|
||||
Message = "Hello! You requested {Title} on Ombi! This is now available! :)",
|
||||
Subject = "Ombi: {Title} is now available!",
|
||||
Agent = agent,
|
||||
};
|
||||
break;
|
||||
case NotificationType.RequestApproved:
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
NotificationType = notificationType,
|
||||
Message = "Hello! Your request for {Title} has been approved!",
|
||||
Subject = "Ombi: your request has been approved",
|
||||
Agent = agent,
|
||||
};
|
||||
break;
|
||||
case NotificationType.AdminNote:
|
||||
continue;
|
||||
case NotificationType.Test:
|
||||
continue;
|
||||
case NotificationType.RequestDeclined:
|
||||
notificationToAdd = new NotificationTemplates
|
||||
{
|
||||
//"Ombi: Your request has been declined",
|
||||
//$"Hello! Your request for {model.Title} has been declined, Sorry!",
|
||||
NotificationType = notificationType,
|
||||
Message = "Hello! Your request for {Title} has been declined, Sorry!",
|
||||
Subject = "Ombi: your request has been declined",
|
||||
Agent = agent,
|
||||
};
|
||||
break;
|
||||
case NotificationType.ItemAddedToFaultQueue:
|
||||
continue;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
NotificationTemplates.Add(notificationToAdd);
|
||||
}
|
||||
}
|
||||
SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
14
src/Ombi.Store/Entities/NotificationTemplates.cs
Normal file
14
src/Ombi.Store/Entities/NotificationTemplates.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Store.Entities
|
||||
{
|
||||
[Table("NotificationTemplates")]
|
||||
public class NotificationTemplates : Entity
|
||||
{
|
||||
public NotificationType NotificationType { get; set; }
|
||||
public NotificationAgent Agent { get; set; }
|
||||
public string Subject { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public interface INotificationTemplatesRepository
|
||||
{
|
||||
IQueryable<NotificationTemplates> All();
|
||||
Task<IEnumerable<NotificationTemplates>> GetAllTemplates();
|
||||
Task<IEnumerable<NotificationTemplates>> GetAllTemplates(NotificationAgent agent);
|
||||
Task<NotificationTemplates> Insert(NotificationTemplates entity);
|
||||
Task Update(NotificationTemplates template);
|
||||
Task UpdateRange(IEnumerable<NotificationTemplates> template);
|
||||
Task<NotificationTemplates> GetTemplate(NotificationAgent agent, NotificationType type);
|
||||
}
|
||||
}
|
64
src/Ombi.Store/Repository/NotificationTemplatesRepository.cs
Normal file
64
src/Ombi.Store/Repository/NotificationTemplatesRepository.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Store.Repository
|
||||
{
|
||||
public class NotificationTemplatesRepository : INotificationTemplatesRepository
|
||||
{
|
||||
public NotificationTemplatesRepository(IOmbiContext ctx)
|
||||
{
|
||||
Db = ctx;
|
||||
}
|
||||
|
||||
private IOmbiContext Db { get; }
|
||||
|
||||
public IQueryable<NotificationTemplates> All()
|
||||
{
|
||||
return Db.NotificationTemplates.AsQueryable();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NotificationTemplates>> GetAllTemplates()
|
||||
{
|
||||
return await Db.NotificationTemplates.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NotificationTemplates>> GetAllTemplates(NotificationAgent agent)
|
||||
{
|
||||
return await Db.NotificationTemplates.Where(x => x.Agent == agent).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<NotificationTemplates> GetTemplate(NotificationAgent agent, NotificationType type)
|
||||
{
|
||||
return await Db.NotificationTemplates.FirstOrDefaultAsync(x => x.Agent == agent && x.NotificationType == type);
|
||||
}
|
||||
|
||||
public async Task Update(NotificationTemplates template)
|
||||
{
|
||||
await Db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateRange(IEnumerable<NotificationTemplates> templates)
|
||||
{
|
||||
foreach (var t in templates)
|
||||
{
|
||||
|
||||
Db.Attach(t);
|
||||
Db.Entry(t).State = EntityState.Modified;
|
||||
}
|
||||
await Db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<NotificationTemplates> Insert(NotificationTemplates entity)
|
||||
{
|
||||
var settings = await Db.NotificationTemplates.AddAsync(entity).ConfigureAwait(false);
|
||||
await Db.SaveChangesAsync().ConfigureAwait(false);
|
||||
return settings.Entity;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,3 +64,14 @@ CREATE TABLE IF NOT EXISTS RequestHistory
|
|||
RequestId INTEGER NOT NULL
|
||||
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS NotificationTemplates
|
||||
(
|
||||
|
||||
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
NotificationType INTEGER NOT NULL,
|
||||
Agent INTEGER NOT NULL,
|
||||
Subject BLOB NULL,
|
||||
Message BLOB NULL
|
||||
|
||||
);
|
|
@ -61,8 +61,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Radarr", "Ombi.Api
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notification.Agents", "Ombi.Notification.Agents\Ombi.Notification.Agents.csproj", "{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -149,10 +147,6 @@ Global
|
|||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -172,6 +166,5 @@ Global
|
|||
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||
{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D} = {EA30DD15-6280-4687-B370-2956EC2E54E5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
44
src/Ombi/ClientApp/app/interfaces/INotifcationSettings.ts
Normal file
44
src/Ombi/ClientApp/app/interfaces/INotifcationSettings.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
export interface ISettings {
|
||||
id: number,
|
||||
}
|
||||
|
||||
export interface INotificationSettings extends ISettings {
|
||||
enabled: boolean,
|
||||
}
|
||||
|
||||
export interface IEmailNotificationSettings extends INotificationSettings {
|
||||
host: string,
|
||||
password: string,
|
||||
port: number,
|
||||
sender: string,
|
||||
username: string,
|
||||
authentication: boolean,
|
||||
adminEmail: string,
|
||||
notificationTemplates: INotificationTemplates[],
|
||||
}
|
||||
|
||||
export interface INotificationTemplates {
|
||||
subject: string,
|
||||
message: string,
|
||||
notificationType: NotificationType,
|
||||
notificationAgent: NotificationAgent,
|
||||
}
|
||||
|
||||
export enum NotificationAgent {
|
||||
Email,
|
||||
Discord,
|
||||
Pushbullet,
|
||||
Pushover,
|
||||
Telegram,
|
||||
}
|
||||
|
||||
export enum NotificationType {
|
||||
NewRequest,
|
||||
Issue,
|
||||
RequestAvailable,
|
||||
RequestApproved,
|
||||
AdminNote,
|
||||
Test,
|
||||
RequestDeclined,
|
||||
ItemAddedToFaultQueue
|
||||
}
|
15
src/Ombi/ClientApp/app/pipes/HumanizePipe.ts
Normal file
15
src/Ombi/ClientApp/app/pipes/HumanizePipe.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'humanize'
|
||||
})
|
||||
export class HumanizePipe implements PipeTransform {
|
||||
transform(value: string) {
|
||||
if ((typeof value) !== 'string') {
|
||||
return value;
|
||||
}
|
||||
value = value.split(/(?=[A-Z])/).join(' ');
|
||||
value = value[0].toUpperCase() + value.slice(1);
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -222,6 +222,7 @@
|
|||
</div>
|
||||
<div class="col-md-3">
|
||||
<button *ngIf="!child.approved" type="button" (click)="approveSeasonRequest(child)" class="btn btn-sm btn-success-outline" style="text-align: right"><i class="fa fa-plus"></i> Approve</button>
|
||||
<button *ngIf="!child.approved && !child.available && !child.denied" type="button" (click)="denySeasonRequest(child)" class="btn btn-sm btn-danger-outline" style="text-align: right"><i class="fa fa-plus"></i> Deny</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'rxjs/add/operator/distinctUntilChanged';
|
|||
import 'rxjs/add/operator/map';
|
||||
import "rxjs/add/operator/takeUntil";
|
||||
|
||||
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
@ -62,7 +61,7 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
|
|||
|
||||
|
||||
|
||||
loadMore() {
|
||||
public loadMore() {
|
||||
this.requestService.getTvRequests(this.amountToLoad, this.currentlyLoaded + 1)
|
||||
.takeUntil(this.subscriptions)
|
||||
.subscribe(x => {
|
||||
|
@ -71,28 +70,28 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
search(text: any) {
|
||||
public search(text: any) {
|
||||
this.searchChanged.next(text.target.value);
|
||||
}
|
||||
|
||||
removeRequest(request: ITvRequestModel) {
|
||||
public removeRequest(request: ITvRequestModel) {
|
||||
this.requestService.removeTvRequest(request);
|
||||
this.removeRequestFromUi(request);
|
||||
}
|
||||
|
||||
changeAvailability(request: ITvRequestModel, available: boolean) {
|
||||
public changeAvailability(request: ITvRequestModel, available: boolean) {
|
||||
request.available = available;
|
||||
|
||||
this.updateRequest(request);
|
||||
}
|
||||
|
||||
approve(request: ITvRequestModel) {
|
||||
public approve(request: ITvRequestModel) {
|
||||
request.approved = true;
|
||||
request.denied = false;
|
||||
this.updateRequest(request);
|
||||
}
|
||||
|
||||
deny(request: ITvRequestModel) {
|
||||
public deny(request: ITvRequestModel) {
|
||||
request.approved = false;
|
||||
request.denied = true;
|
||||
this.updateRequest(request);
|
||||
|
@ -100,6 +99,14 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
|
|||
|
||||
public approveSeasonRequest(request: IChildTvRequest) {
|
||||
request.approved = true;
|
||||
request.denied = false;
|
||||
this.requestService.updateTvRequest(this.selectedSeason)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
public denySeasonRequest(request: IChildTvRequest) {
|
||||
request.approved = false;
|
||||
request.denied = true;
|
||||
this.requestService.updateTvRequest(this.selectedSeason)
|
||||
.subscribe();
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
ICustomizationSettings,
|
||||
IRadarrSettings
|
||||
} from '../interfaces/ISettings';
|
||||
import { IEmailNotificationSettings } from '../interfaces/INotifcationSettings';
|
||||
|
||||
@Injectable()
|
||||
export class SettingsService extends ServiceAuthHelpers {
|
||||
|
@ -80,6 +81,11 @@ export class SettingsService extends ServiceAuthHelpers {
|
|||
return this.httpAuth.post(`${this.url}/customization`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData).catch(this.handleError)
|
||||
}
|
||||
|
||||
|
||||
|
||||
getEmailNotificationSettings(): Observable<IEmailNotificationSettings> {
|
||||
return this.httpAuth.get(`${this.url}/notifications/email`).map(this.extractData).catch(this.handleError)
|
||||
}
|
||||
|
||||
saveEmailNotificationSettings(settings: IEmailNotificationSettings): Observable<boolean> {
|
||||
return this.httpAuth.post(`${this.url}/notifications/email`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData).catch(this.handleError)
|
||||
}
|
||||
}
|
|
@ -12,10 +12,9 @@
|
|||
<div class="form-group">
|
||||
<label for="logo" class="control-label">Custom Logo</label>
|
||||
<div>
|
||||
<input type="text" [(ngModel)]="settings.logo" class="form-control form-control-custom " id="logo" name="logo" value="{{settings.logo}}">
|
||||
<input type="text" [(ngModel)]="settings.logo" class="form-control form-control-custom " id="logo" name="logo" value="{{settings.logo}}" tooltipPosition="top" pTooltip="This will be used on all of the notifications e.g. Newsletter, email notification and also the Landing page">
|
||||
</div>
|
||||
</div>
|
||||
<small>This will be used on all of the notifications e.g. Newsletter, email notification and also the Landing page</small>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -16,10 +16,9 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="BeforeLogin" name="BeforeLogin" [(ngModel)]="settings.beforeLogin" ng-checked="settings.beforeLogin">
|
||||
<input type="checkbox" id="BeforeLogin" name="BeforeLogin" [(ngModel)]="settings.beforeLogin" ng-checked="settings.beforeLogin" tooltipPosition="top" pTooltip="If enabled then this will show the landing page before the login page, if this is disabled the user will log in first and then see the landing page.">
|
||||
<label for="BeforeLogin">Show before the login</label>
|
||||
</div>
|
||||
<small>If enabled then this will show the landing page before the login page, if this is disabled the user will log in first and then see the landing page.</small>
|
||||
</div>
|
||||
|
||||
<p class="form-group">Notice Message</p>
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
<settings-menu></settings-menu>
|
||||
<div *ngIf="settings">
|
||||
<fieldset>
|
||||
<legend>Email Notifications</legend>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="enable" [(ngModel)]="settings.enabled" ng-checked="settings.enabled">
|
||||
<label for="enable">Enabled</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="Authentication" [(ngModel)]="settings.authentication" ng-checked="settings.authentication"><label for="Authentication">Enable SMTP Authentication</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="host" class="control-label">SMTP Host</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="host" name="host" placeholder="localhost" [(ngModel)]="settings.host" value="{{settings.host}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">SMTP Port</label>
|
||||
<div>
|
||||
<input type="text" [(ngModel)]="settings.port" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="sender" class="control-label">Email Sender</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="sender" name="sender" [(ngModel)]="settings.sender" value="{{settings.sender}}" tooltipPosition="top" pTooltip="The email address that the emails will be sent from">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="adminEmail" class="control-label">Admin Email</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="adminEmail" name="adminEmail" [(ngModel)]="settings.adminEmail" value="{{settings.adminEmail}}" tooltipPosition="top" pTooltip="The administrator email will be used to send emails for admin only notifications (e.g. New Requests that require approvals)">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group" *ngIf="settings.authentication">
|
||||
<label for="username" class="control-label">Username</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom " id="username" name="username" [(ngModel)]="settings.username" value="{{settings.username}}" pTooltip="The username if authentication is enabled" tooltipPosition="top">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngIf="settings.authentication">
|
||||
<label for="password" class="control-label">Password</label>
|
||||
<div>
|
||||
<input type="password" class="form-control form-control-custom " id="password" name="password" [(ngModel)]="settings.password" value="{{settings.password}}" pTooltip="The password if authentication is enabled" tooltipPosition="top">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button id="testPlex" type="submit" (click)="test()" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<notification-templates [templates]="settings.notificationTemplates"></notification-templates>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
|
@ -0,0 +1,34 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { IEmailNotificationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { NotificationService } from "../../services/notification.service";
|
||||
|
||||
@Component({
|
||||
templateUrl: './emailnotification.component.html',
|
||||
})
|
||||
export class EmailNotificationComponent implements OnInit {
|
||||
|
||||
constructor(private settingsService: SettingsService, private notificationService: NotificationService) { }
|
||||
|
||||
settings: IEmailNotificationSettings;
|
||||
NotificationType = NotificationType;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.settingsService.getEmailNotificationSettings().subscribe(x => this.settings = x);
|
||||
}
|
||||
|
||||
test() {
|
||||
// TODO Emby Service
|
||||
}
|
||||
|
||||
save() {
|
||||
this.settingsService.saveEmailNotificationSettings(this.settings).subscribe(x => {
|
||||
if (x) {
|
||||
this.notificationService.success("Settings Saved", "Successfully saved Email settings");
|
||||
} else {
|
||||
this.notificationService.success("Settings Saved", "There was an error when saving the Email settings");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
<ngb-accordion [closeOthers]="true" activeIds="0-header">
|
||||
<ngb-panel *ngFor="let template of templates" id="{{template.notificationType}}" title="{{NotificationType[template.notificationType] | humanize}}">
|
||||
<ng-template ngbPanelContent>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label">Subject</label>
|
||||
<div>
|
||||
<input type="text" class="form-control form-control-custom" [(ngModel)]="template.subject" value="{{template.subject}}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password" class="control-label">Message</label>
|
||||
<div>
|
||||
<textarea type="text" class="form-control form-control-custom" [(ngModel)]="template.message" value="{{template.message}}"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
</ngb-panel>
|
||||
</ngb-accordion>
|
|
@ -0,0 +1,12 @@
|
|||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { INotificationTemplates, NotificationType } from '../../interfaces/INotifcationSettings';
|
||||
|
||||
@Component({
|
||||
selector:'notification-templates',
|
||||
templateUrl: './notificationtemplate.component.html',
|
||||
})
|
||||
export class NotificationTemplate {
|
||||
@Input() templates: INotificationTemplates[];
|
||||
NotificationType = NotificationType;
|
||||
}
|
|
@ -6,10 +6,9 @@
|
|||
<div class="form-group">
|
||||
<label for="portNumber" class="control-label">Port</label>
|
||||
<div>
|
||||
<input type="text" [(ngModel)]="settings.port" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}">
|
||||
<input type="text" [(ngModel)]="settings.port" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}" pTooltip="You will have to restart after changing the port.">
|
||||
</div>
|
||||
</div>
|
||||
<small class="control-label">You will have to restart after changing the port.</small>
|
||||
|
||||
<!--<div class="form-group">
|
||||
<label for="BaseUrl" class="control-label">Base Url @Html.ToolTip("This will make Ombi run with a base url, usually used in reverse proxy scenarios")</label>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgbModule, NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import { AuthService } from '../auth/auth.service';
|
||||
import { AuthGuard } from '../auth/auth.guard';
|
||||
|
@ -17,10 +17,13 @@ import { SonarrComponent } from './sonarr/sonarr.component';
|
|||
import { RadarrComponent } from './radarr/radarr.component';
|
||||
import { LandingPageComponent } from './landingpage/landingpage.component';
|
||||
import { CustomizationComponent } from './customization/customization.component';
|
||||
import { EmailNotificationComponent } from './notifications/emailnotification.component';
|
||||
import { NotificationTemplate } from './notifications/notificationtemplate.component';
|
||||
|
||||
import { SettingsMenuComponent } from './settingsmenu.component';
|
||||
import { HumanizePipe } from '../pipes/HumanizePipe';
|
||||
|
||||
import { MenuModule, InputSwitchModule, InputTextModule } from 'primeng/primeng';
|
||||
import { MenuModule, InputSwitchModule, InputTextModule, TooltipModule } from 'primeng/primeng';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'Settings/Ombi', component: OmbiComponent, canActivate: [AuthGuard] },
|
||||
|
@ -30,6 +33,7 @@ const routes: Routes = [
|
|||
{ path: 'Settings/Radarr', component: RadarrComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Customization', component: CustomizationComponent, canActivate: [AuthGuard] },
|
||||
{ path: 'Settings/Email', component: EmailNotificationComponent, canActivate: [AuthGuard] },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -41,7 +45,9 @@ const routes: Routes = [
|
|||
InputSwitchModule,
|
||||
InputTextModule,
|
||||
AuthModule,
|
||||
NgbModule
|
||||
NgbModule,
|
||||
TooltipModule,
|
||||
NgbAccordionModule
|
||||
],
|
||||
declarations: [
|
||||
SettingsMenuComponent,
|
||||
|
@ -51,7 +57,10 @@ const routes: Routes = [
|
|||
LandingPageComponent,
|
||||
CustomizationComponent,
|
||||
SonarrComponent,
|
||||
RadarrComponent
|
||||
RadarrComponent,
|
||||
EmailNotificationComponent,
|
||||
HumanizePipe,
|
||||
NotificationTemplate
|
||||
],
|
||||
exports: [
|
||||
RouterModule
|
||||
|
|
|
@ -1,24 +1,46 @@
|
|||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Models.Notifications;
|
||||
using Ombi.Settings.Settings.Models;
|
||||
using Ombi.Settings.Settings.Models.External;
|
||||
using Ombi.Settings.Settings.Models.Notifications;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// The Settings Controller
|
||||
/// </summary>
|
||||
/// <seealso cref="Ombi.Controllers.BaseV1ApiController" />
|
||||
[Admin]
|
||||
public class SettingsController : BaseV1ApiController
|
||||
{
|
||||
public SettingsController(ISettingsResolver resolver)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SettingsController" /> class.
|
||||
/// </summary>
|
||||
/// <param name="resolver">The resolver.</param>
|
||||
/// <param name="mapper">The mapper.</param>
|
||||
public SettingsController(ISettingsResolver resolver, IMapper mapper, INotificationTemplatesRepository templateRepo)
|
||||
{
|
||||
SettingsResolver = resolver;
|
||||
Mapper = mapper;
|
||||
TemplateRepository = templateRepo;
|
||||
}
|
||||
|
||||
private ISettingsResolver SettingsResolver { get; }
|
||||
private IMapper Mapper { get; }
|
||||
private INotificationTemplatesRepository TemplateRepository { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Ombi settings.
|
||||
|
@ -169,6 +191,48 @@ namespace Ombi.Controllers
|
|||
return await Save(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the email notification settings.
|
||||
/// </summary>
|
||||
/// <param name="model">The model.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost("notifications/email")]
|
||||
public async Task<bool> EmailNotificationSettings([FromBody] EmailNotificationsViewModel model)
|
||||
{
|
||||
// Save the email settings
|
||||
var settings = Mapper.Map<EmailNotificationSettings>(model);
|
||||
var result = await Save(settings);
|
||||
|
||||
// Save the templates
|
||||
await TemplateRepository.UpdateRange(model.NotificationTemplates);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Email Notification Settings.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("notifications/email")]
|
||||
public async Task<EmailNotificationsViewModel> EmailNotificationSettings()
|
||||
{
|
||||
var emailSettings = await Get<EmailNotificationSettings>();
|
||||
var model = Mapper.Map<EmailNotificationsViewModel>(emailSettings);
|
||||
|
||||
// Lookup to see if we have any templates saved
|
||||
model.NotificationTemplates = await BuildTemplates(NotificationAgent.Email);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
|
||||
{
|
||||
var templates = await TemplateRepository.GetAllTemplates(agent);
|
||||
return templates.ToList();
|
||||
}
|
||||
|
||||
|
||||
private async Task<T> Get<T>()
|
||||
{
|
||||
var settings = SettingsResolver.Resolve<T>();
|
||||
|
|
|
@ -248,3 +248,28 @@ button.list-group-item:focus {
|
|||
background-color: $bg-colour;
|
||||
border-color: $bg-colour-disabled;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.card-header {
|
||||
background-color: $bg-colour;
|
||||
color: #ebebeb;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #1f1f1f;
|
||||
}
|
||||
.card-header > a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
background: $bg-colour-disabled;
|
||||
}
|
||||
|
||||
.panel {
|
||||
margin-bottom: 21px;
|
||||
background-color: $bg-colour-disabled;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
|
||||
}
|
|
@ -68,6 +68,15 @@ hr {
|
|||
box-shadow: 0 0 0 !important;
|
||||
}
|
||||
|
||||
.form-small {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
|
||||
.form-half {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 3.5rem $i;
|
||||
|
@ -236,14 +245,6 @@ label {
|
|||
border-color: $success-colour $i;
|
||||
}
|
||||
|
||||
#movieList .mix {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#tvList .mix {
|
||||
display: none;
|
||||
}
|
||||
|
||||
$border-radius: 10px;
|
||||
|
||||
.scroll-top-wrapper {
|
||||
|
@ -715,9 +716,22 @@ body {
|
|||
background: $form-color $i;
|
||||
}
|
||||
|
||||
// PrimeNg Overide
|
||||
.card-header {
|
||||
color: #ebebeb;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid transparent;
|
||||
background: $form-color;
|
||||
}
|
||||
.card-header > a{
|
||||
color:white;
|
||||
}
|
||||
|
||||
|
||||
// PrimeNg Overide
|
||||
.ui-growl-item {
|
||||
margin-top: 35px $i;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue