mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-13 00:32:57 -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 System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Ombi.Core.Engine.Interfaces;
|
using Ombi.Core.Engine.Interfaces;
|
||||||
|
using Ombi.Core.Notifications;
|
||||||
|
using Ombi.Store;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -136,10 +138,10 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
//Log.Fatal(e);
|
//Log.Fatal(e);
|
||||||
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
|
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
|
||||||
var notification = new NotificationModel
|
var notification = new NotificationOptions
|
||||||
{
|
{
|
||||||
DateTime = DateTime.Now,
|
DateTime = DateTime.Now,
|
||||||
User = Username,
|
RequestedUser = Username,
|
||||||
RequestType = RequestType.Movie,
|
RequestType = RequestType.Movie,
|
||||||
Title = model.Title,
|
Title = model.Title,
|
||||||
NotificationType = NotificationType.ItemAddedToFaultQueue
|
NotificationType = NotificationType.ItemAddedToFaultQueue
|
||||||
|
@ -208,10 +210,10 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
if (ShouldSendNotification(model.Type))
|
if (ShouldSendNotification(model.Type))
|
||||||
{
|
{
|
||||||
var notificationModel = new NotificationModel
|
var notificationModel = new NotificationOptions
|
||||||
{
|
{
|
||||||
Title = model.Title,
|
Title = model.Title,
|
||||||
User = Username,
|
RequestedUser = Username,
|
||||||
DateTime = DateTime.Now,
|
DateTime = DateTime.Now,
|
||||||
NotificationType = NotificationType.NewRequest,
|
NotificationType = NotificationType.NewRequest,
|
||||||
RequestType = model.Type,
|
RequestType = model.Type,
|
||||||
|
|
|
@ -15,6 +15,8 @@ using System.Linq;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Engine.Interfaces;
|
using Ombi.Core.Engine.Interfaces;
|
||||||
|
using Ombi.Core.Notifications;
|
||||||
|
using Ombi.Store;
|
||||||
|
|
||||||
namespace Ombi.Core.Engine
|
namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
|
@ -272,17 +274,20 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
if (ShouldSendNotification(model.Type))
|
if (ShouldSendNotification(model.Type))
|
||||||
{
|
{
|
||||||
var notificationModel = new NotificationModel
|
var n = new NotificationOptions();
|
||||||
{
|
|
||||||
Title = model.Title,
|
|
||||||
User = Username,
|
|
||||||
DateTime = DateTime.Now,
|
|
||||||
NotificationType = NotificationType.NewRequest,
|
|
||||||
RequestType = model.Type,
|
|
||||||
ImgSrc = model.PosterPath
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||||
|
|
|
@ -4,24 +4,6 @@ using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ombi.Core.Models.Requests
|
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
|
public enum IssueState
|
||||||
{
|
{
|
||||||
None = 99,
|
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.Engine.Interfaces;
|
||||||
using Ombi.Core.IdentityResolver;
|
using Ombi.Core.IdentityResolver;
|
||||||
using Ombi.Core.Models.Requests;
|
using Ombi.Core.Models.Requests;
|
||||||
|
using Ombi.Core.Notifications;
|
||||||
using Ombi.Core.Requests.Models;
|
using Ombi.Core.Requests.Models;
|
||||||
using Ombi.Core.Rule;
|
using Ombi.Core.Rule;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
@ -75,6 +76,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IUserRepository, UserRepository>();
|
services.AddTransient<IUserRepository, UserRepository>();
|
||||||
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
services.AddTransient<ISettingsResolver, SettingsResolver>();
|
||||||
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
|
services.AddTransient<IPlexContentRepository, PlexContentRepository>();
|
||||||
|
services.AddTransient<INotificationTemplatesRepository, NotificationTemplatesRepository>();
|
||||||
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
|
services.AddTransient(typeof(ISettingsService<>), typeof(SettingsServiceV2<>));
|
||||||
}
|
}
|
||||||
public static void RegisterServices(this IServiceCollection services)
|
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.Helpers
|
||||||
|
|
||||||
namespace Ombi.Notifications
|
|
||||||
{
|
{
|
||||||
public enum NotificationType
|
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.Api.Discord;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Notifications;
|
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
using Ombi.Settings.Settings.Models.Notifications;
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
namespace Ombi.Notification.Agents
|
namespace Ombi.Notifications.Agents
|
||||||
{
|
{
|
||||||
public class DiscordNotification : BaseNotification<DiscordNotificationSettings>
|
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;
|
Api = api;
|
||||||
Logger = log;
|
Logger = log;
|
||||||
|
@ -45,9 +45,9 @@ namespace Ombi.Notification.Agents
|
||||||
return true;
|
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
|
var notification = new NotificationMessage
|
||||||
{
|
{
|
||||||
|
@ -56,9 +56,9 @@ namespace Ombi.Notification.Agents
|
||||||
await Send(notification, settings);
|
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
|
var notification = new NotificationMessage
|
||||||
{
|
{
|
||||||
Message = message,
|
Message = message,
|
||||||
|
@ -66,9 +66,9 @@ namespace Ombi.Notification.Agents
|
||||||
await Send(notification, settings);
|
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
|
var notification = new NotificationMessage
|
||||||
{
|
{
|
||||||
Message = message,
|
Message = message,
|
||||||
|
@ -76,7 +76,7 @@ namespace Ombi.Notification.Agents
|
||||||
await Send(notification, settings);
|
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 message = $"Hello! Your request for {model.Title} has been declined, Sorry!";
|
||||||
var notification = new NotificationMessage
|
var notification = new NotificationMessage
|
||||||
|
@ -86,7 +86,7 @@ namespace Ombi.Notification.Agents
|
||||||
await Send(notification, settings);
|
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 message = $"Hello! The request for {model.Title} has now been approved!";
|
||||||
var notification = new NotificationMessage
|
var notification = new NotificationMessage
|
||||||
|
@ -96,7 +96,7 @@ namespace Ombi.Notification.Agents
|
||||||
await Send(notification, settings);
|
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 message = $"Hello! The request for {model.Title} is now available!";
|
||||||
var notification = new NotificationMessage
|
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 message = $"This is a test from Ombi, if you can see this then we have successfully pushed a notification!";
|
||||||
var notification = new NotificationMessage
|
var notification = new NotificationMessage
|
|
@ -2,17 +2,18 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MailKit.Net.Smtp;
|
using MailKit.Net.Smtp;
|
||||||
using MimeKit;
|
using MimeKit;
|
||||||
using Ombi.Core.Models.Requests;
|
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
using Ombi.Notifications.Templates;
|
using Ombi.Notifications.Templates;
|
||||||
using Ombi.Settings.Settings.Models.Notifications;
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
namespace Ombi.Notifications.Email
|
namespace Ombi.Notifications.Agents
|
||||||
{
|
{
|
||||||
public class EmailNotification : BaseNotification<EmailNotificationSettings>
|
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 (settings.Authentication)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(settings.EmailUsername) || string.IsNullOrEmpty(settings.EmailPassword))
|
if (string.IsNullOrEmpty(settings.Username) || string.IsNullOrEmpty(settings.Password))
|
||||||
{
|
{
|
||||||
return false;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -39,69 +40,78 @@ namespace Ombi.Notifications.Email
|
||||||
return true;
|
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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(template.Subject, template.Message, model.ImgSrc);
|
||||||
$"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
|
var message = new NotificationMessage
|
||||||
{
|
{
|
||||||
Message = html,
|
Message = html,
|
||||||
Subject = $"Ombi: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
|
Subject = $"Ombi: New {model.RequestType} request for {model.Title}!",
|
||||||
To = settings.RecipientEmail,
|
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);
|
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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(
|
||||||
$"Ombi: New issue for {model.Title}!",
|
$"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);
|
model.ImgSrc);
|
||||||
|
|
||||||
var message = new NotificationMessage
|
var message = new NotificationMessage
|
||||||
{
|
{
|
||||||
Message = html,
|
Message = html,
|
||||||
Subject = $"Ombi: New issue for {model.Title}!",
|
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);
|
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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(
|
||||||
"Ombi: A request could not be added.",
|
"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);
|
model.ImgSrc);
|
||||||
|
|
||||||
var message = new NotificationMessage
|
var message = new NotificationMessage
|
||||||
{
|
{
|
||||||
Message = html,
|
Message = html,
|
||||||
Subject = $"Ombi: A request could not be added",
|
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);
|
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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(
|
||||||
|
@ -122,7 +132,7 @@ namespace Ombi.Notifications.Email
|
||||||
await Send(message, settings);
|
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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(
|
||||||
|
@ -142,7 +152,7 @@ namespace Ombi.Notifications.Email
|
||||||
await Send(message, settings);
|
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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(
|
||||||
|
@ -178,12 +188,12 @@ namespace Ombi.Notifications.Email
|
||||||
Body = body.ToMessageBody(),
|
Body = body.ToMessageBody(),
|
||||||
Subject = model.Subject
|
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));
|
message.To.Add(new MailboxAddress(model.To, model.To));
|
||||||
|
|
||||||
using (var client = new SmtpClient())
|
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
|
// Note: since we don't have an OAuth2 token, disable
|
||||||
// the XOAUTH2 authentication mechanism.
|
// the XOAUTH2 authentication mechanism.
|
||||||
|
@ -191,7 +201,7 @@ namespace Ombi.Notifications.Email
|
||||||
|
|
||||||
if (settings.Authentication)
|
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);
|
//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.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 email = new EmailBasicTemplate();
|
||||||
var html = email.LoadTemplate(
|
var html = email.LoadTemplate(
|
||||||
|
@ -216,7 +226,7 @@ namespace Ombi.Notifications.Email
|
||||||
{
|
{
|
||||||
Message = html,
|
Message = html,
|
||||||
Subject = $"Ombi: Test",
|
Subject = $"Ombi: Test",
|
||||||
To = settings.RecipientEmail,
|
To = settings.AdminEmail,
|
||||||
};
|
};
|
||||||
|
|
||||||
message.Other.Add("PlainTextBody", "This is just a test! Success!");
|
message.Other.Add("PlainTextBody", "This is just a test! Success!");
|
|
@ -1,28 +1,32 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models;
|
using Ombi.Helpers;
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
|
using Ombi.Store;
|
||||||
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
namespace Ombi.Notifications
|
namespace Ombi.Notifications
|
||||||
{
|
{
|
||||||
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
|
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;
|
Settings = settings;
|
||||||
|
TemplateRepository = templateRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ISettingsService<T> Settings { get; }
|
protected ISettingsService<T> Settings { get; }
|
||||||
|
protected INotificationTemplatesRepository TemplateRepository { get; }
|
||||||
public abstract string NotificationName { get; }
|
public abstract string NotificationName { get; }
|
||||||
|
|
||||||
public async Task NotifyAsync(NotificationModel model)
|
public async Task NotifyAsync(NotificationOptions model)
|
||||||
{
|
{
|
||||||
var configuration = GetConfiguration();
|
var configuration = GetConfiguration();
|
||||||
await NotifyAsync(model, configuration);
|
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);
|
if (settings == null) await NotifyAsync(model);
|
||||||
|
|
||||||
|
@ -79,13 +83,13 @@ namespace Ombi.Notifications
|
||||||
|
|
||||||
|
|
||||||
protected abstract bool ValidateConfiguration(T settings);
|
protected abstract bool ValidateConfiguration(T settings);
|
||||||
protected abstract Task NewRequest(NotificationModel model, T settings);
|
protected abstract Task NewRequest(NotificationOptions model, T settings);
|
||||||
protected abstract Task Issue(NotificationModel model, T settings);
|
protected abstract Task Issue(NotificationOptions model, T settings);
|
||||||
protected abstract Task AddedToRequestQueue(NotificationModel model, T settings);
|
protected abstract Task AddedToRequestQueue(NotificationOptions model, T settings);
|
||||||
protected abstract Task RequestDeclined(NotificationModel model, T settings);
|
protected abstract Task RequestDeclined(NotificationOptions model, T settings);
|
||||||
protected abstract Task RequestApproved(NotificationModel model, T settings);
|
protected abstract Task RequestApproved(NotificationOptions model, T settings);
|
||||||
protected abstract Task AvailableRequest(NotificationModel model, T settings);
|
protected abstract Task AvailableRequest(NotificationOptions model, T settings);
|
||||||
protected abstract Task Send(NotificationMessage 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; }
|
string NotificationName { get; }
|
||||||
|
|
||||||
Task NotifyAsync(NotificationModel model);
|
Task NotifyAsync(NotificationOptions model);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sends a notification to the user, this is usually for testing the settings.
|
/// 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="model">The model.</param>
|
||||||
/// <param name="settings">The settings.</param>
|
/// <param name="settings">The settings.</param>
|
||||||
/// <returns></returns>
|
/// <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 System.Threading.Tasks;
|
using Ombi.Notifications;
|
||||||
using Ombi.Core.Settings.Models;
|
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
namespace Ombi.Notifications
|
namespace Ombi.Core.Notifications
|
||||||
{
|
{
|
||||||
public interface INotificationService
|
public interface INotificationService
|
||||||
{
|
{
|
||||||
Task Publish(NotificationModel model);
|
Task Publish(NotificationOptions model);
|
||||||
Task Publish(NotificationModel model, Settings.Settings.Models.Settings settings);
|
Task Publish(NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings);
|
||||||
Task PublishTest(NotificationModel model, Settings.Settings.Models.Settings settings, INotification type);
|
Task PublishTest(NotificationOptions model, Ombi.Settings.Settings.Models.Settings settings, INotification type);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,17 @@
|
||||||
using System;
|
using System;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Store;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Notifications.Models
|
namespace Ombi.Notifications.Models
|
||||||
{
|
{
|
||||||
public class NotificationModel
|
public class NotificationOptions
|
||||||
{
|
{
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string Body { 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 NotificationType NotificationType { get; set; }
|
||||||
public string User { get; set; }
|
public string RequestedUser { get; set; }
|
||||||
public string UserEmail { get; set; }
|
public string UserEmail { get; set; }
|
||||||
public RequestType RequestType { get; set; }
|
public RequestType RequestType { get; set; }
|
||||||
public string ImgSrc { 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.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Core.Notifications;
|
||||||
using Ombi.Helpers;
|
using Ombi.Helpers;
|
||||||
using Ombi.Notifications.Models;
|
using Ombi.Notifications.Models;
|
||||||
|
|
||||||
|
@ -16,13 +17,13 @@ namespace Ombi.Notifications
|
||||||
Log = log;
|
Log = log;
|
||||||
NotificationAgents = new List<INotification>();
|
NotificationAgents = new List<INotification>();
|
||||||
|
|
||||||
var baseSearchType = typeof(BaseNotification<>).FullName;
|
var baseSearchType = typeof(BaseNotification<>).Name;
|
||||||
|
|
||||||
var ass = typeof(NotificationService).GetTypeInfo().Assembly;
|
var ass = typeof(NotificationService).GetTypeInfo().Assembly;
|
||||||
|
|
||||||
foreach (var ti in ass.DefinedTypes)
|
foreach (var ti in ass.DefinedTypes)
|
||||||
{
|
{
|
||||||
if (ti?.BaseType?.FullName == baseSearchType)
|
if (ti?.BaseType?.Name == baseSearchType)
|
||||||
{
|
{
|
||||||
var type = ti?.AsType();
|
var type = ti?.AsType();
|
||||||
var ctors = type.GetConstructors();
|
var ctors = type.GetConstructors();
|
||||||
|
@ -48,7 +49,7 @@ namespace Ombi.Notifications
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The model.</param>
|
/// <param name="model">The model.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Publish(NotificationModel model)
|
public async Task Publish(NotificationOptions model)
|
||||||
{
|
{
|
||||||
var notificationTasks = NotificationAgents.Select(notification => NotifyAsync(notification, model));
|
var notificationTasks = NotificationAgents.Select(notification => NotifyAsync(notification, model));
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ namespace Ombi.Notifications
|
||||||
/// <param name="model">The model.</param>
|
/// <param name="model">The model.</param>
|
||||||
/// <param name="settings">The settings.</param>
|
/// <param name="settings">The settings.</param>
|
||||||
/// <returns></returns>
|
/// <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));
|
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
|
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
|
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);
|
await type.NotifyAsync(model, settings);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ombi.Api.Discord\Ombi.Api.Discord.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
|
<ProjectReference Include="..\Ombi.Notifications.Templates\Ombi.Notifications.Templates.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
<ProjectReference Include="..\Ombi.Settings\Ombi.Settings.csproj" />
|
||||||
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
<ProjectReference Include="..\Ombi.Store\Ombi.Store.csproj" />
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
namespace Ombi.Settings.Settings.Models.Notifications
|
namespace Ombi.Settings.Settings.Models.Notifications
|
||||||
{
|
{
|
||||||
public sealed class EmailNotificationSettings : Settings
|
public class EmailNotificationSettings : Settings
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public string EmailHost { get; set; }
|
public string Host { get; set; }
|
||||||
public string EmailPassword { get; set; }
|
public string Password { get; set; }
|
||||||
public int EmailPort { get; set; }
|
public int Port { get; set; }
|
||||||
public string EmailSender { get; set; }
|
public string Sender { get; set; }
|
||||||
public string EmailUsername { get; set; }
|
public string Username { get; set; }
|
||||||
public bool Authentication { 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<T> Entry<T>(T entry) where T : class;
|
||||||
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;
|
EntityEntry<TEntity> Attach<TEntity>(TEntity entity) where TEntity : class;
|
||||||
DbSet<TEntity> Set<TEntity>() 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 Microsoft.EntityFrameworkCore;
|
||||||
|
using Ombi.Helpers;
|
||||||
using Ombi.Store.Entities;
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
namespace Ombi.Store.Context
|
namespace Ombi.Store.Context
|
||||||
{
|
{
|
||||||
public class OmbiContext : DbContext, IOmbiContext
|
public sealed class OmbiContext : DbContext, IOmbiContext
|
||||||
{
|
{
|
||||||
private static bool _created;
|
private static bool _created;
|
||||||
public OmbiContext()
|
public OmbiContext()
|
||||||
|
@ -26,6 +30,9 @@ namespace Ombi.Store.Context
|
||||||
// Run Script
|
// Run Script
|
||||||
|
|
||||||
Database.ExecuteSqlCommand(file, 0);
|
Database.ExecuteSqlCommand(file, 0);
|
||||||
|
|
||||||
|
// Add the notifcation templates
|
||||||
|
AddAllTemplates();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbSet<RequestBlobs> Requests { get; set; }
|
public DbSet<RequestBlobs> Requests { get; set; }
|
||||||
|
@ -33,10 +40,93 @@ namespace Ombi.Store.Context
|
||||||
public DbSet<User> Users { get; set; }
|
public DbSet<User> Users { get; set; }
|
||||||
public DbSet<PlexContent> PlexContent { get; set; }
|
public DbSet<PlexContent> PlexContent { get; set; }
|
||||||
public DbSet<RadarrCache> RadarrCache { get; set; }
|
public DbSet<RadarrCache> RadarrCache { get; set; }
|
||||||
|
public DbSet<NotificationTemplates> NotificationTemplates { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
optionsBuilder.UseSqlite("Data Source=Ombi.db");
|
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
|
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
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Discord", "Ombi.Api.Discord\Ombi.Api.Discord.csproj", "{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Notification.Agents", "Ombi.Notification.Agents\Ombi.Notification.Agents.csproj", "{DC9E3E44-802A-4D9B-A70F-D198CFE4C73D}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -172,6 +166,5 @@ Global
|
||||||
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
{FC6A8F7C-9722-4AE4-960D-277ACB0E81CB} = {6F42AB98-9196-44C4-B888-D5E409F415A1}
|
||||||
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
{94D04C1F-E35A-499C-B0A0-9FADEBDF8336} = {9293CA11-360A-4C20-A674-B9E794431BF5}
|
||||||
{5AF2B6D2-5CC6-49FE-928A-BA27AF52B194} = {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
|
EndGlobalSection
|
||||||
EndGlobal
|
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>
|
||||||
<div class="col-md-3">
|
<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" 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>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
|
|
|
@ -5,7 +5,6 @@ import 'rxjs/add/operator/distinctUntilChanged';
|
||||||
import 'rxjs/add/operator/map';
|
import 'rxjs/add/operator/map';
|
||||||
import "rxjs/add/operator/takeUntil";
|
import "rxjs/add/operator/takeUntil";
|
||||||
|
|
||||||
|
|
||||||
import 'rxjs/add/operator/debounceTime';
|
import 'rxjs/add/operator/debounceTime';
|
||||||
import 'rxjs/add/operator/distinctUntilChanged';
|
import 'rxjs/add/operator/distinctUntilChanged';
|
||||||
import 'rxjs/add/operator/map';
|
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)
|
this.requestService.getTvRequests(this.amountToLoad, this.currentlyLoaded + 1)
|
||||||
.takeUntil(this.subscriptions)
|
.takeUntil(this.subscriptions)
|
||||||
.subscribe(x => {
|
.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);
|
this.searchChanged.next(text.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeRequest(request: ITvRequestModel) {
|
public removeRequest(request: ITvRequestModel) {
|
||||||
this.requestService.removeTvRequest(request);
|
this.requestService.removeTvRequest(request);
|
||||||
this.removeRequestFromUi(request);
|
this.removeRequestFromUi(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
changeAvailability(request: ITvRequestModel, available: boolean) {
|
public changeAvailability(request: ITvRequestModel, available: boolean) {
|
||||||
request.available = available;
|
request.available = available;
|
||||||
|
|
||||||
this.updateRequest(request);
|
this.updateRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
approve(request: ITvRequestModel) {
|
public approve(request: ITvRequestModel) {
|
||||||
request.approved = true;
|
request.approved = true;
|
||||||
request.denied = false;
|
request.denied = false;
|
||||||
this.updateRequest(request);
|
this.updateRequest(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
deny(request: ITvRequestModel) {
|
public deny(request: ITvRequestModel) {
|
||||||
request.approved = false;
|
request.approved = false;
|
||||||
request.denied = true;
|
request.denied = true;
|
||||||
this.updateRequest(request);
|
this.updateRequest(request);
|
||||||
|
@ -100,6 +99,14 @@ export class TvRequestsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
public approveSeasonRequest(request: IChildTvRequest) {
|
public approveSeasonRequest(request: IChildTvRequest) {
|
||||||
request.approved = true;
|
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)
|
this.requestService.updateTvRequest(this.selectedSeason)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
ICustomizationSettings,
|
ICustomizationSettings,
|
||||||
IRadarrSettings
|
IRadarrSettings
|
||||||
} from '../interfaces/ISettings';
|
} from '../interfaces/ISettings';
|
||||||
|
import { IEmailNotificationSettings } from '../interfaces/INotifcationSettings';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SettingsService extends ServiceAuthHelpers {
|
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)
|
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">
|
<div class="form-group">
|
||||||
<label for="logo" class="control-label">Custom Logo</label>
|
<label for="logo" class="control-label">Custom Logo</label>
|
||||||
<div>
|
<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>
|
||||||
</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">
|
<div class="form-group">
|
||||||
|
|
|
@ -16,10 +16,9 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="checkbox">
|
<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>
|
<label for="BeforeLogin">Show before the login</label>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
|
|
||||||
<p class="form-group">Notice Message</p>
|
<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">
|
<div class="form-group">
|
||||||
<label for="portNumber" class="control-label">Port</label>
|
<label for="portNumber" class="control-label">Port</label>
|
||||||
<div>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<small class="control-label">You will have to restart after changing the port.</small>
|
|
||||||
|
|
||||||
<!--<div class="form-group">
|
<!--<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>
|
<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 { CommonModule } from '@angular/common';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { RouterModule, Routes } from '@angular/router';
|
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 { AuthService } from '../auth/auth.service';
|
||||||
import { AuthGuard } from '../auth/auth.guard';
|
import { AuthGuard } from '../auth/auth.guard';
|
||||||
|
@ -17,10 +17,13 @@ import { SonarrComponent } from './sonarr/sonarr.component';
|
||||||
import { RadarrComponent } from './radarr/radarr.component';
|
import { RadarrComponent } from './radarr/radarr.component';
|
||||||
import { LandingPageComponent } from './landingpage/landingpage.component';
|
import { LandingPageComponent } from './landingpage/landingpage.component';
|
||||||
import { CustomizationComponent } from './customization/customization.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 { 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 = [
|
const routes: Routes = [
|
||||||
{ path: 'Settings/Ombi', component: OmbiComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/Ombi', component: OmbiComponent, canActivate: [AuthGuard] },
|
||||||
|
@ -30,6 +33,7 @@ const routes: Routes = [
|
||||||
{ path: 'Settings/Radarr', component: RadarrComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/Radarr', component: RadarrComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/LandingPage', component: LandingPageComponent, canActivate: [AuthGuard] },
|
||||||
{ path: 'Settings/Customization', component: CustomizationComponent, canActivate: [AuthGuard] },
|
{ path: 'Settings/Customization', component: CustomizationComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: 'Settings/Email', component: EmailNotificationComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -41,7 +45,9 @@ const routes: Routes = [
|
||||||
InputSwitchModule,
|
InputSwitchModule,
|
||||||
InputTextModule,
|
InputTextModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
NgbModule
|
NgbModule,
|
||||||
|
TooltipModule,
|
||||||
|
NgbAccordionModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
SettingsMenuComponent,
|
SettingsMenuComponent,
|
||||||
|
@ -51,7 +57,10 @@ const routes: Routes = [
|
||||||
LandingPageComponent,
|
LandingPageComponent,
|
||||||
CustomizationComponent,
|
CustomizationComponent,
|
||||||
SonarrComponent,
|
SonarrComponent,
|
||||||
RadarrComponent
|
RadarrComponent,
|
||||||
|
EmailNotificationComponent,
|
||||||
|
HumanizePipe,
|
||||||
|
NotificationTemplate
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule
|
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.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Ombi.Attributes;
|
using Ombi.Attributes;
|
||||||
using Ombi.Core.Settings;
|
using Ombi.Core.Settings;
|
||||||
using Ombi.Core.Settings.Models;
|
using Ombi.Core.Settings.Models;
|
||||||
using Ombi.Core.Settings.Models.External;
|
using Ombi.Core.Settings.Models.External;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Models.Notifications;
|
||||||
using Ombi.Settings.Settings.Models;
|
using Ombi.Settings.Settings.Models;
|
||||||
using Ombi.Settings.Settings.Models.External;
|
using Ombi.Settings.Settings.Models.External;
|
||||||
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
using Ombi.Store.Repository;
|
||||||
|
|
||||||
namespace Ombi.Controllers
|
namespace Ombi.Controllers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Settings Controller
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="Ombi.Controllers.BaseV1ApiController" />
|
||||||
[Admin]
|
[Admin]
|
||||||
public class SettingsController : BaseV1ApiController
|
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;
|
SettingsResolver = resolver;
|
||||||
|
Mapper = mapper;
|
||||||
|
TemplateRepository = templateRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ISettingsResolver SettingsResolver { get; }
|
private ISettingsResolver SettingsResolver { get; }
|
||||||
|
private IMapper Mapper { get; }
|
||||||
|
private INotificationTemplatesRepository TemplateRepository { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Ombi settings.
|
/// Gets the Ombi settings.
|
||||||
|
@ -169,6 +191,48 @@ namespace Ombi.Controllers
|
||||||
return await Save(settings);
|
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>()
|
private async Task<T> Get<T>()
|
||||||
{
|
{
|
||||||
var settings = SettingsResolver.Resolve<T>();
|
var settings = SettingsResolver.Resolve<T>();
|
||||||
|
@ -180,5 +244,5 @@ namespace Ombi.Controllers
|
||||||
var settings = SettingsResolver.Resolve<T>();
|
var settings = SettingsResolver.Resolve<T>();
|
||||||
return await settings.SaveSettingsAsync(settingsModel);
|
return await settings.SaveSettingsAsync(settingsModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -248,3 +248,28 @@ button.list-group-item:focus {
|
||||||
background-color: $bg-colour;
|
background-color: $bg-colour;
|
||||||
border-color: $bg-colour-disabled;
|
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;
|
box-shadow: 0 0 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-small {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.form-half {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 3.5rem $i;
|
font-size: 3.5rem $i;
|
||||||
|
@ -236,14 +245,6 @@ label {
|
||||||
border-color: $success-colour $i;
|
border-color: $success-colour $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
#movieList .mix {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tvList .mix {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
$border-radius: 10px;
|
$border-radius: 10px;
|
||||||
|
|
||||||
.scroll-top-wrapper {
|
.scroll-top-wrapper {
|
||||||
|
@ -715,9 +716,22 @@ body {
|
||||||
background: $form-color $i;
|
background: $form-color $i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrimeNg Overide
|
.card-header {
|
||||||
|
color: #ebebeb;
|
||||||
.ui-growl-item{
|
padding: 10px 15px;
|
||||||
margin-top:35px $i;
|
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