diff --git a/CHANGELOG.md b/CHANGELOG.md index 91ef9d69d..662b43716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ # Changelog -## (unreleased) +## v3.0.3304 (2018-05-09) ### **New Features** - Updated to prevent security vulnerability as noted here: https://github.com/aspnet/Announcements/issues/300. [Jamie Rees] +- Update README.md. [Jamie] + ### **Fixes** - [LC] - Added classes to root/quality override divs. [Anojh] diff --git a/README.md b/README.md index 1c2dc9559..2049af360 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,9 @@ ____ [![Patreon](https://img.shields.io/badge/patreon-donate-yellow.svg)](https://patreon.com/tidusjar/Ombi) [![Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://paypal.me/PlexRequestsNet) +___ +Get it on Google Play + ___ We also now have merch up on Teespring! diff --git a/src/Ombi.Core/Engine/BaseMediaEngine.cs b/src/Ombi.Core/Engine/BaseMediaEngine.cs index 552b2ac38..273c71ef0 100644 --- a/src/Ombi.Core/Engine/BaseMediaEngine.cs +++ b/src/Ombi.Core/Engine/BaseMediaEngine.cs @@ -9,13 +9,12 @@ using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Rule.Interfaces; using Ombi.Store.Entities.Requests; -using Ombi.Store.Repository; using Ombi.Store.Repository.Requests; -using Ombi.Store.Entities; -using Microsoft.AspNetCore.Identity; using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; namespace Ombi.Core.Engine { @@ -26,11 +25,12 @@ namespace Ombi.Core.Engine private Dictionary _dbTv; protected BaseMediaEngine(IPrincipal identity, IRequestServiceMain requestService, - IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService ombiSettings) : base(identity, um, rules) + IRuleEvaluator rules, OmbiUserManager um, ICacheService cache, ISettingsService ombiSettings, IRepository sub) : base(identity, um, rules) { RequestService = requestService; Cache = cache; OmbiSettings = ombiSettings; + _subscriptionRepository = sub; } protected IRequestServiceMain RequestService { get; } @@ -38,6 +38,7 @@ namespace Ombi.Core.Engine protected ITvRequestRepository TvRepository => RequestService.TvRequestService; protected readonly ICacheService Cache; protected readonly ISettingsService OmbiSettings; + protected readonly IRepository _subscriptionRepository; protected async Task> GetMovieRequests() { @@ -78,7 +79,7 @@ namespace Ombi.Core.Engine var pendingTv = 0; var approvedTv = 0; - var availableTv = 0; + var availableTv = 0; foreach (var tv in tvQuery) { foreach (var child in tv.ChildRequests) @@ -108,21 +109,51 @@ namespace Ombi.Core.Engine protected async Task HideFromOtherUsers() { + var user = await GetUser(); if (await IsInRole(OmbiRoles.Admin) || await IsInRole(OmbiRoles.PowerUser)) { - return new HideResult(); + return new HideResult + { + UserId = user.Id + }; } var settings = await Cache.GetOrAdd(CacheKeys.OmbiSettings, async () => await OmbiSettings.GetSettingsAsync()); var result = new HideResult { - Hide = settings.HideRequestsUsers + Hide = settings.HideRequestsUsers, + UserId = user.Id + }; + return result; + } + + public async Task SubscribeToRequest(int requestId, RequestType type) + { + var user = await GetUser(); + var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x => + x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type); + if (existingSub != null) + { + return; + } + var sub = new RequestSubscription + { + UserId = user.Id, + RequestId = requestId, + RequestType = type }; - if (settings.HideRequestsUsers) + + await _subscriptionRepository.Add(sub); + } + + public async Task UnSubscribeRequest(int requestId, RequestType type) + { + var user = await GetUser(); + var existingSub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(x => + x.UserId.Equals(user.Id) && x.RequestId == requestId && x.RequestType == type); + if (existingSub != null) { - var user = await GetUser(); - result.UserId = user.Id; + await _subscriptionRepository.Delete(existingSub); } - return result; } public class HideResult diff --git a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index 08ec9b594..c1ba76a7c 100644 --- a/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -18,6 +18,7 @@ namespace Ombi.Core.Engine.Interfaces Task ApproveMovieById(int requestId); Task DenyMovieById(int modelId); Task> Filter(FilterViewModel vm); + } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs index 5dbf6e449..553ad79dd 100644 --- a/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/src/Ombi.Core/Engine/Interfaces/IRequestEngine.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Core.Models.Requests; +using Ombi.Store.Entities; namespace Ombi.Core.Engine.Interfaces { @@ -18,5 +19,7 @@ namespace Ombi.Core.Engine.Interfaces Task MarkUnavailable(int modelId); Task MarkAvailable(int modelId); Task GetTotal(); + Task UnSubscribeRequest(int requestId, RequestType type); + Task SubscribeToRequest(int requestId, RequestType type); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index 3d88b7230..460752bb1 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -25,7 +25,8 @@ namespace Ombi.Core.Engine { public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator r, IMovieSender sender, ILogger log, - OmbiUserManager manager, IRepository rl, ICacheService cache, ISettingsService ombiSettings) : base(user, requestService, r, manager, cache, ombiSettings) + OmbiUserManager manager, IRepository rl, ICacheService cache, ISettingsService ombiSettings, IRepository sub) + : base(user, requestService, r, manager, cache, ombiSettings, sub) { MovieApi = movieApi; NotificationHelper = helper; @@ -137,9 +138,10 @@ namespace Ombi.Core.Engine { allRequests = await MovieRepository.GetWithUser().Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } - allRequests.ForEach(x => + allRequests.ForEach(async x => { x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); + await CheckForSubscription(shouldHide, x); }); return allRequests; } @@ -173,9 +175,30 @@ namespace Ombi.Core.Engine { allRequests = await MovieRepository.GetWithUser().ToListAsync(); } + + allRequests.ForEach(async x => + { + x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); + await CheckForSubscription(shouldHide, x); + }); return allRequests; } + private async Task CheckForSubscription(HideResult shouldHide, MovieRequests x) + { + if (shouldHide.UserId == x.RequestedUserId) + { + x.ShowSubscribe = false; + } + else + { + x.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => + s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.Movie); + x.Subscribed = sub != null; + } + } + /// /// Searches the movie request. /// @@ -194,9 +217,10 @@ namespace Ombi.Core.Engine allRequests = await MovieRepository.GetWithUser().ToListAsync(); } var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToList(); - results.ForEach(x => + results.ForEach(async x => { x.PosterPath = PosterPathHelper.FixPosterPath(x.PosterPath); + await CheckForSubscription(shouldHide, x); }); return results; } diff --git a/src/Ombi.Core/Engine/MovieSearchEngine.cs b/src/Ombi.Core/Engine/MovieSearchEngine.cs index 448ff9235..915eefe02 100644 --- a/src/Ombi.Core/Engine/MovieSearchEngine.cs +++ b/src/Ombi.Core/Engine/MovieSearchEngine.cs @@ -15,14 +15,16 @@ using Ombi.Core.Authentication; using Ombi.Core.Settings; using Ombi.Helpers; using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; namespace Ombi.Core.Engine { public class MovieSearchEngine : BaseMediaEngine, IMovieEngine { public MovieSearchEngine(IPrincipal identity, IRequestServiceMain service, IMovieDbApi movApi, IMapper mapper, - ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s) - : base(identity, service, r, um, mem, s) + ILogger logger, IRuleEvaluator r, OmbiUserManager um, ICacheService mem, ISettingsService s, IRepository sub) + : base(identity, service, r, um, mem, s, sub) { MovieApi = movApi; Mapper = mapper; diff --git a/src/Ombi.Core/Engine/TvRequestEngine.cs b/src/Ombi.Core/Engine/TvRequestEngine.cs index 2ce4da292..d74b45e5e 100644 --- a/src/Ombi.Core/Engine/TvRequestEngine.cs +++ b/src/Ombi.Core/Engine/TvRequestEngine.cs @@ -29,7 +29,8 @@ namespace Ombi.Core.Engine { public TvRequestEngine(ITvMazeApi tvApi, IMovieDbApi movApi, IRequestServiceMain requestService, IPrincipal user, INotificationHelper helper, IRuleEvaluator rule, OmbiUserManager manager, - ITvSender sender, IAuditRepository audit, IRepository rl, ISettingsService settings, ICacheService cache) : base(user, requestService, rule, manager, cache, settings) + ITvSender sender, IAuditRepository audit, IRepository rl, ISettingsService settings, ICacheService cache, + IRepository sub) : base(user, requestService, rule, manager, cache, settings, sub) { TvApi = tvApi; MovieDbApi = movApi; @@ -156,6 +157,8 @@ namespace Ombi.Core.Engine .Skip(position).Take(count).OrderByDescending(x => x.ReleaseDate).ToListAsync(); } + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; } @@ -181,25 +184,28 @@ namespace Ombi.Core.Engine .ThenInclude(x => x.Episodes) .Skip(position).Take(count).ToListAsync(); } + + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); return ParseIntoTreeNode(allRequests); } public async Task> GetRequests() { var shouldHide = await HideFromOtherUsers(); - IQueryable allRequests; + List allRequests; if (shouldHide.Hide) { - allRequests = TvRepository.Get(shouldHide.UserId); + allRequests = await TvRepository.Get(shouldHide.UserId).ToListAsync(); FilterChildren(allRequests, shouldHide); } else { - allRequests = TvRepository.Get(); + allRequests = await TvRepository.Get().ToListAsync(); } - return await allRequests.ToListAsync(); + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; } private static void FilterChildren(IEnumerable allRequests, HideResult shouldHide) @@ -232,6 +238,8 @@ namespace Ombi.Core.Engine allRequests = await TvRepository.GetChild().Include(x => x.SeasonRequests).Where(x => x.ParentRequestId == tvId).ToListAsync(); } + allRequests.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); + return allRequests; } @@ -248,6 +256,8 @@ namespace Ombi.Core.Engine allRequests = TvRepository.Get(); } var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); + + results.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); return results; } @@ -264,6 +274,7 @@ namespace Ombi.Core.Engine allRequests = TvRepository.Get(); } var results = await allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)).ToListAsync(); + results.ForEach(async r => { await CheckForSubscription(shouldHide, r); }); return ParseIntoTreeNode(results); } @@ -445,6 +456,29 @@ namespace Ombi.Core.Engine } } + private async Task CheckForSubscription(HideResult shouldHide, TvRequests x) + { + foreach (var tv in x.ChildRequests) + { + await CheckForSubscription(shouldHide, tv); + } + } + + private async Task CheckForSubscription(HideResult shouldHide, ChildRequests x) + { + if (shouldHide.UserId == x.RequestedUserId) + { + x.ShowSubscribe = false; + } + else + { + x.ShowSubscribe = true; + var sub = await _subscriptionRepository.GetAll().FirstOrDefaultAsync(s => + s.UserId == shouldHide.UserId && s.RequestId == x.Id && s.RequestType == RequestType.TvShow); + x.Subscribed = sub != null; + } + } + private async Task AddExistingRequest(ChildRequests newRequest, TvRequests existingRequest) { // Add the child diff --git a/src/Ombi.Core/Engine/TvSearchEngine.cs b/src/Ombi.Core/Engine/TvSearchEngine.cs index 1664ecfa7..bc5a2e984 100644 --- a/src/Ombi.Core/Engine/TvSearchEngine.cs +++ b/src/Ombi.Core/Engine/TvSearchEngine.cs @@ -20,6 +20,7 @@ using Microsoft.Extensions.Caching.Memory; using Ombi.Core.Authentication; using Ombi.Helpers; using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities; namespace Ombi.Core.Engine { @@ -27,8 +28,8 @@ namespace Ombi.Core.Engine { public TvSearchEngine(IPrincipal identity, IRequestServiceMain service, ITvMazeApi tvMaze, IMapper mapper, ISettingsService plexSettings, ISettingsService embySettings, IPlexContentRepository repo, IEmbyContentRepository embyRepo, ITraktApi trakt, IRuleEvaluator r, OmbiUserManager um, - ICacheService memCache, ISettingsService s) - : base(identity, service, r, um, memCache, s) + ICacheService memCache, ISettingsService s, IRepository sub) + : base(identity, service, r, um, memCache, s, sub) { TvMazeApi = tvMaze; Mapper = mapper; diff --git a/src/Ombi.Notifications/Agents/DiscordNotification.cs b/src/Ombi.Notifications/Agents/DiscordNotification.cs index 0a5d5dabc..66280ef70 100644 --- a/src/Ombi.Notifications/Agents/DiscordNotification.cs +++ b/src/Ombi.Notifications/Agents/DiscordNotification.cs @@ -20,8 +20,8 @@ namespace Ombi.Notifications.Agents { public DiscordNotification(IDiscordApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, - IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s) - : base(sn, r, m, t,s,log) + IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository sub) + : base(sn, r, m, t,s,log, sub) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/EmailNotification.cs b/src/Ombi.Notifications/Agents/EmailNotification.cs index d8f005e2b..53046ade0 100644 --- a/src/Ombi.Notifications/Agents/EmailNotification.cs +++ b/src/Ombi.Notifications/Agents/EmailNotification.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using MailKit.Net.Smtp; using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using MimeKit; using Ombi.Core.Settings; @@ -21,7 +22,7 @@ namespace Ombi.Notifications.Agents public class EmailNotification : BaseNotification, IEmailNotification { public EmailNotification(ISettingsService settings, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, IEmailProvider prov, ISettingsService c, - ILogger log, UserManager um) : base(settings, r, m, t, c, log) + ILogger log, UserManager um, IRepository sub) : base(settings, r, m, t, c, log, sub) { EmailProvider = prov; Logger = log; @@ -52,7 +53,7 @@ namespace Ombi.Notifications.Agents return true; } - + private async Task LoadTemplate(NotificationType type, NotificationOptions model, EmailNotificationSettings settings) { var parsed = await LoadTemplate(NotificationAgent.Email, type, model); @@ -62,8 +63,8 @@ namespace Ombi.Notifications.Agents return null; } var email = new EmailBasicTemplate(); - var html = email.LoadTemplate(parsed.Subject, parsed.Message,parsed.Image, Customization.Logo); - + var html = email.LoadTemplate(parsed.Subject, parsed.Message, parsed.Image, Customization.Logo); + var message = new NotificationMessage { @@ -154,7 +155,7 @@ namespace Ombi.Notifications.Agents { message.To = model.Recipient; } - + await Send(message, settings); } @@ -176,7 +177,7 @@ namespace Ombi.Notifications.Agents // Issues resolved should be sent to the user message.To = model.Recipient; - + await Send(message, settings); } @@ -227,10 +228,12 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.RequestDeclined, model, settings); message.Other.Add("PlainTextBody", plaintext); + await SendToSubscribers(settings, message); message.To = model.RequestType == RequestType.Movie ? MovieRequest.RequestedUser.Email : TvRequest.RequestedUser.Email; await Send(message, settings); + } protected override async Task RequestApproved(NotificationOptions model, EmailNotificationSettings settings) @@ -244,12 +247,32 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.RequestApproved, model, settings); message.Other.Add("PlainTextBody", plaintext); + await SendToSubscribers(settings, message); + message.To = model.RequestType == RequestType.Movie ? MovieRequest.RequestedUser.Email : TvRequest.RequestedUser.Email; await Send(message, settings); } + private async Task SendToSubscribers(EmailNotificationSettings settings, NotificationMessage message) + { + if (await SubsribedUsers.AnyAsync()) + { + foreach (var user in SubsribedUsers) + { + if (user.Email.IsNullOrEmpty()) + { + continue; + } + + message.To = user.Email; + + await Send(message, settings); + } + } + } + protected override async Task AvailableRequest(NotificationOptions model, EmailNotificationSettings settings) { var message = await LoadTemplate(NotificationType.RequestAvailable, model, settings); @@ -260,7 +283,7 @@ namespace Ombi.Notifications.Agents var plaintext = await LoadPlainTextMessage(NotificationType.RequestAvailable, model, settings); message.Other.Add("PlainTextBody", plaintext); - + await SendToSubscribers(settings, message); message.To = model.RequestType == RequestType.Movie ? MovieRequest.RequestedUser.Email : TvRequest.RequestedUser.Email; diff --git a/src/Ombi.Notifications/Agents/MattermostNotification.cs b/src/Ombi.Notifications/Agents/MattermostNotification.cs index f07d62b72..8590bfaae 100644 --- a/src/Ombi.Notifications/Agents/MattermostNotification.cs +++ b/src/Ombi.Notifications/Agents/MattermostNotification.cs @@ -21,7 +21,7 @@ namespace Ombi.Notifications.Agents public class MattermostNotification : BaseNotification, IMattermostNotification { public MattermostNotification(IMattermostApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t,s,log) + ISettingsService s, IRepository sub) : base(sn, r, m, t,s,log, sub) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/MobileNotification.cs b/src/Ombi.Notifications/Agents/MobileNotification.cs index 575801bd2..1a716d9d6 100644 --- a/src/Ombi.Notifications/Agents/MobileNotification.cs +++ b/src/Ombi.Notifications/Agents/MobileNotification.cs @@ -22,7 +22,7 @@ namespace Ombi.Notifications.Agents { public MobileNotification(IOneSignalApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, ISettingsService s, IRepository notification, - UserManager um) : base(sn, r, m, t, s,log) + UserManager um, IRepository sub) : base(sn, r, m, t, s,log, sub) { _api = api; _logger = log; @@ -167,6 +167,7 @@ namespace Ombi.Notifications.Agents // Send to user var playerIds = GetUsers(model, NotificationType.RequestDeclined); + await AddSubscribedUsers(playerIds); await Send(playerIds, notification, settings); } @@ -185,6 +186,8 @@ namespace Ombi.Notifications.Agents // Send to user var playerIds = GetUsers(model, NotificationType.RequestApproved); + + await AddSubscribedUsers(playerIds); await Send(playerIds, notification, settings); } @@ -202,6 +205,8 @@ namespace Ombi.Notifications.Agents }; // Send to user var playerIds = GetUsers(model, NotificationType.RequestAvailable); + + await AddSubscribedUsers(playerIds); await Send(playerIds, notification, settings); } protected override Task Send(NotificationMessage model, MobileNotificationSettings settings) @@ -269,6 +274,20 @@ namespace Ombi.Notifications.Agents return playerIds; } + private async Task AddSubscribedUsers(List playerIds) + { + if (await SubsribedUsers.AnyAsync()) + { + foreach (var user in SubsribedUsers.Include(x => x.NotificationUserIds)) + { + var notificationId = user.NotificationUserIds; + if (notificationId.Any()) + { + playerIds.AddRange(notificationId.Select(x => x.PlayerId)); + } + } + } + } } } \ No newline at end of file diff --git a/src/Ombi.Notifications/Agents/PushbulletNotification.cs b/src/Ombi.Notifications/Agents/PushbulletNotification.cs index 21a384db6..24aa8cd22 100644 --- a/src/Ombi.Notifications/Agents/PushbulletNotification.cs +++ b/src/Ombi.Notifications/Agents/PushbulletNotification.cs @@ -17,7 +17,7 @@ namespace Ombi.Notifications.Agents public class PushbulletNotification : BaseNotification, IPushbulletNotification { public PushbulletNotification(IPushbulletApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t,s,log) + ISettingsService s, IRepository sub) : base(sn, r, m, t,s,log, sub) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/PushoverNotification.cs b/src/Ombi.Notifications/Agents/PushoverNotification.cs index 505adc44e..5b82eb8a3 100644 --- a/src/Ombi.Notifications/Agents/PushoverNotification.cs +++ b/src/Ombi.Notifications/Agents/PushoverNotification.cs @@ -18,7 +18,7 @@ namespace Ombi.Notifications.Agents public class PushoverNotification : BaseNotification, IPushoverNotification { public PushoverNotification(IPushoverApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t, s, log) + ISettingsService s, IRepository sub) : base(sn, r, m, t, s, log, sub) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/SlackNotification.cs b/src/Ombi.Notifications/Agents/SlackNotification.cs index d723c065a..894758591 100644 --- a/src/Ombi.Notifications/Agents/SlackNotification.cs +++ b/src/Ombi.Notifications/Agents/SlackNotification.cs @@ -18,7 +18,7 @@ namespace Ombi.Notifications.Agents public class SlackNotification : BaseNotification, ISlackNotification { public SlackNotification(ISlackApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, ITvRequestRepository t, - ISettingsService s) : base(sn, r, m, t, s, log) + ISettingsService s, IRepository sub) : base(sn, r, m, t, s, log, sub) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Agents/TelegramNotification.cs b/src/Ombi.Notifications/Agents/TelegramNotification.cs index 921fd3ad2..827b0b590 100644 --- a/src/Ombi.Notifications/Agents/TelegramNotification.cs +++ b/src/Ombi.Notifications/Agents/TelegramNotification.cs @@ -18,7 +18,8 @@ namespace Ombi.Notifications.Agents { public TelegramNotification(ITelegramApi api, ISettingsService sn, ILogger log, INotificationTemplatesRepository r, IMovieRequestRepository m, - ITvRequestRepository t, ISettingsService s) : base(sn, r, m, t,s,log) + ITvRequestRepository t, ISettingsService s + , IRepository sub) : base(sn, r, m, t,s,log, sub) { Api = api; Logger = log; diff --git a/src/Ombi.Notifications/Interfaces/BaseNotification.cs b/src/Ombi.Notifications/Interfaces/BaseNotification.cs index f21d3fb12..507b8059b 100644 --- a/src/Ombi.Notifications/Interfaces/BaseNotification.cs +++ b/src/Ombi.Notifications/Interfaces/BaseNotification.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; @@ -17,7 +19,7 @@ namespace Ombi.Notifications.Interfaces public abstract class BaseNotification : INotification where T : Settings.Settings.Models.Settings, new() { protected BaseNotification(ISettingsService settings, INotificationTemplatesRepository templateRepo, IMovieRequestRepository movie, ITvRequestRepository tv, - ISettingsService customization, ILogger> log) + ISettingsService customization, ILogger> log, IRepository sub) { Settings = settings; TemplateRepository = templateRepo; @@ -26,21 +28,24 @@ namespace Ombi.Notifications.Interfaces CustomizationSettings = customization; Settings.ClearCache(); CustomizationSettings.ClearCache(); + RequestSubscription = sub; _log = log; } - + protected ISettingsService Settings { get; } protected INotificationTemplatesRepository TemplateRepository { get; } protected IMovieRequestRepository MovieRepository { get; } protected ITvRequestRepository TvRepository { get; } protected CustomizationSettings Customization { get; set; } + protected IRepository RequestSubscription { get; set; } private ISettingsService CustomizationSettings { get; } private readonly ILogger> _log; protected ChildRequests TvRequest { get; set; } protected MovieRequests MovieRequest { get; set; } - + protected IQueryable SubsribedUsers { get; private set; } + public abstract string NotificationName { get; } public async Task NotifyAsync(NotificationOptions model) @@ -54,20 +59,21 @@ namespace Ombi.Notifications.Interfaces { Settings.ClearCache(); if (settings == null) await NotifyAsync(model); - + var notificationSettings = (T)settings; - + if (!ValidateConfiguration(notificationSettings)) { return; } - + // Is this a test? // The request id for tests is -1 // Also issues are 0 since there might not be a request associated if (model.RequestId > 0) { await LoadRequest(model.RequestId, model.RequestType); + SubsribedUsers = GetSubscriptions(model.RequestId, model.RequestType); } Customization = await CustomizationSettings.GetSettingsAsync(); @@ -126,7 +132,7 @@ namespace Ombi.Notifications.Interfaces } else { - TvRequest = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); + TvRequest = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId); } } @@ -152,13 +158,19 @@ namespace Ombi.Notifications.Interfaces } if (!template.Enabled) { - return new NotificationMessageContent {Disabled = true}; + return new NotificationMessageContent { Disabled = true }; } var parsed = Parse(model, template); return parsed; } + protected IQueryable GetSubscriptions(int requestId, RequestType type) + { + var subs = RequestSubscription.GetAll().Include(x => x.User).Where(x => x.RequestId == requestId && type == x.RequestType); + return subs.Select(x => x.User); + } + private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template) { var resolver = new NotificationMessageResolver(); @@ -166,7 +178,7 @@ namespace Ombi.Notifications.Interfaces if (model.RequestType == RequestType.Movie) { _log.LogDebug("Notification options: {@model}, Req: {@MovieRequest}, Settings: {@Customization}", model, MovieRequest, Customization); - + curlys.Setup(model, MovieRequest, Customization); } else diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 55d7db563..0c716c7c4 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -42,5 +42,6 @@ namespace Ombi.Store.Context DbSet SickRageEpisodeCache { get; set; } DbSet RequestLogs { get; set; } DbSet RecentlyAddedLogs { get; set; } + DbSet RequestSubscription { 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 d1963e765..8fb05f6ae 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -47,6 +47,7 @@ namespace Ombi.Store.Context public DbSet SonarrEpisodeCache { get; set; } public DbSet SickRageCache { get; set; } public DbSet SickRageEpisodeCache { get; set; } + public DbSet RequestSubscription { get; set; } public DbSet ApplicationConfigurations { get; set; } diff --git a/src/Ombi.Store/Entities/RequestSubscription.cs b/src/Ombi.Store/Entities/RequestSubscription.cs new file mode 100644 index 000000000..2f6d530c9 --- /dev/null +++ b/src/Ombi.Store/Entities/RequestSubscription.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("RequestSubscription")] + public class RequestSubscription : Entity + { + public string UserId { get; set; } + public int RequestId { get; set; } + public RequestType RequestType { get; set; } + + [ForeignKey(nameof(UserId))] + public OmbiUser User { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Entities/Requests/ChildRequests.cs b/src/Ombi.Store/Entities/Requests/ChildRequests.cs index 64c0bcc5c..3b5156ce5 100644 --- a/src/Ombi.Store/Entities/Requests/ChildRequests.cs +++ b/src/Ombi.Store/Entities/Requests/ChildRequests.cs @@ -13,6 +13,15 @@ namespace Ombi.Store.Entities.Requests public int? IssueId { get; set; } public SeriesType SeriesType { get; set; } + /// + /// This is to see if the user is subscribed in the UI + /// + [NotMapped] + public bool Subscribed { get; set; } + + [NotMapped] + public bool ShowSubscribe { get; set; } + [ForeignKey(nameof(IssueId))] public List Issues { get; set; } diff --git a/src/Ombi.Store/Entities/Requests/MovieRequests.cs b/src/Ombi.Store/Entities/Requests/MovieRequests.cs index 998c27707..675035140 100644 --- a/src/Ombi.Store/Entities/Requests/MovieRequests.cs +++ b/src/Ombi.Store/Entities/Requests/MovieRequests.cs @@ -12,6 +12,11 @@ namespace Ombi.Store.Entities.Requests [ForeignKey(nameof(IssueId))] public List Issues { get; set; } + [NotMapped] + public bool Subscribed { get; set; } + [NotMapped] + public bool ShowSubscribe { get; set; } + public int RootPathOverride { get; set; } public int QualityOverride { get; set; } } diff --git a/src/Ombi.Store/Entities/Requests/TvRequests.cs b/src/Ombi.Store/Entities/Requests/TvRequests.cs index 7a6abc792..432bc88ab 100644 --- a/src/Ombi.Store/Entities/Requests/TvRequests.cs +++ b/src/Ombi.Store/Entities/Requests/TvRequests.cs @@ -16,6 +16,7 @@ namespace Ombi.Store.Entities.Requests public string Background { get; set; } public DateTime ReleaseDate { get; set; } public string Status { get; set; } + /// /// This is so we can correctly send the right amount of seasons to Sonarr /// diff --git a/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.Designer.cs b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.Designer.cs new file mode 100644 index 000000000..e83458fef --- /dev/null +++ b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.Designer.cs @@ -0,0 +1,981 @@ +// +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 Ombi.Store.Entities.Requests; +using System; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180516090124_RequestSubscription")] + partial class RequestSubscription + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); + + 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("ImdbId"); + + b.Property("ProviderId"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + b.Property("Type"); + + b.Property("Url"); + + 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("ImdbId"); + + b.Property("ParentId"); + + b.Property("ProviderId"); + + b.Property("SeasonNumber"); + + b.Property("TheMovieDbId"); + + b.Property("Title"); + + b.Property("TvDbId"); + + 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.NotificationUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("PlayerId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("NotificationUserId"); + }); + + 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("UserAccessToken"); + + 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("HasFile"); + + b.Property("TheMovieDbId"); + + b.HasKey("Id"); + + b.ToTable("RadarrCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.RecentlyAddedLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ContentId"); + + b.Property("ContentType"); + + b.Property("EpisodeNumber"); + + b.Property("SeasonNumber"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.ToTable("RecentlyAddedLog"); + }); + + 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("SeriesType"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("ParentRequestId"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("ChildRequests"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Value"); + + b.HasKey("Id"); + + b.ToTable("IssueCategory"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.IssueComments", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Comment"); + + b.Property("Date"); + + b.Property("IssuesId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("IssuesId"); + + b.HasIndex("UserId"); + + b.ToTable("IssueComments"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Description"); + + b.Property("IssueCategoryId"); + + b.Property("IssueId"); + + b.Property("ProviderId"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("ResovledDate"); + + b.Property("Status"); + + b.Property("Subject"); + + b.Property("Title"); + + b.Property("UserReportedId"); + + b.HasKey("Id"); + + b.HasIndex("IssueCategoryId"); + + b.HasIndex("IssueId"); + + b.HasIndex("UserReportedId"); + + b.ToTable("Issues"); + }); + + 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("DigitalReleaseDate"); + + 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.TvRequests", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Background"); + + b.Property("ImdbId"); + + b.Property("Overview"); + + b.Property("PosterPath"); + + b.Property("QualityOverride"); + + 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.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + + 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("HasFile"); + + 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.NotificationUserId", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("NotificationUserIds") + .HasForeignKey("UserId"); + }); + + 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.IssueComments", b => + { + b.HasOne("Ombi.Store.Entities.Requests.Issues", "Issues") + .WithMany("Comments") + .HasForeignKey("IssuesId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Requests.Issues", b => + { + b.HasOne("Ombi.Store.Entities.Requests.IssueCategory", "IssueCategory") + .WithMany() + .HasForeignKey("IssueCategoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Ombi.Store.Entities.Requests.ChildRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.Requests.MovieRequests") + .WithMany("Issues") + .HasForeignKey("IssueId"); + + b.HasOne("Ombi.Store.Entities.OmbiUser", "UserReported") + .WithMany() + .HasForeignKey("UserReportedId"); + }); + + 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.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "user") + .WithMany() + .HasForeignKey("UserId"); + }); + + 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/20180516090124_RequestSubscription.cs b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.cs new file mode 100644 index 000000000..9ba204b60 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180516090124_RequestSubscription.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using System; +using System.Collections.Generic; + +namespace Ombi.Store.Migrations +{ + public partial class RequestSubscription : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "RequestSubscription", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RequestId = table.Column(nullable: false), + RequestType = table.Column(nullable: false), + UserId = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_RequestSubscription", x => x.Id); + table.ForeignKey( + name: "FK_RequestSubscription_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_RequestSubscription_UserId", + table: "RequestSubscription", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "RequestSubscription"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 794bde8a6..2da0729a8 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -20,7 +20,7 @@ namespace Ombi.Store.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.0.2-rtm-10011"); + .HasAnnotation("ProductVersion", "2.0.3-rtm-10026"); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { @@ -676,6 +676,24 @@ namespace Ombi.Store.Migrations b.ToTable("TvRequests"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("RequestSubscription"); + }); + modelBuilder.Entity("Ombi.Store.Entities.SickRageCache", b => { b.Property("Id") @@ -927,6 +945,13 @@ namespace Ombi.Store.Migrations .HasForeignKey("UserId"); }); + modelBuilder.Entity("Ombi.Store.Entities.RequestSubscription", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "user") + .WithMany() + .HasForeignKey("UserId"); + }); + modelBuilder.Entity("Ombi.Store.Entities.Tokens", b => { b.HasOne("Ombi.Store.Entities.OmbiUser", "User") diff --git a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs index 78c3da7dd..d4a550528 100644 --- a/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs +++ b/src/Ombi.Store/Repository/Requests/MovieRequestRepository.cs @@ -57,7 +57,7 @@ namespace Ombi.Store.Repository.Requests public IQueryable GetWithUser(string userId) { return Db.MovieRequests - .Where(x => x.RequestedUserId == userId) + .Where(x => x.RequestedUserId == userId) .Include(x => x.RequestedUser) .ThenInclude(x => x.NotificationUserIds) .AsQueryable(); diff --git a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts index e744aea31..1895914c3 100644 --- a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts @@ -12,6 +12,8 @@ export interface IMovieRequests extends IFullBaseRequest { rootPathOverride: number; qualityOverride: number; digitalReleaseDate: Date; + subscribed: boolean; + showSubscribe: boolean; // For the UI rootPathOverrideTitle: string; @@ -77,6 +79,8 @@ export interface ITvRequests { export interface IChildRequests extends IBaseRequest { seasonRequests: INewSeasonRequests[]; + subscribed: boolean; + showSubscribe: boolean; } export interface ITvUpdateModel { diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index e67aeb5e6..629fe3147 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -124,7 +124,13 @@ -
+
+ +
+ + + +
@@ -164,7 +170,7 @@
- +
- -
+ + + +
diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts index e9da2342f..4a10dc937 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts @@ -94,6 +94,22 @@ export class TvRequestChildrenComponent { }); } + public subscribe(request: IChildRequests) { + request.subscribed = true; + this.requestService.subscribeToTv(request.id) + .subscribe(x => { + this.notificationService.success("Subscribed To TV Show!"); + }); + } + + public unSubscribe(request: IChildRequests) { + request.subscribed = false; + this.requestService.unSubscribeToTv(request.id) + .subscribe(x => { + this.notificationService.success("Unsubscribed TV Show!"); + }); + } + private removeRequestFromUi(key: IChildRequests) { const index = this.childRequests.indexOf(key, 0); if (index > -1) { diff --git a/src/Ombi/ClientApp/app/services/request.service.ts b/src/Ombi/ClientApp/app/services/request.service.ts index acbe319ab..2da0c1a8a 100644 --- a/src/Ombi/ClientApp/app/services/request.service.ts +++ b/src/Ombi/ClientApp/app/services/request.service.ts @@ -117,4 +117,17 @@ export class RequestService extends ServiceHelpers { public filterMovies(filter: IFilter): Observable> { return this.http.post>(`${this.url}movie/filter`, JSON.stringify(filter), {headers: this.headers}); } + + public subscribeToMovie(requestId: number): Observable { + return this.http.post(`${this.url}movie/subscribe/${requestId}`, {headers: this.headers}); + } + public unSubscribeToMovie(requestId: number): Observable { + return this.http.post(`${this.url}movie/unsubscribe/${requestId}`, {headers: this.headers}); + } + public subscribeToTv(requestId: number): Observable { + return this.http.post(`${this.url}tv/subscribe/${requestId}`, {headers: this.headers}); + } + public unSubscribeToTv(requestId: number): Observable { + return this.http.post(`${this.url}tv/unsubscribe/${requestId}`, {headers: this.headers}); + } } diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 5f38f3435..0a56c32b6 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Ombi.Store.Entities.Requests; using System.Diagnostics; using Ombi.Models; +using Ombi.Store.Entities; namespace Ombi.Controllers { @@ -356,5 +357,45 @@ namespace Ombi.Controllers { return await MovieRequestEngine.Filter(vm); } + + /// + /// Subscribes for notifications to a movie request + /// + [HttpPost("movie/subscribe/{requestId:int}")] + public async Task SubscribeToMovie(int requestId) + { + await MovieRequestEngine.SubscribeToRequest(requestId, RequestType.Movie); + return true; + } + + /// + /// Subscribes for notifications to a TV request + /// + [HttpPost("tv/subscribe/{requestId:int}")] + public async Task SubscribeToTv(int requestId) + { + await TvRequestEngine.SubscribeToRequest(requestId, RequestType.TvShow); + return true; + } + + /// + /// UnSubscribes for notifications to a movie request + /// + [HttpPost("movie/unsubscribe/{requestId:int}")] + public async Task UnSubscribeToMovie(int requestId) + { + await MovieRequestEngine.UnSubscribeRequest(requestId, RequestType.Movie); + return true; + } + + /// + /// UnSubscribes for notifications to a TV request + /// + [HttpPost("tv/unsubscribe/{requestId:int}")] + public async Task UnSubscribeToTv(int requestId) + { + await TvRequestEngine.UnSubscribeRequest(requestId, RequestType.TvShow); + return true; + } } } \ No newline at end of file