Merge branch 'develop' of https://github.com/tidusjar/ombi into develop

pull/2754/head
TidusJar 6 years ago
commit 732873acda

@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
Approved = false, Approved = false,
RequestedUserId = userDetails.Id, RequestedUserId = userDetails.Id,
Background = movieInfo.BackdropPath, Background = movieInfo.BackdropPath,
LangCode = model.LanguageCode LangCode = model.LanguageCode,
RequestedByAlias = model.RequestedByAlias
}; };
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");

@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
Title = album.title, Title = album.title,
Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url, Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.url,
Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url, Cover = album.images?.FirstOrDefault(x => x.coverType.Equals("cover"))?.url,
ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty ForeignArtistId = album?.artist?.foreignArtistId ?? string.Empty,
RequestedByAlias = model.RequestedByAlias
}; };
if (requestModel.Cover.IsNullOrEmpty()) if (requestModel.Cover.IsNullOrEmpty())
{ {

@ -72,6 +72,7 @@ namespace Ombi.Core.Helpers
SeasonRequests = new List<SeasonRequests>(), SeasonRequests = new List<SeasonRequests>(),
Title = ShowInfo.name, Title = ShowInfo.name,
ReleaseYear = FirstAir, ReleaseYear = FirstAir,
RequestedByAlias = model.RequestedByAlias,
SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard SeriesType = ShowInfo.genres.Any( s => s.Equals("Anime", StringComparison.InvariantCultureIgnoreCase)) ? SeriesType.Anime : SeriesType.Standard
}; };

@ -24,11 +24,20 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using Newtonsoft.Json;
namespace Ombi.Core.Models.Requests namespace Ombi.Core.Models.Requests
{ {
public class MovieRequestViewModel public class MovieRequestViewModel
{ {
public int TheMovieDbId { get; set; } public int TheMovieDbId { get; set; }
public string LanguageCode { get; set; } = "en"; public string LanguageCode { get; set; } = "en";
/// <summary>
/// This is only set from a HTTP Header
/// </summary>
[JsonIgnore]
public string RequestedByAlias { get; set; }
} }
} }

@ -3,5 +3,6 @@
public class MusicAlbumRequestViewModel public class MusicAlbumRequestViewModel
{ {
public string ForeignAlbumId { get; set; } public string ForeignAlbumId { get; set; }
public string RequestedByAlias { get; set; }
} }
} }

@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json;
namespace Ombi.Core.Models.Requests namespace Ombi.Core.Models.Requests
{ {
@ -9,6 +10,8 @@ namespace Ombi.Core.Models.Requests
public bool FirstSeason { get; set; } public bool FirstSeason { get; set; }
public int TvDbId { get; set; } public int TvDbId { get; set; }
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>(); public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
[JsonIgnore]
public string RequestedByAlias { get; set; }
} }
public class SeasonsViewModel public class SeasonsViewModel

@ -34,7 +34,7 @@ namespace Ombi.Store.Entities
public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue(); public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue();
[NotMapped] [NotMapped]
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias; public virtual string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
[NotMapped] [NotMapped]
public bool EmailLogin { get; set; } public bool EmailLogin { get; set; }

@ -17,6 +17,7 @@ namespace Ombi.Store.Entities.Requests
public DateTime MarkedAsDenied { get; set; } public DateTime MarkedAsDenied { get; set; }
public string DeniedReason { get; set; } public string DeniedReason { get; set; }
public RequestType RequestType { get; set; } public RequestType RequestType { get; set; }
public string RequestedByAlias { get; set; }
[ForeignKey(nameof(RequestedUserId))] [ForeignKey(nameof(RequestedUserId))]
public OmbiUser RequestedUser { get; set; } public OmbiUser RequestedUser { get; set; }

File diff suppressed because it is too large Load Diff

@ -0,0 +1,40 @@
using Microsoft.EntityFrameworkCore.Migrations;
namespace Ombi.Store.Migrations
{
public partial class RequestedByAlias : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "RequestedByAlias",
table: "MovieRequests",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "RequestedByAlias",
table: "ChildRequests",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "RequestedByAlias",
table: "AlbumRequests",
nullable: true);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "RequestedByAlias",
table: "MovieRequests");
migrationBuilder.DropColumn(
name: "RequestedByAlias",
table: "ChildRequests");
migrationBuilder.DropColumn(
name: "RequestedByAlias",
table: "AlbumRequests");
}
}
}

@ -14,7 +14,7 @@ namespace Ombi.Store.Migrations
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687"); .HasAnnotation("ProductVersion", "2.2.1-servicing-10028");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
@ -583,6 +583,8 @@ namespace Ombi.Store.Migrations
b.Property<int>("RequestType"); b.Property<int>("RequestType");
b.Property<string>("RequestedByAlias");
b.Property<DateTime>("RequestedDate"); b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId"); b.Property<string>("RequestedUserId");
@ -621,6 +623,8 @@ namespace Ombi.Store.Migrations
b.Property<int>("RequestType"); b.Property<int>("RequestType");
b.Property<string>("RequestedByAlias");
b.Property<DateTime>("RequestedDate"); b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId"); b.Property<string>("RequestedUserId");
@ -749,6 +753,8 @@ namespace Ombi.Store.Migrations
b.Property<int>("RequestType"); b.Property<int>("RequestType");
b.Property<string>("RequestedByAlias");
b.Property<DateTime>("RequestedDate"); b.Property<DateTime>("RequestedDate");
b.Property<string>("RequestedUserId"); b.Property<string>("RequestedUserId");

@ -1,71 +1,71 @@
using System; //using System;
using Microsoft.AspNetCore.Builder; //using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; //using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; //using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features.Authentication; //using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.Identity; //using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection; //using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; //using Microsoft.Extensions.Options;
using Moq; //using Moq;
using Ombi.Api.Emby; //using Ombi.Api.Emby;
using Ombi.Api.Plex; //using Ombi.Api.Plex;
using Ombi.Core.Authentication; //using Ombi.Core.Authentication;
using Ombi.Core.Settings; //using Ombi.Core.Settings;
using Ombi.Core.Settings.Models.External; //using Ombi.Core.Settings.Models.External;
using Ombi.Models.Identity; //using Ombi.Models.Identity;
using Ombi.Store.Context; //using Ombi.Store.Context;
using Ombi.Store.Entities; //using Ombi.Store.Entities;
using Ombi.Store.Repository; //using Ombi.Store.Repository;
namespace Ombi.Tests //namespace Ombi.Tests
{ //{
public class TestStartup // public class TestStartup
{ // {
public IServiceProvider ConfigureServices(IServiceCollection services) // public IServiceProvider ConfigureServices(IServiceCollection services)
{ // {
var _plexApi = new Mock<IPlexApi>(); // var _plexApi = new Mock<IPlexApi>();
var _embyApi = new Mock<IEmbyApi>(); // var _embyApi = new Mock<IEmbyApi>();
var _tokenSettings = new Mock<IOptions<TokenAuthentication>>(); // var _tokenSettings = new Mock<IOptions<TokenAuthentication>>();
var _embySettings = new Mock<ISettingsService<EmbySettings>>(); // var _embySettings = new Mock<ISettingsService<EmbySettings>>();
var _plexSettings = new Mock<ISettingsService<PlexSettings>>(); // var _plexSettings = new Mock<ISettingsService<PlexSettings>>();
var audit = new Mock<IAuditRepository>(); // var audit = new Mock<IAuditRepository>();
var tokenRepo = new Mock<ITokenRepository>(); // var tokenRepo = new Mock<ITokenRepository>();
services.AddEntityFrameworkInMemoryDatabase() // services.AddEntityFrameworkInMemoryDatabase()
.AddDbContext<OmbiContext>(); // .AddDbContext<OmbiContext>();
services.AddIdentity<OmbiUser, IdentityRole>() // services.AddIdentity<OmbiUser, IdentityRole>()
.AddEntityFrameworkStores<OmbiContext>().AddUserManager<OmbiUserManager>(); // .AddEntityFrameworkStores<OmbiContext>().AddUserManager<OmbiUserManager>();
services.AddTransient(x => _plexApi.Object); // services.AddTransient(x => _plexApi.Object);
services.AddTransient(x => _embyApi.Object); // services.AddTransient(x => _embyApi.Object);
services.AddTransient(x => _tokenSettings.Object); // services.AddTransient(x => _tokenSettings.Object);
services.AddTransient(x => _embySettings.Object); // services.AddTransient(x => _embySettings.Object);
services.AddTransient(x => _plexSettings.Object); // services.AddTransient(x => _plexSettings.Object);
services.AddTransient(x => audit.Object); // services.AddTransient(x => audit.Object);
services.AddTransient(x => tokenRepo.Object); // services.AddTransient(x => tokenRepo.Object);
// Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified) // // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified)
var context = new DefaultHttpContext(); // var context = new DefaultHttpContext();
context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature()); // context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());
services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context }); // services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context });
services.Configure<IdentityOptions>(options => // services.Configure<IdentityOptions>(options =>
{ // {
options.Password.RequireDigit = false; // options.Password.RequireDigit = false;
options.Password.RequiredLength = 1; // options.Password.RequiredLength = 1;
options.Password.RequireLowercase = false; // options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false; // options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false; // options.Password.RequireUppercase = false;
options.User.AllowedUserNameCharacters = string.Empty; // options.User.AllowedUserNameCharacters = string.Empty;
}); // });
return services.BuildServiceProvider(); // return services.BuildServiceProvider();
} // }
public void Configure(IApplicationBuilder app, IHostingEnvironment env) // public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ // {
} // }
} // }
} //}

@ -87,6 +87,7 @@ export interface IBaseRequest {
requestedUser: IUser; requestedUser: IUser;
canApprove: boolean; canApprove: boolean;
title: string; title: string;
requestedByAlias: string;
} }
export interface ITvRequests { export interface ITvRequests {

@ -1,36 +1,39 @@
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" (keyup)="search($event)"> <input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search"
(keyup)="search($event)">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay"> <button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }} <i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
</button> </button>
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" <button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
aria-expanded="true"> aria-haspopup="true" aria-expanded="true">
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }} <i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2"> <ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
<li> <li>
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }} <a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
translate }}
</a> </a>
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{ 'Requests.SortRequestDateDesc' | translate }} <a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{
'Requests.SortRequestDateDesc' | translate }}
</a> </a>
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}} <a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
</a> </a>
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}} <a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
</a> </a>
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}} <a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
</a> </a>
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}} <a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
</a> </a>
</li> </li>
@ -58,16 +61,20 @@
<div class="col-sm-5 small-padding"> <div class="col-sm-5 small-padding">
<div> <div>
<a href="http://www.imdb.com/title/{{request.imdbId}}/" target="_blank"> <a href="http://www.imdb.com/title/{{request.imdbId}}/" target="_blank">
<h4 class="request-title">{{request.title}} ({{request.releaseDate | amLocal | amDateFormat: 'YYYY'}})</h4> <h4 class="request-title">{{request.title}} ({{request.releaseDate | amLocal | amDateFormat:
'YYYY'}})</h4>
</a> </a>
</div> </div>
<br /> <br />
<div class="request-info"> <div class="request-info">
<div class="request-by"> <div class="request-by">
<span>{{ 'Requests.RequestedBy' | translate }} </span> <span>{{ 'Requests.RequestedBy' | translate }} </span>
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span> <span *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span> <span *ngIf="!request.requestedByAlias">
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span> <span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
</span>
</div> </div>
<div class="request-status"> <div class="request-status">
<span>{{ 'Requests.Status' | translate }} </span> <span>{{ 'Requests.Status' | translate }} </span>
@ -77,13 +84,14 @@
<div class="requested-status"> <div class="requested-status">
<span>{{ 'Requests.RequestStatus' | translate }} </span> <span>{{ 'Requests.RequestStatus' | translate }} </span>
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span> <span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info" [translate]="'Common.ProcessingRequest'"></span> <span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info"
[translate]="'Common.ProcessingRequest'"></span>
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span> <span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}"> <span *ngIf="request.deniedReason" title="{{request.deniedReason}}">
<i class="fa fa-info-circle"></i> <i class="fa fa-info-circle"></i>
</span> </span>
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel" class="label label-warning" <span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
[translate]="'Common.PendingApproval'"></span> class="label label-warning" [translate]="'Common.PendingApproval'"></span>
</div> </div>
<div *ngIf="request.denied" id="requestDenied"> <div *ngIf="request.denied" id="requestDenied">
@ -93,16 +101,21 @@
</div> </div>
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}</div> <div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate |
<div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">{{ 'Requests.DigitalRelease' | translate: {date: request.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }}</div> amLocal | amDateFormat: 'LL'} }}</div>
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}</div> <div *ngIf="request.digitalReleaseDate" id="digitalReleaseDate">{{ 'Requests.DigitalRelease' |
translate: {date: request.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }}</div>
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
| amDateFormat: 'LL'}}</div>
<br /> <br />
</div> </div>
<div *ngIf="isAdmin"> <div *ngIf="isAdmin">
<div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' | translate }} <div *ngIf="request.qualityOverrideTitle" class="quality-override">{{ 'Requests.QualityOverride' |
translate }}
<span>{{request.qualityOverrideTitle}} </span> <span>{{request.qualityOverrideTitle}} </span>
</div> </div>
<div *ngIf="request.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' | translate }} <div *ngIf="request.rootPathOverrideTitle" class="root-override">{{ 'Requests.RootFolderOverride' |
translate }}
<span>{{request.rootPathOverrideTitle}} </span> <span>{{request.rootPathOverrideTitle}} </span>
</div> </div>
</div> </div>
@ -112,10 +125,12 @@
<div class="row"> <div class="row">
<div class="col-md-2 col-md-push-6"> <div class="col-md-2 col-md-push-6">
<a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)" pTooltip="Subscribe for notifications"> <a *ngIf="request.showSubscribe && !request.subscribed" style="color:white" (click)="subscribe(request)"
pTooltip="Subscribe for notifications">
<i class="fa fa-rss"></i> <i class="fa fa-rss"></i>
</a> </a>
<a *ngIf="request.showSubscribe && request.subscribed" style="color:red" (click)="unSubscribe(request)" pTooltip="Unsubscribe notification"> <a *ngIf="request.showSubscribe && request.subscribed" style="color:red" (click)="unSubscribe(request)"
pTooltip="Unsubscribe notification">
<i class="fa fa-rss"></i> <i class="fa fa-rss"></i>
</a> </a>
</div> </div>
@ -123,7 +138,8 @@
<div *ngIf="isAdmin"> <div *ngIf="isAdmin">
<div *ngIf="!request.approved" id="approveBtn"> <div *ngIf="!request.approved" id="approveBtn">
<form> <form>
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"> <button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve"
type="submit">
<i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }} <i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}
</button> </button>
</form> </form>
@ -133,7 +149,8 @@
<button type="button" class="btn btn-sm btn-warning-outline"> <button type="button" class="btn btn-sm btn-warning-outline">
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
</button> </button>
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<span class="caret"></span> <span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span> <span class="sr-only">Toggle Dropdown</span>
</button> </button>
@ -149,7 +166,8 @@
<button type="button" class="btn btn-sm btn-warning-outline"> <button type="button" class="btn btn-sm btn-warning-outline">
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
</button> </button>
<button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-warning-outline dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<span class="caret"></span> <span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span> <span class="sr-only">Toggle Dropdown</span>
</button> </button>
@ -166,15 +184,15 @@
</button> </button>
</div> </div>
</div> </div>
<form id="markBtnGroup"> <form id="markBtnGroup">
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right" <button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)"
value="false" class="btn btn-sm btn-info-outline change"> style="text-align: right" value="false" class="btn btn-sm btn-info-outline change">
<i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }} <i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
</button> </button>
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)" style="text-align: right" <button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)"
value="true" class="btn btn-sm btn-success-outline change"> style="text-align: right" value="true" class="btn btn-sm btn-success-outline change">
<i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
</button> </button>
</form> </form>
@ -190,8 +208,8 @@
</form> </form>
</div> </div>
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn"> <div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" <button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
aria-expanded="true"> aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
<span class="caret"></span> <span class="caret"></span>
</button> </button>
@ -204,8 +222,8 @@
</div> </div>
</div> </div>
<br/> <br />
<br/> <br />
@ -216,11 +234,11 @@
</div> </div>
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false"> <p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
<span>Please enter a rejection reason, the user will be notified of this:</span> <span>Please enter a rejection reason, the user will be notified of this:</span>
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea> <textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
<p-footer> <p-footer>
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button> <button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button> <button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
</p-footer> </p-footer>
</p-dialog> </p-dialog>

@ -1,36 +1,39 @@
<div class="form-group"> <div class="form-group">
<div class="input-group"> <div class="input-group">
<input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search" (keyup)="search($event)"> <input type="text" id="search" class="form-control form-control-custom searchwidth" placeholder="Search"
(keyup)="search($event)">
<span class="input-group-btn"> <span class="input-group-btn">
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay"> <button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }} <i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
</button> </button>
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" <button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
aria-expanded="true"> aria-haspopup="true" aria-expanded="true">
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }} <i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2"> <ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
<li> <li>
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }} <a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
translate }}
</a> </a>
<a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{ 'Requests.SortRequestDateDesc' | translate }} <a class="active" (click)="setOrder(OrderType.RequestedDateDesc, $event)">{{
'Requests.SortRequestDateDesc' | translate }}
</a> </a>
<a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}} <a (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
</a> </a>
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}} <a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
</a> </a>
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}} <a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
</a> </a>
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}} <a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
</a> </a>
</li> </li>
@ -45,7 +48,7 @@
<div class="col-md-12"> <div class="col-md-12">
<div *ngFor="let request of albumRequests" class="col-md-4"> <div *ngFor="let request of albumRequests" class="col-md-4">
<div class="row"> <div class="row">
<div class="album-bg backdrop" [style.background-image]="request.background"></div> <div class="album-bg backdrop" [style.background-image]="request.background"></div>
<div class="album-tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div> <div class="album-tint" style="background-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%,rgba(0,0,0,0.6) 100%);"></div>
@ -53,15 +56,15 @@
<div class="col-sm-12 small-padding"> <div class="col-sm-12 small-padding">
<img *ngIf="request.disk" class="img-responsive poster album-cover" src="{{request.disk}}" alt="poster"> <img *ngIf="request.disk" class="img-responsive poster album-cover" src="{{request.disk}}" alt="poster">
</div> </div>
<div class="col-sm-12 small-padding"> <div class="col-sm-12 small-padding">
<div> <div>
<h4> <h4>
<a href="" target="_blank"> <a href="" target="_blank">
{{request.title | truncate: 36}} {{request.title | truncate: 36}}
</a> </a>
</h4> </h4>
<h5> <h5>
<a href=""> <a href="">
@ -69,25 +72,29 @@
</a> </a>
</h5> </h5>
</div> </div>
<div class="request-info"> <div class="request-info">
<div class="request-by"> <div class="request-by">
<span>{{ 'Requests.RequestedBy' | translate }} </span> <span>{{ 'Requests.RequestedBy' | translate }} </span>
<span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span> <span *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span> <span *ngIf="!request.requestedByAlias">
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span> <span *ngIf="!isAdmin">{{request.requestedUser.userName}}</span>
<span *ngIf="isAdmin && request.requestedUser.alias">{{request.requestedUser.alias}}</span>
<span *ngIf="isAdmin && !request.requestedUser.alias">{{request.requestedUser.userName}}</span>
</span>
</div> </div>
<div class="requested-status"> <div class="requested-status">
<span>{{ 'Requests.RequestStatus' | translate }} </span> <span>{{ 'Requests.RequestStatus' | translate }} </span>
<span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span> <span *ngIf="request.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
<span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info" [translate]="'Common.ProcessingRequest'"></span> <span *ngIf="request.approved && !request.available" id="processingRequestLabel" class="label label-info"
[translate]="'Common.ProcessingRequest'"></span>
<span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span> <span *ngIf="request.denied" class="label label-danger" id="requestDeclinedLabel" [translate]="'Common.RequestDenied'"></span>
<span *ngIf="request.deniedReason" title="{{request.deniedReason}}"> <span *ngIf="request.deniedReason" title="{{request.deniedReason}}">
<i class="fa fa-info-circle"></i> <i class="fa fa-info-circle"></i>
</span> </span>
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel" class="label label-warning" <span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
[translate]="'Common.PendingApproval'"></span> class="label label-warning" [translate]="'Common.PendingApproval'"></span>
</div> </div>
<div *ngIf="request.denied" id="requestDenied"> <div *ngIf="request.denied" id="requestDenied">
@ -97,8 +104,10 @@
</div> </div>
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}</div> <div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal |
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}</div> amDateFormat: 'LL'} }}</div>
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
| amDateFormat: 'LL'}}</div>
<br /> <br />
</div> </div>
<!-- <div *ngIf="isAdmin"> <!-- <div *ngIf="isAdmin">
@ -125,8 +134,9 @@
</div> --> </div> -->
<div *ngIf="isAdmin"> <div *ngIf="isAdmin">
<div *ngIf="!request.approved" id="approveBtn"> <div *ngIf="!request.approved" id="approveBtn">
<form class="col-md-6"> <form class="col-md-6">
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit"> <button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve"
type="submit">
<i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }} <i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}
</button> </button>
</form> </form>
@ -169,15 +179,15 @@
</button> </button>
</div> </div>
</div> </div>
<form id="markBtnGroup"> <form id="markBtnGroup">
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right" <button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)"
value="false" class="btn btn-sm btn-info-outline change"> style="text-align: right" value="false" class="btn btn-sm btn-info-outline change">
<i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }} <i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}
</button> </button>
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)" style="text-align: right" <button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, true)"
value="true" class="btn btn-sm btn-success-outline change"> style="text-align: right" value="true" class="btn btn-sm btn-success-outline change">
<i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}
</button> </button>
</form> </form>
@ -193,8 +203,8 @@
</form> </form>
</div> </div>
<div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn"> <div class="dropdown" *ngIf="issueCategories && issuesEnabled" id="issuesBtn">
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" <button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
aria-expanded="true"> aria-haspopup="true" aria-expanded="true">
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }} <i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
<span class="caret"></span> <span class="caret"></span>
</button> </button>
@ -207,8 +217,8 @@
</div> </div>
</div> </div>
<br/> <br />
<br/> <br />
@ -272,8 +282,8 @@
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false"> <p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
<span>Please enter a rejection reason, the user will be notified of this:</span> <span>Please enter a rejection reason, the user will be notified of this:</span>
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea> <textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
<p-footer> <p-footer>
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button> <button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button> <button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
</p-footer> </p-footer>
</p-dialog> </p-dialog>

@ -5,31 +5,43 @@
<div class="col-md-2"> <div class="col-md-2">
<span [translate]="'Requests.RequestedBy'"></span> <span [translate]="'Requests.RequestedBy'"></span>
<span *ngIf="child.requestedByAlias">{{child.requestedByAlias}}</span>
<span *ngIf="!isAdmin">{{child.requestedUser.userName}}</span> <span *ngIf="!child.requestedByAlias">
<span *ngIf="isAdmin && child.requestedUser.alias">{{child.requestedUser.alias}}</span> <span *ngIf="!isAdmin">{{child.requestedUser.userName}}</span>
<span *ngIf="isAdmin && !child.requestedUser.alias">{{child.requestedUser.userName}}</span> <span *ngIf="isAdmin && child.requestedUser.alias">{{child.requestedUser.alias}}</span>
<span *ngIf="isAdmin && !child.requestedUser.alias">{{child.requestedUser.userName}}</span>
</span>
</div> </div>
<div class="col-md-1 col-md-push-9"> <div class="col-md-1 col-md-push-9">
<button id="subscribeBtn" *ngIf="child.showSubscribe && !child.subscribed" (click)="subscribe(child)" class="btn btn-sm btn-primary-outline" pTooltip="Subscribe for notifications" type="submit"><i class="fa fa-rss"></i> Subscribe</button> <button id="subscribeBtn" *ngIf="child.showSubscribe && !child.subscribed" (click)="subscribe(child)"
<button id="subscribeBtn" *ngIf="child.showSubscribe && child.subscribed" (click)="unSubscribe(child)" class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i class="fa fa-rss"></i> UnSubscribe</button> class="btn btn-sm btn-primary-outline" pTooltip="Subscribe for notifications" type="submit"><i
class="fa fa-rss"></i> Subscribe</button>
<button id="subscribeBtn" *ngIf="child.showSubscribe && child.subscribed" (click)="unSubscribe(child)"
<div *ngIf="isAdmin"> class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i
<button id="approveBtn" *ngIf="child.canApprove && !child.approved" (click)="approve(child)" class="btn btn-sm btn-success-outline" type="submit"><i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}</button> class="fa fa-rss"></i> UnSubscribe</button>
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)" style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i> {{ 'Requests.MarkUnavailable' | translate }}</button>
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right" value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> {{ 'Requests.MarkAvailable' | translate }}</button>
<div *ngIf="isAdmin">
<button id="approveBtn" *ngIf="child.canApprove && !child.approved" (click)="approve(child)" class="btn btn-sm btn-success-outline"
type="submit"><i class="fa fa-plus"></i> {{ 'Common.Approve' | translate }}</button>
<button id="unavailableBtn" *ngIf="child.available" (click)="changeAvailability(child, false)"
style="text-align: right" value="false" class="btn btn-sm btn-info-outline change"><i class="fa fa-minus"></i>
{{ 'Requests.MarkUnavailable' | translate }}</button>
<button id="availableBtn" *ngIf="!child.available" (click)="changeAvailability(child, true)" style="text-align: right"
value="true" class="btn btn-sm btn-success-outline change"><i class="fa fa-plus"></i> {{
'Requests.MarkAvailable' | translate }}</button>
<button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny"> <button id="denyBtn" *ngIf="!child.denied" type="button" (click)="deny(child)" class="btn btn-sm btn-danger-outline deny">
<i class="fa fa-times"></i> {{ 'Requests.Deny' | translate }}</button> <i class="fa fa-times"></i> {{ 'Requests.Deny' | translate }}</button>
</div>
<div *ngIf="isAdmin || isRequestUser(child)">
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny"><i
class="fa fa-times"></i> {{ 'Requests.Remove' | translate }}</button>
</div> </div>
<div *ngIf="isAdmin || isRequestUser(child)">
<button id="removeBtn" type="button" (click)="removeRequest(child)" class="btn btn-sm btn-danger-outline deny"><i class="fa fa-times"></i> {{ 'Requests.Remove' | translate }}</button>
</div>
</div> </div>
</div> </div>
<div class="col-md-12"> <div class="col-md-12">
@ -77,15 +89,19 @@
{{ep.airDate | amLocal | amDateFormat: 'L' }} {{ep.airDate | amLocal | amDateFormat: 'L' }}
</td> </td>
<td> <td>
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel" [translate]="'Common.Denied'"> <span *ngIf="child.denied" class="label label-danger" id="deniedLabel"
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i> [translate]="'Common.Denied'">
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i>
</span> </span>
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span> <span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel"
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info" id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span> [translate]="'Common.Available'"></span>
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info"
id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
<div *ngIf="!child.denied && !ep.approved"> <div *ngIf="!child.denied && !ep.approved">
<div *ngIf="!ep.available"><span class="label label-warning" id="pendingApprovalLabel" [translate]="'Common.PendingApproval'"></span></div> <div *ngIf="!ep.available"><span class="label label-warning" id="pendingApprovalLabel"
[translate]="'Common.PendingApproval'"></span></div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -105,8 +121,8 @@
<p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false"> <p-dialog *ngIf="requestToDeny" header="Deny Request '{{requestToDeny.title}}''" [(visible)]="denyDisplay" [draggable]="false">
<span>Please enter a rejection reason, the user will be notified of this:</span> <span>Please enter a rejection reason, the user will be notified of this:</span>
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea> <textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
<p-footer> <p-footer>
<button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button> <button type="button" (click)="denyRequest();" label="Reject" class="btn btn-success">Deny</button>
<button type="button"(click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button> <button type="button" (click)="denyDisplay=false" label="Close" class="btn btn-danger">Close</button>
</p-footer> </p-footer>
</p-dialog> </p-dialog>

@ -1,66 +0,0 @@
<div class="modal-header">
<h3>Add A Friend!</h3>
</div>
<div class="modal-body">
<p>You can invite a user to share your Plex Library here. The invited user will be asked to confirm friendship.</p>
<p>Please note that this user will not appear in your Ombi Users since they have not accepted the Plex Invite, as soon as they accept
the Plex invite then the User Importer job will run (if enabled) and add the user into Ombi.
</p>
<div *ngIf="plexServers">
<form novalidate [formGroup]="form" (ngSubmit)="onSubmit(form)" style="padding-top:5%;">
<div class="form-group">
<label for="username" class="control-label">Username/Email</label>
<input type="text" class="form-control form-control-custom " id="username" name="username" p formControlName="username" [ngClass]="{'form-error': form.get('username').hasError('required')}">
<small *ngIf="form.get('username').hasError('required')" class="error-text">The Username/Email is required</small>
</div>
<div class="form-group">
<label for="select" class="control-label">Select a Server</label>
<div id="profiles">
<select formControlName="selectedServer" (change)="selected()" class="form-control form-control-custom" id="select" [ngClass]="{'form-error': form.get('selectedServer').hasError('required')}">
<option *ngFor="let server of plexServers" value="{{server.machineId}}">{{server.serverName}}</option>
</select>
</div>
<small *ngIf="form.get('selectedServer').hasError('required')" class="error-text">You need to select a server!</small>
</div>
<div *ngIf="plexLibs" class="form-group">
<label for="select" class="control-label">Libraries to share</label>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="selectAll" formControlName="allLibsSelected">
<label for="selectAll">All</label>
</div>
</div>
<div *ngIf="!form.value.allLibsSelected">
<div *ngFor="let lib of plexLibs">
<div class="col-md-4">
<div class="checkbox">
<input type="checkbox" id="{{lib.id}}" value={{lib.id}} (change)="checkedLib($event.target.checked, $event.target.value)">
<label for="{{lib.id}}">{{lib.title}}</label>
</div>
</div>
</div>
</div>
</div>
<br>
<br>
<br>
</form>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary-outline" (click)="onSubmit(form)" [disabled]="form.invalid">Add</button>
<button type="button" class="btn btn-danger-outline" (click)="activeModal.close('Close click')">Close</button>
</div>

@ -1,84 +0,0 @@
import { Component, Input, OnInit } from "@angular/core";
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
import { NgbActiveModal } from "@ng-bootstrap/ng-bootstrap";
import { NotificationService, PlexService } from "../services";
import { IPlexSection, IPlexServersAdd } from "../interfaces";
@Component({
selector: "ngbd-modal-content",
templateUrl: "./addplexuser.component.html",
})
export class AddPlexUserComponent implements OnInit {
@Input() public name: string;
public plexServers: IPlexServersAdd[];
public plexLibs: IPlexSection[];
public libsSelected: number[] = [];
public form: FormGroup;
constructor(public activeModal: NgbActiveModal,
private plexService: PlexService,
private notificationService: NotificationService,
private fb: FormBuilder) {
}
public ngOnInit(): void {
this.form = this.fb.group({
selectedServer: [null, Validators.required],
allLibsSelected: [true],
username:[null, Validators.required],
});
this.getServers();
}
public getServers() {
this.plexService.getServersFromSettings().subscribe(x => {
if (x.success) {
this.plexServers = x.servers;
}
});
}
public getPlexLibs(machineId: string) {
this.plexService.getLibrariesFromSettings(machineId).subscribe(x => {
if (x.successful) {
this.plexLibs = x.data;
}
});
}
public selected() {
this.getPlexLibs(this.form.value.selectedServer);
}
public checkedLib(checked: boolean, value: number) {
if(checked) {
this.libsSelected.push(value);
} else {
this.libsSelected = this.libsSelected.filter(v => v !== value);
}
}
public onSubmit(form: FormGroup) {
if (form.invalid) {
this.notificationService.error("Please check your entered values");
return;
}
const libs = form.value.allLibsSelected ? [] : this.libsSelected;
this.plexService.addUserToServer({ username: form.value.username, machineIdentifier: form.value.selectedServer, libsSelected: libs }).subscribe(x => {
if (x.success) {
this.notificationService.success("User added to Plex");
} else {
this.notificationService.error(x.error);
}
this.activeModal.close();
});
}
}

@ -5,9 +5,7 @@
<button type="button" class="btn btn-success-outline" data-test="adduserbtn" [routerLink]="['/usermanagement/user']">Add User To Ombi</button> <button type="button" class="btn btn-success-outline" data-test="adduserbtn" [routerLink]="['/usermanagement/user']">Add User To Ombi</button>
<button type="button" style="float:right;" class="btn btn-primary-outline"(click)="showBulkEdit = !showBulkEdit" [disabled]="!hasChecked()">Bulk Edit</button> <button type="button" style="float:right;" class="btn btn-primary-outline"(click)="showBulkEdit = !showBulkEdit" [disabled]="!hasChecked()">Bulk Edit</button>
<div *ngIf="plexEnabled">
<button type="button" style="float:right;" class="btn btn-success-outline" (click)="open()">Add Plex Friend</button>
</div>
<!-- Table --> <!-- Table -->
<table class="table table-striped table-hover table-responsive table-condensed table-usermanagement"> <table class="table table-striped table-hover table-responsive table-condensed table-usermanagement">
<thead> <thead>

@ -1,9 +1,7 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces"; import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces";
import { IdentityService, NotificationService, SettingsService } from "../services"; import { IdentityService, NotificationService, SettingsService } from "../services";
import { AddPlexUserComponent } from "./addplexuser.component";
@Component({ @Component({
templateUrl: "./usermanagement.component.html", templateUrl: "./usermanagement.component.html",
@ -27,8 +25,7 @@ export class UserManagementComponent implements OnInit {
constructor(private identityService: IdentityService, constructor(private identityService: IdentityService,
private settingsService: SettingsService, private settingsService: SettingsService,
private notificationService: NotificationService, private notificationService: NotificationService,
private plexSettings: SettingsService, private plexSettings: SettingsService) { }
private modalService: NgbModal) { }
public ngOnInit() { public ngOnInit() {
this.users = []; this.users = [];
@ -43,11 +40,6 @@ export class UserManagementComponent implements OnInit {
this.settingsService.getEmailNotificationSettings().subscribe(x => this.emailSettings = x); this.settingsService.getEmailNotificationSettings().subscribe(x => this.emailSettings = x);
} }
public open() {
const modalRef = this.modalService.open(AddPlexUserComponent, {container:"ombi", backdropClass:"custom-modal-backdrop", windowClass:"window"});
modalRef.componentInstance.name = "World";
}
public welcomeEmail(user: IUser) { public welcomeEmail(user: IUser) {
if (!user.emailAddress) { if (!user.emailAddress) {
this.notificationService.error("The user needs an email address."); this.notificationService.error("The user needs an email address.");

@ -16,7 +16,6 @@ import { IdentityService, PlexService, RadarrService, SonarrService } from "../s
import { AuthGuard } from "../auth/auth.guard"; import { AuthGuard } from "../auth/auth.guard";
import { OrderModule } from "ngx-order-pipe"; import { OrderModule } from "ngx-order-pipe";
import { AddPlexUserComponent } from "./addplexuser.component";
import { SharedModule } from "../shared/shared.module"; import { SharedModule } from "../shared/shared.module";
@ -45,12 +44,8 @@ const routes: Routes = [
declarations: [ declarations: [
UserManagementComponent, UserManagementComponent,
UpdateDetailsComponent, UpdateDetailsComponent,
AddPlexUserComponent,
UserManagementUserComponent, UserManagementUserComponent,
], ],
entryComponents:[
AddPlexUserComponent,
],
exports: [ exports: [
RouterModule, RouterModule,
], ],

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization; using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Ombi.Core.Engine; using Ombi.Core.Engine;
using Ombi.Core.Models.Requests; using Ombi.Core.Models.Requests;
@ -11,6 +12,7 @@ using Ombi.Core.Models;
using Ombi.Core.Models.UI; using Ombi.Core.Models.UI;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using ILogger = Microsoft.Extensions.Logging.ILogger; using ILogger = Microsoft.Extensions.Logging.ILogger;
using System.Linq;
namespace Ombi.Controllers namespace Ombi.Controllers
{ {
@ -76,6 +78,7 @@ namespace Ombi.Controllers
[HttpPost] [HttpPost]
public async Task<RequestEngineResult> RequestAlbum([FromBody] MusicAlbumRequestViewModel album) public async Task<RequestEngineResult> RequestAlbum([FromBody] MusicAlbumRequestViewModel album)
{ {
album.RequestedByAlias = GetApiAlias();
var result = await _engine.RequestAlbum(album); var result = await _engine.RequestAlbum(album);
if (result.Result) if (result.Result)
{ {
@ -168,5 +171,17 @@ namespace Ombi.Controllers
{ {
return await _engine.GetRemainingRequests(); return await _engine.GetRemainingRequests();
} }
private string GetApiAlias()
{
// Make sure this only applies when using the API KEY
if (HttpContext.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
{
if (HttpContext.Request.Headers.TryGetValue("ApiAlias", out var apiAlias))
{
return apiAlias;
}
}
return null;
}
} }
} }

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization; using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Ombi.Core.Engine; using Ombi.Core.Engine;
using Ombi.Core.Engine.Interfaces; using Ombi.Core.Engine.Interfaces;
@ -8,12 +9,14 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Ombi.Store.Entities.Requests; using Ombi.Store.Entities.Requests;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Ombi.Attributes; using Ombi.Attributes;
using Ombi.Core.Models.UI; using Ombi.Core.Models.UI;
using Ombi.Models; using Ombi.Models;
using Ombi.Store.Entities; using Ombi.Store.Entities;
using Ombi.Core.Models; using Ombi.Core.Models;
using Ombi.Helpers;
namespace Ombi.Controllers namespace Ombi.Controllers
{ {
@ -82,6 +85,7 @@ namespace Ombi.Controllers
[HttpPost("movie")] [HttpPost("movie")]
public async Task<RequestEngineResult> RequestMovie([FromBody] MovieRequestViewModel movie) public async Task<RequestEngineResult> RequestMovie([FromBody] MovieRequestViewModel movie)
{ {
movie.RequestedByAlias = GetApiAlias();
var result = await MovieRequestEngine.RequestMovie(movie); var result = await MovieRequestEngine.RequestMovie(movie);
if (result.Result) if (result.Result)
{ {
@ -277,6 +281,7 @@ namespace Ombi.Controllers
[HttpPost("tv")] [HttpPost("tv")]
public async Task<RequestEngineResult> RequestTv([FromBody] TvRequestViewModel tv) public async Task<RequestEngineResult> RequestTv([FromBody] TvRequestViewModel tv)
{ {
tv.RequestedByAlias = GetApiAlias();
var result = await TvRequestEngine.RequestTvShow(tv); var result = await TvRequestEngine.RequestTvShow(tv);
if (result.Result) if (result.Result)
{ {
@ -521,5 +526,19 @@ namespace Ombi.Controllers
{ {
return await TvRequestEngine.GetRemainingRequests(); return await TvRequestEngine.GetRemainingRequests();
} }
private string GetApiAlias()
{
// Make sure this only applies when using the API KEY
if (HttpContext.Request.Headers.Keys.Contains("ApiKey", StringComparer.InvariantCultureIgnoreCase))
{
if (HttpContext.Request.Headers.TryGetValue("ApiAlias", out var apiAlias))
{
return apiAlias;
}
}
return null;
}
} }
} }
Loading…
Cancel
Save