Added the Notification Preferences to the user

pull/2519/head
TidusJar 6 years ago
parent 8573b7c729
commit 2204559b0a

@ -0,0 +1,43 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations
{
public partial class UserNotificationPreferences : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserNotificationPreferences",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<string>(nullable: true),
Agent = table.Column<int>(nullable: false),
Enabled = table.Column<bool>(nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_UserNotificationPreferences", x => x.Id);
table.ForeignKey(
name: "FK_UserNotificationPreferences_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_UserNotificationPreferences_UserId",
table: "UserNotificationPreferences",
column: "UserId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserNotificationPreferences");
}
}
}

@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); .HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
@ -870,6 +870,26 @@ namespace Ombi.Store.Migrations
b.ToTable("Tokens"); b.ToTable("Tokens");
}); });
modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Agent");
b.Property<bool>("Enabled");
b.Property<string>("UserId");
b.Property<string>("Value");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("UserNotificationPreferences");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
@ -1068,6 +1088,13 @@ namespace Ombi.Store.Migrations
.HasForeignKey("UserId"); .HasForeignKey("UserId");
}); });
modelBuilder.Entity("Ombi.Store.Entities.UserNotificationPreferences", b =>
{
b.HasOne("Ombi.Store.Entities.OmbiUser", "User")
.WithMany("UserNotificationPreferences")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b =>
{ {
b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season")

@ -66,3 +66,22 @@ export interface IMassEmailModel {
body: string; body: string;
users: IUser[]; users: IUser[];
} }
export interface INotificationPreferences {
id: number;
userId: string;
agent: INotificationAgent;
enabled: boolean;
value: string;
}
export enum INotificationAgent {
Email = 0,
Discord = 1,
Pushbullet = 2,
Pushover = 3,
Telegram = 4,
Slack = 5,
Mattermost = 6,
Mobile = 7,
}

@ -4,7 +4,7 @@ import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { ICheckbox, ICreateWizardUser, IIdentityResult, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces"; import { ICheckbox, ICreateWizardUser, IIdentityResult, INotificationPreferences, IResetPasswordToken, IUpdateLocalUser, IUser, IWizardUserResult } from "../interfaces";
import { ServiceHelpers } from "./service.helpers"; import { ServiceHelpers } from "./service.helpers";
@Injectable() @Injectable()
@ -43,6 +43,11 @@ export class IdentityService extends ServiceHelpers {
public updateUser(user: IUser): Observable<IIdentityResult> { public updateUser(user: IUser): Observable<IIdentityResult> {
return this.http.put<IIdentityResult>(this.url, JSON.stringify(user), {headers: this.headers}); return this.http.put<IIdentityResult>(this.url, JSON.stringify(user), {headers: this.headers});
} }
public updateNotificationPreferences(pref: INotificationPreferences[]): Observable<boolean> {
return this.http.post<boolean>(`${this.url}NotificationPreferences`, JSON.stringify(pref), {headers: this.headers});
}
public updateLocalUser(user: IUpdateLocalUser): Observable<IIdentityResult> { public updateLocalUser(user: IUpdateLocalUser): Observable<IIdentityResult> {
return this.http.put<IIdentityResult>(this.url + "local", JSON.stringify(user), {headers: this.headers}); return this.http.put<IIdentityResult>(this.url + "local", JSON.stringify(user), {headers: this.headers});
} }
@ -67,6 +72,13 @@ export class IdentityService extends ServiceHelpers {
return this.http.post<any>(`${this.url}welcomeEmail`, JSON.stringify(user), {headers: this.headers}); return this.http.post<any>(`${this.url}welcomeEmail`, JSON.stringify(user), {headers: this.headers});
} }
public getNotificationPreferences(): Observable<INotificationPreferences[]> {
return this.http.get<INotificationPreferences[]>(`${this.url}notificationpreferences`, {headers: this.headers});
}
public getNotificationPreferencesForUser(userId: string): Observable<INotificationPreferences[]> {
return this.http.get<INotificationPreferences[]>(`${this.url}notificationpreferences/${userId}`, {headers: this.headers});
}
public hasRole(role: string): boolean { public hasRole(role: string): boolean {
const roles = localStorage.getItem("roles") as string[] | null; const roles = localStorage.getItem("roles") as string[] | null;
if (roles) { if (roles) {

@ -1,152 +1,149 @@
<div *ngIf="!edit"> <h3 *ngIf="!edit">Create User</h3>
<h3 >Create User</h3> <h3 *ngIf="edit && user"> User: {{user.userName}}</h3>
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button> <button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button>
<div *ngIf="!edit || edit && user">
<div class="modal-body" style="margin-top: 45px;"> <div class="modal-body" style="margin-top: 45px;">
<div class="col-md-6"> <div class="col-md-6">
<h4>User Details</h4> <h4>User Details</h4>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h4>Roles</h4> <h4>User Settings</h4>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-group"> <div class="form-group">
<label for="username" class="control-label">Username</label> <label for="username" class="control-label">Username</label>
<div> <div>
<input type="text" [(ngModel)]="user.userName" class="form-control form-control-custom " id="username" name="username" value="{{user?.userName}}"> <input type="text" [(ngModel)]="user.userName" class="form-control form-control-custom " id="username"
name="username" value="{{user?.userName}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="alias" class="control-label">Alias</label> <label for="alias" class="control-label">Alias</label>
<div> <div>
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}"> <input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias"
name="alias" value="{{user?.alias}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="emailAddress" class="control-label">Email Address</label> <label for="emailAddress" class="control-label">Email Address</label>
<div> <div>
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}"> <input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress"
name="emailAddress" value="{{user?.emailAddress}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="password" class="control-label">Password</label> <label for="password" class="control-label">Password</label>
<div> <div>
<input type="password" [(ngModel)]="user.password" class="form-control form-control-custom " id="password" name="password"> <input type="password" [(ngModel)]="user.password" class="form-control form-control-custom " id="password"
name="password">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group" *ngIf="!edit">
<label for="confirmPass" class="control-label">Confirm Password</label> <label for="confirmPass" class="control-label">Confirm Password</label>
<div> <div>
<input type="password" [(ngModel)]="confirmPass" class="form-control form-control-custom " id="confirmPass" name="confirmPass"> <input type="password" [(ngModel)]="confirmPass" class="form-control form-control-custom " id="confirmPass"
name="confirmPass">
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<ngb-accordion [closeOthers]="true" activeIds="0-header">
<ngb-panel title="Roles">
<ng-template ngbPanelContent>
<div class="panel panel-default a">
<div class="panel-body">
<div *ngIf="!edit">
<div *ngFor="let c of availableClaims"> <div *ngFor="let c of availableClaims">
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled"> <input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}"
[attr.name]="'create' + c.value" ng-checked="c.enabled">
<label for="create{{c.value}}">{{c.value | humanize}}</label> <label for="create{{c.value}}">{{c.value | humanize}}</label>
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
<div>
<input type="text" [(ngModel)]="user.movieRequestLimit" class="form-control form-small form-control-custom " id="movieRequestLimit" name="movieRequestLimit" value="{{user?.movieRequestLimit}}">
</div>
</div> </div>
<div *ngIf="edit">
<div *ngFor="let c of user.claims">
<div class="form-group"> <div class="form-group">
<label for="episodeRequestLimit" class="control-label">Episode Request Limit</label> <div class="checkbox">
<div> <input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}"
<input type="text" [(ngModel)]="user.episodeRequestLimit" class="form-control form-small form-control-custom " id="episodeRequestLimit" name="episodeRequestLimit" value="{{user?.episodeRequestLimit}}"> [attr.name]="'create' + c.value" ng-checked="c.enabled">
</div> <label for="create{{c.value}}">{{c.value | humanize}}</label>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-12">
<button type="button" class="btn btn-danger-outline" (click)="create()">Create</button>
</div> </div>
</div> </div>
</div> </div>
<div *ngIf="edit"> </ng-template>
</ngb-panel>
<div *ngIf="user"> <ngb-panel title="Request Limits">
<div class="user-details"> <ng-template ngbPanelContent>
<h3>User: {{user.userName}}</h3> <div class="panel panel-default a">
<button type="button" class="btn btn-primary-outline" style="float:right;" [routerLink]="['/usermanagement/']">Back</button> <div class="panel-body">
<p-confirmDialog></p-confirmDialog>
<div class="modal-body" style="margin-top: 45px;">
<div class="col-md-6">
<h4>User Details</h4>
</div>
<div class="col-md-6">
<h4>Roles</h4>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="username" class="control-label">Username</label>
<div>
<input type="text" [(ngModel)]="user.userName" [readonly]="true" class="form-control form-control-custom " id="username" name="username" value="{{user?.userName}}">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="alias" class="control-label">Alias</label> <label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
<div> <div>
<input type="text" [(ngModel)]="user.alias" class="form-control form-control-custom " id="alias" name="alias" value="{{user?.alias}}"> <input type="text" [(ngModel)]="user.movieRequestLimit" class="form-control form-small form-control-custom "
id="movieRequestLimit" name="movieRequestLimit" value="{{user?.movieRequestLimit}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="emailAddress" class="control-label">Email Address</label> <label for="episodeRequestLimit" class="control-label">Episode Request Limit</label>
<div> <div>
<input type="text" [(ngModel)]="user.emailAddress" class="form-control form-control-custom " id="emailAddress" name="emailAddress" value="{{user?.emailAddress}}" [disabled]="user?.userType == 2"> <input type="text" [(ngModel)]="user.episodeRequestLimit" class="form-control form-small form-control-custom "
id="episodeRequestLimit" name="episodeRequestLimit" value="{{user?.episodeRequestLimit}}">
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> </ng-template>
<div *ngFor="let c of user.claims"> </ngb-panel>
<ngb-panel title="Notification Preferences" *ngIf="notificationPreferences">
<ng-template ngbPanelContent>
<div class="panel panel-default a">
<div class="panel-body">
<div *ngFor="let pref of notificationPreferences">
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <label for="{{pref.agent}}" class="control-label">{{NotificationAgent[pref.agent] | humanize}}</label>
<input type="checkbox" [(ngModel)]="c.enabled" [value]="c.value" id="create{{c.value}}" [attr.name]="'create' + c.value" ng-checked="c.enabled"> <div>
<label for="create{{c.value}}">{{c.value | humanize}}</label> <input type="text" [(ngModel)]="pref.value" class="form-control form-control-custom"
name="{{pref.agent}}" value="{{pref?.value}}">
</div> </div>
</div> </div>
</div> </div>
<div class="form-group">
<label for="movieRequestLimit" class="control-label">Movie Request Limit</label>
<div>
<input type="text" [(ngModel)]="user.movieRequestLimit" class="form-control form-small form-control-custom " id="movieRequestLimit" name="movieRequestLimit" value="{{user?.movieRequestLimit}}">
</div> </div>
</div> </div>
<div class="form-group"> </ng-template>
<label for="episodeRequestLimit" class="control-label">Episode Request Limit</label> </ngb-panel>
<div> </ngb-accordion>
<input type="text" [(ngModel)]="user.episodeRequestLimit" class="form-control form-small form-control-custom " id="episodeRequestLimit" name="episodeRequestLimit" value="{{user?.episodeRequestLimit}}">
</div>
</div> </div>
</div> </div>
</div> </div>
<div> <div class="row">
<div class="col-md-12">
<button *ngIf="!edit" type="button" class="btn btn-danger-outline" (click)="create()">Create</button>
<div *ngIf="edit">
<button type="button" class="btn btn-primary-outline" (click)="update()">Update</button> <button type="button" class="btn btn-primary-outline" (click)="update()">Update</button>
<button type="button" class="btn btn-danger-outline" (click)="delete()">Delete</button> <button type="button" class="btn btn-danger-outline" (click)="delete()">Delete</button>
<button type="button" style="float:right;" class="btn btn-info-outline" (click)="resetPassword()" pTooltip="You need your SMTP settings setup">Send Reset Password Link</button> <button type="button" style="float:right;" class="btn btn-info-outline" (click)="resetPassword()"
pTooltip="You need your SMTP settings setup">Send Reset Password Link</button>
</div> </div>
</div> </div>

@ -1,7 +1,7 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { ICheckbox, IUser, UserType } from "../interfaces"; import { ICheckbox, INotificationAgent, INotificationPreferences, IUser, UserType } from "../interfaces";
import { IdentityService, NotificationService } from "../services"; import { IdentityService, NotificationService } from "../services";
import { ConfirmationService } from "primeng/primeng"; import { ConfirmationService } from "primeng/primeng";
@ -15,7 +15,9 @@ export class UserManagementUserComponent implements OnInit {
public userId: string; public userId: string;
public availableClaims: ICheckbox[]; public availableClaims: ICheckbox[];
public confirmPass: ""; public confirmPass: "";
public notificationPreferences: INotificationPreferences[];
public NotificationAgent = INotificationAgent;
public edit: boolean; public edit: boolean;
constructor(private identityService: IdentityService, constructor(private identityService: IdentityService,
@ -38,6 +40,11 @@ export class UserManagementUserComponent implements OnInit {
public ngOnInit() { public ngOnInit() {
this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x); this.identityService.getAllAvailableClaims().subscribe(x => this.availableClaims = x);
if(this.edit) {
this.identityService.getNotificationPreferencesForUser(this.userId).subscribe(x => this.notificationPreferences = x);
} else {
this.identityService.getNotificationPreferences().subscribe(x => this.notificationPreferences = x);
}
if(!this.edit) { if(!this.edit) {
this.user = { this.user = {
alias: "", alias: "",
@ -142,6 +149,7 @@ export class UserManagementUserComponent implements OnInit {
this.identityService.updateUser(this.user).subscribe(x => { this.identityService.updateUser(this.user).subscribe(x => {
if (x.successful) { if (x.successful) {
this.identityService.updateNotificationPreferences(this.notificationPreferences).subscribe();
this.notificationService.success(`The user ${this.user.userName} has been updated successfully`); this.notificationService.success(`The user ${this.user.userName} has been updated successfully`);
this.router.navigate(["usermanagement"]); this.router.navigate(["usermanagement"]);
} else { } else {

@ -21,6 +21,7 @@ import { AddPlexUserComponent } from "./addplexuser.component";
const routes: Routes = [ const routes: Routes = [
{ path: "", component: UserManagementComponent, canActivate: [AuthGuard] }, { path: "", component: UserManagementComponent, canActivate: [AuthGuard] },
{ path: "user", component: UserManagementUserComponent, canActivate: [AuthGuard] }, { path: "user", component: UserManagementUserComponent, canActivate: [AuthGuard] },
{ path: "user/:id", component: UserManagementUserComponent, canActivate: [AuthGuard] },
{ path: "updatedetails", component: UpdateDetailsComponent, canActivate: [AuthGuard] }, { path: "updatedetails", component: UpdateDetailsComponent, canActivate: [AuthGuard] },
]; ];
@ -40,8 +41,6 @@ const routes: Routes = [
], ],
declarations: [ declarations: [
UserManagementComponent, UserManagementComponent,
UserManagementAddComponent,
UserManagementEditComponent,
UpdateDetailsComponent, UpdateDetailsComponent,
AddPlexUserComponent, AddPlexUserComponent,
UserManagementUserComponent, UserManagementUserComponent,

@ -794,9 +794,26 @@ namespace Ombi.Controllers
public async Task<List<UserNotificationPreferences>> GetUserPreferences() public async Task<List<UserNotificationPreferences>> GetUserPreferences()
{ {
var user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name); var user = await UserManager.Users.FirstOrDefaultAsync(x => x.UserName == User.Identity.Name);
return await GetPreferences(user);
}
[HttpGet("notificationpreferences/{userId}")]
public async Task<List<UserNotificationPreferences>> GetUserPreferences(string userId)
{
var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
return await GetPreferences(user);
}
private readonly List<NotificationAgent> _excludedAgents = new List<NotificationAgent>
{
NotificationAgent.Email,
NotificationAgent.Mobile
};
private async Task<List<UserNotificationPreferences>> GetPreferences(OmbiUser user)
{
var userPreferences = await _userNotificationPreferences.GetAll().Where(x => x.UserId == user.Id).ToListAsync(); var userPreferences = await _userNotificationPreferences.GetAll().Where(x => x.UserId == user.Id).ToListAsync();
var agents = Enum.GetValues(typeof(NotificationAgent)).Cast<NotificationAgent>(); var agents = Enum.GetValues(typeof(NotificationAgent)).Cast<NotificationAgent>().Where(x => !_excludedAgents.Contains(x));
foreach (var a in agents) foreach (var a in agents)
{ {
var agent = userPreferences.FirstOrDefault(x => x.Agent == a); var agent = userPreferences.FirstOrDefault(x => x.Agent == a);
@ -806,20 +823,20 @@ namespace Ombi.Controllers
userPreferences.Add(new UserNotificationPreferences userPreferences.Add(new UserNotificationPreferences
{ {
Agent = a, Agent = a,
UserId = user.Id,
}); });
} }
else
{
userPreferences.Add(agent);
}
} }
return userPreferences; return userPreferences;
} }
[HttpPost("NotificationPreferences")] [HttpPost("NotificationPreferences")]
public async Task<IActionResult> AddUserNotificationPreference([FromBody] AddNotificationPreference pref) public async Task<IActionResult> AddUserNotificationPreference([FromBody] List<AddNotificationPreference> preferences)
{ {
foreach (var pref in preferences)
{
// Make sure the user exists // Make sure the user exists
var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == pref.UserId); var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == pref.UserId);
if (user == null) if (user == null)
@ -855,6 +872,7 @@ namespace Ombi.Controllers
Value = pref.Value Value = pref.Value
}); });
}
return Json(true); return Json(true);
} }

Loading…
Cancel
Save