mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-08-14 02:26:55 -07:00
Merge branch 'develop' of https://github.com/tidusjar/ombi into develop
This commit is contained in:
commit
732873acda
23 changed files with 1531 additions and 342 deletions
|
@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
|
|||
Approved = false,
|
||||
RequestedUserId = userDetails.Id,
|
||||
Background = movieInfo.BackdropPath,
|
||||
LangCode = model.LanguageCode
|
||||
LangCode = model.LanguageCode,
|
||||
RequestedByAlias = model.RequestedByAlias
|
||||
};
|
||||
|
||||
var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US");
|
||||
|
|
|
@ -83,7 +83,8 @@ namespace Ombi.Core.Engine
|
|||
Title = album.title,
|
||||
Disk = album.images?.FirstOrDefault(x => x.coverType.Equals("disc"))?.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())
|
||||
{
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace Ombi.Core.Helpers
|
|||
SeasonRequests = new List<SeasonRequests>(),
|
||||
Title = ShowInfo.name,
|
||||
ReleaseYear = FirstAir,
|
||||
RequestedByAlias = model.RequestedByAlias,
|
||||
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.
|
||||
// ************************************************************************/
|
||||
#endregion
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
public class MovieRequestViewModel
|
||||
{
|
||||
public int TheMovieDbId { get; set; }
|
||||
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 string ForeignAlbumId { get; set; }
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Ombi.Core.Models.Requests
|
||||
{
|
||||
|
@ -9,6 +10,8 @@ namespace Ombi.Core.Models.Requests
|
|||
public bool FirstSeason { get; set; }
|
||||
public int TvDbId { get; set; }
|
||||
public List<SeasonsViewModel> Seasons { get; set; } = new List<SeasonsViewModel>();
|
||||
[JsonIgnore]
|
||||
public string RequestedByAlias { get; set; }
|
||||
}
|
||||
|
||||
public class SeasonsViewModel
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace Ombi.Store.Entities
|
|||
public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue();
|
||||
|
||||
[NotMapped]
|
||||
public string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
||||
public virtual string UserAlias => string.IsNullOrEmpty(Alias) ? UserName : Alias;
|
||||
|
||||
[NotMapped]
|
||||
public bool EmailLogin { get; set; }
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Ombi.Store.Entities.Requests
|
|||
public DateTime MarkedAsDenied { get; set; }
|
||||
public string DeniedReason { get; set; }
|
||||
public RequestType RequestType { get; set; }
|
||||
public string RequestedByAlias { get; set; }
|
||||
|
||||
[ForeignKey(nameof(RequestedUserId))]
|
||||
public OmbiUser RequestedUser { get; set; }
|
||||
|
|
1212
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs
generated
Normal file
1212
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
40
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs
Normal file
40
src/Ombi.Store/Migrations/20190116212601_RequestedByAlias.cs
Normal file
|
@ -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
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
|
||||
.HasAnnotation("ProductVersion", "2.2.1-servicing-10028");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
|
@ -583,6 +583,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("RequestedByAlias");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
@ -621,6 +623,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("RequestedByAlias");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
@ -749,6 +753,8 @@ namespace Ombi.Store.Migrations
|
|||
|
||||
b.Property<int>("RequestType");
|
||||
|
||||
b.Property<string>("RequestedByAlias");
|
||||
|
||||
b.Property<DateTime>("RequestedDate");
|
||||
|
||||
b.Property<string>("RequestedUserId");
|
||||
|
|
|
@ -1,71 +1,71 @@
|
|||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using Ombi.Api.Emby;
|
||||
using Ombi.Api.Plex;
|
||||
using Ombi.Core.Authentication;
|
||||
using Ombi.Core.Settings;
|
||||
using Ombi.Core.Settings.Models.External;
|
||||
using Ombi.Models.Identity;
|
||||
using Ombi.Store.Context;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Store.Repository;
|
||||
//using System;
|
||||
//using Microsoft.AspNetCore.Builder;
|
||||
//using Microsoft.AspNetCore.Hosting;
|
||||
//using Microsoft.AspNetCore.Http;
|
||||
//using Microsoft.AspNetCore.Http.Features.Authentication;
|
||||
//using Microsoft.AspNetCore.Identity;
|
||||
//using Microsoft.Extensions.DependencyInjection;
|
||||
//using Microsoft.Extensions.Options;
|
||||
//using Moq;
|
||||
//using Ombi.Api.Emby;
|
||||
//using Ombi.Api.Plex;
|
||||
//using Ombi.Core.Authentication;
|
||||
//using Ombi.Core.Settings;
|
||||
//using Ombi.Core.Settings.Models.External;
|
||||
//using Ombi.Models.Identity;
|
||||
//using Ombi.Store.Context;
|
||||
//using Ombi.Store.Entities;
|
||||
//using Ombi.Store.Repository;
|
||||
|
||||
namespace Ombi.Tests
|
||||
{
|
||||
public class TestStartup
|
||||
{
|
||||
public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
var _plexApi = new Mock<IPlexApi>();
|
||||
var _embyApi = new Mock<IEmbyApi>();
|
||||
var _tokenSettings = new Mock<IOptions<TokenAuthentication>>();
|
||||
var _embySettings = new Mock<ISettingsService<EmbySettings>>();
|
||||
var _plexSettings = new Mock<ISettingsService<PlexSettings>>();
|
||||
var audit = new Mock<IAuditRepository>();
|
||||
var tokenRepo = new Mock<ITokenRepository>();
|
||||
//namespace Ombi.Tests
|
||||
//{
|
||||
// public class TestStartup
|
||||
// {
|
||||
// public IServiceProvider ConfigureServices(IServiceCollection services)
|
||||
// {
|
||||
// var _plexApi = new Mock<IPlexApi>();
|
||||
// var _embyApi = new Mock<IEmbyApi>();
|
||||
// var _tokenSettings = new Mock<IOptions<TokenAuthentication>>();
|
||||
// var _embySettings = new Mock<ISettingsService<EmbySettings>>();
|
||||
// var _plexSettings = new Mock<ISettingsService<PlexSettings>>();
|
||||
// var audit = new Mock<IAuditRepository>();
|
||||
// var tokenRepo = new Mock<ITokenRepository>();
|
||||
|
||||
services.AddEntityFrameworkInMemoryDatabase()
|
||||
.AddDbContext<OmbiContext>();
|
||||
services.AddIdentity<OmbiUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<OmbiContext>().AddUserManager<OmbiUserManager>();
|
||||
// services.AddEntityFrameworkInMemoryDatabase()
|
||||
// .AddDbContext<OmbiContext>();
|
||||
// services.AddIdentity<OmbiUser, IdentityRole>()
|
||||
// .AddEntityFrameworkStores<OmbiContext>().AddUserManager<OmbiUserManager>();
|
||||
|
||||
services.AddTransient(x => _plexApi.Object);
|
||||
services.AddTransient(x => _embyApi.Object);
|
||||
services.AddTransient(x => _tokenSettings.Object);
|
||||
services.AddTransient(x => _embySettings.Object);
|
||||
services.AddTransient(x => _plexSettings.Object);
|
||||
services.AddTransient(x => audit.Object);
|
||||
services.AddTransient(x => tokenRepo.Object);
|
||||
// Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified)
|
||||
var context = new DefaultHttpContext();
|
||||
context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());
|
||||
services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context });
|
||||
// services.AddTransient(x => _plexApi.Object);
|
||||
// services.AddTransient(x => _embyApi.Object);
|
||||
// services.AddTransient(x => _tokenSettings.Object);
|
||||
// services.AddTransient(x => _embySettings.Object);
|
||||
// services.AddTransient(x => _plexSettings.Object);
|
||||
// services.AddTransient(x => audit.Object);
|
||||
// services.AddTransient(x => tokenRepo.Object);
|
||||
// // Taken from https://github.com/aspnet/MusicStore/blob/dev/test/MusicStore.Test/ManageControllerTest.cs (and modified)
|
||||
// var context = new DefaultHttpContext();
|
||||
// context.Features.Set<IHttpAuthenticationFeature>(new HttpAuthenticationFeature());
|
||||
// services.AddSingleton<IHttpContextAccessor>(h => new HttpContextAccessor { HttpContext = context });
|
||||
|
||||
|
||||
services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequiredLength = 1;
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.User.AllowedUserNameCharacters = string.Empty;
|
||||
});
|
||||
// services.Configure<IdentityOptions>(options =>
|
||||
// {
|
||||
// options.Password.RequireDigit = false;
|
||||
// options.Password.RequiredLength = 1;
|
||||
// options.Password.RequireLowercase = false;
|
||||
// options.Password.RequireNonAlphanumeric = false;
|
||||
// options.Password.RequireUppercase = false;
|
||||
// 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;
|
||||
canApprove: boolean;
|
||||
title: string;
|
||||
requestedByAlias: string;
|
||||
}
|
||||
|
||||
export interface ITvRequests {
|
||||
|
|
|
@ -1,36 +1,39 @@
|
|||
<div class="form-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">
|
||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
||||
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||
</button>
|
||||
|
||||
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||
<li>
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }}
|
||||
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
|
||||
translate }}
|
||||
|
||||
</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 (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
@ -58,16 +61,20 @@
|
|||
<div class="col-sm-5 small-padding">
|
||||
<div>
|
||||
<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>
|
||||
</div>
|
||||
<br />
|
||||
<div class="request-info">
|
||||
<div class="request-by">
|
||||
<span>{{ 'Requests.RequestedBy' | translate }} </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 *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
|
||||
<span *ngIf="!request.requestedByAlias">
|
||||
<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 class="request-status">
|
||||
<span>{{ 'Requests.Status' | translate }} </span>
|
||||
|
@ -77,13 +84,14 @@
|
|||
<div class="requested-status">
|
||||
<span>{{ 'Requests.RequestStatus' | translate }} </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.deniedReason" title="{{request.deniedReason}}">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel" class="label label-warning"
|
||||
[translate]="'Common.PendingApproval'"></span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
|
||||
class="label label-warning" [translate]="'Common.PendingApproval'"></span>
|
||||
|
||||
</div>
|
||||
<div *ngIf="request.denied" id="requestDenied">
|
||||
|
@ -93,16 +101,21 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate | 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>
|
||||
<div id="releaseDate">{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate |
|
||||
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 />
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -112,10 +125,12 @@
|
|||
<div class="row">
|
||||
<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>
|
||||
</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>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -123,7 +138,8 @@
|
|||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="!request.approved" id="approveBtn">
|
||||
<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 }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -133,7 +149,8 @@
|
|||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeRootFolder' | translate }}
|
||||
</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="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
@ -149,7 +166,8 @@
|
|||
<button type="button" class="btn btn-sm btn-warning-outline">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ChangeQualityProfile' | translate }}
|
||||
</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="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
|
@ -166,15 +184,15 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<form id="markBtnGroup">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right"
|
||||
value="false" class="btn btn-sm btn-info-outline change">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, 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="!request.available" (click)="changeAvailability(request, true)" style="text-align: right"
|
||||
value="true" class="btn btn-sm btn-success-outline change">
|
||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, 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>
|
||||
</form>
|
||||
|
@ -190,8 +208,8 @@
|
|||
</form>
|
||||
</div>
|
||||
<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"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
|
@ -204,8 +222,8 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
@ -216,11 +234,11 @@
|
|||
</div>
|
||||
|
||||
<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>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<span>Please enter a rejection reason, the user will be notified of this:</span>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<p-footer>
|
||||
<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-dialog>
|
||||
|
||||
|
|
|
@ -1,36 +1,39 @@
|
|||
<div class="form-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">
|
||||
<button id="filterBtn" class="btn btn-sm btn-info-outline" (click)="filterDisplay = !filterDisplay">
|
||||
<i class="fa fa-filter"></i> {{ 'Requests.Filter' | translate }}
|
||||
</button>
|
||||
|
||||
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-sort"></i> {{ 'Requests.Sort' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
|
||||
<li>
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' | translate }}
|
||||
|
||||
<a (click)="setOrder(OrderType.RequestedDateAsc, $event)">{{ 'Requests.SortRequestDateAsc' |
|
||||
translate }}
|
||||
|
||||
</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 (click)="setOrder(OrderType.TitleAsc, $event)">{{ 'Requests.SortTitleAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.TitleDesc, $event)">{{ 'Requests.SortTitleDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusAsc, $event)">{{ 'Requests.SortStatusAsc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
<a (click)="setOrder(OrderType.StatusDesc, $event)">{{ 'Requests.SortStatusDesc' | translate}}
|
||||
|
||||
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
@ -45,7 +48,7 @@
|
|||
|
||||
|
||||
<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="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>
|
||||
|
@ -53,15 +56,15 @@
|
|||
<div class="col-sm-12 small-padding">
|
||||
<img *ngIf="request.disk" class="img-responsive poster album-cover" src="{{request.disk}}" alt="poster">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="col-sm-12 small-padding">
|
||||
<div>
|
||||
<h4>
|
||||
<a href="" target="_blank">
|
||||
{{request.title | truncate: 36}}
|
||||
{{request.title | truncate: 36}}
|
||||
</a>
|
||||
|
||||
|
||||
</h4>
|
||||
<h5>
|
||||
<a href="">
|
||||
|
@ -69,25 +72,29 @@
|
|||
</a>
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="request-info">
|
||||
<div class="request-by">
|
||||
<span>{{ 'Requests.RequestedBy' | translate }} </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 *ngIf="request.requestedByAlias">{{request.requestedByAlias}}</span>
|
||||
<span *ngIf="!request.requestedByAlias">
|
||||
<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 class="requested-status">
|
||||
<span>{{ 'Requests.RequestStatus' | translate }} </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.deniedReason" title="{{request.deniedReason}}">
|
||||
<i class="fa fa-info-circle"></i>
|
||||
</span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel" class="label label-warning"
|
||||
[translate]="'Common.PendingApproval'"></span>
|
||||
<span *ngIf="!request.approved && !request.availble && !request.denied" id="pendingApprovalLabel"
|
||||
class="label label-warning" [translate]="'Common.PendingApproval'"></span>
|
||||
|
||||
</div>
|
||||
<div *ngIf="request.denied" id="requestDenied">
|
||||
|
@ -97,8 +104,10 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}</div>
|
||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}</div>
|
||||
<div id="releaseDate">{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal |
|
||||
amDateFormat: 'LL'} }}</div>
|
||||
<div id="requestedDate">{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal
|
||||
| amDateFormat: 'LL'}}</div>
|
||||
<br />
|
||||
</div>
|
||||
<!-- <div *ngIf="isAdmin">
|
||||
|
@ -125,8 +134,9 @@
|
|||
</div> -->
|
||||
<div *ngIf="isAdmin">
|
||||
<div *ngIf="!request.approved" id="approveBtn">
|
||||
<form class="col-md-6">
|
||||
<button (click)="approve(request)" style="text-align: right" class="btn btn-sm btn-success-outline approve" type="submit">
|
||||
<form class="col-md-6">
|
||||
<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 }}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -169,15 +179,15 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<form id="markBtnGroup">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, false)" style="text-align: right"
|
||||
value="false" class="btn btn-sm btn-info-outline change">
|
||||
<button id="unavailableBtn" *ngIf="request.available" (click)="changeAvailability(request, 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="!request.available" (click)="changeAvailability(request, true)" style="text-align: right"
|
||||
value="true" class="btn btn-sm btn-success-outline change">
|
||||
<button id="availableBtn" *ngIf="!request.available" (click)="changeAvailability(request, 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>
|
||||
</form>
|
||||
|
@ -193,8 +203,8 @@
|
|||
</form>
|
||||
</div>
|
||||
<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"
|
||||
aria-expanded="true">
|
||||
<button class="btn btn-sm btn-primary-outline dropdown-toggle" type="button" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<i class="fa fa-plus"></i> {{ 'Requests.ReportIssue' | translate }}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
|
@ -207,8 +217,8 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<br />
|
||||
<br />
|
||||
|
||||
|
||||
|
||||
|
@ -272,8 +282,8 @@
|
|||
<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>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<p-footer>
|
||||
<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>
|
||||
</p-footer>
|
||||
</p-dialog>
|
||||
<p-footer>
|
||||
<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>
|
||||
</p-footer>
|
||||
</p-dialog>
|
|
@ -5,31 +5,43 @@
|
|||
|
||||
<div class="col-md-2">
|
||||
<span [translate]="'Requests.RequestedBy'"></span>
|
||||
|
||||
<span *ngIf="!isAdmin">{{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 *ngIf="child.requestedByAlias">{{child.requestedByAlias}}</span>
|
||||
<span *ngIf="!child.requestedByAlias">
|
||||
<span *ngIf="!isAdmin">{{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 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)="unSubscribe(child)" class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i class="fa fa-rss"></i> UnSubscribe</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="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)="unSubscribe(child)"
|
||||
class="btn btn-sm btn-danger-outline" pTooltip="UnSubscribe for notifications" type="submit"><i
|
||||
class="fa fa-rss"></i> UnSubscribe</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">
|
||||
<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 *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 class="col-md-12">
|
||||
|
@ -77,15 +89,19 @@
|
|||
{{ep.airDate | amLocal | amDateFormat: 'L' }}
|
||||
</td>
|
||||
<td>
|
||||
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel" [translate]="'Common.Denied'">
|
||||
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i>
|
||||
<span *ngIf="child.denied" class="label label-danger" id="deniedLabel"
|
||||
[translate]="'Common.Denied'">
|
||||
<i style="color:red;" class="fa fa-check" pTooltip="{{child.deniedReason}}"></i>
|
||||
</span>
|
||||
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel" [translate]="'Common.Available'"></span>
|
||||
<span *ngIf="!child.denied &&ep.approved && !ep.available" class="label label-info" id="processingRequestLabel" [translate]="'Common.ProcessingRequest'"></span>
|
||||
<span *ngIf="!child.denied && ep.available" class="label label-success" id="availableLabel"
|
||||
[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="!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>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -105,8 +121,8 @@
|
|||
<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>
|
||||
<textarea [(ngModel)]="rejectionReason" class="form-control-custom form-control"></textarea>
|
||||
<p-footer>
|
||||
<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>
|
||||
</p-footer>
|
||||
</p-dialog>
|
||||
<p-footer>
|
||||
<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>
|
||||
</p-footer>
|
||||
</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" 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 class="table table-striped table-hover table-responsive table-condensed table-usermanagement">
|
||||
<thead>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { NgbModal } from "@ng-bootstrap/ng-bootstrap";
|
||||
import { ICheckbox, ICustomizationSettings, IEmailNotificationSettings, IUser } from "../interfaces";
|
||||
import { IdentityService, NotificationService, SettingsService } from "../services";
|
||||
import { AddPlexUserComponent } from "./addplexuser.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: "./usermanagement.component.html",
|
||||
|
@ -27,8 +25,7 @@ export class UserManagementComponent implements OnInit {
|
|||
constructor(private identityService: IdentityService,
|
||||
private settingsService: SettingsService,
|
||||
private notificationService: NotificationService,
|
||||
private plexSettings: SettingsService,
|
||||
private modalService: NgbModal) { }
|
||||
private plexSettings: SettingsService) { }
|
||||
|
||||
public ngOnInit() {
|
||||
this.users = [];
|
||||
|
@ -43,11 +40,6 @@ export class UserManagementComponent implements OnInit {
|
|||
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) {
|
||||
if (!user.emailAddress) {
|
||||
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 { OrderModule } from "ngx-order-pipe";
|
||||
import { AddPlexUserComponent } from "./addplexuser.component";
|
||||
|
||||
import { SharedModule } from "../shared/shared.module";
|
||||
|
||||
|
@ -45,12 +44,8 @@ const routes: Routes = [
|
|||
declarations: [
|
||||
UserManagementComponent,
|
||||
UpdateDetailsComponent,
|
||||
AddPlexUserComponent,
|
||||
UserManagementUserComponent,
|
||||
],
|
||||
entryComponents:[
|
||||
AddPlexUserComponent,
|
||||
],
|
||||
exports: [
|
||||
RouterModule,
|
||||
],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Authorization;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Ombi.Core.Engine;
|
||||
using Ombi.Core.Models.Requests;
|
||||
|
@ -11,6 +12,7 @@ using Ombi.Core.Models;
|
|||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Store.Entities;
|
||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ombi.Controllers
|
||||
{
|
||||
|
@ -76,6 +78,7 @@ namespace Ombi.Controllers
|
|||
[HttpPost]
|
||||
public async Task<RequestEngineResult> RequestAlbum([FromBody] MusicAlbumRequestViewModel album)
|
||||
{
|
||||
album.RequestedByAlias = GetApiAlias();
|
||||
var result = await _engine.RequestAlbum(album);
|
||||
if (result.Result)
|
||||
{
|
||||
|
@ -168,5 +171,17 @@ namespace Ombi.Controllers
|
|||
{
|
||||
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 Ombi.Core.Engine;
|
||||
using Ombi.Core.Engine.Interfaces;
|
||||
|
@ -8,12 +9,14 @@ using System.Collections.Generic;
|
|||
using System.Threading.Tasks;
|
||||
using Ombi.Store.Entities.Requests;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Ombi.Attributes;
|
||||
using Ombi.Core.Models.UI;
|
||||
using Ombi.Models;
|
||||
using Ombi.Store.Entities;
|
||||
using Ombi.Core.Models;
|
||||
using Ombi.Helpers;
|
||||
|
||||
namespace Ombi.Controllers
|
||||
{
|
||||
|
@ -82,6 +85,7 @@ namespace Ombi.Controllers
|
|||
[HttpPost("movie")]
|
||||
public async Task<RequestEngineResult> RequestMovie([FromBody] MovieRequestViewModel movie)
|
||||
{
|
||||
movie.RequestedByAlias = GetApiAlias();
|
||||
var result = await MovieRequestEngine.RequestMovie(movie);
|
||||
if (result.Result)
|
||||
{
|
||||
|
@ -277,6 +281,7 @@ namespace Ombi.Controllers
|
|||
[HttpPost("tv")]
|
||||
public async Task<RequestEngineResult> RequestTv([FromBody] TvRequestViewModel tv)
|
||||
{
|
||||
tv.RequestedByAlias = GetApiAlias();
|
||||
var result = await TvRequestEngine.RequestTvShow(tv);
|
||||
if (result.Result)
|
||||
{
|
||||
|
@ -521,5 +526,19 @@ namespace Ombi.Controllers
|
|||
{
|
||||
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…
Add table
Add a link
Reference in a new issue