Email Notifications are now fully customizable and work! #865

We also have reactive forms in the UI, should apply this to other settings.
pull/1488/head
Jamie.Rees 7 years ago
parent 3b0b35f760
commit 9a4dbb3dce

@ -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,
@ -98,64 +95,41 @@ namespace Ombi.Core.Engine
Status = movieInfo.Status,
RequestedDate = DateTime.UtcNow,
Approved = false,
RequestedUser =Username,
RequestedUser = Username,
Issues = IssueState.None
};
try
var ruleResults = RunRequestRules(requestModel).ToList();
if (ruleResults.Any(x => !x.Success))
{
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
return new RequestEngineResult
{
var result = await Sender.Send(requestModel);
if (result.Success && result.MovieSent)
{
return await AddMovieRequest(requestModel, /*settings,*/
$"{fullMovieName} has been successfully added!");
}
if (!result.Success)
{
Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message);
return new RequestEngineResult
{
Message = result.Message,
ErrorMessage = result.Message,
RequestAdded = false
};
}
}
return await AddMovieRequest(requestModel, /*settings,*/
$"{fullMovieName} has been successfully added!");
ErrorMessage = ruleResults.FirstOrDefault(x => !string.IsNullOrEmpty(x.Message)).Message
};
}
catch (Exception e)
if (requestModel.Approved) // The rules have auto approved this
{
//Log.Fatal(e);
//await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault, e.Message);
var notification = new NotificationOptions
var result = await Sender.Send(requestModel);
if (result.Success && result.MovieSent)
{
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 await AddMovieRequest(requestModel, /*settings,*/
$"{fullMovieName} has been successfully added!");
}
if (!result.Success)
{
Logger.LogWarning("Tried auto sending movie but failed. Message: {0}", result.Message);
return new RequestEngineResult
{
Message = result.Message,
ErrorMessage = result.Message,
RequestAdded = false
};
}
}
return null;
return await AddMovieRequest(requestModel, /*settings,*/
$"{fullMovieName} has been successfully added!");
}
public async Task<IEnumerable<MovieRequestModel>> GetRequests(int count, int position)
@ -210,21 +184,9 @@ 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();
//var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type);
//if (usersLimit == null)

@ -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()

@ -0,0 +1,9 @@
using Ombi.Core.Models.Requests;
namespace Ombi.Core
{
public interface INotificationHelper
{
void NewRequest(BaseRequestModel model);
}
}

@ -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,19 +14,17 @@ 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;
user: ILocalUser;
ngOnInit(): void {
ngOnInit() : void {
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,82 +1,99 @@

<settings-menu></settings-menu>
<div *ngIf="settings">
<div *ngIf="emailForm">
<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>
<form novalidate [formGroup]="emailForm" (ngSubmit)="onSubmit(emailForm)">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="enable" formControlName="enabled">
<label for="enable">Enabled</label>
</div>
</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 class="form-group">
<div class="checkbox">
<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>
<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 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" formControlName="host">
</div>
</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 class="form-group">
<label for="portNumber" class="control-label">SMTP Port</label>
<div>
<input type="text" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" formControlName="port">
</div>
</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 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" formControlName="sender" tooltipPosition="top" pTooltip="The email address that the emails will be sent from">
</div>
</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 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" 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>
<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 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" formControlName="username" pTooltip="The username if authentication is enabled" tooltipPosition="top">
</div>
</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 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" formControlName="password" pTooltip="The password if authentication is enabled" tooltipPosition="top">
</div>
</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 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>
<div class="form-group">
<div>
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
<div class="form-group">
<div>
<button [disabled]="emailForm.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
</div>
</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">
<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 for="password" class="control-label">Subject</label>
<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…
Cancel
Save