mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-13 08:42:57 -07:00
Merge DNC into Master (#2034)
* Fix the issue where the user could not login if the plex account only allows email logins * Fixed #2019 * Added Mass Email functionality (#2027) * !wip * !wip * !qwip * !wip * Mass email is done * Update README.md * /bin/bash: wip: command not found
This commit is contained in:
parent
0f9bbc80dc
commit
dda467828c
19 changed files with 404 additions and 3621 deletions
3622
CHANGELOG.md
3622
CHANGELOG.md
File diff suppressed because it is too large
Load diff
|
@ -70,10 +70,9 @@ We are planning to bring back these features in V3 but for now you can find a li
|
||||||
| Login page | Yes (brand new) | Yes |
|
| Login page | Yes (brand new) | Yes |
|
||||||
| Custom Notification Messages | Yes | No |
|
| Custom Notification Messages | Yes | No |
|
||||||
| Sending newsletters | Planned | Yes |
|
| Sending newsletters | Planned | Yes |
|
||||||
| Send a Mass Email | Planned | Yes |
|
| Send a Mass Email | Yes | Yes |
|
||||||
| SickRage | Yes | Yes |
|
| SickRage | Yes | Yes |
|
||||||
| CouchPotato | Yes | Yes |
|
| CouchPotato | Yes | Yes |
|
||||||
| Watcher | Planned | Yes |
|
|
||||||
| DogNzb | Yes | No |
|
| DogNzb | Yes | No |
|
||||||
| Issues | Yes | Yes |
|
| Issues | Yes | Yes |
|
||||||
| Headphones | No (support dropped) | Yes |
|
| Headphones | No (support dropped) | Yes |
|
||||||
|
|
|
@ -110,7 +110,8 @@ namespace Ombi.Core.Authentication
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private async Task<bool> CheckPlexPasswordAsync(OmbiUser user, string password)
|
private async Task<bool> CheckPlexPasswordAsync(OmbiUser user, string password)
|
||||||
{
|
{
|
||||||
var result = await _plexApi.SignIn(new UserRequest { password = password, login = user.UserName });
|
var login = user.EmailLogin ? user.Email : user.UserName;
|
||||||
|
var result = await _plexApi.SignIn(new UserRequest { password = password, login = login });
|
||||||
if (result.user?.authentication_token != null)
|
if (result.user?.authentication_token != null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
10
src/Ombi.Core/IMassEmailSender.cs
Normal file
10
src/Ombi.Core/IMassEmailSender.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Senders
|
||||||
|
{
|
||||||
|
public interface IMassEmailSender
|
||||||
|
{
|
||||||
|
Task<bool> SendMassEmail(MassEmailModel model);
|
||||||
|
}
|
||||||
|
}
|
40
src/Ombi.Core/Models/MassEmailModel.cs
Normal file
40
src/Ombi.Core/Models/MassEmailModel.cs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2018 Jamie Rees
|
||||||
|
// File: MassEmailModel.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Models
|
||||||
|
{
|
||||||
|
public class MassEmailModel
|
||||||
|
{
|
||||||
|
public string Subject { get; set; }
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
public List<OmbiUser> Users { get; set; }
|
||||||
|
}
|
||||||
|
}
|
95
src/Ombi.Core/Senders/MassEmailSender.cs
Normal file
95
src/Ombi.Core/Senders/MassEmailSender.cs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2018 Jamie Rees
|
||||||
|
// File: MassEmailSender.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Ombi.Core.Authentication;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Ombi.Core.Settings;
|
||||||
|
using Ombi.Helpers;
|
||||||
|
using Ombi.Notifications;
|
||||||
|
using Ombi.Notifications.Models;
|
||||||
|
using Ombi.Settings.Settings.Models;
|
||||||
|
using Ombi.Settings.Settings.Models.Notifications;
|
||||||
|
using Ombi.Store.Entities;
|
||||||
|
|
||||||
|
namespace Ombi.Core.Senders
|
||||||
|
{
|
||||||
|
public class MassEmailSender : IMassEmailSender
|
||||||
|
{
|
||||||
|
public MassEmailSender(IEmailProvider emailProvider, ISettingsService<CustomizationSettings> custom, ISettingsService<EmailNotificationSettings> email,
|
||||||
|
ILogger<MassEmailSender> log, OmbiUserManager manager)
|
||||||
|
{
|
||||||
|
_email = emailProvider;
|
||||||
|
_customizationService = custom;
|
||||||
|
_emailService = email;
|
||||||
|
_log = log;
|
||||||
|
_userManager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IEmailProvider _email;
|
||||||
|
private readonly ISettingsService<CustomizationSettings> _customizationService;
|
||||||
|
private readonly ISettingsService<EmailNotificationSettings> _emailService;
|
||||||
|
private readonly ILogger<MassEmailSender> _log;
|
||||||
|
private readonly OmbiUserManager _userManager;
|
||||||
|
|
||||||
|
public async Task<bool> SendMassEmail(MassEmailModel model)
|
||||||
|
{
|
||||||
|
var customization = await _customizationService.GetSettingsAsync();
|
||||||
|
var email = await _emailService.GetSettingsAsync();
|
||||||
|
var messagesSent = new List<Task>();
|
||||||
|
foreach (var user in model.Users)
|
||||||
|
{
|
||||||
|
var fullUser = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == user.Id);
|
||||||
|
if (!fullUser.Email.HasValue())
|
||||||
|
{
|
||||||
|
_log.LogInformation("User {0} has no email, cannot send mass email to this user", fullUser.UserName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var resolver = new NotificationMessageResolver();
|
||||||
|
var curlys = new NotificationMessageCurlys();
|
||||||
|
curlys.Setup(fullUser, customization);
|
||||||
|
var template = new NotificationTemplates() { Message = model.Body, Subject = model.Subject };
|
||||||
|
var content = resolver.ParseMessage(template, curlys);
|
||||||
|
var msg = new NotificationMessage
|
||||||
|
{
|
||||||
|
Message = content.Message,
|
||||||
|
To = fullUser.Email,
|
||||||
|
Subject = content.Subject
|
||||||
|
};
|
||||||
|
messagesSent.Add(_email.SendAdHoc(msg, email));
|
||||||
|
_log.LogInformation("Sent mass email to user {0} @ {1}", fullUser.UserName, fullUser.Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.WhenAll(messagesSent);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -80,6 +80,7 @@ namespace Ombi.DependencyInjection
|
||||||
services.AddTransient<IRuleEvaluator, RuleEvaluator>();
|
services.AddTransient<IRuleEvaluator, RuleEvaluator>();
|
||||||
services.AddTransient<IMovieSender, MovieSender>();
|
services.AddTransient<IMovieSender, MovieSender>();
|
||||||
services.AddTransient<ITvSender, TvSender>();
|
services.AddTransient<ITvSender, TvSender>();
|
||||||
|
services.AddTransient<IMassEmailSender, MassEmailSender>();
|
||||||
}
|
}
|
||||||
public static void RegisterHttp(this IServiceCollection services)
|
public static void RegisterHttp(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
|
@ -81,11 +81,14 @@ namespace Ombi.Notifications
|
||||||
IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty;
|
IssueStatus = opts.Substitutes.TryGetValue("IssueStatus", out val) ? val : string.Empty;
|
||||||
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
|
IssueSubject = opts.Substitutes.TryGetValue("IssueSubject", out val) ? val : string.Empty;
|
||||||
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
|
NewIssueComment = opts.Substitutes.TryGetValue("NewIssueComment", out val) ? val : string.Empty;
|
||||||
IssueUser = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
|
RequestedUser = opts.Substitutes.TryGetValue("IssueUser", out val) ? val : string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
// User Defined
|
// User Defined
|
||||||
public string RequestedUser { get; set; }
|
public string RequestedUser { get; set; }
|
||||||
|
public string UserName => RequestedUser;
|
||||||
|
public string IssueUser => RequestedUser;
|
||||||
|
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string RequestedDate { get; set; }
|
public string RequestedDate { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
@ -102,7 +105,6 @@ namespace Ombi.Notifications
|
||||||
public string IssueStatus { get; set; }
|
public string IssueStatus { get; set; }
|
||||||
public string IssueSubject { get; set; }
|
public string IssueSubject { get; set; }
|
||||||
public string NewIssueComment { get; set; }
|
public string NewIssueComment { get; set; }
|
||||||
public string IssueUser { get; set; }
|
|
||||||
|
|
||||||
// System Defined
|
// System Defined
|
||||||
private string LongDate => DateTime.Now.ToString("D");
|
private string LongDate => DateTime.Now.ToString("D");
|
||||||
|
@ -134,6 +136,7 @@ namespace Ombi.Notifications
|
||||||
{nameof(IssueSubject),IssueSubject},
|
{nameof(IssueSubject),IssueSubject},
|
||||||
{nameof(NewIssueComment),NewIssueComment},
|
{nameof(NewIssueComment),NewIssueComment},
|
||||||
{nameof(IssueUser),IssueUser},
|
{nameof(IssueUser),IssueUser},
|
||||||
|
{nameof(UserName),UserName},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -227,11 +227,21 @@ namespace Ombi.Schedule.Jobs.Plex
|
||||||
var existingImdb = false;
|
var existingImdb = false;
|
||||||
var existingMovieDbId = false;
|
var existingMovieDbId = false;
|
||||||
var existingTvDbId = false;
|
var existingTvDbId = false;
|
||||||
|
if (item.ImdbId.HasValue())
|
||||||
existingImdb = await Repo.GetAll().AnyAsync(x => x.ImdbId == item.ImdbId && x.Type == PlexMediaTypeEntity.Show);
|
{
|
||||||
existingMovieDbId = await Repo.GetAll().AnyAsync(x => x.TheMovieDbId == item.TheMovieDbId && x.Type == PlexMediaTypeEntity.Show);
|
existingImdb = await Repo.GetAll().AnyAsync(x =>
|
||||||
existingTvDbId = await Repo.GetAll().AnyAsync(x => x.TvDbId == item.TvDbId && x.Type == PlexMediaTypeEntity.Show);
|
x.ImdbId == item.ImdbId && x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
}
|
||||||
|
if (item.TheMovieDbId.HasValue())
|
||||||
|
{
|
||||||
|
existingMovieDbId = await Repo.GetAll().AnyAsync(x =>
|
||||||
|
x.TheMovieDbId == item.TheMovieDbId && x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
}
|
||||||
|
if (item.TvDbId.HasValue())
|
||||||
|
{
|
||||||
|
existingTvDbId = await Repo.GetAll().AnyAsync(x =>
|
||||||
|
x.TvDbId == item.TvDbId && x.Type == PlexMediaTypeEntity.Show);
|
||||||
|
}
|
||||||
if (existingImdb || existingTvDbId || existingMovieDbId)
|
if (existingImdb || existingTvDbId || existingMovieDbId)
|
||||||
{
|
{
|
||||||
// We already have it!
|
// We already have it!
|
||||||
|
|
|
@ -32,5 +32,8 @@ namespace Ombi.Store.Entities
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public bool EmailLogin { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -49,3 +49,14 @@ export interface IMobileUsersViewModel {
|
||||||
username: string;
|
username: string;
|
||||||
devices: number;
|
devices: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IMassEmailUserModel {
|
||||||
|
user: IUser;
|
||||||
|
selected: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IMassEmailModel {
|
||||||
|
subject: string;
|
||||||
|
body: string;
|
||||||
|
users: IUser[];
|
||||||
|
}
|
||||||
|
|
|
@ -12,3 +12,4 @@ export * from "./status.service";
|
||||||
export * from "./job.service";
|
export * from "./job.service";
|
||||||
export * from "./issues.service";
|
export * from "./issues.service";
|
||||||
export * from "./mobile.service";
|
export * from "./mobile.service";
|
||||||
|
export * from "./notificationMessage.service";
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { PlatformLocation } from "@angular/common";
|
||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
|
||||||
|
import { HttpClient } from "@angular/common/http";
|
||||||
|
import { Observable } from "rxjs/Rx";
|
||||||
|
|
||||||
|
import { IMassEmailModel } from "./../interfaces";
|
||||||
|
|
||||||
|
import { ServiceHelpers } from "./service.helpers";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NotificationMessageService extends ServiceHelpers {
|
||||||
|
constructor(http: HttpClient, public platformLocation: PlatformLocation) {
|
||||||
|
super(http, "/api/v1/notifications/", platformLocation);
|
||||||
|
}
|
||||||
|
public sendMassEmail(model: IMassEmailModel): Observable<boolean> {
|
||||||
|
return this.http.post<boolean>(`${this.url}massemail/`, JSON.stringify(model) ,{headers: this.headers});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<settings-menu></settings-menu>
|
||||||
|
|
||||||
|
<wiki [url]="'https://github.com/tidusjar/Ombi/wiki/Mass-Email'"></wiki>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Mass Email</legend>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control form-control-custom " id="subject" name="subject" placeholder="Subject" [(ngModel)]="subject" [ngClass]="{'form-error': missingSubject}">
|
||||||
|
<small *ngIf="missingSubject" class="error-text">Hey! We need a subject!</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" >
|
||||||
|
<textarea rows="10" type="text" class="form-control-custom form-control " id="themeContent" name="themeContent" [(ngModel)]="message"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="logo" class="control-label">Message Preview</label>
|
||||||
|
<br/>
|
||||||
|
<small>May appear differently on email clients</small>
|
||||||
|
<hr/>
|
||||||
|
<div [innerHTML]="message"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<button type="submit" id="save" (click)="send()" class="btn btn-primary-outline">Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<!--Users Section-->
|
||||||
|
<label class="control-label">Recipients</label>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="all" (click)="selectAllUsers()">
|
||||||
|
<label for="all">Select All</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group" *ngFor="let u of users">
|
||||||
|
<div class="checkbox">
|
||||||
|
<input type="checkbox" id="{{u.user.id}}" [(ngModel)]="u.selected" (click)="selectSingleUser(u)">
|
||||||
|
<label for="{{u.user.id}}">{{u.user.userName}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</fieldset>
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
|
||||||
|
import { IMassEmailModel, IMassEmailUserModel } from "../../interfaces";
|
||||||
|
import { IdentityService, NotificationMessageService, NotificationService, SettingsService } from "../../services";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "./massemail.component.html",
|
||||||
|
})
|
||||||
|
export class MassEmailComponent implements OnInit {
|
||||||
|
|
||||||
|
public users: IMassEmailUserModel[] = [];
|
||||||
|
public message: string;
|
||||||
|
public subject: string;
|
||||||
|
|
||||||
|
public missingSubject = false;
|
||||||
|
|
||||||
|
public emailEnabled: boolean;
|
||||||
|
|
||||||
|
constructor(private readonly notification: NotificationService,
|
||||||
|
private readonly identityService: IdentityService,
|
||||||
|
private readonly notificationMessageService: NotificationMessageService,
|
||||||
|
private readonly settingsService: SettingsService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ngOnInit(): void {
|
||||||
|
this.identityService.getUsers().subscribe(x => {
|
||||||
|
x.forEach(u => {
|
||||||
|
this.users.push({
|
||||||
|
user: u,
|
||||||
|
selected: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.settingsService.getEmailSettingsEnabled().subscribe(x => this.emailEnabled = x);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectAllUsers() {
|
||||||
|
this.users.forEach(u => u.selected = !u.selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectSingleUser(user: IMassEmailUserModel) {
|
||||||
|
user.selected = !user.selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public send() {
|
||||||
|
if(!this.subject) {
|
||||||
|
this.missingSubject = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!this.emailEnabled) {
|
||||||
|
this.notification.error("You have not yet setup your email notifications, do that first!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.missingSubject = false;
|
||||||
|
// Where(x => x.selected).Select(x => x.user)
|
||||||
|
const selectedUsers = this.users.filter(u => {
|
||||||
|
return u.selected;
|
||||||
|
}).map(u => u.user);
|
||||||
|
|
||||||
|
if(selectedUsers.length <=0) {
|
||||||
|
this.notification.error("You need to select at least one user to send the email");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = <IMassEmailModel>{
|
||||||
|
users: selectedUsers,
|
||||||
|
subject: this.subject,
|
||||||
|
body: this.message,
|
||||||
|
};
|
||||||
|
this.notification.info("Sending","Sending mass email... Please wait");
|
||||||
|
this.notificationMessageService.sendMassEmail(model).subscribe(x => {
|
||||||
|
this.notification.success("We have sent the mass email to the users selected!");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,8 @@ import { ClipboardModule } from "ngx-clipboard/dist";
|
||||||
|
|
||||||
import { AuthGuard } from "../auth/auth.guard";
|
import { AuthGuard } from "../auth/auth.guard";
|
||||||
import { AuthService } from "../auth/auth.service";
|
import { AuthService } from "../auth/auth.service";
|
||||||
import { CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, PlexService, RadarrService, SonarrService, TesterService, ValidationService } from "../services";
|
import { CouchPotatoService, EmbyService, IssuesService, JobService, MobileService, NotificationMessageService, PlexService, RadarrService,
|
||||||
|
SonarrService, TesterService, ValidationService } from "../services";
|
||||||
|
|
||||||
import { PipeModule } from "../pipes/pipe.module";
|
import { PipeModule } from "../pipes/pipe.module";
|
||||||
import { AboutComponent } from "./about/about.component";
|
import { AboutComponent } from "./about/about.component";
|
||||||
|
@ -19,6 +20,7 @@ import { EmbyComponent } from "./emby/emby.component";
|
||||||
import { IssuesComponent } from "./issues/issues.component";
|
import { IssuesComponent } from "./issues/issues.component";
|
||||||
import { JobsComponent } from "./jobs/jobs.component";
|
import { JobsComponent } from "./jobs/jobs.component";
|
||||||
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
import { LandingPageComponent } from "./landingpage/landingpage.component";
|
||||||
|
import { MassEmailComponent } from "./massemail/massemail.component";
|
||||||
import { DiscordComponent } from "./notifications/discord.component";
|
import { DiscordComponent } from "./notifications/discord.component";
|
||||||
import { EmailNotificationComponent } from "./notifications/emailnotification.component";
|
import { EmailNotificationComponent } from "./notifications/emailnotification.component";
|
||||||
import { MattermostComponent } from "./notifications/mattermost.component";
|
import { MattermostComponent } from "./notifications/mattermost.component";
|
||||||
|
@ -66,6 +68,7 @@ const routes: Routes = [
|
||||||
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
|
{ path: "Issues", component: IssuesComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
|
{ path: "Authentication", component: AuthenticationComponent, canActivate: [AuthGuard] },
|
||||||
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
|
{ path: "Mobile", component: MobileComponent, canActivate: [AuthGuard] },
|
||||||
|
{ path: "MassEmail", component: MassEmailComponent, canActivate: [AuthGuard] },
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -114,6 +117,7 @@ const routes: Routes = [
|
||||||
IssuesComponent,
|
IssuesComponent,
|
||||||
AuthenticationComponent,
|
AuthenticationComponent,
|
||||||
MobileComponent,
|
MobileComponent,
|
||||||
|
MassEmailComponent,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
RouterModule,
|
RouterModule,
|
||||||
|
@ -131,6 +135,7 @@ const routes: Routes = [
|
||||||
PlexService,
|
PlexService,
|
||||||
EmbyService,
|
EmbyService,
|
||||||
MobileService,
|
MobileService,
|
||||||
|
NotificationMessageService,
|
||||||
],
|
],
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Email']">Email</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Email']">Email</a></li>
|
||||||
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/MassEmail']">Mass Email</a></li>
|
||||||
<!--<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Newsletter']">Newsletter</a></li>-->
|
<!--<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Newsletter']">Newsletter</a></li>-->
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Discord']">Discord</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Discord']">Discord</a></li>
|
||||||
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Slack']">Slack</a></li>
|
<li [routerLinkActive]="['active']"><a [routerLink]="['/Settings/Slack']">Slack</a></li>
|
||||||
|
|
55
src/Ombi/Controllers/NotificationsController.cs
Normal file
55
src/Ombi/Controllers/NotificationsController.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#region Copyright
|
||||||
|
// /************************************************************************
|
||||||
|
// Copyright (c) 2018 Jamie Rees
|
||||||
|
// File: NotificationsController.cs
|
||||||
|
// Created By: Jamie Rees
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
// ************************************************************************/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Ombi.Attributes;
|
||||||
|
using Ombi.Core.Models;
|
||||||
|
using Ombi.Core.Senders;
|
||||||
|
|
||||||
|
namespace Ombi.Controllers
|
||||||
|
{
|
||||||
|
[Admin]
|
||||||
|
[ApiV1]
|
||||||
|
[Produces("application/json")]
|
||||||
|
public class NotificationsController : Controller
|
||||||
|
{
|
||||||
|
public NotificationsController(IMassEmailSender sender)
|
||||||
|
{
|
||||||
|
_sender = sender;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly IMassEmailSender _sender;
|
||||||
|
|
||||||
|
[HttpPost("massemail")]
|
||||||
|
public async Task<bool> SendMassEmail([FromBody]MassEmailModel model)
|
||||||
|
{
|
||||||
|
return await _sender.SendMassEmail(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -58,6 +58,8 @@ namespace Ombi.Controllers
|
||||||
{
|
{
|
||||||
return new UnauthorizedResult();
|
return new UnauthorizedResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user.EmailLogin = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify Password
|
// Verify Password
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue