Added the testing notifications and discord notification #865

pull/1488/head
Jamie.Rees 7 years ago
parent 5970ee963d
commit d73899fc53

@ -0,0 +1,11 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/.DS_Store": true,
"**/*.js": {"when": "$(basename).ts"}, // Hide JS files when there is a ts file
"**/*.js.map" : true
}
}

@ -14,15 +14,10 @@ namespace Ombi.Api.Discord
private string Endpoint => "https://discordapp.com/api/"; //webhooks/270828242636636161/lLysOMhJ96AFO1kvev0bSqP-WCZxKUh1UwfubhIcLkpS0DtM3cg4Pgeraw3waoTXbZii
private Api Api { get; }
public async Task SendMessage(string message, string webhookId, string webhookToken, string username = null)
public async Task SendMessage(DiscordWebhookBody body, string webhookId, string webhookToken)
{
var request = new Request(Endpoint, $"webhooks/{webhookId}/{webhookToken}", HttpMethod.Post);
var body = new DiscordWebhookBody
{
content = message,
username = username
};
request.AddJsonBody(body);
request.AddHeader("Content-Type", "application/json");

@ -1,9 +1,10 @@
using System.Threading.Tasks;
using Ombi.Api.Discord.Models;
namespace Ombi.Api.Discord
{
public interface IDiscordApi
{
Task SendMessage(string message, string webhookId, string webhookToken, string username = null);
Task SendMessage(DiscordWebhookBody body, string webhookId, string webhookToken);
}
}

@ -1,8 +1,24 @@
namespace Ombi.Api.Discord.Models
using System.Collections.Generic;
namespace Ombi.Api.Discord.Models
{
public class DiscordWebhookBody
{
public string content { get; set; }
public string username { get; set; }
public List<DiscordEmbeds> embeds { get; set; }
}
public class DiscordEmbeds
{
public string title { get; set; }
public string type => "rich"; // Always rich or embedded content
public string description { get; set; } // Don't really need to set this
public DiscordImage image { get; set; }
}
public class DiscordImage
{
public string url { get; set; }
}
}

@ -0,0 +1,21 @@
using System.Collections.Generic;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
namespace Ombi.Core.Models.UI
{
/// <summary>
/// The view model for the notification settings page
/// </summary>
/// <seealso cref="DiscordNotificationSettings" />
public class DiscordNotificationsViewModel : DiscordNotificationSettings
{
/// <summary>
/// Gets or sets the notification templates.
/// </summary>
/// <value>
/// The notification templates.
/// </value>
public List<NotificationTemplates> NotificationTemplates { get; set; }
}
}

@ -2,7 +2,7 @@
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Entities;
namespace Ombi.Models.Notifications
namespace Ombi.Core.Models.UI
{
/// <summary>
/// The view model for the notification settings page

@ -27,6 +27,7 @@ using Ombi.Settings.Settings;
using Ombi.Store.Context;
using Ombi.Store.Repository;
using Ombi.Core.Rules;
using Ombi.Notifications.Agents;
using Ombi.Schedule.Jobs.Radarr;
namespace Ombi.DependencyInjection
@ -84,7 +85,11 @@ namespace Ombi.DependencyInjection
services.AddTransient<IRequestServiceMain, RequestService>();
services.AddTransient(typeof(IRequestService<>), typeof(JsonRequestService<>));
services.AddSingleton<INotificationService, NotificationService>();
services.AddSingleton<INotificationHelper, NotificationHelper>();
services.AddTransient<INotificationHelper, NotificationHelper>();
services.AddTransient<IDiscordNotification, DiscordNotification>();
services.AddTransient<IEmailNotification, EmailNotification>();
}
public static void RegisterJobs(this IServiceCollection services)

@ -1,5 +1,5 @@
using AutoMapper;
using Ombi.Models.Notifications;
using Ombi.Core.Models.UI;
using Ombi.Settings.Settings.Models.Notifications;
namespace Ombi.Mapping.Profiles
@ -9,6 +9,7 @@ namespace Ombi.Mapping.Profiles
public SettingsProfile()
{
CreateMap<EmailNotificationsViewModel, EmailNotificationSettings>().ReverseMap();
CreateMap<DiscordNotificationsViewModel, DiscordNotificationSettings>().ReverseMap();
}
}
}

@ -1,16 +1,19 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Ombi.Api.Discord;
using Ombi.Api.Discord.Models;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Notifications.Interfaces;
using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models.Notifications;
using Ombi.Store.Repository;
namespace Ombi.Notifications.Agents
{
public class DiscordNotification : BaseNotification<DiscordNotificationSettings>
public class DiscordNotification : BaseNotification<DiscordNotificationSettings>, IDiscordNotification
{
public DiscordNotification(IDiscordApi api, ISettingsService<DiscordNotificationSettings> sn, ILogger<DiscordNotification> log, INotificationTemplatesRepository r) : base(sn, r)
{
@ -47,21 +50,22 @@ namespace Ombi.Notifications.Agents
protected override async Task NewRequest(NotificationOptions model, DiscordNotificationSettings settings)
{
var message = $"{model.Title} has been requested by user: {model.RequestedUser}";
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, NotificationType.NewRequest);
var notification = new NotificationMessage
{
Message = message,
Message = template.Message,
};
await Send(notification, settings);
}
protected override async Task Issue(NotificationOptions model, DiscordNotificationSettings settings)
{
var message = $"A new issue: {model.Body} has been reported by user: {model.RequestedUser} for the title: {model.Title}";
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, NotificationType.Issue);
var notification = new NotificationMessage
{
Message = message,
Message = template.Message,
};
await Send(notification, settings);
}
@ -71,37 +75,41 @@ namespace Ombi.Notifications.Agents
var message = $"Hello! The user '{model.RequestedUser}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying";
var notification = new NotificationMessage
{
Message = message,
Message = message
};
notification.Other.Add("image", model.ImgSrc);
await Send(notification, settings);
}
protected override async Task RequestDeclined(NotificationOptions model, DiscordNotificationSettings settings)
{
var message = $"Hello! Your request for {model.Title} has been declined, Sorry!";
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, NotificationType.RequestDeclined);
var notification = new NotificationMessage
{
Message = message,
Message = template.Message,
};
await Send(notification, settings);
}
protected override async Task RequestApproved(NotificationOptions model, DiscordNotificationSettings settings)
{
var message = $"Hello! The request for {model.Title} has now been approved!";
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, NotificationType.RequestApproved);
var notification = new NotificationMessage
{
Message = message,
Message = template.Message,
};
await Send(notification, settings);
}
protected override async Task AvailableRequest(NotificationOptions model, DiscordNotificationSettings settings)
{
var message = $"Hello! The request for {model.Title} is now available!";
var template = await TemplateRepository.GetTemplate(NotificationAgent.Email, NotificationType.RequestAvailable);
var notification = new NotificationMessage
{
Message = message,
Message = template.Message,
};
await Send(notification, settings);
}
@ -110,7 +118,28 @@ namespace Ombi.Notifications.Agents
{
try
{
await Api.SendMessage(model.Message, settings.WebookId, settings.Token, settings.Username);
var discordBody = new DiscordWebhookBody
{
content = model.Message,
username = settings.Username,
};
string image;
if (model.Other.TryGetValue("image", out image))
{
discordBody.embeds = new List<DiscordEmbeds>
{
new DiscordEmbeds
{
image = new DiscordImage
{
url = image
}
}
};
}
await Api.SendMessage(discordBody, settings.WebookId, settings.Token);
}
catch (Exception e)
{

@ -4,6 +4,7 @@ using MailKit.Net.Smtp;
using MimeKit;
using Ombi.Core.Settings;
using Ombi.Helpers;
using Ombi.Notifications.Interfaces;
using Ombi.Notifications.Models;
using Ombi.Notifications.Templates;
using Ombi.Settings.Settings.Models.Notifications;
@ -11,7 +12,7 @@ using Ombi.Store.Repository;
namespace Ombi.Notifications.Agents
{
public class EmailNotification : BaseNotification<EmailNotificationSettings>
public class EmailNotification : BaseNotification<EmailNotificationSettings>, IEmailNotification
{
public EmailNotification(ISettingsService<EmailNotificationSettings> settings, INotificationTemplatesRepository r) : base(settings, r)
{

@ -0,0 +1,6 @@
namespace Ombi.Notifications.Agents
{
public interface IDiscordNotification : INotification
{
}
}

@ -0,0 +1,6 @@
namespace Ombi.Notifications.Agents
{
public interface IEmailNotification : INotification
{
}
}

@ -1,15 +1,11 @@
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
namespace Ombi.Notifications.Interfaces
{
public abstract class BaseNotification<T> : INotification where T : Settings.Settings.Models.Settings, new()
{

@ -46,28 +46,6 @@ export enum NotificationType {
export interface IDiscordNotifcationSettings extends INotificationSettings{
webhookUrl : string,
username : string,
// public string WebhookUrl { get; set; }
// public string Username { get; set; }
// [JsonIgnore]
// public string WebookId => SplitWebUrl(4);
// [JsonIgnore]
// public string Token => SplitWebUrl(5);
// private string SplitWebUrl(int index)
// {
// if (!WebhookUrl.StartsWith("http", StringComparison.InvariantCulture))
// {
// WebhookUrl = "https://" + WebhookUrl;
// }
// var split = WebhookUrl.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
// return split.Length < index
// ? string.Empty
// : split[index];
// }
username: string,
notificationTemplates: INotificationTemplates[],
}

@ -1,3 +1,45 @@
<div *ngIf="request" style="background-color:black; color:white;">
{{request.title}}
<style>
/*$bg-colour: #333333;*/
.card {
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
border-radius: 5px; /* 5px rounded corners */
background: #4f4e4e;
border-bottom: #333333 solid 1px;
margin-left: 10px;
margin-right: 10px;
margin-top: 5px;
}
img {
border-radius: 5px 5px 0 0;
}
/* On mouse-over, add a deeper shadow */
.card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.card-container {
padding: 2px 16px;
}
.image-fill {
overflow: hidden;
background-size: cover;
background-position: center;
position: relative;
}
</style>
<div *ngIf="request" class="card" style="color: white;">
<div style="position: relative; overflow: hidden;">
<img src="{{request.posterPath}}" alt="poster" class="image-fill">
</div>
<div class="card-container">
{{request.title}}
</div>
</div>

@ -1,7 +1,6 @@

<style>
.landing-box {
height: 150px;
background: #333333 !important;
border-radius: 2%;
display: flex;
@ -10,40 +9,45 @@
text-align: center;
box-shadow: 5px 3px 5px black;
}
.dragula-container {
padding-bottom: 30px;
}
.gu-mirror {
margin-left: 100px !important;
transform: rotate(4deg);
-moz-transform: rotate(4deg);
-webkit-transform: rotate(4deg);
text-align: center;
}
</style>
<div *ngIf="tvRequests">
<div class="col-md-4">
<h4 class="text-center">New Requests</h4>
<div class="landing-box">
<md-card-title>Title</md-card-title>
<div [dragula]='"requests-bag"' [dragulaModel]="tvRequests.new">
<br />
<br />
<request-card *ngFor="let item of tvRequests.new" [request]="item" ></request-card>
<div class="dragula-container" [dragula]='"requests-bag"' [dragulaModel]="tvRequests.new">
<request-card *ngFor="let item of tvRequests.new" [request]="item"></request-card>
</div>
</div>
</div>
<div class="col-md-4">
<h4 class="text-center">Approved Requests</h4>
<div class="landing-box">
<div [dragula]='"requests-bag"' [dragulaModel]="tvRequests.approved">
<br />
<br />
<div class="dragula-container" [dragula]='"requests-bag"' [dragulaModel]="tvRequests.approved">
<request-card *ngFor="let item of tvRequests.approved" [request]="item"></request-card>
</div>
</div>
</div>
<div class="col-md-4">
<h4 class="text-center">Available</h4>
<div class="landing-box">
<md-card-title>Title</md-card-title>
<div style="border: dashed">
<div [dragula]='"requests-bag"' [dragulaModel]="tvRequests.available">
<br />
<br />
<request-card *ngFor="let item of tvRequests.available" [request]="item"></request-card>
</div>
<div class="dragula-container" [dragula]='"requests-bag"' [dragulaModel]="tvRequests.available">
<request-card *ngFor="let item of tvRequests.available" [request]="item"></request-card>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { AuthHttp } from 'angular2-jwt';
import { Observable } from 'rxjs/Rx';
import { ServiceAuthHelpers } from '../service.helpers';
import { IDiscordNotifcationSettings, IEmailNotificationSettings } from '../../interfaces/INotifcationSettings'
@Injectable()
export class TesterService extends ServiceAuthHelpers {
constructor(http: AuthHttp) {
super(http, '/api/v1/tester/');
}
discordTest(settings: IDiscordNotifcationSettings): Observable<boolean> {
return this.http.post(`${this.url}discord`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
}
emailTest(settings: IEmailNotificationSettings): Observable<boolean> {
return this.http.post(`${this.url}email`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData);
}
}

@ -13,7 +13,7 @@ import {
ICustomizationSettings,
IRadarrSettings
} from '../interfaces/ISettings';
import { IEmailNotificationSettings } from '../interfaces/INotifcationSettings';
import { IEmailNotificationSettings, IDiscordNotifcationSettings } from '../interfaces/INotifcationSettings';
@Injectable()
export class SettingsService extends ServiceAuthHelpers {
@ -88,4 +88,12 @@ export class SettingsService extends ServiceAuthHelpers {
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)
}
getDiscordNotificationSettings(): Observable<IDiscordNotifcationSettings> {
return this.httpAuth.get(`${this.url}/notifications/discord`).map(this.extractData).catch(this.handleError)
}
saveDiscordNotificationSettings(settings: IDiscordNotifcationSettings): Observable<boolean> {
return this.httpAuth.post(`${this.url}/notifications/discord`, JSON.stringify(settings), { headers: this.headers }).map(this.extractData).catch(this.handleError)
}
}

@ -1,10 +1,10 @@

<settings-menu></settings-menu>
<div *ngIf="emailForm">
<div *ngIf="form">
<fieldset>
<legend>Email Notifications</legend>
<legend>Discord Notifications</legend>
<div class="col-md-6">
<form novalidate [formGroup]="emailForm" (ngSubmit)="onSubmit(emailForm)">
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)">
<div class="form-group">
<div class="checkbox">
@ -13,68 +13,29 @@
</div>
</div>
<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 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 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 *ngIf="form.invalid && form.dirty" class="alert alert-danger">
<div *ngIf="form.get('webhookUrl').hasError('required')">The Webhook Url is required</div>
</div>
<div class="form-group">
<label for="sender" class="control-label">Email Sender</label>
<label for="webhookUrl" class="control-label">Webhook Url</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">
<input type="text" class="form-control form-control-custom " id="webhookUrl" name="webhookUrl" formControlName="webhookUrl">
</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" 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="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">
<input type="text" class="form-control form-control-custom " id="username" name="username" formControlName="username" pTooltip="Optional, this will override the username you used for the Webhook">
</div>
</div>
<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 class="form-group">
<div>
<button id="testPlex" type="submit" (click)="test()" class="btn btn-primary-outline">
<button [disabled]="form.invalid" type="submit" (click)="test(form)" class="btn btn-primary-outline">
Test Connectivity
<div id="spinner"></div>
</button>
@ -85,7 +46,7 @@
<div class="form-group">
<div>
<button [disabled]="emailForm.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
<button [disabled]="form.invalid" type="submit" id="save" class="btn btn-primary-outline">Submit</button>
</div>
</div>
</form>

@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { INotificationTemplates, IEmailNotificationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
import { INotificationTemplates, IDiscordNotifcationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
import { SettingsService } from '../../services/settings.service';
import { NotificationService } from "../../services/notification.service";
import { ValidationService } from "../../services/helpers/validation.service";
import { TesterService } from "../../services/applications/tester.service";
@Component({
templateUrl: './discord.component.html',
@ -13,75 +13,58 @@ export class DiscordComponent implements OnInit {
constructor(private settingsService: SettingsService,
private notificationService: NotificationService,
private fb: FormBuilder,
private validationService: ValidationService) { }
private testerService : TesterService) { }
NotificationType = NotificationType;
templates: INotificationTemplates[];
emailForm: FormGroup;
form: FormGroup;
ngOnInit(): void {
this.settingsService.getEmailNotificationSettings().subscribe(x => {
this.settingsService.getDiscordNotificationSettings().subscribe(x => {
this.templates = x.notificationTemplates;
this.emailForm = this.fb.group({
this.form = 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');
}
webhookUrl: [x.webhookUrl, [Validators.required]],
this.subscribeToAuthChanges();
});
});
}
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;
var settings = <IDiscordNotifcationSettings>form.value;
settings.notificationTemplates = this.templates;
this.settingsService.saveEmailNotificationSettings(settings).subscribe(x => {
this.settingsService.saveDiscordNotificationSettings(settings).subscribe(x => {
if (x) {
this.notificationService.success("Settings Saved", "Successfully saved Email settings");
this.notificationService.success("Settings Saved", "Successfully saved the Discord settings");
} else {
this.notificationService.success("Settings Saved", "There was an error when saving the Email settings");
this.notificationService.success("Settings Saved", "There was an error when saving the Discord settings");
}
});
}
save() {
}
private subscribeToAuthChanges() {
const authCtrl = this.emailForm.controls.authentication;
const changes$ = authCtrl.valueChanges;
changes$.subscribe((auth: boolean) => {
test(form: FormGroup) {
if (form.invalid) {
this.notificationService.error("Validation", "Please check your entered values");
return
}
if (auth) {
this.validationService.enableValidation(this.emailForm, 'username');
this.validationService.enableValidation(this.emailForm, 'password');
this.testerService.discordTest(form.value).subscribex(x => {
if (x) {
this.notificationService.success("Successful", "Successfully sent a Discord message, please check the discord channel");
} else {
this.validationService.disableValidation(this.emailForm, 'username');
this.validationService.disableValidation(this.emailForm, 'password');
this.notificationService.success("Error", "There was an error when sending the Discord message. Please check your settings");
}
});
})
}
}

@ -74,7 +74,7 @@
<div class="form-group">
<div>
<button id="testPlex" type="submit" (click)="test()" class="btn btn-primary-outline">
<button [disabled]="emailForm.invalid" type="submit" (click)="test(emailForm)" class="btn btn-primary-outline">
Test Connectivity
<div id="spinner"></div>
</button>

@ -4,6 +4,7 @@ import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { INotificationTemplates, IEmailNotificationSettings, NotificationType } from '../../interfaces/INotifcationSettings';
import { SettingsService } from '../../services/settings.service';
import { NotificationService } from "../../services/notification.service";
import { TesterService } from "../../services/applications/tester.service";
import { ValidationService } from "../../services/helpers/validation.service";
@Component({
@ -13,7 +14,8 @@ export class EmailNotificationComponent implements OnInit {
constructor(private settingsService: SettingsService,
private notificationService: NotificationService,
private fb: FormBuilder,
private validationService: ValidationService) { }
private validationService: ValidationService,
private testerService: TesterService) { }
NotificationType = NotificationType;
templates: INotificationTemplates[];
@ -45,8 +47,6 @@ export class EmailNotificationComponent implements OnInit {
}
onSubmit(form: FormGroup) {
console.log(form.value, form.valid);
if (form.invalid) {
this.notificationService.error("Validation", "Please check your entered values");
return
@ -65,8 +65,19 @@ export class EmailNotificationComponent implements OnInit {
}
save() {
test(form: FormGroup) {
if (form.invalid) {
this.notificationService.error("Validation", "Please check your entered values");
return
}
this.testerService.emailTest(form.value).subscribe(x => {
if (x) {
this.notificationService.success("Sent", "Successfully sent an email message, please check your inbox");
} else {
this.notificationService.success("Error", "There was an error when sending the Email message, please check your settings.");
}
})
}
private subscribeToAuthChanges() {

@ -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 { TesterService } from '../services/applications/tester.service';
import { ValidationService } from '../services/helpers/validation.service';
import { OmbiComponent } from './ombi/ombi.component';
@ -73,7 +74,8 @@ const routes: Routes = [
AuthService,
RadarrService,
AuthGuard,
ValidationService
ValidationService,
TesterService
],
})

@ -0,0 +1,70 @@
using System;
using Hangfire;
using Microsoft.AspNetCore.Mvc;
using Ombi.Attributes;
using Ombi.Core.Notifications;
using Ombi.Helpers;
using Ombi.Notifications.Agents;
using Ombi.Notifications.Models;
using Ombi.Settings.Settings.Models.Notifications;
namespace Ombi.Controllers.External
{
/// <summary>
/// The Tester Controller
/// </summary>
/// <seealso cref="Ombi.Controllers.BaseV1ApiController" />
[Admin]
public class TesterController : BaseV1ApiController
{
/// <summary>
/// Initializes a new instance of the <see cref="TesterController" /> class.
/// </summary>
/// <param name="service">The service.</param>
/// <param name="notification">The notification.</param>
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN)
{
Service = service;
DiscordNotification = notification;
EmailNotification = emailN;
}
private INotificationService Service { get; }
private IDiscordNotification DiscordNotification { get; }
private IEmailNotification EmailNotification { get; }
/// <summary>
/// Sends a test message to discord using the provided settings
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns></returns>
[HttpPost("discord")]
public bool Discord([FromBody] DiscordNotificationSettings settings)
{
settings.Enabled = true;
BackgroundJob.Enqueue(() => Service.PublishTest(new NotificationOptions{NotificationType = NotificationType.Test}, settings, DiscordNotification));
return true;
}
/// <summary>
/// Sends a test message via email to the admin email using the provided settings
/// </summary>
/// <param name="settings">The settings.</param>
/// <returns></returns>
[HttpPost("email")]
public bool Email([FromBody] EmailNotificationSettings settings)
{
settings.Enabled = true;
var notificationModel = new NotificationOptions
{
NotificationType = NotificationType.Test,
DateTime = DateTime.Now,
ImgSrc = "https://imgs.xkcd.com/comics/shouldnt_be_hard.png"
};
BackgroundJob.Enqueue(() => Service.PublishTest(notificationModel, settings, EmailNotification));
return true;
}
}
}

@ -6,11 +6,11 @@ using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ombi.Attributes;
using Ombi.Core.Models.UI;
using Ombi.Core.Settings;
using Ombi.Core.Settings.Models;
using Ombi.Core.Settings.Models.External;
using Ombi.Helpers;
using Ombi.Models.Notifications;
using Ombi.Settings.Settings.Models;
using Ombi.Settings.Settings.Models.External;
using Ombi.Settings.Settings.Models.Notifications;
@ -208,41 +208,74 @@ namespace Ombi.Controllers
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);
/// <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);
// Lookup to see if we have any templates saved
model.NotificationTemplates = await BuildTemplates(NotificationAgent.Email);
return model;
}
return model;
}
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
{
var templates = await TemplateRepository.GetAllTemplates(agent);
return templates.ToList();
}
/// <summary>
/// Saves the discord notification settings.
/// </summary>
/// <param name="model">The model.</param>
/// <returns></returns>
[HttpPost("notifications/email")]
public async Task<bool> DiscordNotificationSettings([FromBody] DiscordNotificationsViewModel model)
{
// Save the email settings
var settings = Mapper.Map<DiscordNotificationSettings>(model);
var result = await Save(settings);
// Save the templates
await TemplateRepository.UpdateRange(model.NotificationTemplates);
private async Task<T> Get<T>()
{
var settings = SettingsResolver.Resolve<T>();
return await settings.GetSettingsAsync();
}
return result;
}
private async Task<bool> Save<T>(T settingsModel)
{
var settings = SettingsResolver.Resolve<T>();
return await settings.SaveSettingsAsync(settingsModel);
/// <summary>
/// Gets the discord Notification Settings.
/// </summary>
/// <returns></returns>
[HttpGet("notifications/email")]
public async Task<DiscordNotificationsViewModel> DiscordNotificationSettings()
{
var emailSettings = await Get<DiscordNotificationSettings>();
var model = Mapper.Map<DiscordNotificationsViewModel>(emailSettings);
// Lookup to see if we have any templates saved
model.NotificationTemplates = await BuildTemplates(NotificationAgent.Discord);
return model;
}
private async Task<List<NotificationTemplates>> BuildTemplates(NotificationAgent agent)
{
var templates = await TemplateRepository.GetAllTemplates(agent);
return templates.ToList();
}
private async Task<T> Get<T>()
{
var settings = SettingsResolver.Resolve<T>();
return await settings.GetSettingsAsync();
}
private async Task<bool> Save<T>(T settingsModel)
{
var settings = SettingsResolver.Resolve<T>();
return await settings.SaveSettingsAsync(settingsModel);
}
}
}
}

@ -6,4 +6,5 @@
@import "../bower_components/font-awesome/scss/font-awesome.scss";*/
@import '../bootstrap.css';
@import './Styles.scss';
@import './Styles.scss';
@import '_loading.scss';

@ -0,0 +1,69 @@
.app-loading-container {
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-box-pack: center;
-webkit-box-align: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
.app-loading {
margin: auto;
font-size: 2em;
}
.app-loading-one {
opacity: 0;
-webkit-animation: dot 1.3s infinite;
-webkit-animation-delay: 0.0s;
animation: app-loading-dot 1.3s infinite;
animation-delay: 0.0s;
}
.app-loading-two {
opacity: 0;
-webkit-animation: dot 1.3s infinite;
-webkit-animation-delay: 0.2s;
animation: app-loading-dot 1.3s infinite;
animation-delay: 0.2s;
}
.app-loading-three {
opacity: 0;
-webkit-animation: dot 1.3s infinite;
-webkit-animation-delay: 0.3s;
animation: app-loading-dot 1.3s infinite;
animation-delay: 0.3s;
}
@-webkit-keyframes app-loading-dot {
0% {
opacity: 0;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes app-loading-dot {
0% {
opacity: 0;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}

@ -1 +1,11 @@
<ombi>Loading..</ombi>
<ombi>
<div class="app-loading-container">
<div class="app-loading">
Loading
<span class="app-loading-one">.</span>
<span class="app-loading-two">.</span>
<span class="app-loading-three">.</span>
</div>
</div>
</ombi>

@ -48,6 +48,8 @@ module.exports = function (env) {
'font-awesome/scss/font-awesome.scss',
'pace-progress',
'pace-progress/themes/orange/pace-theme-flash.css',
'ng2-dragula',
'dragula/dist/dragula.min.css'
]
},
output: {

Loading…
Cancel
Save