diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 0a31f2d76..a142f2e62 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -15,6 +15,7 @@ using Ombi.Core.Authentication; using Ombi.Core.Engine.Interfaces; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; namespace Ombi.Core.Engine { @@ -22,18 +23,20 @@ namespace Ombi.Core.Engine { public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger log, - OmbiUserManager manager) : base(user, requestService, r, manager) + OmbiUserManager manager, IRepository rl) : base(user, requestService, r, manager) { MovieApi = movieApi; NotificationHelper = helper; Sender = sender; Logger = log; + _requestLog = rl; } private IMovieDbApi MovieApi { get; } private INotificationHelper NotificationHelper { get; } private IMovieSender Sender { get; } private ILogger Logger { get; } + private readonly IRepository _requestLog; /// /// Requests the movie. @@ -321,6 +324,14 @@ namespace Ombi.Core.Engine NotificationHelper.NewRequest(model); } + await _requestLog.Add(new RequestLog + { + UserId = (await GetUser()).Id, + RequestDate = DateTime.UtcNow, + RequestId = model.Id, + RequestType = RequestType.Movie, + }); + return new RequestEngineResult { Result = true, Message = $"{movieName} has been successfully added!" }; } diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 26a83fa78..3da977db3 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -1,4 +1,5 @@ -using AutoMapper; +using System; +using AutoMapper; using Ombi.Api.TvMaze; using Ombi.Core.Models.Requests; using Ombi.Core.Models.Search; @@ -25,18 +26,20 @@ namespace Ombi.Core.Engine { public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, - ITvSender sender, IAuditRepository audit) : base(user, requestService, rule, manager) + ITvSender sender, IAuditRepository audit, IRepository rl) : base(user, requestService, rule, manager) { TvApi = tvApi; NotificationHelper = helper; TvSender = sender; Audit = audit; + _requestLog = rl; } private INotificationHelper NotificationHelper { get; } private ITvMazeApi TvApi { get; } private ITvSender TvSender { get; } private IAuditRepository Audit { get; } + private readonly IRepository _requestLog; public async Task RequestTvShow(SearchTvShowViewModel tv) { @@ -401,6 +404,14 @@ namespace Ombi.Core.Engine }; } + await _requestLog.Add(new RequestLog + { + UserId = (await GetUser()).Id, + RequestDate = DateTime.UtcNow, + RequestId = model.Id, + RequestType = RequestType.TvShow, + }); + return new RequestEngineResult {Result = true}; } } diff --git a/src/Ombi.Core/Models/UI/UserViewModel.cs b/src/Ombi.Core/Models/UI/UserViewModel.cs index dffe6000a..1c1e6162b 100644 --- a/src/Ombi.Core/Models/UI/UserViewModel.cs +++ b/src/Ombi.Core/Models/UI/UserViewModel.cs @@ -14,6 +14,8 @@ namespace Ombi.Core.Models.UI public DateTime? LastLoggedIn { get; set; } public bool HasLoggedIn { get; set; } public UserType UserType { get; set; } + public int MovieRequestLimit { get; set; } + public int EpisodeRequestLimit { get; set; } } public class ClaimCheckboxes diff --git a/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs new file mode 100644 index 000000000..1c1912d20 --- /dev/null +++ b/src/Ombi.Core/Rule/Rules/Request/RequestLimitRule.cs @@ -0,0 +1,95 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: RequestLimitRule.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; + +namespace Ombi.Core.Rule.Rules.Request +{ + public class RequestLimitRule : BaseRequestRule, IRules + { + public RequestLimitRule(IRepository rl, OmbiUserManager um) + { + _requestLog = rl; + _userManager = um; + } + + private readonly IRepository _requestLog; + private readonly OmbiUserManager _userManager; + + public async Task Execute(BaseRequest obj) + { + var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == obj.RequestedUserId); + + var movieLimit = user.MovieRequestLimit; + var episodeLimit = user.EpisodeRequestLimit; + + var requestLog = _requestLog.GetAll().Where(x => x.UserId == obj.RequestedUserId); + if (obj.RequestType == RequestType.Movie) + { + var movieLogs = requestLog.Where(x => x.RequestType == RequestType.Movie); + + // Count how many requests in the past 7 days + var count = await movieLogs.CountAsync(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + count += 1; // Since we are including this request + if (count > movieLimit) + { + return Fail("You have exceeded your Movie request quota!"); + } + } + else + { + var child = (ChildRequests) obj; + var requestCount = 0; + // Get the count of requests to be made + foreach (var s in child.SeasonRequests) + { + requestCount = s.Episodes.Count; + } + + var tvLogs = requestLog.Where(x => x.RequestType == RequestType.TvShow); + + // Count how many requests in the past 7 days + var tv = tvLogs.Where(x => x.RequestDate >= DateTime.UtcNow.AddDays(-7)); + var count = await tv.Select(x => x.EpisodeCount).CountAsync(); + count += requestCount; // Add the amount of requests in + if (count > episodeLimit) + { + return Fail("You have exceeded your Episode request quota!"); + } + } + return Success(); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 0d3045d2a..1fa5c38fd 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -40,5 +40,6 @@ namespace Ombi.Store.Context DbSet CouchPotatoCache { get; set; } DbSet SickRageCache { get; set; } DbSet SickRageEpisodeCache { get; set; } + DbSet RequestLogs { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 8621a2f07..3651bfd64 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -35,6 +35,7 @@ namespace Ombi.Store.Context public DbSet ChildRequests { get; set; } public DbSet MovieIssues { get; set; } public DbSet TvIssues { get; set; } + public DbSet RequestLogs { get; set; } public DbSet Audit { get; set; } public DbSet Tokens { get; set; } diff --git a/src/Ombi.Store/Entities/OmbiUser.cs b/src/Ombi.Store/Entities/OmbiUser.cs index f2188b2f7..c81d9b1c8 100644 --- a/src/Ombi.Store/Entities/OmbiUser.cs +++ b/src/Ombi.Store/Entities/OmbiUser.cs @@ -19,6 +19,9 @@ namespace Ombi.Store.Entities public string EmbyConnectUserId { get; set; } + public int? MovieRequestLimit { get; set; } + public int? EpisodeRequestLimit { get; set; } + [NotMapped] public bool IsEmbyConnect => UserType == UserType.EmbyUser && EmbyConnectUserId.HasValue(); diff --git a/src/Ombi.Store/Entities/Requests/RequestLog.cs b/src/Ombi.Store/Entities/Requests/RequestLog.cs new file mode 100644 index 000000000..b64a587fd --- /dev/null +++ b/src/Ombi.Store/Entities/Requests/RequestLog.cs @@ -0,0 +1,46 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2017 Jamie Rees +// File: RequestLog.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion + +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities.Requests +{ + [Table("RequestLog")] + public class RequestLog : Entity + { + public string UserId { get; set; } + public RequestType RequestType { get; set; } + public DateTime RequestDate { get; set; } + public int RequestId { get; set; } + + public int EpisodeCount { get; set; } + + [ForeignKey(nameof(UserId))] + public OmbiUser User { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Migrations/20171208232545_RequestLimit.Designer.cs b/src/Ombi.Store/Migrations/20171208232545_RequestLimit.Designer.cs new file mode 100644 index 000000000..16a066a02 --- /dev/null +++ b/src/Ombi.Store/Migrations/20171208232545_RequestLimit.Designer.cs @@ -0,0 +1,845 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.EntityFrameworkCore.Storage.Internal; +using Ombi.Helpers; +using Ombi.Store.Context; +using Ombi.Store.Entities; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20171208232545_RequestLimit")] + partial class RequestLimit + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.0-rtm-26452"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Name") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("AspNetRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("RoleId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ClaimType"); + + b.Property("ClaimValue"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider"); + + b.Property("ProviderKey"); + + b.Property("ProviderDisplayName"); + + b.Property("UserId") + .IsRequired(); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId"); + + b.Property("RoleId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId"); + + b.Property("LoginProvider"); + + b.Property("Name"); + + b.Property("Value"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.ApplicationConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Type"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("ApplicationConfiguration"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuditArea"); + + b.Property("AuditType"); + + b.Property("DateTime"); + + b.Property("Description"); + + b.Property("User"); + + b.HasKey("Id"); + + b.ToTable("Audit"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.CouchPotatoCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("CouchPotatoCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId") + .IsRequired(); + + b.Property("ProviderId"); + + b.Property("Title"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("EmbyContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("EmbyId"); + + b.Property("EpisodeNumber"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.ToTable("EmbyEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.GlobalSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Content"); + + b.Property("SettingsName"); + + b.HasKey("Id"); + + b.ToTable("GlobalSettings"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.NotificationTemplates", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("Message"); + + b.Property("NotificationType"); + + b.Property("Subject"); + + b.HasKey("Id"); + + b.ToTable("NotificationTemplates"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.OmbiUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AccessFailedCount"); + + b.Property("Alias"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken(); + + b.Property("Email") + .HasMaxLength(256); + + b.Property("EmailConfirmed"); + + b.Property("EmbyConnectUserId"); + + b.Property("EpisodeRequestLimit"); + + b.Property("LastLoggedIn"); + + b.Property("LockoutEnabled"); + + b.Property("LockoutEnd"); + + b.Property("MovieRequestLimit"); + + b.Property("NormalizedEmail") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasMaxLength(256); + + b.Property("PasswordHash"); + + b.Property("PhoneNumber"); + + b.Property("PhoneNumberConfirmed"); + + b.Property("ProviderUserId"); + + b.Property("SecurityStamp"); + + b.Property("TwoFactorEnabled"); + + b.Property("UserName") + .HasMaxLength(256); + + b.Property("UserType"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("AspNetUsers"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("GrandparentKey"); + + b.Property("Key"); + + b.Property("ParentKey"); + + b.Property("SeasonNumber"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("GrandparentKey"); + + b.ToTable("PlexEpisode"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ParentKey"); + + b.Property("PlexContentId"); + + b.Property("PlexServerContentId"); + + b.Property("SeasonKey"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("PlexServerContentId"); + + b.ToTable("PlexSeasonsContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexServerContent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ImdbId"); + + b.Property("Key"); + + b.Property("Quality"); + + b.Property("ReleaseYear"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.ToTable("PlexServerContent"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RadarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("IssueId"); + + b.Property("ParentRequestId"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueId"); + + b.Property("MovieId"); + + b.Property("Subect"); + + b.HasKey("Id"); + + b.HasIndex("IssueId"); + + b.HasIndex("MovieId"); + + b.ToTable("MovieIssues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("Background"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("ImdbId"); + + b.Property("IssueId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("RootPathOverride"); + + b.Property("Status"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("MovieRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueId"); + + b.Property("Subect"); + + b.Property("TvId"); + + b.HasKey("Id"); + + b.HasIndex("IssueId"); + + b.HasIndex("TvId"); + + b.ToTable("TvIssues"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("ReleaseDate"); + + b.Property("RootFolder"); + + b.Property("Status"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("TvRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SickRageEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SickRageEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.SonarrEpisodeCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("TvDbId"); + + b.HasKey("Id"); + + b.ToTable("SonarrEpisodeCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Token"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Tokens"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AirDate"); + + b.Property("Approved"); + + b.Property("Available"); + + b.Property("EpisodeNumber"); + + b.Property("Requested"); + + b.Property("SeasonId"); + + b.Property("Title"); + + b.Property("Url"); + + b.HasKey("Id"); + + b.HasIndex("SeasonId"); + + b.ToTable("EpisodeRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChildRequestId"); + + b.Property("SeasonNumber"); + + b.HasKey("Id"); + + b.HasIndex("ChildRequestId"); + + b.ToTable("SeasonRequests"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.EmbyEpisode", b => + { + b.HasOne("Ombi.Store.Entities.EmbyContent", "Series") + .WithMany("Episodes") + .HasForeignKey("ParentId") + .HasPrincipalKey("EmbyId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexEpisode", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent", "Series") + .WithMany("Episodes") + .HasForeignKey("GrandparentKey") + .HasPrincipalKey("Key") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.PlexSeasonsContent", b => + { + b.HasOne("Ombi.Store.Entities.PlexServerContent") + .WithMany("Seasons") + .HasForeignKey("PlexServerContentId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.ChildRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.TvRequests", "ParentRequest") + .WithMany("ChildRequests") + .HasForeignKey("ParentRequestId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieIssues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.MovieRequests", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "Child") + .WithMany() + .HasForeignKey("TvId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => + { + b.HasOne("Ombi.Store.Repository.Requests.SeasonRequests", "Season") + .WithMany("Episodes") + .HasForeignKey("SeasonId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("Ombi.Store.Repository.Requests.SeasonRequests", b => + { + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests", "ChildRequest") + .WithMany("SeasonRequests") + .HasForeignKey("ChildRequestId") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Ombi.Store/Migrations/20171208232545_RequestLimit.cs b/src/Ombi.Store/Migrations/20171208232545_RequestLimit.cs new file mode 100644 index 000000000..a98197b47 --- /dev/null +++ b/src/Ombi.Store/Migrations/20171208232545_RequestLimit.cs @@ -0,0 +1,66 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class RequestLimit : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "EpisodeRequestLimit", + table: "AspNetUsers", + type: "INTEGER", + nullable: true); + + migrationBuilder.AddColumn( + name: "MovieRequestLimit", + table: "AspNetUsers", + type: "INTEGER", + nullable: true); + + migrationBuilder.CreateTable( + name: "RequestLog", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + EpisodeCount = table.Column(type: "INTEGER", nullable: false), + RequestDate = table.Column(type: "TEXT", nullable: false), + RequestId = table.Column(type: "INTEGER", nullable: false), + RequestType = table.Column(type: "INTEGER", nullable: false), + UserId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RequestLog", x => x.Id); + table.ForeignKey( + name: "FK_RequestLog_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_RequestLog_UserId", + table: "RequestLog", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RequestLog"); + + migrationBuilder.DropColumn( + name: "EpisodeRequestLimit", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "MovieRequestLimit", + table: "AspNetUsers"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 9ab94c7c5..b920fda1e 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -274,12 +274,16 @@ namespace Ombi.Store.Migrations b.Property("EmbyConnectUserId"); + b.Property("EpisodeRequestLimit"); + b.Property("LastLoggedIn"); b.Property("LockoutEnabled"); b.Property("LockoutEnd"); + b.Property("MovieRequestLimit"); + b.Property("NormalizedEmail") .HasMaxLength(256); @@ -507,6 +511,28 @@ namespace Ombi.Store.Migrations b.ToTable("MovieRequests"); }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("EpisodeCount"); + + b.Property("RequestDate"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestLog"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => { b.Property("Id") @@ -771,6 +797,13 @@ namespace Ombi.Store.Migrations .HasForeignKey("RequestedUserId"); }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.RequestLog", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Requests.TvIssues", b => { b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") diff --git a/src/Ombi.Store/Repository/IRepository.cs b/src/Ombi.Store/Repository/IRepository.cs index 17b9d5d17..126694889 100644 --- a/src/Ombi.Store/Repository/IRepository.cs +++ b/src/Ombi.Store/Repository/IRepository.cs @@ -17,6 +17,7 @@ namespace Ombi.Store.Repository Task DeleteRange(IEnumerable req); Task Delete(T request); Task SaveChangesAsync(); + Task Add(T content); IIncludableQueryable Include( IQueryable source, Expression> navigationPropertyPath) diff --git a/src/Ombi/ClientApp/app/interfaces/IUser.ts b/src/Ombi/ClientApp/app/interfaces/IUser.ts index 7cb3b8386..19fee24a8 100644 --- a/src/Ombi/ClientApp/app/interfaces/IUser.ts +++ b/src/Ombi/ClientApp/app/interfaces/IUser.ts @@ -10,6 +10,8 @@ export interface IUser { userType: UserType; lastLoggedIn: Date; hasLoggedIn: boolean; + movieRequestLimit: number; + episodeRequestLimit: number; // FOR UI checked: boolean; } diff --git a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html index 029a754a3..71ad58344 100644 --- a/src/Ombi/ClientApp/app/settings/settingsmenu.component.html +++ b/src/Ombi/ClientApp/app/settings/settingsmenu.component.html @@ -9,7 +9,7 @@ diff --git a/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.html index 992897df6..8f51c2aee 100644 --- a/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/app/settings/usermanagement/usermanagement.component.html @@ -2,7 +2,7 @@
- User Management Settings + User Importer Settings
diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html index 9f2f5328c..683bd5620 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.html @@ -55,6 +55,19 @@
+
+ +
+ +
+
+ +
+ +
+ +
+
diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts b/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts index 76968b126..7d0b4167a 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-add.component.ts @@ -26,10 +26,12 @@ export class UserManagementAddComponent implements OnInit { password: "", userName: "", userType: UserType.LocalUser, - checked:false, + checked: false, hasLoggedIn: false, - lastLoggedIn:new Date(), - }; + lastLoggedIn: new Date(), + episodeRequestLimit: 0, + movieRequestLimit: 0, + }; } public create() { diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html index 23038e68e..e0bb0a096 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-edit.component.html @@ -27,7 +27,7 @@
- +
@@ -44,6 +44,19 @@
+
+ +
+ +
+
+ +
+ +
+ +
+
diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index a4fca0b37..799e4cc34 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -252,7 +252,9 @@ namespace Ombi.Controllers UserType = (Core.Models.UserType)(int)user.UserType, Claims = new List(), LastLoggedIn = user.LastLoggedIn, - HasLoggedIn = user.LastLoggedIn.HasValue + HasLoggedIn = user.LastLoggedIn.HasValue, + EpisodeRequestLimit = user.EpisodeRequestLimit ?? 0, + MovieRequestLimit = user.MovieRequestLimit ?? 0 }; foreach (var role in userRoles) @@ -302,6 +304,8 @@ namespace Ombi.Controllers Email = user.EmailAddress, UserName = user.UserName, UserType = UserType.LocalUser, + MovieRequestLimit = user.MovieRequestLimit, + EpisodeRequestLimit = user.EpisodeRequestLimit }; var userResult = await UserManager.CreateAsync(ombiUser, user.Password); @@ -442,6 +446,8 @@ namespace Ombi.Controllers var user = await UserManager.Users.FirstOrDefaultAsync(x => x.Id == ui.Id); user.Alias = ui.Alias; user.Email = ui.EmailAddress; + user.MovieRequestLimit = ui.MovieRequestLimit; + user.EpisodeRequestLimit = ui.EpisodeRequestLimit; var updateResult = await UserManager.UpdateAsync(user); if (!updateResult.Succeeded) { @@ -554,11 +560,6 @@ namespace Ombi.Controllers return claims; } - //public async Task SendWelcomeEmail([FromBody] UserViewModel user) - //{ - - //} - /// /// Send out the email with the reset link ///