mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-11 07:46:05 -07:00
Email Notifications are now fully customizable and work! #865
We also have reactive forms in the UI, should apply this to other settings.
This commit is contained in:
parent
3b0b35f760
commit
9a4dbb3dce
21 changed files with 361 additions and 232 deletions
|
@ -1,4 +1,5 @@
|
|||
using Ombi.Core.Claims;
|
||||
using System;
|
||||
using Ombi.Core.Claims;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Rule;
|
||||
using Ombi.Core.Rules;
|
||||
|
@ -6,7 +7,12 @@ using Ombi.Store.Entities;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using Hangfire;
|
||||
using Ombi.Core.Models.Requests.Movie;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
|
||||
namespace Ombi.Core.Engine.Interfaces
|
||||
{
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
using Hangfire;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Api.TheMovieDb;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Models.Requests.Movie;
|
||||
using Ombi.Core.Models.Search;
|
||||
using Ombi.Core.Rules;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -16,24 +14,22 @@ using System.Security.Principal;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Store;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class MovieRequestEngine : BaseMediaEngine, IMovieRequestEngine
|
||||
{
|
||||
public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationService notificationService, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log) : base(user, requestService, r)
|
||||
INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger<MovieRequestEngine> log) : base(user, requestService, r)
|
||||
{
|
||||
MovieApi = movieApi;
|
||||
NotificationService = notificationService;
|
||||
NotificationHelper = helper;
|
||||
Sender = sender;
|
||||
Logger = log;
|
||||
}
|
||||
|
||||
private IMovieDbApi MovieApi { get; }
|
||||
private INotificationService NotificationService { get; }
|
||||
private INotificationHelper NotificationHelper { get; }
|
||||
private IMovieSender Sender { get; }
|
||||
private ILogger<MovieRequestEngine> Logger { get; }
|
||||
|
||||
|
@ -84,6 +80,7 @@ namespace Ombi.Core.Engine
|
|||
// });
|
||||
//}
|
||||
|
||||
|
||||
var requestModel = new MovieRequestModel
|
||||
{
|
||||
ProviderId = movieInfo.Id,
|
||||
|
@ -102,14 +99,14 @@ namespace Ombi.Core.Engine
|
|||
Issues = IssueState.None
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var ruleResults = RunRequestRules(requestModel).ToList();
|
||||
if (ruleResults.Any(x => !x.Success))
|
||||
{
|
||||
return new RequestEngineResult
|
||||
{
|
||||
ErrorMessage = ruleResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message
|
||||
};
|
||||
}
|
||||
|
||||
if (requestModel.Approved) // The rules have auto approved this
|
||||
{
|
||||
|
@ -134,29 +131,6 @@ namespace Ombi.Core.Engine
|
|||
return await AddMovieRequest(requestModel, /*settings,*/
|
||||
$"{fullMovieName} has been successfully added!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
//Log.Fatal(e);
|
||||
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
|
||||
var notification = new NotificationOptions
|
||||
{
|
||||
DateTime = DateTime.Now,
|
||||
RequestedUser = Username,
|
||||
RequestType = RequestType.Movie,
|
||||
Title = model.Title,
|
||||
NotificationType = NotificationType.ItemAddedToFaultQueue
|
||||
};
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notification).Wait());
|
||||
|
||||
//return Response.AsJson(new JsonResponseModel
|
||||
//{
|
||||
// Result = true,
|
||||
// Message = $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}"
|
||||
//});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<MovieRequestModel>> GetRequests(int count, int position)
|
||||
{
|
||||
|
@ -210,19 +184,7 @@ namespace Ombi.Core.Engine
|
|||
|
||||
if (ShouldSendNotification(model.Type))
|
||||
{
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
Title = model.Title,
|
||||
RequestedUser = Username,
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.NewRequest,
|
||||
RequestType = model.Type,
|
||||
ImgSrc = model.Type == RequestType.Movie
|
||||
? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}"
|
||||
: model.PosterPath
|
||||
};
|
||||
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel).Wait());
|
||||
NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||
|
|
|
@ -15,23 +15,21 @@ using System.Linq;
|
|||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Store;
|
||||
|
||||
namespace Ombi.Core.Engine
|
||||
{
|
||||
public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine
|
||||
{
|
||||
public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user,
|
||||
INotificationService notificationService, IMapper map,
|
||||
INotificationHelper helper, IMapper map,
|
||||
IRuleEvaluator rule) : base(user, requestService, rule)
|
||||
{
|
||||
TvApi = tvApi;
|
||||
NotificationService = notificationService;
|
||||
NotificationHelper = helper;
|
||||
Mapper = map;
|
||||
}
|
||||
|
||||
private INotificationService NotificationService { get; }
|
||||
private INotificationHelper NotificationHelper { get; }
|
||||
private ITvMazeApi TvApi { get; }
|
||||
private IMapper Mapper { get; }
|
||||
|
||||
|
@ -270,24 +268,11 @@ namespace Ombi.Core.Engine
|
|||
return await AfterRequest(model);
|
||||
}
|
||||
|
||||
private async Task<RequestEngineResult> AfterRequest(TvRequestModel model)
|
||||
private Task<RequestEngineResult> AfterRequest(TvRequestModel model)
|
||||
{
|
||||
if (ShouldSendNotification(model.Type))
|
||||
{
|
||||
var n = new NotificationOptions();
|
||||
|
||||
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()
|
||||
);
|
||||
NotificationHelper.NewRequest(model);
|
||||
}
|
||||
|
||||
//var limit = await RequestLimitRepo.GetAllAsync();
|
||||
|
@ -308,7 +293,7 @@ namespace Ombi.Core.Engine
|
|||
// await RequestLimitRepo.UpdateAsync(usersLimit);
|
||||
//}
|
||||
|
||||
return new RequestEngineResult { RequestAdded = true };
|
||||
return Task.FromResult(new RequestEngineResult { RequestAdded = true });
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<TvRequestModel>> GetApprovedRequests()
|
||||
|
|
9
src/Ombi.Core/INotificationHelper.cs
Normal file
9
src/Ombi.Core/INotificationHelper.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using Ombi.Core.Models.Requests;
|
||||
|
||||
namespace Ombi.Core
|
||||
{
|
||||
public interface INotificationHelper
|
||||
{
|
||||
void NewRequest(BaseRequestModel model);
|
||||
}
|
||||
}
|
36
src/Ombi.Core/NotificationHelper.cs
Normal file
36
src/Ombi.Core/NotificationHelper.cs
Normal file
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using Hangfire;
|
||||
using Ombi.Core.Models.Requests;
|
||||
using Ombi.Core.Notifications;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Core
|
||||
{
|
||||
public class NotificationHelper : INotificationHelper
|
||||
{
|
||||
public NotificationHelper(INotificationService service)
|
||||
{
|
||||
NotificationService = service;
|
||||
}
|
||||
private INotificationService NotificationService { get; }
|
||||
|
||||
public void NewRequest(BaseRequestModel model)
|
||||
{
|
||||
var notificationModel = new NotificationOptions
|
||||
{
|
||||
Title = model.Title,
|
||||
RequestedUser = model.RequestedUser,
|
||||
DateTime = DateTime.Now,
|
||||
NotificationType = NotificationType.NewRequest,
|
||||
RequestType = model.Type,
|
||||
ImgSrc = model.Type == RequestType.Movie
|
||||
? $"https://image.tmdb.org/t/p/w300/{model.PosterPath}"
|
||||
: model.PosterPath
|
||||
};
|
||||
BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -84,6 +84,7 @@ namespace Ombi.DependencyInjection
|
|||
services.AddTransient<IRequestServiceMain, RequestService>();
|
||||
services.AddTransient(typeof(IRequestService<>), typeof(JsonRequestService<>));
|
||||
services.AddSingleton<INotificationService, NotificationService>();
|
||||
services.AddSingleton<INotificationHelper, NotificationHelper>();
|
||||
}
|
||||
|
||||
public static void RegisterJobs(this IServiceCollection services)
|
||||
|
|
|
@ -40,31 +40,43 @@ namespace Ombi.Notifications.Agents
|
|||
return true;
|
||||
}
|
||||
|
||||
private async Task<NotificationMessageContent> LoadTemplate(NotificationType type, NotificationOptions model)
|
||||
private async Task<NotificationMessage> LoadTemplate(NotificationType type, NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, type);
|
||||
if (!template.Enabled)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// Need to do the parsing
|
||||
var resolver = new NotificationMessageResolver();
|
||||
return resolver.ParseMessage(template, new NotificationMessageCurlys(model.RequestedUser, model.Title, DateTime.Now.ToString("D"),
|
||||
var parsed = resolver.ParseMessage(template, new NotificationMessageCurlys(model.RequestedUser, model.Title, DateTime.Now.ToString("D"),
|
||||
model.NotificationType.ToString(), null));
|
||||
|
||||
}
|
||||
|
||||
protected override async Task NewRequest(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var template = await LoadTemplate(NotificationType.NewRequest, model);
|
||||
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(template.Subject, template.Message, model.ImgSrc);
|
||||
var html = email.LoadTemplate(parsed.Subject, parsed.Message, model.ImgSrc);
|
||||
|
||||
|
||||
var message = new NotificationMessage
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: New {model.RequestType} request for {model.Title}!",
|
||||
Subject = parsed.Subject,
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override async Task NewRequest(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var message = await LoadTemplate(NotificationType.NewRequest, model, settings);
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -72,18 +84,11 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task Issue(NotificationOptions model, EmailNotificationSettings settings)
|
||||
{
|
||||
var email = new EmailBasicTemplate();
|
||||
var html = email.LoadTemplate(
|
||||
$"Ombi: New issue for {model.Title}!",
|
||||
$"Hello! The user '{model.RequestedUser}' has reported a new issue {model.Body} for the title {model.Title}!",
|
||||
model.ImgSrc);
|
||||
|
||||
var message = new NotificationMessage
|
||||
var message = await LoadTemplate(NotificationType.Issue, model, settings);
|
||||
if (message == null)
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: New issue for {model.Title}!",
|
||||
To = settings.AdminEmail,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
message.Other.Add("PlainTextBody", $"Hello! The user '{model.RequestedUser}' has reported a new issue {model.Body} for the title {model.Title}!");
|
||||
|
||||
|
@ -113,18 +118,11 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task RequestDeclined(NotificationOptions 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
|
||||
var message = await LoadTemplate(NotificationType.RequestDeclined, model, settings);
|
||||
if (message == null)
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: Your request has been declined",
|
||||
To = model.UserEmail,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been declined, Sorry!");
|
||||
|
||||
|
@ -134,18 +132,11 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task RequestApproved(NotificationOptions 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
|
||||
var message = await LoadTemplate(NotificationType.RequestApproved, model, settings);
|
||||
if (message == null)
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: Your request has been approved!",
|
||||
To = model.UserEmail,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
message.Other.Add("PlainTextBody", $"Hello! Your request for {model.Title} has been approved!");
|
||||
|
||||
|
@ -154,19 +145,11 @@ namespace Ombi.Notifications.Agents
|
|||
|
||||
protected override async Task AvailableRequest(NotificationOptions 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
|
||||
var message = await LoadTemplate(NotificationType.RequestAvailable, model, settings);
|
||||
if (message == null)
|
||||
{
|
||||
Message = html,
|
||||
Subject = $"Ombi: {model.Title} is now available!",
|
||||
To = model.UserEmail,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
message.Other.Add("PlainTextBody", $"Hello! You requested {model.Title} on Ombi! This is now available on Plex! :)");
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Notifications.Models;
|
||||
using Ombi.Store;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
|
@ -36,7 +39,6 @@ namespace Ombi.Notifications
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch (model.NotificationType)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Ombi.Store.Entities;
|
||||
|
||||
namespace Ombi.Notifications
|
||||
|
@ -18,19 +19,31 @@ namespace Ombi.Notifications
|
|||
Type = type;
|
||||
Issue = issue;
|
||||
}
|
||||
|
||||
// User Defined
|
||||
private string RequestedUser { get; }
|
||||
private string Title { get; }
|
||||
private string RequestedDate { get; }
|
||||
private string Type { get; }
|
||||
private string Issue { get; }
|
||||
|
||||
// System Defined
|
||||
private string LongDate => DateTime.Now.ToString("D");
|
||||
private string ShortDate => DateTime.Now.ToString("d");
|
||||
private string LongTime => DateTime.Now.ToString("T");
|
||||
private string ShortTime => DateTime.Now.ToString("t");
|
||||
|
||||
public Dictionary<string, string> Curlys => new Dictionary<string, string>
|
||||
{
|
||||
{nameof(RequestedUser), RequestedUser },
|
||||
{nameof(Title), Title },
|
||||
{nameof(RequestedDate), RequestedDate },
|
||||
{nameof(Type), Type },
|
||||
{nameof(Issue), Issue }
|
||||
{nameof(Issue), Issue },
|
||||
{nameof(LongDate),LongDate},
|
||||
{nameof(ShortDate),ShortDate},
|
||||
{nameof(LongTime),LongTime},
|
||||
{nameof(ShortTime),ShortTime},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Ombi.Helpers;
|
||||
using Ombi.Store.Entities;
|
||||
|
@ -64,25 +63,27 @@ namespace Ombi.Store.Context
|
|||
{
|
||||
foreach (var notificationType in allTypes)
|
||||
{
|
||||
var notificationToAdd = new NotificationTemplates();
|
||||
NotificationTemplates notificationToAdd;
|
||||
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}",
|
||||
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,
|
||||
Enabled = true,
|
||||
};
|
||||
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}",
|
||||
Message = "Hello! The user '{RequestedUser}' has reported a new issue for the title {Title}! </br> {Issue}",
|
||||
Subject = "Ombi: New issue for {Title}!",
|
||||
Agent = agent,
|
||||
Enabled = true,
|
||||
};
|
||||
break;
|
||||
case NotificationType.RequestAvailable:
|
||||
|
@ -92,6 +93,7 @@ namespace Ombi.Store.Context
|
|||
Message = "Hello! You requested {Title} on Ombi! This is now available! :)",
|
||||
Subject = "Ombi: {Title} is now available!",
|
||||
Agent = agent,
|
||||
Enabled = true,
|
||||
};
|
||||
break;
|
||||
case NotificationType.RequestApproved:
|
||||
|
@ -101,6 +103,7 @@ namespace Ombi.Store.Context
|
|||
Message = "Hello! Your request for {Title} has been approved!",
|
||||
Subject = "Ombi: your request has been approved",
|
||||
Agent = agent,
|
||||
Enabled = true,
|
||||
};
|
||||
break;
|
||||
case NotificationType.AdminNote:
|
||||
|
@ -110,12 +113,11 @@ namespace Ombi.Store.Context
|
|||
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,
|
||||
Enabled = true,
|
||||
};
|
||||
break;
|
||||
case NotificationType.ItemAddedToFaultQueue:
|
||||
|
|
|
@ -10,5 +10,6 @@ namespace Ombi.Store.Entities
|
|||
public NotificationAgent Agent { get; set; }
|
||||
public string Subject { get; set; }
|
||||
public string Message { get; set; }
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
}
|
|
@ -72,6 +72,7 @@ CREATE TABLE IF NOT EXISTS NotificationTemplates
|
|||
NotificationType INTEGER NOT NULL,
|
||||
Agent INTEGER NOT NULL,
|
||||
Subject BLOB NULL,
|
||||
Message BLOB NULL
|
||||
Message BLOB NULL,
|
||||
Enabled INTEGER NOT NULL
|
||||
|
||||
);
|
|
@ -14,8 +14,8 @@ import { ICustomizationSettings } from './interfaces/ISettings';
|
|||
})
|
||||
export class AppComponent implements OnInit {
|
||||
|
||||
constructor(public notificationService: NotificationService, public authService: AuthService, private router: Router, private settingsService: SettingsService
|
||||
) {
|
||||
constructor(public notificationService: NotificationService, public authService: AuthService, private router: Router, private settingsService: SettingsService)
|
||||
{
|
||||
}
|
||||
|
||||
customizationSettings: ICustomizationSettings;
|
||||
|
@ -25,8 +25,6 @@ export class AppComponent implements OnInit {
|
|||
|
||||
this.user = this.authService.claims();
|
||||
|
||||
|
||||
|
||||
this.settingsService.getCustomization().subscribe(x => this.customizationSettings = x);
|
||||
|
||||
this.router.events.subscribe(() => {
|
||||
|
@ -34,7 +32,6 @@ export class AppComponent implements OnInit {
|
|||
this.user = this.authService.claims();
|
||||
this.showNav = this.authService.loggedIn();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
hasRole(role: string): boolean {
|
||||
|
|
|
@ -22,6 +22,7 @@ export interface INotificationTemplates {
|
|||
message: string,
|
||||
notificationType: NotificationType,
|
||||
notificationAgent: NotificationAgent,
|
||||
enabled:boolean,
|
||||
}
|
||||
|
||||
export enum NotificationAgent {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import { FormGroup, Validators, ValidatorFn } from '@angular/forms';
|
||||
|
||||
@Injectable()
|
||||
export class ValidationService {
|
||||
|
||||
/**
|
||||
* Disable validation on a control
|
||||
* @param form
|
||||
* @param name
|
||||
*/
|
||||
public disableValidation(form: FormGroup, name: string): void {
|
||||
form.controls[name].clearValidators();
|
||||
form.controls[name].updateValueAndValidity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable validation with the default validation attribute of required
|
||||
* @param form
|
||||
* @param name
|
||||
*/
|
||||
public enableValidation(form: FormGroup, name: string): void;
|
||||
public enableValidation(form: FormGroup, name: string, validators?: ValidatorFn[]): void {
|
||||
if (validators) {
|
||||
// If we provide some use them
|
||||
form.controls[name].setValidators(validators);
|
||||
} else {
|
||||
// It's just required by default
|
||||
form.controls[name].setValidators([Validators.required]);
|
||||
}
|
||||
form.controls[name].updateValueAndValidity();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,68 +1,83 @@
|
|||
|
||||
<settings-menu></settings-menu>
|
||||
<div *ngIf="settings">
|
||||
<div *ngIf="emailForm">
|
||||
<fieldset>
|
||||
<legend>Email Notifications</legend>
|
||||
<div class="col-md-6">
|
||||
<form novalidate [formGroup]="emailForm" (ngSubmit)="onSubmit(emailForm)">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" id="enable" [(ngModel)]="settings.enabled" ng-checked="settings.enabled">
|
||||
<input type="checkbox" id="enable" formControlName="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>
|
||||
<input type="checkbox" id="Authentication" formControlName="authentication"><label for="Authentication">Enable SMTP Authentication</label>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="emailForm.invalid && emailForm.dirty" class="alert alert-danger">
|
||||
<div *ngIf="emailForm.get('host').hasError('required')">Host is required</div>
|
||||
<div *ngIf="emailForm.get('port').hasError('required')">The Port is required</div>
|
||||
<div *ngIf="emailForm.get('sender').hasError('required')">The Email Sender is required</div>
|
||||
<div *ngIf="emailForm.get('sender').hasError('email')">The Email Sender needs to be a valid email address</div>
|
||||
<div *ngIf="emailForm.get('adminEmail').hasError('required')">The Email Sender is required</div>
|
||||
<div *ngIf="emailForm.get('adminEmail').hasError('email')">The Admin Email needs to be a valid email address</div>
|
||||
<div *ngIf="emailForm.get('username').hasError('required')">The Username is required</div>
|
||||
<div *ngIf="emailForm.get('password').hasError('required')">The Password is required</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}}">
|
||||
<input type="text" class="form-control form-control-custom " id="host" name="host" placeholder="localhost" formControlName="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}}">
|
||||
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" formControlName="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">
|
||||
<input type="text" class="form-control form-control-custom " id="sender" name="sender" formControlName="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)">
|
||||
<input type="text" class="form-control form-control-custom " id="adminEmail" name="adminEmail" formControlName="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">
|
||||
<div class="form-group" *ngIf="emailForm.controls['username'].validator">
|
||||
<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">
|
||||
<input type="text" class="form-control form-control-custom " id="username" name="username" formControlName="username" pTooltip="The username if authentication is enabled" tooltipPosition="top">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" *ngIf="settings.authentication">
|
||||
<div class="form-group" *ngIf="emailForm.get('password').validator">
|
||||
<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">
|
||||
<input type="password" class="form-control form-control-custom " id="password" name="password" formControlName="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>
|
||||
<button id="testPlex" type="submit" (click)="test()" class="btn btn-primary-outline">
|
||||
Test Connectivity
|
||||
<div id="spinner"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -70,13 +85,15 @@
|
|||
|
||||
<div class="form-group">
|
||||
<div>
|
||||
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||
<button [disabled]="emailForm.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6">
|
||||
<notification-templates [templates]="settings.notificationTemplates"></notification-templates>
|
||||
<notification-templates [templates]="templates"></notification-templates>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
|
@ -1,34 +1,87 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
|
||||
|
||||
import { IEmailNotificationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
|
||||
import { INotificationTemplates, IEmailNotificationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { NotificationService } from "../../services/notification.service";
|
||||
import { ValidationService } from "../../services/helpers/validation.service";
|
||||
|
||||
@Component({
|
||||
templateUrl: './emailnotification.component.html',
|
||||
})
|
||||
export class EmailNotificationComponent implements OnInit {
|
||||
constructor(private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
private fb: FormBuilder,
|
||||
private validationService: ValidationService) { }
|
||||
|
||||
constructor(private settingsService: SettingsService, private notificationService: NotificationService) { }
|
||||
|
||||
settings: IEmailNotificationSettings;
|
||||
NotificationType = NotificationType;
|
||||
templates: INotificationTemplates[];
|
||||
|
||||
emailForm: FormGroup;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.settingsService.getEmailNotificationSettings().subscribe(x => this.settings = x);
|
||||
this.settingsService.getEmailNotificationSettings().subscribe(x => {
|
||||
this.templates = x.notificationTemplates;
|
||||
|
||||
this.emailForm = this.fb.group({
|
||||
enabled: [x.enabled],
|
||||
authentication: [x.authentication],
|
||||
host: [x.host, [Validators.required]],
|
||||
password: [x.password],
|
||||
port: [x.port, [Validators.required]],
|
||||
sender: [x.sender, [Validators.required, Validators.email]],
|
||||
username: [x.username],
|
||||
adminEmail: [x.adminEmail, [Validators.required, Validators.email]],
|
||||
});
|
||||
|
||||
if (x.authentication) {
|
||||
this.validationService.enableValidation(this.emailForm, 'username');
|
||||
this.validationService.enableValidation(this.emailForm, 'password');
|
||||
}
|
||||
|
||||
test() {
|
||||
// TODO Emby Service
|
||||
this.subscribeToAuthChanges();
|
||||
});
|
||||
}
|
||||
|
||||
save() {
|
||||
this.settingsService.saveEmailNotificationSettings(this.settings).subscribe(x => {
|
||||
onSubmit(form: FormGroup) {
|
||||
console.log(form.value, form.valid);
|
||||
|
||||
if (form.invalid) {
|
||||
this.notificationService.error("Validation", "Please check your entered values");
|
||||
return
|
||||
}
|
||||
|
||||
var settings = <IEmailNotificationSettings>form.value;
|
||||
settings.notificationTemplates = this.templates;
|
||||
|
||||
this.settingsService.saveEmailNotificationSettings(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");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
save() {
|
||||
|
||||
}
|
||||
|
||||
private subscribeToAuthChanges() {
|
||||
const authCtrl = this.emailForm.controls.authentication;
|
||||
const changes$ = authCtrl.valueChanges;
|
||||
|
||||
changes$.subscribe((auth: boolean) => {
|
||||
|
||||
if (auth) {
|
||||
this.validationService.enableValidation(this.emailForm, 'username');
|
||||
this.validationService.enableValidation(this.emailForm, 'password');
|
||||
} else {
|
||||
this.validationService.disableValidation(this.emailForm, 'username');
|
||||
this.validationService.disableValidation(this.emailForm, 'password');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,18 +1,27 @@
|
|||
|
||||
<i class="fa fa-info-circle" tooltipPosition="top" [escape]="false" pTooltip="{{helpText}}"></i>
|
||||
|
||||
|
||||
<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 class="checkbox">
|
||||
<input type="checkbox" id="enabled" [(ngModel)]="template.enabled" ng-checked="template.enabled"><label for="enabled">Enable</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label 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>
|
||||
<label class="control-label">Message</label>
|
||||
<div>
|
||||
<textarea type="text" class="form-control form-control-custom" [(ngModel)]="template.message" value="{{template.message}}"></textarea>
|
||||
</div>
|
||||
|
|
|
@ -9,4 +9,16 @@ import { INotificationTemplates, NotificationType } from '../../interfaces/INoti
|
|||
export class NotificationTemplate {
|
||||
@Input() templates: INotificationTemplates[];
|
||||
NotificationType = NotificationType;
|
||||
|
||||
helpText = `
|
||||
{RequestedUser} : The User who requested the content <br/>
|
||||
{RequestedDate} : The Date the media was requested <br/>
|
||||
{Title} : The title of the request e.g. Lion King <br/>
|
||||
{Type} : The request type e.g. Movie/Tv Show <br/>
|
||||
{LongDate} : 15 June 2017 <br/>
|
||||
{ShortDate} : 15/06/2017 <br/>
|
||||
{LongTime} : 16:02:34 <br/>
|
||||
{ShortTime} : 16:02 <br/>
|
||||
|
||||
`
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { NgModule, } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { NgbModule, NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
|
@ -9,6 +9,7 @@ import { AuthGuard } from '../auth/auth.guard';
|
|||
import { AuthModule } from '../auth/auth.module';
|
||||
import { SonarrService } from '../services/applications/sonarr.service';
|
||||
import { RadarrService } from '../services/applications/radarr.service';
|
||||
import { ValidationService } from '../services/helpers/validation.service';
|
||||
|
||||
import { OmbiComponent } from './ombi/ombi.component';
|
||||
import { PlexComponent } from './plex/plex.component';
|
||||
|
@ -23,7 +24,7 @@ import { NotificationTemplate } from './notifications/notificationtemplate.compo
|
|||
import { SettingsMenuComponent } from './settingsmenu.component';
|
||||
import { HumanizePipe } from '../pipes/HumanizePipe';
|
||||
|
||||
import { MenuModule, InputSwitchModule, InputTextModule, TooltipModule } from 'primeng/primeng';
|
||||
import { MenuModule, InputSwitchModule, InputTextModule, TooltipModule, AutoCompleteModule } from 'primeng/primeng';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'Settings/Ombi', component: OmbiComponent, canActivate: [AuthGuard] },
|
||||
|
@ -40,6 +41,7 @@ const routes: Routes = [
|
|||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
RouterModule.forChild(routes),
|
||||
MenuModule,
|
||||
InputSwitchModule,
|
||||
|
@ -47,7 +49,8 @@ const routes: Routes = [
|
|||
AuthModule,
|
||||
NgbModule,
|
||||
TooltipModule,
|
||||
NgbAccordionModule
|
||||
NgbAccordionModule,
|
||||
AutoCompleteModule
|
||||
],
|
||||
declarations: [
|
||||
SettingsMenuComponent,
|
||||
|
@ -70,6 +73,7 @@ const routes: Routes = [
|
|||
AuthService,
|
||||
RadarrService,
|
||||
AuthGuard,
|
||||
ValidationService
|
||||
],
|
||||
|
||||
})
|
||||
|
|
|
@ -9,6 +9,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Ombi.Models;
|
||||
using Ombi.Notifications.Models;
|
||||
|
||||
namespace Ombi.Controllers
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue