From 8b9c37562846a2e78a492bd17349f617892798ec Mon Sep 17 00:00:00 2001 From: Jamie Rees Date: Wed, 4 Apr 2018 13:48:40 +0100 Subject: [PATCH 01/74] Made a start on the VoteEngine !wip --- src/Ombi.Core/Engine/VoteEngine.cs | 96 ++++++++++++++++++++++++++ src/Ombi.Store/Context/IOmbiContext.cs | 1 + src/Ombi.Store/Context/OmbiContext.cs | 1 + src/Ombi.Store/Entities/Votes.cs | 25 +++++++ src/Ombi/Startup.cs | 2 + 5 files changed, 125 insertions(+) create mode 100644 src/Ombi.Core/Engine/VoteEngine.cs create mode 100644 src/Ombi.Store/Entities/Votes.cs diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs new file mode 100644 index 000000000..5c132075e --- /dev/null +++ b/src/Ombi.Core/Engine/VoteEngine.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Authentication; +using Ombi.Core.Engine.Interfaces; +using Ombi.Core.Rule.Interfaces; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Core.Engine +{ + public class VoteEngine : BaseEngine + { + public VoteEngine(IRepository votes, IPrincipal user, OmbiUserManager um, IRuleEvaluator r) : base(user, um, r) + { + _voteRepository = votes; + } + + private readonly IRepository _voteRepository; + + public async Task GetVotesForMovie(int requestId) + { + return await _voteRepository.GetAll().FirstOrDefaultAsync(x => x.RequestType == RequestType.Movie && x.RequestId == requestId); + } + public IQueryable GetVotesForMovie(IEnumerable requestIds) + { + return _voteRepository.GetAll().Where(x => x.RequestType == RequestType.Movie && requestIds.Contains(x.RequestId)); + } + + public async Task GetVotesForTv(int requestId) + { + return await _voteRepository.GetAll().FirstOrDefaultAsync(x => x.RequestType == RequestType.TvShow && x.RequestId == requestId); + } + + public IQueryable GetVotesForTv(IEnumerable requestIds) + { + return _voteRepository.GetAll().Where(x => x.RequestType == RequestType.TvShow && requestIds.Contains(x.RequestId)); + } + + public async Task UpvoteMovie(int requestId) + { + var user = await GetUser(); + await _voteRepository.Add(new Votes + { + Date = DateTime.UtcNow, + RequestId = requestId, + RequestType = RequestType.Movie, + UserId = user.Id, + VoteType = VoteType.Upvote + }); + } + + public async Task DownvoteMovie(int requestId) + { + var user = await GetUser(); + await _voteRepository.Add(new Votes + { + Date = DateTime.UtcNow, + RequestId = requestId, + RequestType = RequestType.Movie, + UserId = user.Id, + VoteType = VoteType.Downvote + }); + } + + public async Task UpvoteTv(int requestId) + { + var user = await GetUser(); + await _voteRepository.Add(new Votes + { + Date = DateTime.UtcNow, + RequestId = requestId, + RequestType = RequestType.TvShow, + UserId = user.Id, + VoteType = VoteType.Upvote + }); + } + + public async Task DownvoteTv(int requestId) + { + var user = await GetUser(); + await _voteRepository.Add(new Votes + { + Date = DateTime.UtcNow, + RequestId = requestId, + RequestType = RequestType.TvShow, + UserId = user.Id, + VoteType = VoteType.Downvote + }); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Store/Context/IOmbiContext.cs b/src/Ombi.Store/Context/IOmbiContext.cs index 55d7db563..03e528412 100644 --- a/src/Ombi.Store/Context/IOmbiContext.cs +++ b/src/Ombi.Store/Context/IOmbiContext.cs @@ -25,6 +25,7 @@ namespace Ombi.Store.Context DbSet Set() where TEntity : class; DbSet NotificationTemplates { get; set; } DbSet ApplicationConfigurations { get; set; } + DbSet Votes { get; set; } void Seed(); DbSet Audit { get; set; } DbSet MovieRequests { get; set; } diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index e4c9be516..6a408144f 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -39,6 +39,7 @@ namespace Ombi.Store.Context public DbSet IssueComments { get; set; } public DbSet RequestLogs { get; set; } public DbSet RecentlyAddedLogs { get; set; } + public DbSet Votes { get; set; } public DbSet Audit { get; set; } diff --git a/src/Ombi.Store/Entities/Votes.cs b/src/Ombi.Store/Entities/Votes.cs new file mode 100644 index 000000000..61e5651da --- /dev/null +++ b/src/Ombi.Store/Entities/Votes.cs @@ -0,0 +1,25 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Ombi.Store.Entities +{ + [Table("Votes")] + public class Votes : Entity + { + public int RequestId { get; set; } + public VoteType VoteType { get; set; } + public RequestType RequestType { get; set; } + public string UserId { get; set; } + public DateTime Date { get; set; } + public bool Deleted { get; set; } + + [ForeignKey(nameof(UserId))] + public OmbiUser User { get; set; } + } + + public enum VoteType + { + Upvote = 0, + Downvote = 1 + } +} \ No newline at end of file diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 1d7d67550..a3cb7f4be 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -169,6 +169,8 @@ namespace Ombi { // Generate a API Key settings.ApiKey = Guid.NewGuid().ToString("N"); + var userManager = app.ApplicationServices.GetService(); + userManager.CreateAsync(new OmbiUser {UserName = "API User", UserType = UserType.LocalUser}).Wait(); ombiService.SaveSettings(settings); } if (settings.BaseUrl.HasValue()) From 3120aae34b05b770aa10e271f74e8600801bb8a4 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 21 Sep 2018 22:07:29 +0100 Subject: [PATCH 02/74] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d0faf5b4..d0d86a692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## (unreleased) +## v3.0.3776 (2018-09-21) ### **New Features** From 938547fd75ca79d8f9c3ab0d09f7f3fc1ca2fd7b Mon Sep 17 00:00:00 2001 From: Jamie Date: Sat, 22 Sep 2018 22:53:32 +0100 Subject: [PATCH 03/74] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4485d3d2b..5bb60935c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.0.3776 (2018-09-21) +## v3.0.3786 (2018-09-22) ### **Fixes** From 7fe01206c9297f91438dd5edcadd2e2d41627809 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 23 Sep 2018 22:02:34 +0100 Subject: [PATCH 04/74] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5bf0fb75..cb87ae237 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## v3.0.3786 (2018-09-22) +## v3.0.3795 (2018-09-23) ### **Fixes** From 5b6283f6b834048415a330866979bf1143a689da Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 27 Sep 2018 13:18:19 +0100 Subject: [PATCH 05/74] Revert "Feature/purge issues" --- src/Ombi.DependencyInjection/IocExtensions.cs | 1 - src/Ombi.Schedule.Tests/IssuesPurgeTests.cs | 85 ------------------- .../Jobs/Lidarr/LidarrAvailabilityChecker.cs | 2 +- src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs | 9 -- src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs | 63 -------------- .../Settings/Models/IssueSettings.cs | 3 - .../Settings/Models/JobSettings.cs | 1 - .../Settings/Models/JobSettingsHelper.cs | 5 -- src/Ombi.Store/Entities/Requests/Issues.cs | 1 - .../ClientApp/app/interfaces/ISettings.ts | 3 - .../music/musicrequests.component.html | 2 +- .../app/settings/issues/issues.component.html | 14 --- .../app/settings/issues/issues.component.ts | 6 +- .../app/settings/jobs/jobs.component.html | 9 +- .../app/settings/jobs/jobs.component.ts | 1 - .../usermanagement-user.component.html | 12 +-- src/Ombi/Controllers/SettingsController.cs | 3 +- 17 files changed, 12 insertions(+), 208 deletions(-) delete mode 100644 src/Ombi.Schedule.Tests/IssuesPurgeTests.cs delete mode 100644 src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs delete mode 100644 src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 3af06b476..334b94675 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -191,7 +191,6 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); } } } diff --git a/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs deleted file mode 100644 index 932022cd8..000000000 --- a/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Moq; -using NUnit.Framework; -using Ombi.Core.Settings; -using Ombi.Schedule.Jobs.Ombi; -using Ombi.Settings.Settings.Models; -using Ombi.Store.Entities.Requests; -using Ombi.Store.Repository; -using System.Threading.Tasks; - -namespace Ombi.Schedule.Tests -{ - [TestFixture] - public class IssuesPurgeTests - { - - [SetUp] - public void Setup() - { - Repo = new Mock>(); - Settings = new Mock>(); - Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings()); - Job = new IssuesPurge(Repo.Object, Settings.Object); - } - - public Mock> Repo { get; set; } - public Mock> Settings { get; set; } - public IssuesPurge Job { get; set; } - - [Test] - public async Task DoesNotRun_WhenDisabled() - { - await Job.Start(); - Repo.Verify(x => x.GetAll(),Times.Never); - } - - [Test] - public async Task Deletes_Correct_Issue() - { - var issues = new List() - { - new Issues - { - Status = IssueStatus.Resolved, - ResovledDate = DateTime.Now.AddDays(-5).AddHours(-1) - } - }; - - Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); - Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); - await Job.Start(); - - Assert.That(issues.First().Status, Is.EqualTo(IssueStatus.Deleted)); - Repo.Verify(x => x.SaveChangesAsync(), Times.Once); - } - - [Test] - public async Task DoesNot_Delete_AnyIssues() - { - var issues = new List() - { - new Issues - { - Status = IssueStatus.Resolved, - ResovledDate = DateTime.Now.AddDays(-2) - }, - new Issues - { - Status = IssueStatus.Resolved, - ResovledDate = DateTime.Now.AddDays(-6) - } - }; - - Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); - Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); - await Job.Start(); - - Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted)); - Assert.That(issues[1].Status, Is.EqualTo(IssueStatus.Deleted)); - Repo.Verify(x => x.SaveChangesAsync(), Times.Once); - } - } -} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs index 9db24784d..d5ba14a6d 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs @@ -43,7 +43,7 @@ namespace Ombi.Schedule.Jobs.Lidarr var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId)); if (cachedAlbum != null) { - if (cachedAlbum.FullyAvailable) + if (cachedAlbum.Monitored && cachedAlbum.FullyAvailable) { request.Available = true; request.MarkedAsAvailable = DateTime.Now; diff --git a/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs deleted file mode 100644 index fbd1e3aaf..000000000 --- a/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace Ombi.Schedule.Jobs.Ombi -{ - public interface IIssuesPurge : IBaseJob - { - Task Start(); - } -} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs deleted file mode 100644 index 92ca31071..000000000 --- a/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Ombi.Core.Settings; -using Ombi.Settings.Settings.Models; -using Ombi.Store.Entities.Requests; -using Ombi.Store.Repository; - -namespace Ombi.Schedule.Jobs.Ombi -{ - public class IssuesPurge : IIssuesPurge - { - public IssuesPurge(IRepository issuesRepo, ISettingsService issueSettings) - { - _issuesRepository = issuesRepo; - _settings = issueSettings; - _settings.ClearCache(); - } - - private readonly IRepository _issuesRepository; - private readonly ISettingsService _settings; - - public async Task Start() - { - var settings = await _settings.GetSettingsAsync(); - if (!settings.DeleteIssues) - { - return; - } - - var now = DateTime.Now.AddDays(-settings.DaysAfterResolvedToDelete).Date; - var resolved = _issuesRepository.GetAll().Where(x => x.Status == IssueStatus.Resolved); - var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date <= now); - - foreach (var d in toDelete) - { - d.Status = IssueStatus.Deleted; - } - - await _issuesRepository.SaveChangesAsync(); - } - - private bool _disposed; - protected virtual void Dispose(bool disposing) - { - if (_disposed) - return; - - if (disposing) - { - _issuesRepository?.Dispose(); - _settings?.Dispose(); - } - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/IssueSettings.cs b/src/Ombi.Settings/Settings/Models/IssueSettings.cs index d7a35c0d9..e025c82d1 100644 --- a/src/Ombi.Settings/Settings/Models/IssueSettings.cs +++ b/src/Ombi.Settings/Settings/Models/IssueSettings.cs @@ -4,8 +4,5 @@ { public bool Enabled { get; set; } public bool EnableInProgress { get; set; } - - public bool DeleteIssues { get; set; } - public int DaysAfterResolvedToDelete { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettings.cs b/src/Ombi.Settings/Settings/Models/JobSettings.cs index 8b283cdf7..48c721e29 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettings.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettings.cs @@ -14,6 +14,5 @@ public string RefreshMetadata { get; set; } public string Newsletter { get; set; } public string LidarrArtistSync { get; set; } - public string IssuesPurge { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index 4491ca27a..0f8fec5fd 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -57,11 +57,6 @@ namespace Ombi.Settings.Settings.Models return Get(s.LidarrArtistSync, Cron.Hourly(40)); } - public static string IssuePurge(JobSettings s) - { - return Get(s.IssuesPurge, Cron.Daily()); - } - private static string Get(string settings, string defaultCron) { return settings.HasValue() ? settings : defaultCron; diff --git a/src/Ombi.Store/Entities/Requests/Issues.cs b/src/Ombi.Store/Entities/Requests/Issues.cs index 9fbc6a83e..b1021e362 100644 --- a/src/Ombi.Store/Entities/Requests/Issues.cs +++ b/src/Ombi.Store/Entities/Requests/Issues.cs @@ -29,6 +29,5 @@ namespace Ombi.Store.Entities.Requests Pending = 0, InProgress = 1, Resolved = 2, - Deleted = 3, } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index 7a889e722..db4db935e 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -144,14 +144,11 @@ export interface IJobSettings { newsletter: string; plexRecentlyAddedSync: string; lidarrArtistSync: string; - issuesPurge: string; } export interface IIssueSettings extends ISettings { enabled: boolean; enableInProgress: boolean; - deleteIssues: boolean; - daysAfterResolvedToDelete: number; } export interface IAuthenticationSettings extends ISettings { diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html index f6ad4bb2a..c89c2be0a 100644 --- a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html @@ -59,7 +59,7 @@

- {{request.title | truncate: 36}} + {{request.title}}

diff --git a/src/Ombi/ClientApp/app/settings/issues/issues.component.html b/src/Ombi/ClientApp/app/settings/issues/issues.component.html index ee780a1a1..e30d84e26 100644 --- a/src/Ombi/ClientApp/app/settings/issues/issues.component.html +++ b/src/Ombi/ClientApp/app/settings/issues/issues.component.html @@ -18,20 +18,6 @@
-
-
- - -
-
- -
-
- - -
-
-
diff --git a/src/Ombi/ClientApp/app/settings/issues/issues.component.ts b/src/Ombi/ClientApp/app/settings/issues/issues.component.ts index 37ead870a..c8a85e8e1 100644 --- a/src/Ombi/ClientApp/app/settings/issues/issues.component.ts +++ b/src/Ombi/ClientApp/app/settings/issues/issues.component.ts @@ -21,10 +21,8 @@ export class IssuesComponent implements OnInit { public ngOnInit() { this.settingsService.getIssueSettings().subscribe(x => { this.form = this.fb.group({ - enabled: [x.enabled], - enableInProgress: [x.enableInProgress], - deleteIssues: [x.deleteIssues], - daysAfterResolvedToDelete: [x.daysAfterResolvedToDelete], + enabled: [x.enabled], + enableInProgress: [x.enableInProgress], }); }); this.getCategories(); diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html index 1365710f0..a4dcd6fb3 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html @@ -85,19 +85,12 @@
-
+
The Newsletter is required
- -
- - - The Issues Purge is required - -
diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts index d8ce106ae..756d6ba89 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts @@ -35,7 +35,6 @@ export class JobsComponent implements OnInit { newsletter: [x.newsletter, Validators.required], plexRecentlyAddedSync: [x.plexRecentlyAddedSync, Validators.required], lidarrArtistSync: [x.lidarrArtistSync, Validators.required], - issuesPurge: [x.issuesPurge, Validators.required], }); }); } diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html index 6f3ae63ee..0d13b17c3 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html @@ -144,7 +144,7 @@
-
+
@@ -162,7 +162,7 @@
-
+
@@ -179,7 +179,7 @@
-
+
-
+
- -
+ +
+
@@ -52,8 +53,8 @@
- +
diff --git a/src/Ombi/ClientApp/app/settings/issues/issues.component.ts b/src/Ombi/ClientApp/app/settings/issues/issues.component.ts index 37ead870a..cfe0bd65c 100644 --- a/src/Ombi/ClientApp/app/settings/issues/issues.component.ts +++ b/src/Ombi/ClientApp/app/settings/issues/issues.component.ts @@ -55,6 +55,11 @@ export class IssuesComponent implements OnInit { const settings = form.value; + if(settings.deleteIssues && settings.daysAfterResolvedToDelete <= 0) { + this.notificationService.error("You need to enter days greater than 0"); + return; + } + this.settingsService.saveIssueSettings(settings).subscribe(x => { if (x) { this.notificationService.success("Successfully saved the Issue settings"); From 3f7eb804709e7447a05a5e26b0a81de148190358 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 28 Sep 2018 21:00:05 +0100 Subject: [PATCH 11/74] !wip done api --- src/Ombi.Core/Engine/IVoteEngine.cs | 5 +- src/Ombi.Core/Engine/VoteEngine.cs | 54 ++++++++++++++++++++ src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 4 +- src/Ombi/Controllers/VoteController.cs | 7 +++ 4 files changed, 67 insertions(+), 3 deletions(-) diff --git a/src/Ombi.Core/Engine/IVoteEngine.cs b/src/Ombi.Core/Engine/IVoteEngine.cs index 45b0761a4..681f333c5 100644 --- a/src/Ombi.Core/Engine/IVoteEngine.cs +++ b/src/Ombi.Core/Engine/IVoteEngine.cs @@ -1,6 +1,8 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Ombi.Core.Models; +using Ombi.Core.Models.UI; using Ombi.Store.Entities; namespace Ombi.Core.Engine @@ -12,5 +14,6 @@ namespace Ombi.Core.Engine IQueryable GetVotes(int requestId, RequestType requestType); Task RemoveCurrentVote(Votes currentVote); Task UpVote(int requestId, RequestType requestType); + Task> GetMovieViewModel(); } } \ No newline at end of file diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs index ecd22e848..3875b6705 100644 --- a/src/Ombi.Core/Engine/VoteEngine.cs +++ b/src/Ombi.Core/Engine/VoteEngine.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Principal; +using System.Text; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Ombi.Core.Authentication; @@ -10,6 +11,7 @@ using Ombi.Core.Models; using Ombi.Core.Models.UI; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Settings; +using Ombi.Schedule.Jobs.Ombi; using Ombi.Settings.Settings.Models; using Ombi.Store.Entities; using Ombi.Store.Repository; @@ -38,6 +40,8 @@ namespace Ombi.Core.Engine { var vm = new List(); var movieRequests = await _movieRequestEngine.GetRequests(); + var tvRequestsTask = _tvRequestEngine.GetRequestsLite(); + var musicRequestsTask = _musicRequestEngine.GetRequests(); foreach (var r in movieRequests) { // Make model @@ -57,6 +61,56 @@ namespace Ombi.Core.Engine }); } + foreach (var r in await musicRequestsTask) + { + // Make model + var votes = GetVotes(r.Id, RequestType.Movie); + var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); + var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); + vm.Add(new VoteViewModel + { + Upvotes = upVotes, + Downvotes = downVotes, + RequestId = r.Id, + RequestType = RequestType.Movie, + Title = r.Title, + Image = r.Disk, + Background = r.Cover, + Description = r.ArtistName + }); + } + + foreach (var r in await tvRequestsTask) + { + // Make model + var votes = GetVotes(r.Id, RequestType.Movie); + var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); + var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); + + var finalsb = new StringBuilder(); + foreach (var childRequests in r.ChildRequests) + { + foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber)) + { + var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); + var episodeString = NewsletterJob.BuildEpisodeList(orderedEpisodes.Select(x => x.EpisodeNumber)); + finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); + finalsb.Append("
"); + } + } + vm.Add(new VoteViewModel + { + Upvotes = upVotes, + Downvotes = downVotes, + RequestId = r.Id, + RequestType = RequestType.Movie, + Title = r.Title, + Image = r.PosterPath, + Background = r.Background, + Description = finalsb.ToString() + }); + } + return vm; } diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index f152f6b4b..9f5e58482 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -654,7 +654,7 @@ namespace Ombi.Schedule.Jobs.Ombi AddInfoTable(sb); var title = ""; - if (!String.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) + if (!string.IsNullOrEmpty(info.premiered) && info.premiered.Length > 4) { title = $"{t.Title} ({info.premiered.Remove(4)})"; } else @@ -715,7 +715,7 @@ namespace Ombi.Schedule.Jobs.Ombi } } - public string BuildEpisodeList(IEnumerable orderedEpisodes) + public static string BuildEpisodeList(IEnumerable orderedEpisodes) { var epSb = new StringBuilder(); var previousEpisodes = new List(); diff --git a/src/Ombi/Controllers/VoteController.cs b/src/Ombi/Controllers/VoteController.cs index 5039626be..f1466e3ed 100644 --- a/src/Ombi/Controllers/VoteController.cs +++ b/src/Ombi/Controllers/VoteController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; using Ombi.Core.Engine; +using Ombi.Core.Models.UI; using Ombi.Store.Entities; namespace Ombi.Controllers @@ -20,6 +21,12 @@ namespace Ombi.Controllers private readonly IVoteEngine _engine; + [HttpGet] + public Task> GetView() + { + return _engine.GetMovieViewModel(); + } + /// /// Get's all the votes for the request id /// From bb4d59e35588a7ad4bc9818d14c8102b65416e16 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 28 Sep 2018 21:02:30 +0100 Subject: [PATCH 12/74] Removed the pinID from the OAuth url #2548 --- src/Ombi.Api.Plex/IPlexApi.cs | 2 +- src/Ombi.Api.Plex/PlexApi.cs | 3 +-- src/Ombi.Core/Authentication/PlexOAuthManager.cs | 8 ++++---- src/Ombi.Core/IPlexOAuthManager.cs | 4 ++-- src/Ombi/Controllers/External/PlexController.cs | 4 ++-- src/Ombi/Controllers/TokenController.cs | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Ombi.Api.Plex/IPlexApi.cs b/src/Ombi.Api.Plex/IPlexApi.cs index 343eaa2d7..c79ec50c9 100644 --- a/src/Ombi.Api.Plex/IPlexApi.cs +++ b/src/Ombi.Api.Plex/IPlexApi.cs @@ -24,7 +24,7 @@ namespace Ombi.Api.Plex Task GetAccount(string authToken); Task GetRecentlyAdded(string authToken, string uri, string sectionId); Task GetPin(int pinId); - Task GetOAuthUrl(int pinId, string code, string applicationUrl); + Task GetOAuthUrl(string code, string applicationUrl); Task AddUser(string emailAddress, string serverId, string authToken, int[] libs); } } \ No newline at end of file diff --git a/src/Ombi.Api.Plex/PlexApi.cs b/src/Ombi.Api.Plex/PlexApi.cs index f0808622f..fe6ba23e2 100644 --- a/src/Ombi.Api.Plex/PlexApi.cs +++ b/src/Ombi.Api.Plex/PlexApi.cs @@ -217,12 +217,11 @@ namespace Ombi.Api.Plex return await Api.Request(request); } - public async Task GetOAuthUrl(int pinId, string code, string applicationUrl) + public async Task GetOAuthUrl(string code, string applicationUrl) { var request = new Request("auth#", "https://app.plex.tv", HttpMethod.Get); await AddHeaders(request); - request.AddQueryString("pinID", pinId.ToString()); request.AddQueryString("code", code); request.AddQueryString("context[device][product]", ApplicationName); request.AddQueryString("context[device][environment]", "bundled"); diff --git a/src/Ombi.Core/Authentication/PlexOAuthManager.cs b/src/Ombi.Core/Authentication/PlexOAuthManager.cs index 426037bb7..76b1b5d97 100644 --- a/src/Ombi.Core/Authentication/PlexOAuthManager.cs +++ b/src/Ombi.Core/Authentication/PlexOAuthManager.cs @@ -36,17 +36,17 @@ namespace Ombi.Core.Authentication return await _api.GetAccount(accessToken); } - public async Task GetOAuthUrl(int pinId, string code, string websiteAddress = null) + public async Task GetOAuthUrl(string code, string websiteAddress = null) { var settings = await _customizationSettingsService.GetSettingsAsync(); - var url = await _api.GetOAuthUrl(pinId, code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl); + var url = await _api.GetOAuthUrl(code, settings.ApplicationUrl.IsNullOrEmpty() ? websiteAddress : settings.ApplicationUrl); return url; } - public async Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress) + public async Task GetWizardOAuthUrl(string code, string websiteAddress) { - var url = await _api.GetOAuthUrl(pinId, code, websiteAddress); + var url = await _api.GetOAuthUrl(code, websiteAddress); return url; } } diff --git a/src/Ombi.Core/IPlexOAuthManager.cs b/src/Ombi.Core/IPlexOAuthManager.cs index a5c0c44ff..7668ee882 100644 --- a/src/Ombi.Core/IPlexOAuthManager.cs +++ b/src/Ombi.Core/IPlexOAuthManager.cs @@ -7,8 +7,8 @@ namespace Ombi.Core.Authentication public interface IPlexOAuthManager { Task GetAccessTokenFromPin(int pinId); - Task GetOAuthUrl(int pinId, string code, string websiteAddress = null); - Task GetWizardOAuthUrl(int pinId, string code, string websiteAddress); + Task GetOAuthUrl(string code, string websiteAddress = null); + Task GetWizardOAuthUrl(string code, string websiteAddress); Task GetAccount(string accessToken); } } \ No newline at end of file diff --git a/src/Ombi/Controllers/External/PlexController.cs b/src/Ombi/Controllers/External/PlexController.cs index d7cefd091..f0492b580 100644 --- a/src/Ombi/Controllers/External/PlexController.cs +++ b/src/Ombi/Controllers/External/PlexController.cs @@ -283,12 +283,12 @@ namespace Ombi.Controllers.External Uri url; if (!wizard.Wizard) { - url = await _plexOAuthManager.GetOAuthUrl(wizard.Pin.id, wizard.Pin.code); + url = await _plexOAuthManager.GetOAuthUrl(wizard.Pin.code); } else { var websiteAddress =$"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; - url = await _plexOAuthManager.GetWizardOAuthUrl(wizard.Pin.id, wizard.Pin.code, websiteAddress); + url = await _plexOAuthManager.GetWizardOAuthUrl(wizard.Pin.code, websiteAddress); } if (url == null) diff --git a/src/Ombi/Controllers/TokenController.cs b/src/Ombi/Controllers/TokenController.cs index 1314a741a..87136fd00 100644 --- a/src/Ombi/Controllers/TokenController.cs +++ b/src/Ombi/Controllers/TokenController.cs @@ -83,7 +83,7 @@ namespace Ombi.Controllers var websiteAddress = $"{this.Request.Scheme}://{this.Request.Host}{this.Request.PathBase}"; //https://app.plex.tv/auth#?forwardUrl=http://google.com/&clientID=Ombi-Test&context%5Bdevice%5D%5Bproduct%5D=Ombi%20SSO&pinID=798798&code=4lgfd - var url = await _plexOAuthManager.GetOAuthUrl(model.PlexTvPin.id, model.PlexTvPin.code, websiteAddress); + var url = await _plexOAuthManager.GetOAuthUrl(model.PlexTvPin.code, websiteAddress); if (url == null) { return new JsonResult(new From 660df9912315a4679656c18a86b9a75a32fc350d Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 28 Sep 2018 23:21:01 +0100 Subject: [PATCH 13/74] Got most of the UI done for the voting feature !wip --- src/Ombi.Core/Engine/VoteEngine.cs | 16 +- src/Ombi.Schedule.Tests/NewsletterTests.cs | 14 +- .../20180928201334_Votes.Designer.cs | 1182 +++++++++++++++++ .../Migrations/20180928201334_Votes.cs | 46 + .../Migrations/OmbiContextModelSnapshot.cs | 31 + src/Ombi/ClientApp/app/app.module.ts | 4 +- .../ClientApp/app/interfaces/IRequestModel.ts | 1 + src/Ombi/ClientApp/app/interfaces/IVote.ts | 23 + src/Ombi/ClientApp/app/interfaces/index.ts | 1 + src/Ombi/ClientApp/app/services/index.ts | 1 + .../ClientApp/app/services/vote.service.ts | 36 + .../ClientApp/app/vote/vote.component.html | 35 + .../ClientApp/app/vote/vote.component.scss | 12 + src/Ombi/ClientApp/app/vote/vote.component.ts | 70 + src/Ombi/ClientApp/app/vote/vote.module.ts | 41 + src/Ombi/ClientApp/styles/base.scss | 3 +- src/Ombi/Controllers/VoteController.cs | 58 + 17 files changed, 1551 insertions(+), 23 deletions(-) create mode 100644 src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs create mode 100644 src/Ombi.Store/Migrations/20180928201334_Votes.cs create mode 100644 src/Ombi/ClientApp/app/interfaces/IVote.ts create mode 100644 src/Ombi/ClientApp/app/services/vote.service.ts create mode 100644 src/Ombi/ClientApp/app/vote/vote.component.html create mode 100644 src/Ombi/ClientApp/app/vote/vote.component.scss create mode 100644 src/Ombi/ClientApp/app/vote/vote.component.ts create mode 100644 src/Ombi/ClientApp/app/vote/vote.module.ts diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs index 3875b6705..8f70a1ed5 100644 --- a/src/Ombi.Core/Engine/VoteEngine.cs +++ b/src/Ombi.Core/Engine/VoteEngine.cs @@ -40,7 +40,7 @@ namespace Ombi.Core.Engine { var vm = new List(); var movieRequests = await _movieRequestEngine.GetRequests(); - var tvRequestsTask = _tvRequestEngine.GetRequestsLite(); + var tvRequestsTask = _tvRequestEngine.GetRequests(); var musicRequestsTask = _musicRequestEngine.GetRequests(); foreach (var r in movieRequests) { @@ -55,8 +55,8 @@ namespace Ombi.Core.Engine RequestId = r.Id, RequestType = RequestType.Movie, Title = r.Title, - Image = r.PosterPath, - Background = r.Background, + Image = $"https://image.tmdb.org/t/p/w500/{r.PosterPath}", + Background = $"https://image.tmdb.org/t/p/w1280{r.Background}", Description = r.Overview }); } @@ -64,7 +64,7 @@ namespace Ombi.Core.Engine foreach (var r in await musicRequestsTask) { // Make model - var votes = GetVotes(r.Id, RequestType.Movie); + var votes = GetVotes(r.Id, RequestType.Album); var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); vm.Add(new VoteViewModel @@ -72,9 +72,9 @@ namespace Ombi.Core.Engine Upvotes = upVotes, Downvotes = downVotes, RequestId = r.Id, - RequestType = RequestType.Movie, + RequestType = RequestType.Album, Title = r.Title, - Image = r.Disk, + Image = r.Cover, Background = r.Cover, Description = r.ArtistName }); @@ -83,7 +83,7 @@ namespace Ombi.Core.Engine foreach (var r in await tvRequestsTask) { // Make model - var votes = GetVotes(r.Id, RequestType.Movie); + var votes = GetVotes(r.Id, RequestType.TvShow); var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); @@ -103,7 +103,7 @@ namespace Ombi.Core.Engine Upvotes = upVotes, Downvotes = downVotes, RequestId = r.Id, - RequestType = RequestType.Movie, + RequestType = RequestType.TvShow, Title = r.Title, Image = r.PosterPath, Background = r.Background, diff --git a/src/Ombi.Schedule.Tests/NewsletterTests.cs b/src/Ombi.Schedule.Tests/NewsletterTests.cs index b3c2ce98a..146cd97cf 100644 --- a/src/Ombi.Schedule.Tests/NewsletterTests.cs +++ b/src/Ombi.Schedule.Tests/NewsletterTests.cs @@ -1,11 +1,6 @@ using System.Collections.Generic; -using Moq; using NUnit.Framework; -using Ombi.Core.Settings; -using Ombi.Schedule.Jobs.Ombi; -using Ombi.Settings.Settings.Models; -using Ombi.Settings.Settings.Models.Notifications; -using Ombi.Store.Entities; +using static Ombi.Schedule.Jobs.Ombi.NewsletterJob; namespace Ombi.Schedule.Tests { @@ -15,17 +10,12 @@ namespace Ombi.Schedule.Tests [TestCaseSource(nameof(EpisodeListData))] public string BuildEpisodeListTest(List episodes) { - var emailSettings = new Mock>(); - var customziation = new Mock>(); - var newsletterSettings = new Mock>(); - var newsletter = new NewsletterJob(null, null, null, null, null, null, customziation.Object, emailSettings.Object, null, null, newsletterSettings.Object, null, null, null, null); - var ep = new List(); foreach (var i in episodes) { ep.Add(i); } - var result = newsletter.BuildEpisodeList(ep); + var result = BuildEpisodeList(ep); return result; } diff --git a/src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs b/src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs new file mode 100644 index 000000000..5d9b47c26 --- /dev/null +++ b/src/Ombi.Store/Migrations/20180928201334_Votes.Designer.cs @@ -0,0 +1,1182 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Ombi.Store.Context; + +namespace Ombi.Store.Migrations +{ + [DbContext(typeof(OmbiContext))] + [Migration("20180928201334_Votes")] + partial class Votes + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + 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.LidarrAlbumCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AddedAt"); + + b.Property("ArtistId"); + + b.Property("ForeignAlbumId"); + + b.Property("Monitored"); + + b.Property("PercentOfTracks"); + + b.Property("ReleaseDate"); + + b.Property("Title"); + + b.Property("TrackCount"); + + b.HasKey("Id"); + + b.ToTable("LidarrAlbumCache"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.LidarrArtistCache", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ArtistId"); + + b.Property("ArtistName"); + + b.Property("ForeignArtistId"); + + b.Property("Monitored"); + + b.HasKey("Id"); + + b.ToTable("LidarrArtistCache"); + }); + + 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("MusicRequestLimit"); + + 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("AlbumId"); + + 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.AlbumRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Approved"); + + b.Property("ArtistName"); + + b.Property("Available"); + + b.Property("Cover"); + + b.Property("Denied"); + + b.Property("DeniedReason"); + + b.Property("Disk"); + + b.Property("ForeignAlbumId"); + + b.Property("ForeignArtistId"); + + b.Property("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + b.Property("Rating"); + + b.Property("ReleaseDate"); + + b.Property("RequestType"); + + b.Property("RequestedDate"); + + b.Property("RequestedUserId"); + + b.Property("Title"); + + b.HasKey("Id"); + + b.HasIndex("RequestedUserId"); + + b.ToTable("AlbumRequests"); + }); + + 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("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + 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("MarkedAsApproved"); + + b.Property("MarkedAsAvailable"); + + b.Property("MarkedAsDenied"); + + 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.Entities.UserNotificationPreferences", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Agent"); + + b.Property("Enabled"); + + b.Property("UserId"); + + b.Property("Value"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserNotificationPreferences"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("RadarrQualityProfile"); + + b.Property("RadarrRootPath"); + + b.Property("SonarrQualityProfile"); + + b.Property("SonarrQualityProfileAnime"); + + b.Property("SonarrRootPath"); + + b.Property("SonarrRootPathAnime"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserQualityProfiles"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + + 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.AlbumRequest", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "RequestedUser") + .WithMany() + .HasForeignKey("RequestedUserId"); + }); + + 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.Entities.UserNotificationPreferences", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany("UserNotificationPreferences") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.UserQualityProfiles", b => + { + b.HasOne("Ombi.Store.Entities.OmbiUser", "User") + .WithMany() + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("Ombi.Store.Entities.Votes", 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/20180928201334_Votes.cs b/src/Ombi.Store/Migrations/20180928201334_Votes.cs new file mode 100644 index 000000000..1c64a19aa --- /dev/null +++ b/src/Ombi.Store/Migrations/20180928201334_Votes.cs @@ -0,0 +1,46 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Ombi.Store.Migrations +{ + public partial class Votes : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Votes", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RequestId = table.Column(nullable: false), + VoteType = table.Column(nullable: false), + RequestType = table.Column(nullable: false), + UserId = table.Column(nullable: true), + Date = table.Column(nullable: false), + Deleted = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Votes", x => x.Id); + table.ForeignKey( + name: "FK_Votes_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_Votes_UserId", + table: "Votes", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Votes"); + } + } +} diff --git a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs index 86694ec23..60927b1ed 100644 --- a/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs +++ b/src/Ombi.Store/Migrations/OmbiContextModelSnapshot.cs @@ -916,6 +916,30 @@ namespace Ombi.Store.Migrations b.ToTable("UserQualityProfiles"); }); + modelBuilder.Entity("Ombi.Store.Entities.Votes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Date"); + + b.Property("Deleted"); + + b.Property("RequestId"); + + b.Property("RequestType"); + + b.Property("UserId"); + + b.Property("VoteType"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Votes"); + }); + modelBuilder.Entity("Ombi.Store.Repository.Requests.EpisodeRequests", b => { b.Property("Id") @@ -1128,6 +1152,13 @@ namespace Ombi.Store.Migrations .HasForeignKey("UserId"); }); + modelBuilder.Entity("Ombi.Store.Entities.Votes", 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") diff --git a/src/Ombi/ClientApp/app/app.module.ts b/src/Ombi/ClientApp/app/app.module.ts index d6eb29b53..0f268e4d2 100644 --- a/src/Ombi/ClientApp/app/app.module.ts +++ b/src/Ombi/ClientApp/app/app.module.ts @@ -15,7 +15,7 @@ import { TranslateLoader, TranslateModule } from "@ngx-translate/core"; import { TranslateHttpLoader } from "@ngx-translate/http-loader"; import { CookieService } from "ng2-cookies"; import { GrowlModule } from "primeng/components/growl/growl"; -import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng"; +import { ButtonModule, CaptchaModule, ConfirmationService, ConfirmDialogModule, DataTableModule, DialogModule, OverlayPanelModule, SharedModule, SidebarModule, TooltipModule } from "primeng/primeng"; // Components import { AppComponent } from "./app.component"; @@ -55,6 +55,7 @@ const routes: Routes = [ { loadChildren: "./requests/requests.module#RequestsModule", path: "requests" }, { loadChildren: "./search/search.module#SearchModule", path: "search" }, { loadChildren: "./recentlyAdded/recentlyAdded.module#RecentlyAddedModule", path: "recentlyadded" }, + { loadChildren: "./vote/vote.module#VoteModule", path: "vote" }, ]; // AoT requires an exported function for factories @@ -97,6 +98,7 @@ export function JwtTokenGetter() { CaptchaModule, TooltipModule, ConfirmDialogModule, + OverlayPanelModule, CommonModule, JwtModule.forRoot({ config: { diff --git a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts index f7fe3df87..310aaa6fd 100644 --- a/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/app/interfaces/IRequestModel.ts @@ -3,6 +3,7 @@ export enum RequestType { movie = 1, tvShow = 2, + } // NEW WORLD diff --git a/src/Ombi/ClientApp/app/interfaces/IVote.ts b/src/Ombi/ClientApp/app/interfaces/IVote.ts new file mode 100644 index 000000000..fb772fea6 --- /dev/null +++ b/src/Ombi/ClientApp/app/interfaces/IVote.ts @@ -0,0 +1,23 @@ +export interface IVoteViewModel { + requestId: number; + requestType: RequestTypes; + image: string; + background: string; + upvotes: number; + downvotes: number; + title: string; + description: string; +} + +export interface IVoteEngineResult { + result: boolean; + message: string; + isError: boolean; + errorMessage: string; +} + +export enum RequestTypes { + TvShow = 0, + Movie = 1, + Album = 2, +} diff --git a/src/Ombi/ClientApp/app/interfaces/index.ts b/src/Ombi/ClientApp/app/interfaces/index.ts index 74a39099e..bfc89edc6 100644 --- a/src/Ombi/ClientApp/app/interfaces/index.ts +++ b/src/Ombi/ClientApp/app/interfaces/index.ts @@ -16,3 +16,4 @@ export * from "./IIssues"; export * from "./IRecentlyAdded"; export * from "./ILidarr"; export * from "./ISearchMusicResult"; +export * from "./IVote"; diff --git a/src/Ombi/ClientApp/app/services/index.ts b/src/Ombi/ClientApp/app/services/index.ts index 59a299d3e..c1ccc2585 100644 --- a/src/Ombi/ClientApp/app/services/index.ts +++ b/src/Ombi/ClientApp/app/services/index.ts @@ -14,3 +14,4 @@ export * from "./issues.service"; export * from "./mobile.service"; export * from "./notificationMessage.service"; export * from "./recentlyAdded.service"; +export * from "./vote.service"; diff --git a/src/Ombi/ClientApp/app/services/vote.service.ts b/src/Ombi/ClientApp/app/services/vote.service.ts new file mode 100644 index 000000000..375f0fc33 --- /dev/null +++ b/src/Ombi/ClientApp/app/services/vote.service.ts @@ -0,0 +1,36 @@ +import { PlatformLocation } from "@angular/common"; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; + +import { IVoteEngineResult, IVoteViewModel } from "../interfaces"; +import { ServiceHelpers } from "./service.helpers"; + +@Injectable() +export class VoteService extends ServiceHelpers { + constructor(public http: HttpClient, public platformLocation: PlatformLocation) { + super(http, "/api/v1/Vote/", platformLocation); + } + + public async getModel(): Promise { + return await this.http.get(`${this.url}`, {headers: this.headers}).toPromise(); + } + + public async upvoteMovie(requestId: number): Promise { + return await this.http.post(`${this.url}up/movie/${requestId}`, {headers: this.headers}).toPromise(); + } + public async upvoteTv(requestId: number): Promise { + return await this.http.post(`${this.url}up/tv/${requestId}`, {headers: this.headers}).toPromise(); + } + public async upvoteAlbum(requestId: number): Promise { + return await this.http.post(`${this.url}up/album/${requestId}`, {headers: this.headers}).toPromise(); + } + public async downvoteMovie(requestId: number): Promise { + return await this.http.post(`${this.url}down/movie/${requestId}`, {headers: this.headers}).toPromise(); + } + public async downvoteTv(requestId: number): Promise { + return await this.http.post(`${this.url}down/tv/${requestId}`, {headers: this.headers}).toPromise(); + } + public async downvoteAlbum(requestId: number): Promise { + return await this.http.post(`${this.url}down/album/${requestId}`, {headers: this.headers}).toPromise(); + } +} diff --git a/src/Ombi/ClientApp/app/vote/vote.component.html b/src/Ombi/ClientApp/app/vote/vote.component.html new file mode 100644 index 000000000..eaeb3ca5e --- /dev/null +++ b/src/Ombi/ClientApp/app/vote/vote.component.html @@ -0,0 +1,35 @@ +

Vote

+ +
+ + + + + + + + + + + + + + + + + + +
TitleDescription
+ + + poster{{vm.title}}
+ + +
+ + + poster + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/vote/vote.component.scss b/src/Ombi/ClientApp/app/vote/vote.component.scss new file mode 100644 index 000000000..a57fb9749 --- /dev/null +++ b/src/Ombi/ClientApp/app/vote/vote.component.scss @@ -0,0 +1,12 @@ +.vcenter { + vertical-align: middle; + float: none; +} + +.hideBackground { + border: 0px solid transparent !important; + background: transparent !important; + -webkit-box-shadow: 0 0px 0px 0 transparent !important; + -moz-box-shadow: 0 0px 0px 0 transparent !important; + box-shadow: 0 0px 0px 0 transparent !important; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/vote/vote.component.ts b/src/Ombi/ClientApp/app/vote/vote.component.ts new file mode 100644 index 000000000..908c1759a --- /dev/null +++ b/src/Ombi/ClientApp/app/vote/vote.component.ts @@ -0,0 +1,70 @@ +import { Component, OnInit, ViewChild } from "@angular/core"; + +import { OverlayPanel } from "primeng/primeng"; +import { NotificationService, VoteService } from "../services"; + +import { IVoteEngineResult, IVoteViewModel, RequestTypes } from "../interfaces"; + +@Component({ + templateUrl: "vote.component.html", + styleUrls: ["vote.component.scss"], +}) +export class VoteComponent implements OnInit { + + public viewModel: IVoteViewModel[]; + public panelImage: string; + @ViewChild("op") public overlayPanel: OverlayPanel; + + constructor(private voteService: VoteService, private notificationSerivce: NotificationService) { } + + public async ngOnInit() { + this.viewModel = await this.voteService.getModel(); + } + + public toggle(event: any, image: string) { + this.panelImage = image; + this.overlayPanel.toggle(event); + } + + public async upvote(vm: IVoteViewModel) { + let result: IVoteEngineResult = {errorMessage:"", isError: false, message:"",result:false}; + switch(vm.requestType) { + case RequestTypes.Album: + result = await this.voteService.upvoteAlbum(vm.requestId); + break; + case RequestTypes.Movie: + result = await this.voteService.upvoteMovie(vm.requestId); + break; + case RequestTypes.TvShow: + result = await this.voteService.upvoteTv(vm.requestId); + break; + } + + if(result.isError) { + this.notificationSerivce.error(result.errorMessage); + } else { + this.notificationSerivce.success("Voted!"); + } + } + + public async downvote(vm: IVoteViewModel) { + let result: IVoteEngineResult = {errorMessage:"", isError: false, message:"",result:false}; + switch(vm.requestType) { + case RequestTypes.Album: + result = await this.voteService.downvoteAlbum(vm.requestId); + break; + case RequestTypes.Movie: + result = await this.voteService.downvoteMovie(vm.requestId); + break; + case RequestTypes.TvShow: + result = await this.voteService.downvoteTv(vm.requestId); + break; + } + + if(result.isError) { + this.notificationSerivce.error(result.errorMessage); + } else { + this.notificationSerivce.success("Voted!"); + } + } +} diff --git a/src/Ombi/ClientApp/app/vote/vote.module.ts b/src/Ombi/ClientApp/app/vote/vote.module.ts new file mode 100644 index 000000000..ee7532da5 --- /dev/null +++ b/src/Ombi/ClientApp/app/vote/vote.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from "@angular/core"; +import { RouterModule, Routes } from "@angular/router"; + +import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; +import { OrderModule } from "ngx-order-pipe"; +import { OverlayPanelModule, SharedModule, TabViewModule } from "primeng/primeng"; + +import { VoteService } from "../services"; + +import { AuthGuard } from "../auth/auth.guard"; + +import { SharedModule as OmbiShared } from "../shared/shared.module"; + +import { VoteComponent } from "./vote.component"; + +const routes: Routes = [ + { path: "", component: VoteComponent, canActivate: [AuthGuard] }, +]; + +@NgModule({ + imports: [ + RouterModule.forChild(routes), + NgbModule.forRoot(), + SharedModule, + OrderModule, + OmbiShared, + TabViewModule, + OverlayPanelModule, + ], + declarations: [ + VoteComponent, + ], + exports: [ + RouterModule, + ], + providers: [ + VoteService, + ], + +}) +export class VoteModule { } diff --git a/src/Ombi/ClientApp/styles/base.scss b/src/Ombi/ClientApp/styles/base.scss index a9f3119fa..98f5e3e99 100644 --- a/src/Ombi/ClientApp/styles/base.scss +++ b/src/Ombi/ClientApp/styles/base.scss @@ -1007,5 +1007,4 @@ a > h4:hover { .album-cover { width:300px; -} - +} \ No newline at end of file diff --git a/src/Ombi/Controllers/VoteController.cs b/src/Ombi/Controllers/VoteController.cs index f1466e3ed..70ea0ec8c 100644 --- a/src/Ombi/Controllers/VoteController.cs +++ b/src/Ombi/Controllers/VoteController.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using Microsoft.EntityFrameworkCore; using Ombi.Core.Engine; +using Ombi.Core.Models; using Ombi.Core.Models.UI; using Ombi.Store.Entities; @@ -21,12 +22,69 @@ namespace Ombi.Controllers private readonly IVoteEngine _engine; + /// + /// Returns the viewmodel to render on the UI + /// [HttpGet] public Task> GetView() { return _engine.GetMovieViewModel(); } + /// + /// Upvotes a movie + /// + [HttpPost("up/movie/{requestId:int}")] + public Task UpvoteMovie(int requestId) + { + return _engine.UpVote(requestId, RequestType.Movie); + } + + /// + /// Upvotes a tv show + /// + [HttpPost("up/tv/{requestId:int}")] + public Task UpvoteTv(int requestId) + { + return _engine.UpVote(requestId, RequestType.TvShow); + } + + /// + /// Upvotes a album + /// + [HttpPost("up/album/{requestId:int}")] + public Task UpvoteAlbum(int requestId) + { + return _engine.UpVote(requestId, RequestType.Album); + } + + /// + /// Downvotes a movie + /// + [HttpPost("down/movie/{requestId:int}")] + public Task DownvoteMovie(int requestId) + { + return _engine.DownVote(requestId, RequestType.Movie); + } + + /// + /// Downvotes a tv show + /// + [HttpPost("down/tv/{requestId:int}")] + public Task DownvoteTv(int requestId) + { + return _engine.DownVote(requestId, RequestType.TvShow); + } + + /// + /// Downvotes a album + /// + [HttpPost("down/album/{requestId:int}")] + public Task DownvoteAlbum(int requestId) + { + return _engine.DownVote(requestId, RequestType.Album); + } + /// /// Get's all the votes for the request id /// From 3ab45eb6fdb5c605111b887e7d039372aef5055d Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 28 Sep 2018 23:35:48 +0100 Subject: [PATCH 14/74] Fixed #2549 --- src/Ombi.Core/Senders/MusicSender.cs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Ombi.Core/Senders/MusicSender.cs b/src/Ombi.Core/Senders/MusicSender.cs index 9e2dab1bd..60e4ca6ee 100644 --- a/src/Ombi.Core/Senders/MusicSender.cs +++ b/src/Ombi.Core/Senders/MusicSender.cs @@ -79,7 +79,31 @@ namespace Ombi.Core.Senders // Search for it if (!settings.AddOnly) { - await _lidarrApi.AlbumSearch(new[] { result.id }, settings.ApiKey, settings.FullUri); + // get the album + var album = await _lidarrApi.GetAllAlbumsByArtistId(result.id, settings.ApiKey, settings.FullUri); + + var albumToSearch = album.FirstOrDefault(x => + x.foreignAlbumId.Equals(model.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase)); + var maxRetryCount = 10; // 5 seconds + var currentRetry = 0; + while (albumToSearch != null) + { + if (currentRetry >= maxRetryCount) + { + break; + } + currentRetry++; + await Task.Delay(500); + album = await _lidarrApi.GetAllAlbumsByArtistId(result.id, settings.ApiKey, settings.FullUri); + albumToSearch = album.FirstOrDefault(x => + x.foreignAlbumId.Equals(model.ForeignAlbumId, StringComparison.InvariantCultureIgnoreCase)); + } + + + if (albumToSearch != null) + { + await _lidarrApi.AlbumSearch(new[] {albumToSearch.id}, settings.ApiKey, settings.FullUri); + } } return new SenderResult { Message = "Album has been requested!", Sent = true, Success = true }; } From 85ff2c40175c843e8ae0d49671c0cc87b26b273d Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 2 Oct 2018 20:42:24 +0100 Subject: [PATCH 15/74] Fixed #2555 --- src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts | 1 + .../app/settings/notifications/newsletter.component.html | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts b/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts index fc8b89f1e..f368ee035 100644 --- a/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/INotificationSettings.ts @@ -59,6 +59,7 @@ export interface INewsletterNotificationSettings extends INotificationSettings { notificationTemplate: INotificationTemplates; disableMovies: boolean; disableTv: boolean; + disableMusic: boolean; externalEmails: string[]; } diff --git a/src/Ombi/ClientApp/app/settings/notifications/newsletter.component.html b/src/Ombi/ClientApp/app/settings/notifications/newsletter.component.html index 0a7c27b64..c4d31ae8f 100644 --- a/src/Ombi/ClientApp/app/settings/notifications/newsletter.component.html +++ b/src/Ombi/ClientApp/app/settings/notifications/newsletter.component.html @@ -20,6 +20,12 @@
+ +
+
+ +
+
From a93fdc66ac56c480d477ccdcfdfe1dd295f912c6 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 3 Oct 2018 13:54:19 +0100 Subject: [PATCH 16/74] Subscribe the user to the request when they vote on it. --- src/Ombi.Core/Engine/VoteEngine.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs index ecd22e848..2cfb1d6b6 100644 --- a/src/Ombi.Core/Engine/VoteEngine.cs +++ b/src/Ombi.Core/Engine/VoteEngine.cs @@ -85,6 +85,7 @@ namespace Ombi.Core.Engine return new VoteEngineResult { ErrorMessage = "You have already voted!" }; } await RemoveCurrentVote(currentVote); + await _movieRequestEngine.SubscribeToRequest(requestId, requestType); await _voteRepository.Add(new Votes { @@ -148,6 +149,8 @@ namespace Ombi.Core.Engine } await RemoveCurrentVote(currentVote); + await _movieRequestEngine.UnSubscribeRequest(requestId, requestType); + await _voteRepository.Add(new Votes { Date = DateTime.UtcNow, From 64a741a60d0b6e2c3e7d77c70e60fcdb84b9fd59 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 3 Oct 2018 14:55:30 +0100 Subject: [PATCH 17/74] Did the vote settings and frontend !wip --- src/Ombi/ClientApp/app/app.component.html | 6 +++ src/Ombi/ClientApp/app/app.component.ts | 2 + .../ClientApp/app/interfaces/ISettings.ts | 7 +++ .../app/services/settings.service.ts | 13 +++++ .../ClientApp/app/settings/settings.module.ts | 3 ++ .../app/settings/settingsmenu.component.html | 1 + .../app/settings/vote/vote.component.html | 50 +++++++++++++++++++ .../app/settings/vote/vote.component.ts | 44 ++++++++++++++++ src/Ombi/Controllers/SettingsController.cs | 32 +++++++++++- src/Ombi/Startup.cs | 2 - src/Ombi/wwwroot/translations/en.json | 1 + 11 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 src/Ombi/ClientApp/app/settings/vote/vote.component.html create mode 100644 src/Ombi/ClientApp/app/settings/vote/vote.component.ts diff --git a/src/Ombi/ClientApp/app/app.component.html b/src/Ombi/ClientApp/app/app.component.html index 24398f088..953e2a9a2 100644 --- a/src/Ombi/ClientApp/app/app.component.html +++ b/src/Ombi/ClientApp/app/app.component.html @@ -54,6 +54,12 @@ {{ 'NavigationBar.UserManagement' | translate }} + diff --git a/src/Ombi/ClientApp/app/settings/vote/vote.component.html b/src/Ombi/ClientApp/app/settings/vote/vote.component.html new file mode 100644 index 000000000..c5877f3d8 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/vote/vote.component.html @@ -0,0 +1,50 @@ + + + +
+ Vote +
+
+
+
+ + +
+
+ +

Vote limits tell Ombi how many votes the request needs before approval.

+

e.g. If the Movie vote limit is 10, it requires 10 Upvotes from 10 different users before it will be approved.

+
+ + + + The limit needs to be greater than or equal to 1 +
+
+ + + The limit needs to be greater than or equal to 1 +
+
+ + + The limit needs to be greater than or equal to 1 +
+ +
+
+ +
+
+
+ +
+ + + + + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/app/settings/vote/vote.component.ts b/src/Ombi/ClientApp/app/settings/vote/vote.component.ts new file mode 100644 index 000000000..d99239b96 --- /dev/null +++ b/src/Ombi/ClientApp/app/settings/vote/vote.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; + +import { NotificationService, SettingsService } from "../../services"; + +@Component({ + templateUrl: "./vote.component.html", +}) +export class VoteComponent implements OnInit { + + public form: FormGroup; + + constructor(private settingsService: SettingsService, + private readonly fb: FormBuilder, + private notificationService: NotificationService) { } + + public ngOnInit() { + this.settingsService.getVoteSettings().subscribe(x => { + this.form = this.fb.group({ + enabled: [x.enabled], + movieVoteMax: [x.movieVoteMax, Validators.min(1)], + musicVoteMax: [x.musicVoteMax, Validators.min(1)], + tvShowVoteMax: [x.tvShowVoteMax, Validators.min(1)], + }); + }); + } + + public onSubmit(form: FormGroup) { + if (form.invalid) { + this.notificationService.error("Please check your entered values"); + return; + } + + const settings = form.value; + + this.settingsService.saveVoteSettings(settings).subscribe(x => { + if (x) { + this.notificationService.success("Successfully saved the Vote settings"); + } else { + this.notificationService.success("There was an error when saving the Vote settings"); + } + }); + } +} diff --git a/src/Ombi/Controllers/SettingsController.cs b/src/Ombi/Controllers/SettingsController.cs index 9785af553..b523250a6 100644 --- a/src/Ombi/Controllers/SettingsController.cs +++ b/src/Ombi/Controllers/SettingsController.cs @@ -599,12 +599,11 @@ namespace Ombi.Controllers /// - /// Save the Issues settings. + /// Save the Vote settings. /// /// The settings. /// [HttpPost("Issues")] - [AllowAnonymous] public async Task IssueSettings([FromBody]IssueSettings settings) { return await Save(settings); @@ -629,6 +628,35 @@ namespace Ombi.Controllers return issues.Enabled; } + /// + /// Save the Vote settings. + /// + /// The settings. + /// + [HttpPost("vote")] + public async Task VoteSettings([FromBody]VoteSettings settings) + { + return await Save(settings); + } + + /// + /// Gets the Vote Settings. + /// + /// + [HttpGet("vote")] + public async Task VoteSettings() + { + return await Get(); + } + + [AllowAnonymous] + [HttpGet("voteenabled")] + public async Task VoteEnabled() + { + var vote = await Get(); + return vote.Enabled; + } + /// /// Saves the email notification settings. /// diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index ffdd87b53..2d86d4471 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -172,8 +172,6 @@ namespace Ombi { // Generate a API Key settings.ApiKey = Guid.NewGuid().ToString("N"); - var userManager = app.ApplicationServices.GetService(); - userManager.CreateAsync(new OmbiUser {UserName = "API User", UserType = UserType.LocalUser}).Wait(); ombiService.SaveSettings(settings); } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index cbe2c034e..1fd65e663 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -50,6 +50,7 @@ "Requests": "Requests", "UserManagement": "User Management", "Issues":"Issues", + "Vote":"Vote", "Donate": "Donate!", "DonateLibraryMaintainer": "Donate to Library Maintainer", "DonateTooltip": From 9f696bfc7d3358135d12fdfa903091ccca2dede5 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 3 Oct 2018 15:03:41 +0100 Subject: [PATCH 18/74] Do not show available, approved stuff on the vote page !wip --- src/Ombi.Core/Engine/VoteEngine.cs | 34 +++++++++++++------ .../ClientApp/app/vote/vote.component.html | 4 +-- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Ombi.Core/Engine/VoteEngine.cs b/src/Ombi.Core/Engine/VoteEngine.cs index ced3b8f53..6f399f45b 100644 --- a/src/Ombi.Core/Engine/VoteEngine.cs +++ b/src/Ombi.Core/Engine/VoteEngine.cs @@ -44,6 +44,10 @@ namespace Ombi.Core.Engine var musicRequestsTask = _musicRequestEngine.GetRequests(); foreach (var r in movieRequests) { + if (r.Available || r.Approved || (r.Denied ?? false)) + { + continue; + } // Make model var votes = GetVotes(r.Id, RequestType.Movie); var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); @@ -63,6 +67,10 @@ namespace Ombi.Core.Engine foreach (var r in await musicRequestsTask) { + if (r.Available || r.Approved || (r.Denied ?? false)) + { + continue; + } // Make model var votes = GetVotes(r.Id, RequestType.Album); var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); @@ -90,6 +98,10 @@ namespace Ombi.Core.Engine var finalsb = new StringBuilder(); foreach (var childRequests in r.ChildRequests) { + if (childRequests.Available || childRequests.Approved || (childRequests.Denied ?? false)) + { + continue; + } foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); @@ -97,18 +109,18 @@ namespace Ombi.Core.Engine finalsb.Append($"Season: {epInformation.SeasonNumber} - Episodes: {episodeString}"); finalsb.Append("
"); } + vm.Add(new VoteViewModel + { + Upvotes = upVotes, + Downvotes = downVotes, + RequestId = childRequests.Id, + RequestType = RequestType.TvShow, + Title = r.Title, + Image = r.PosterPath, + Background = r.Background, + Description = finalsb.ToString() + }); } - vm.Add(new VoteViewModel - { - Upvotes = upVotes, - Downvotes = downVotes, - RequestId = r.Id, - RequestType = RequestType.TvShow, - Title = r.Title, - Image = r.PosterPath, - Background = r.Background, - Description = finalsb.ToString() - }); } return vm; diff --git a/src/Ombi/ClientApp/app/vote/vote.component.html b/src/Ombi/ClientApp/app/vote/vote.component.html index eaeb3ca5e..4f322352d 100644 --- a/src/Ombi/ClientApp/app/vote/vote.component.html +++ b/src/Ombi/ClientApp/app/vote/vote.component.html @@ -14,8 +14,8 @@ - - + + x.VoteType == VoteType.Upvote).CountAsync(); var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); + var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted); vm.Add(new VoteViewModel { Upvotes = upVotes, @@ -84,24 +90,27 @@ namespace Ombi.Core.Engine Title = r.Title, Image = r.Cover, Background = r.Cover, - Description = r.ArtistName + Description = r.ArtistName, + AlreadyVoted = myVote != null, + MyVote = myVote?.VoteType ?? VoteType.Downvote }); } foreach (var r in await tvRequestsTask) { - // Make model - var votes = GetVotes(r.Id, RequestType.TvShow); - var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); - var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); - var finalsb = new StringBuilder(); foreach (var childRequests in r.ChildRequests) { + var finalsb = new StringBuilder(); if (childRequests.Available || childRequests.Approved || (childRequests.Denied ?? false)) { continue; } + var votes = GetVotes(childRequests.Id, RequestType.TvShow); + // Make model + var upVotes = await votes.Where(x => x.VoteType == VoteType.Upvote).CountAsync(); + var downVotes = await votes.Where(x => x.VoteType == VoteType.Downvote).CountAsync(); + var myVote = await votes.FirstOrDefaultAsync(x => x.UserId == user.Id && !x.Deleted); foreach (var epInformation in childRequests.SeasonRequests.OrderBy(x => x.SeasonNumber)) { var orderedEpisodes = epInformation.Episodes.OrderBy(x => x.EpisodeNumber).ToList(); @@ -118,7 +127,9 @@ namespace Ombi.Core.Engine Title = r.Title, Image = r.PosterPath, Background = r.Background, - Description = finalsb.ToString() + Description = finalsb.ToString(), + AlreadyVoted = myVote != null, + MyVote = myVote?.VoteType ?? VoteType.Downvote }); } } @@ -209,7 +220,7 @@ namespace Ombi.Core.Engine { var user = await GetUser(); var currentVote = await GetVoteForUser(requestId, user.Id); - if (currentVote != null && currentVote.VoteType == VoteType.Upvote) + if (currentVote != null && currentVote.VoteType == VoteType.Downvote) { return new VoteEngineResult { ErrorMessage = "You have already voted!" }; } diff --git a/src/Ombi.Core/Models/UI/VoteViewModel.cs b/src/Ombi.Core/Models/UI/VoteViewModel.cs index 71d6c95a9..f58db3dbc 100644 --- a/src/Ombi.Core/Models/UI/VoteViewModel.cs +++ b/src/Ombi.Core/Models/UI/VoteViewModel.cs @@ -12,5 +12,7 @@ namespace Ombi.Core.Models.UI public int Downvotes { get; set; } public string Title { get; set; } public string Description { get; set; } + public bool AlreadyVoted { get; set; } + public VoteType MyVote { get; set; } } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/interfaces/IVote.ts b/src/Ombi/ClientApp/app/interfaces/IVote.ts index fb772fea6..e6aaf9607 100644 --- a/src/Ombi/ClientApp/app/interfaces/IVote.ts +++ b/src/Ombi/ClientApp/app/interfaces/IVote.ts @@ -7,6 +7,8 @@ downvotes: number; title: string; description: string; + alreadyVoted: boolean; + myVote: VoteType; } export interface IVoteEngineResult { @@ -21,3 +23,8 @@ export enum RequestTypes { Movie = 1, Album = 2, } + +export enum VoteType { + Upvote = 0, + Downvote = 1, +} diff --git a/src/Ombi/ClientApp/app/vote/vote.component.html b/src/Ombi/ClientApp/app/vote/vote.component.html index 4f322352d..6d8f8a797 100644 --- a/src/Ombi/ClientApp/app/vote/vote.component.html +++ b/src/Ombi/ClientApp/app/vote/vote.component.html @@ -1,35 +1,92 @@

Vote

-
- - - - - - - - - - - - - - - - - - -
TitleDescription
- - - poster{{vm.title}}
+ + + +
+ +
+
+ + + + + + + + + + + + + + + + + +
TitleDescription
+ + + poster{{vm.title}}
+
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + +
TitleDescription
+ + + poster{{vm.title}}
+
+ +
+ - poster - \ No newline at end of file + poster + \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/vote/vote.component.ts b/src/Ombi/ClientApp/app/vote/vote.component.ts index 908c1759a..ffab837cd 100644 --- a/src/Ombi/ClientApp/app/vote/vote.component.ts +++ b/src/Ombi/ClientApp/app/vote/vote.component.ts @@ -3,7 +3,7 @@ import { Component, OnInit, ViewChild } from "@angular/core"; import { OverlayPanel } from "primeng/primeng"; import { NotificationService, VoteService } from "../services"; -import { IVoteEngineResult, IVoteViewModel, RequestTypes } from "../interfaces"; +import { IVoteEngineResult, IVoteViewModel, RequestTypes, VoteType } from "../interfaces"; @Component({ templateUrl: "vote.component.html", @@ -11,7 +11,12 @@ import { IVoteEngineResult, IVoteViewModel, RequestTypes } from "../interfaces"; }) export class VoteComponent implements OnInit { + public showCurrent: boolean = true; + public showCompleted: boolean; public viewModel: IVoteViewModel[]; + public currentVotes: IVoteViewModel[]; + public completedVotes: IVoteViewModel[]; + public VoteType = VoteType; public panelImage: string; @ViewChild("op") public overlayPanel: OverlayPanel; @@ -19,8 +24,19 @@ export class VoteComponent implements OnInit { public async ngOnInit() { this.viewModel = await this.voteService.getModel(); + this.filterLists(); } + public selectCurrentTab() { + this.showCurrent = true; + this.showCompleted = false; + } + + public selectCompletedVotesTab() { + this.showCurrent = false; + this.showCompleted = true; + } + public toggle(event: any, image: string) { this.panelImage = image; this.overlayPanel.toggle(event); @@ -44,6 +60,9 @@ export class VoteComponent implements OnInit { this.notificationSerivce.error(result.errorMessage); } else { this.notificationSerivce.success("Voted!"); + vm.alreadyVoted = true; + vm.myVote = VoteType.Upvote; + this.filterLists(); } } @@ -64,7 +83,19 @@ export class VoteComponent implements OnInit { if(result.isError) { this.notificationSerivce.error(result.errorMessage); } else { - this.notificationSerivce.success("Voted!"); + this.notificationSerivce.success("Voted!"); + vm.alreadyVoted = true; + vm.myVote = VoteType.Downvote; + this.filterLists(); } } + + private filterLists() { + this.completedVotes = this.viewModel.filter(vm => { + return vm.alreadyVoted; + }); + this.currentVotes = this.viewModel.filter(vm => { + return !vm.alreadyVoted; + }); + } } diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 1fd65e663..93f6c39f0 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -194,5 +194,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } From a7cd9c565d1cf365a0f58c6c47d883954f8c949e Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:53:56 +0100 Subject: [PATCH 20/74] New translations en.json (Danish) --- src/Ombi/wwwroot/translations/da.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index 17c671217..0229b3603 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -48,6 +48,7 @@ "Requests": "Anmodninger", "UserManagement": "Brugeradministration", "Issues": "Problemer", + "Vote": "Vote", "Donate": "Donér!", "DonateLibraryMaintainer": "Donér til vedligeholder af bibliotek", "DonateTooltip": "Sådan overbeviser jeg min kone om, at jeg skal bruge min fritid på at udvikle Ombi :)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 10e6145ff074f7e070143195a2ca3cd74755a374 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:53:57 +0100 Subject: [PATCH 21/74] New translations en.json (Dutch) --- src/Ombi/wwwroot/translations/nl.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index 60d54fe8c..18a1d319d 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -48,6 +48,7 @@ "Requests": "Verzoeklijst", "UserManagement": "Gebruikersbeheer", "Issues": "Problemen", + "Vote": "Vote", "Donate": "Doneer!", "DonateLibraryMaintainer": "Doneren aan bibliotheek beheerder", "DonateTooltip": "Zo heb ik mijn vrouw overtuigd dat ik Ombi mag ontwikkelen ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From ad78a3947170b9f51155ae15252903338fce8905 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:53:58 +0100 Subject: [PATCH 22/74] New translations en.json (French) --- src/Ombi/wwwroot/translations/fr.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index 0834653f8..c20dffdb0 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -48,6 +48,7 @@ "Requests": "Demandes", "UserManagement": "Gestion des utilisateurs", "Issues": "Problèmes", + "Vote": "Vote", "Donate": "Faire un don !", "DonateLibraryMaintainer": "Faire un don au mainteneur de la bibliothèque", "DonateTooltip": "C’est pour convaincre ma femme de me laisser passer mon temps libre à développer Ombi ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 7218aeff775271d426fa1d37b54e5f43a0f38701 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:00 +0100 Subject: [PATCH 23/74] New translations en.json (German) --- src/Ombi/wwwroot/translations/de.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index 2da1aab4e..d5b9e62be 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -48,6 +48,7 @@ "Requests": "Anfragen", "UserManagement": "Benutzerverwaltung", "Issues": "Probleme", + "Vote": "Vote", "Donate": "Spenden!", "DonateLibraryMaintainer": "Spende sie an den Bibliotheks Betreuer", "DonateTooltip": "So überzeuge ich meine Frau, meine Freizeit mit der Entwicklung von Ombi zu verbringen ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 1b097b9147ec8a53d502dc4253987071c17233aa Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:02 +0100 Subject: [PATCH 24/74] New translations en.json (Italian) --- src/Ombi/wwwroot/translations/it.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 25f706303..0ade93919 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -48,6 +48,7 @@ "Requests": "Richieste", "UserManagement": "Gestione degli utenti", "Issues": "Problemi", + "Vote": "Vote", "Donate": "Fai una donazione!", "DonateLibraryMaintainer": "Dona al manutentore della libreria", "DonateTooltip": "Questo è come convinco mia moglie a farmi spendere il mio tempo libero nello sviluppo di Ombi ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 35a28ef62ea843e7037050d4e268897fc5aafbf9 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:03 +0100 Subject: [PATCH 25/74] New translations en.json (Norwegian) --- src/Ombi/wwwroot/translations/no.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index 87df0181f..80cd9e31a 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -48,6 +48,7 @@ "Requests": "Forespørsler", "UserManagement": "Brukeradministrasjon", "Issues": "Mangler", + "Vote": "Vote", "Donate": "Doner!", "DonateLibraryMaintainer": "Doner til vedlikeholderen av biblioteket", "DonateTooltip": "Dette er hvordan jeg overbevise min kone til å la meg bruke min fritid til å utvikle Ombi ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 7f4c251104dce16615ce43c3cf57a5f43cbc968a Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:05 +0100 Subject: [PATCH 26/74] New translations en.json (Polish) --- src/Ombi/wwwroot/translations/pl.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/pl.json b/src/Ombi/wwwroot/translations/pl.json index 8e180e5bb..3aba4da41 100644 --- a/src/Ombi/wwwroot/translations/pl.json +++ b/src/Ombi/wwwroot/translations/pl.json @@ -48,6 +48,7 @@ "Requests": "Zgłoszenia", "UserManagement": "Zarządzanie użytkownikami", "Issues": "Problemy", + "Vote": "Vote", "Donate": "Wesprzyj!", "DonateLibraryMaintainer": "Wesprzyj właściciela biblioteki", "DonateTooltip": "W ten sposób przekonuję moją żonę by spędzać mój wolny czas rozwijając Ombi ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 9ec631ca14aaf629e70a525e217b1ceca185a5ee Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:07 +0100 Subject: [PATCH 27/74] New translations en.json (Portuguese, Brazilian) --- src/Ombi/wwwroot/translations/pt.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/pt.json b/src/Ombi/wwwroot/translations/pt.json index e547d0066..eaf45cac9 100644 --- a/src/Ombi/wwwroot/translations/pt.json +++ b/src/Ombi/wwwroot/translations/pt.json @@ -48,6 +48,7 @@ "Requests": "Solicitações", "UserManagement": "Gerenciador de Usuário", "Issues": "Problemas", + "Vote": "Vote", "Donate": "Fazer uma doação!", "DonateLibraryMaintainer": "Doar para o Dono da Biblioteca", "DonateTooltip": "É assim que eu convenço a minha mulher a deixar-me passar o meu tempo livre desenvolvendo Ombi;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 6f220940a93c66a61006d4fd9f9490995428ecd7 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:08 +0100 Subject: [PATCH 28/74] New translations en.json (Spanish) --- src/Ombi/wwwroot/translations/es.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index a49ea5140..dc7637e79 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -48,6 +48,7 @@ "Requests": "Solicitudes", "UserManagement": "Gestión de usuarios", "Issues": "Incidencias", + "Vote": "Vote", "Donate": "¡Donar!", "DonateLibraryMaintainer": "Donate to Library Maintainer", "DonateTooltip": "Para que mi esposa me deje desarrollar Ombi ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From bd4f423220d0ae24eda95c74af92165cf7088442 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 13:54:09 +0100 Subject: [PATCH 29/74] New translations en.json (Swedish) --- src/Ombi/wwwroot/translations/sv.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index 26a85fe10..8d2e8374a 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -48,6 +48,7 @@ "Requests": "Förfrågningar", "UserManagement": "Användarhantering", "Issues": "Problem", + "Vote": "Vote", "Donate": "Donera!", "DonateLibraryMaintainer": "Donera till bibliotekets utvecklare", "DonateTooltip": "Det är så här jag övertygar min fru att jag vill spendera min fritid att utveckla Ombi ;)", @@ -188,5 +189,9 @@ "TvDue": "TV: {{date}}", "MovieDue": "Movie: {{date}}", "MusicDue": "Music: {{date}}" + }, + "Votes": { + "CompletedVotesTab": "Voted", + "VotesTab": "Votes Needed" } } \ No newline at end of file From 9d8caf43584c4e896f65e2d9cbc9dc0072c977a3 Mon Sep 17 00:00:00 2001 From: Jamie Date: Thu, 4 Oct 2018 14:31:40 +0100 Subject: [PATCH 30/74] New translations en.json (German) --- src/Ombi/wwwroot/translations/de.json | 38 +++++++++++++-------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index d5b9e62be..c3646221d 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -48,7 +48,7 @@ "Requests": "Anfragen", "UserManagement": "Benutzerverwaltung", "Issues": "Probleme", - "Vote": "Vote", + "Vote": "Bewerten", "Donate": "Spenden!", "DonateLibraryMaintainer": "Spende sie an den Bibliotheks Betreuer", "DonateTooltip": "So überzeuge ich meine Frau, meine Freizeit mit der Entwicklung von Ombi zu verbringen ;)", @@ -66,27 +66,27 @@ "Danish": "Dänisch", "Dutch": "Niederländisch", "Norwegian": "Norwegisch", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" + "BrazillianPortuguese": "Portugiesisch (Brasilien)", + "Polish": "Polnisch", + "Swedish": "Schwedisch" }, "OpenMobileApp": "Mobile App", - "RecentlyAdded": "Recently Added" + "RecentlyAdded": "Kürzlich hinzugefügt" }, "Search": { "Title": "Suche", "Paragraph": "Möchtest du etwas sehen, das nicht verfügbar ist? Kein Problem, benutze einfach die Suchbox und fordere es an!", "MoviesTab": "Filme", "TvTab": "Serien", - "MusicTab": "Music", + "MusicTab": "Musik", "Suggestions": "Vorschläge", "NoResults": "Es tut uns leid, wir haben keine Ergebnisse gefunden!", "DigitalDate": "Digital Release: {{date}}", - "TheatricalRelease": "Theatrical Release: {{date}}", + "TheatricalRelease": "Kinostart: {{date}}", "ViewOnPlex": "In Plex anschauen", "ViewOnEmby": "In Emby anschauen", "RequestAdded": "Anfrage für {{title}} wurde erfolgreich hinzugefügt", - "Similar": "Similar", + "Similar": "Ähnliche", "Movies": { "PopularMovies": "Beliebte Filme", "UpcomingMovies": "Kommende Filme", @@ -116,14 +116,14 @@ "Paragraph": "Unten sehen Sie Ihre und alle anderen Anfragen, sowie deren Download und Genehmigungsstatus.", "MoviesTab": "Filme", "TvTab": "Serien", - "MusicTab": "Music", + "MusicTab": "Musik", "RequestedBy": "Angefordert von:", "Status": "Status:", "RequestStatus": "Anfrage Status:", "Denied": " Abgelehnt:", - "TheatricalRelease": "Theatrical Release: {{date}}", + "TheatricalRelease": "Kinostart: {{date}}", "ReleaseDate": "Veröffentlicht: {{date}}", - "TheatricalReleaseSort": "Theatrical Release", + "TheatricalReleaseSort": "Kinostart", "DigitalRelease": "Digital Release: {{date}}", "RequestDate": "Datum der Anfrage:", "QualityOverride": "Qualitäts Überschreiben:", @@ -140,10 +140,10 @@ "GridStatus": "Status", "ReportIssue": "Problem melden", "Filter": "Filter", - "Sort": "Sort", + "Sort": "Sortieren", "SeasonNumberHeading": "Staffel: {seasonNumber}", - "SortTitleAsc": "Title ▲", - "SortTitleDesc": "Title ▼", + "SortTitleAsc": "Titel ▲", + "SortTitleDesc": "Titel ▼", "SortRequestDateAsc": "Request Date ▲", "SortRequestDateDesc": "Request Date ▼", "SortStatusAsc": "Status ▲", @@ -180,18 +180,18 @@ "FilterHeaderAvailability": "Verfügbarkeit", "FilterHeaderRequestStatus": "Status", "Approved": "Bestätigt", - "PendingApproval": "Pending Approval" + "PendingApproval": "Genehmigung ausstehend" }, "UserManagment": { "TvRemaining": "TV: {{remaining}}/{{total}} remaining", "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", "TvDue": "TV: {{date}}", - "MovieDue": "Movie: {{date}}", - "MusicDue": "Music: {{date}}" + "MovieDue": "Film: {{date}}", + "MusicDue": "Musik: {{date}}" }, "Votes": { - "CompletedVotesTab": "Voted", - "VotesTab": "Votes Needed" + "CompletedVotesTab": "Bewertet", + "VotesTab": "Erforderliche Bewertungen" } } \ No newline at end of file From 8540bd6ffbeac6660de81d8002c19af560df96dc Mon Sep 17 00:00:00 2001 From: ImgBotApp Date: Thu, 4 Oct 2018 14:52:45 +0000 Subject: [PATCH 31/74] [ImgBot] optimizes images *Total -- 84.51kb -> 54.82kb (35.13%) /src/Ombi/wwwroot/images/default_tv_poster.png -- 1.73kb -> 0.68kb (60.87%) /src/Ombi/wwwroot/images/default_movie_poster.png -- 1.65kb -> 0.71kb (57.15%) /src/Ombi/wwwroot/images/default-music-placeholder.png -- 78.88kb -> 51.19kb (35.1%) /src/Ombi/wwwroot/images/favicon/apple-touch-icon.png -- 2.25kb -> 2.24kb (0.3%) --- .../images/default-music-placeholder.png | Bin 80771 -> 52419 bytes .../wwwroot/images/default_movie_poster.png | Bin 1685 -> 722 bytes src/Ombi/wwwroot/images/default_tv_poster.png | Bin 1776 -> 695 bytes .../images/favicon/apple-touch-icon.png | Bin 2304 -> 2297 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/Ombi/wwwroot/images/default-music-placeholder.png b/src/Ombi/wwwroot/images/default-music-placeholder.png index f6decc9e07d9189eb9336f0f050605e01b01f6db..4cff17018415abd32454c8fa1accc231b77e36f6 100644 GIT binary patch literal 52419 zcmYIw2{=`2`~KQCwq#0)%nc}nB=gun5jiUJOhV>)wxyCIR6?00oHCPnEJ-C(GHzqY zunlQrV~78<^!=`X-*v9@omgwV>wTZ$e(vXfR-~?u1|vNuJ%S*NS1w=FLlEpN`VSo~ z{Ec?j_bK=n^=&n6H3TV%-T%jm2L7Ah=CYnPf&>U42tFJ^Hh1CQa|q%sfgtmi2qOO+ zLD*fNTC}Lc@6uXpX~p(p-ow}{!vM{{qT3KUa*1LVt z&i1G8ik02h@i&E4J3r()dj{u zCLNk3(mJL+u5rmtI>hiHh~uz`!ND#JVt5>Wz{?a->U^~)NIG6q>B|{v1dnGzR8PL@ znQM(`o;W`3VMO$)b;;}HWPZ@ho?A93(lfXazZtZ1e2g5rNZDBq8rw}J)`TW^&g`bN z+sWkg%te=#m2nX`x+0o0Q&Lk$p2OdE?4BlV{;?S&?~YS;Y8#e!U8+rY{N$6jJH6z$ zCoCm49BW6E@01%)1}R^w!tE`gY5SJs3!(Y7fclX+Q5_wfhF~9`_3(IN!`$W$0q)>X zZfs^Dme|i8)zS+0p1ig*qf&X!-t~Qu@^1f_OZA^tkLtQ5|Iu`%u~5QVUHY|-j)qW4 z-q2jQofE+&J(L;)&(TrEAqy-$Qt7V=KDA@Zq43P0&=S*~jX1U?X46gz(Xo8X9-bt0 zF{0J6yr#ipB-XTlJ9);RZ>wRqjSa4-mLKOjNg*w91TSO2lc|tJUqc8-Xk(_DElYq=x$kthL#xC_09R1olOtjb5$jVzm_&&{o?A+ zNXK%=)RdGU0(sk_-L8!59-k!z?CPC$^&j`z9;4YBok_XB>$c>2rkVX=Zf?$Z`%eET zPi(8qKA9$$Bhl(VHh0p0k~U+F3;GOu-nLeeF$g9VV!_eJZPRJW^730XGybmEa(lRh zgoN&KSawp?E>o6K+dh}neqkvJQ3Y4Z?*?C<2e&Y+UK-msg}5*h;kSQKb|`3c^2wTT z>!e1gw&Jf}O&FrUB zB>xgot=H!>`QhAu%zSq|=OD%TZP75}_|tS3NF zf#{ocZ9U3dNQ#=T!Ig1j=GJh`}LG}ai1?Ad-B*&RItQwp`y6n zSuOCO%XVPPZc7ecps?JQsr2Cx=p-)A7aS{S~XHE}af(To9pcFg`V>ttnSG-6+ zFCDfOld1R|2_XvmRubyz#^NeR zZ`M6)yY;%)@taGfc+8ytL!l-ddtd!E&USD4R2s4i8cUjVV8yX~e0-rc(>1Q}kR9ui zjpxX(Uq}D+VCuTv9|_X*L4t#lSk`Q#*JtX+(8x zLvVH#E`|tk$E0-*vd>>9a*DnxNcw=rBeVYCX7aYm@29(NA`|f1F)V5AcCPy;s0}|% zx<|H7qDK_>`JqFSp6KD0ibcYEN`c6-sjI;ZWrq)f)yH4Ieq}>=kR=EGz4N`8LgF=u z`0R;b9B@RfQGceJ{fhc)4fnZL67&l6vkgW6bx3;p+4e-d<`qTWTm~LyQ{_oA5^A-S!u&?fMpv&45tIfP!)UcW-0<#tLc9FM$xH!5#A;)y1%{ zx5ekA<5twOj%Mr4Szq%Lj(>XbnA(3Ij15LQ!y#N*8}+q>kVzlC@2$731nv4lLe!`? zUvB$qf>Tu$g3UIzPg*z3?JCA-s4k*VeS(wz=4lj=by-WJA2j#Xh++XLeJas6_f}4TXVl8;r;E@dRO6>^5AF=!AC#=0w zlqT^tT_Fu{J+^knKa@8;t-(|{@m$)aziSB`y~BG zoH!hHpcs#Q*z3J z7g0DrnG_^|8=cX{Q6FXi(G5-yAFDu`>3~Ai7KiIR{)O@U0ca*sH!JYecKk;ej z%qbI`o57RI7oUE9Wm{%cB6>$$zyCIAQzdw8cLbbVK))Bm(zS0Ek@gJ_>IN4PgLhkS zoD0b9dE!wm`oKyein}RZfOX%M+@7ax)8IIom4uPG;w8uvcHpPgrb`ZKaeoiv4#yMe z6T+l##i3CEafPPj zXybbGnZbQ~{+984QdYdGkdgp4H`t;(YA^Ll_oS(m-|?#m-H`vOpc2Dpc4hu&zBsq8 zzYd-9A0?npuc-C+MR?E4r-lRp+-ZfkS@^c|o1_aCteD(hn$E3y@_o%Mqsn%(v3SZ- zkn-*T{7TxR)-lLg*M2>s**nOpSMY;{K|opP78Tz2AXK!Jih?V#Myv0mMa~OxMlX38 zl7n}B(J0|joxk$+e-}o0WLtwDpcr{nETM>4qjM5h>}|E(L+bMt$PWZ>4Jz3fQ!n%X zbe3c9tj7;RAW%+i-+`qv``kKVTsHU&awBYdI(L)K!xrn8#I2UJHM2QdKxBm|`YgWo z*Gd-3VFP4gvjIxF*0Ov-eMVE@3`4lPJ>p}m$jja=@ybwCGp~0jvKR#ku)*yUZX3ra z-(iz53(@*{=hDFDjYSXhxd!eFtR%!cmJ8=Ne4Ax#8hx;4DkcO4(9q(#&a)<1b_c-G z96I1oH#7#?PBS;xQ?M;9NeG%SJ`naLq zc~>5M9iN(!-4pAp^1>>_!=tn}Z+g*(^73A)oh#zz3KlB);?W^`ni6LdKuyznxaCY%_rEPKZ6%MG7h_ZpX!rP7-y9)rGoueIByPni zQuZ!VorOT4!PC0=6kK$yk9A!?PXHd^QO%vzjqs0e&d=n{FZu-DQ9$=-0{&=Gd+(Vq z3n#qTS1>IXqGPo)!P+R{MBEkg6pUqf-h9lYld~=#FK>u86tsTge;tRb+g_R(tZfb8 zV7oKHWYncH8yrn6F>Aj9a?keWhy=sy8^Yfl%PTx!p-TGyd#QlIijj52Wp=Q`j}`GH z>C2}9r`EAxRUaDk!p;QLH^k@7L&%%^HTy7wY0t~=UN6u;-48$&J@}p|Jveyd0>RB7 zbJj>|@kXRLJ8z!QhZ!YplOrM`#Iw)75b}%tyGBij$xzX3dqG(^mLrL72H1#~*=-j& zWZ~1BEomP-IcS$TeBxKj4dJzu%WQuwJS=?Eqxw%Il*)vJ#0>Kyp|t=r`ukM376v{! z@`d8~4Oje3ul78HMU1%k67Fuf*2IvgmjCA-n&2>|I|HOT71uUL|Jn&Vt^ScM z>a#Us04d~C?QZa~6a(C=DRdqAucF0zrG)6OQt4dVuJ9KDROO=1>?BR$>m(@;C*-)c z+g%4_4Ute^yu=;eJD@)8Ex{Rf7lNDEd-)zJ{J5|IYKc*o9Hf&?%B}@Z*MwS#CKe^O z-HP^Q**jmiWE5{x8g?U`=RcaCJOqg6KoAQ(n6O9nuyZ`mcF69re69yaBQKtR*?n?G zl;!aaVed27gH6_72d0rHrWO`(UO~9MdO21$h9m$La@tivnM$kB|8Fho2Ws%Bhm!s* zddcgFCALT~D2rh*nkQJTlYZ()J3vW2(rv$bdQ^Kai4I&SH!diJ%C?5_cuz-owL{XQ zD;NGFHy@_IueBJyxEAouNjlq9OiL^=4iMB4MHbx)au6cG$Z~u31z%QCQmRJ@2Av|Z z!064tcf>xhQuIa(NoRXxMmFX|5|apbVveN z`CdL_18XduJd*Ulqipj}Iuswbq4@c=M1|U3Q;a-XO+oeaKeA4M{1`K|AX0K&dV6i>aB4>7jA9Z(K}c=y%7Rdr^vLXOw!3c4n+kRJQ6CoZY<+z(6^nvR3NrElb z?jm<l==D?$)6E1z7y0QTM-lb_}gaQD|0ENU4-)v`3R)yyjD#+!psunE82|nU_Gw)`UFYlg_oi7AlriHvse$V?A zu$_I9`f~?7xG5 zLb;%s&H9d8!R!a`D*e{&`1h4y>iT(lV6spsNxqn>LYQa!zL6hyUJETE%8LIIqeizv zRrX_!l2WRU#f#1vzKH5glWNlhFL`Z=RvnSVHZJjK9tPTP%jE1{M>DDCTDd*!fqm7q zVYvPC?RID19t9?D*tAez1>Sq#X!PH0VQF=5@LC)yAB*re9r7p1d~qpZ&Mukukn`%F z*vy9<2+nM_U?lV()_{55N<0_^f~{HIGUv1dACXV&ZAafgYA~8}k~?>gd}OC?bw_Pi zI$7YkP0>%0Jnpi?Hw>43YW4DZPo#HRT-D*v$JVla2M#P3@aam&-`8qlVrI7Zt+?D= zOiO}*^T4X>CTC#vU2wAtDmim~k{cx9M3NFQ^5WfC$EFzSH7HSH=YXa`;WFjmp3VK+ zOI!m&pz*m*uFU&&)-K{2-B13EP}VNWcE~JtYvg@E?Z{z5MK-{duSPRfREhiNv-PeF zE&3Q5^o;QV%APi(-$QIP-{w{)b8~aYyp@)VtQdYmScJs#j{8}aMi!JceKf0q@$Y0f z8BZOee@T=V_|+t!+;vOx2n4D-Jmr2ysCyO8Y}e@*_4`*+p`m0P14ZV2$W)Ak@P@JF zC}#phG{ak17k@RVG)(^a5fXo=p0H@1pMP8O2o!+SKI3PVzt7c(l^Z+T9A{T4i%gl> z_VxgOXDI90%YRjhKoF*iAKtA6`tSX^8`M02Ck38d{v}B}oE=3GqglO{_|8|J2P$4` zqr+A_8w?Q4J*ra%DtNvG-UUbZt}#pW727+%rO@usb|^^ixhMF)zJX@#{c>*1*?JB_ zuxt8#gvIqshAQc09dgL9*ptf-Zy9Aeov1VwLs`V#UhnNIqm6r}v;i!RH6X)22#tF`0IVEvSV)?p^b#4L)<*tl#^+{IA#ZNhEcTRGo-kuf3GTk3jY=m2I`6zaauZ*Pv5Ew{z2JN>9(6*GaC=1;9j*fN|7E_X2k$5dQlnaK62R zgGT?U=av(kOGKlBe>Slhe7@Q<9K}j*lXsOc5Irouo`0~fWZAmpIb^7{03|QJ)u6G^ zBH$#O6gUAqt)o~9g|$pQ;e+nn>o2g(2f9wT9m^QYVi+e~0MHp%?%b97?<_rq<Pmdq{V&Loi)%dZt zkTaoSmNp$qQ{sThXInkdwx>R|JQcmGW5CzaEV}@?I$)%spk>o(+_Rq!A z%{KLUa?fZ1pU|f4X-V!rp|e>3X2eAcEW}Ix^y?e{dNKscs3={v8AQ3K&$hpVM;G_} z(Bz6%E?+v3XrhOsVw?4JjEnkT+-9QY?jrktHSS{k2}}(f0KB?&2?yVi64FgMNG6bB z?7xg^WIgd~*sU0`m#rg)p-gy%I?()(&6%bbnnIvb%N$aviMsV`CW`>shvR`-xZJ{} zN;05Jh;LGC@*@4)6qHBwpTVL;;k{8?$xWaXwoWoIcLBZu==*I>O1!u!X&v~|eiPbI z-ac=$TzNW$;Lt0AaX`0Z6BZx**x~{T5|WpHwcZ)O-M@Fz$?A3k;z37v@#}6eqNw=kWFDwKLCbw(Wn=`l9exH}YfOHT$~ z^m=LcyfPJwnv}IN(%!u}QbDLYeiz-}zCyxc4e&W0BfCGI7YA zBZ@PU6>B?jmrtDUx#%LFJOYTeXbvdQ<>AFvC`>Yhd?R3s&P@2-L zNzLv0K_^YHFDGdcFb}|IY*z-pP;>?3fJVIFF8&+px@UFa=(?%8m%I*Jopey)OU-fM z%=@WIS6g+kt1_^0YqKI#@AZjlQyoCfYCI-FSC0<@lS7VM*-)s{+I#0`ataC*H4eMv zO5@X?r#)B!wPG;m4XurZ@&1n4yGd0klzmJWt^`7DAEey*My{9!&Lhm{+WUaK-lv+` z9^H&-88O90Im8+g^2;z&OKG;jags{w*Zd6=NP5HS<1upDy$Ktaco&CtXXuR*w{9YOd`j zKZtj52{;=>Oq;Z!GnrH)4*#CMRBOQ|RlduYJ>2IDqM3-Lm+kDMSzWv<2 zuHjZ7*#W9Yo3j@ZcbvUEexDvSQUfm3h8YfG8HjCSvgtWk8+!GIHSk`8;V~jA2kB<% zlKm8c^|Q)ioU$t$jD}K*bGZ=Rw2~}6NIih%bCbC<`{uj=V3j#CEx4)XMkxStFB=r? zXE|T(tkJ&~E&cd^Q7s!~3&i%Lma722uQCI*MRL%-GG`Nrjd?$tViK!d18fge6n%hv zoyJJFCR+iyWiEJb8B$S%uQ7Vo0~qMyt_1IrlTCS05PkWC-vCyj@P_a|KVn3nYmlE| zBf|N~D51QY{%AjGM_y4eDALzt@5-h9vzPk4cPl=#i}y%DC*n6Ax2{@kDwr|(#Ux$w z1w%h`h4nclfWT#g!vkxQus|Tki+;!KE!f0fKDoZsP*geR6;G2`eg>Hm>`+Mnt}1(M znRL`Lf=XWdN3r(P&r|9u>Nw>hI9e3)FR-5Iu(JoUip za;ENMVBAMs5?MD+)6dovdNNww5nzpsmj09?ewZ))QL8vfi~&8`qmsO_jb#v{U4OiT z*3u~l9Dld{4f^}%`Ndy{zf+xG+cLv$6{sm4j=CZo&uZ46eM9&Pw4R`M^(Ekw)-W9L zeG2rl35K63Uh?&40NN+|TB8R@hk;0!1g-T~a0F1XodE_hjAj*p4zzW?w5NEPq5cbm zLDuo7Zp(u1&#JCA`D+5hUF4|T`g#m$*sPpCj1C-)0px3mN z6y0x3{8D)uYVlR+GLeB@_?Fd3IOPGhfcxZH}BMqD2lt% zvbee!S!(&~o(YA$5c0p`iUq-d{Ab~r}i`;z^6V)x}2SJGSybD zMb6|(74Tb7JwV}qD$JrM!a{Snr0pmey_1v0mg98D)M~&ZD-?Ic@S_|=CuyfNavLo} zq_iik_K%-6N?eo+8x_sgUAiu9hx4pP*`tZ1T&1@*ZT&8$|b0|FffDv8M zA+FSvpgTa9j9r|wsy4N2l*4N+t1&zFLeJTG%KT8Yx_Heyc6u5p{H2~B&%iP(&RHMq zIRV|CRw!pQ>aL?%H-hT+?b|AbqKUf$7Y8$M87;`*+~)YXY}v`UBV~v0T|9Pl&`BTg z>|Qr4IisE1Yy2CZq;}6Um&)4pJI-kFSuiZ)SGi{b5yggLp}_pQIQ_VPDzU9(45)0X zfD%K7g$7%K&#W>>TH-sC1Ob3-LyOY4enAn{a8wB#wQSIovL?+N>spgWuCU_p^24F~ zl-Kobt`+)fcO+lr2Y>VjuJ)?n1zFrLRgnv7qsi|BEdEu`f(963;KzN37={V&ZRJDz0-ACSg$4=dohj!Y4Cy?%DfZzzNx0$eg z<6+Yt)lJI$LlyP$(c;gtKyUg6I}@FL(5o@drU$L?zhg~8gaPTRm%1r+V8I)8?JDM z@P#{u+!C2pi!sXUCn_9-YR)&R#TxC}q<3l+=o9iJ{eEU(ef5WK;_j|RyaPU9`IU&TzY3eZ^!=oRFrlM^-JB8)Q1I~2?+q=4cPWU z5drMx-_|X6>)`7qz?I6iMXfaob=Pi}ab*EcCl$gQu75#GihiDsPOg<-U09Z90Lusd z?QEb~t&OkGfu{Nl3GK1=Ixb$+1)c+lvwr4ZM%-Dj!Y&y1 znS2{ExcBnPBn_Qv*VP^dr}Eol*sa_)i*wp>0Bxo{{PSk=Nh=&xjevqvb#52*|Aghj zdxUx|YaoU4jgh=?#xD&)A z-^#k-H0Kqt7=IQB4H>ZGPQM5oWPTt}w>#wxIw0Wy5eDqN;sBC=%gYdTz_bc2o4&il zWrYq&09(NWliKZO$$(`qsNxDG9u$}t6{)|Sn> zXB6YhU{Hp$&ep3oG-96Q^?Fl>&s)p-4=57mxkBRhUEcuMkAqM-5{XPR<;KzVNl0JA zU6YfCs2FGSGgPWb#pYs(5R)Qq(WHtkHT9}IOwi0O5Ajk!+0cN|BEL#_3zI$4J2kMQ z^LEPeR=y=NVQZvsNtPj042BnuAJ>%PjK3;)9MUE%<7D{Q4b^2b1p>*@t&n_A27;d= zD%>iHLyPyBgD9%`=(bbl9YL}#xxDWrK03x|zXy{%#%!@|dxY~VFk$7(M!6q&{Ho@T zKzb|a&!3?z+oJRbc^|lx6C>nBSp|jKk`C5)fZ)UFn>q@AcC`d6G5!Z;2KnqvjWdr` z>Y0qD=5FucMVkQ#HyT2(&1XYg_GtU^i1hN;~vzd@~)2-eUiVaBAEwldyY|U{rob{*i4syuY#~x zdY9>>hq6d;`_b+Gdyoq-JedqLkexhIWiHyKVCj;9h}=sj)T@j>c;@-+#-6#Z6HL0? z-;&#Z4qbq3?0@$vTG=0$>N|oO3?LM^ZXl0sRXs~hb?n`@L6=G`sL3||>$8{Y8~W8v z+P1g@ZQN><-_hXUd|%+)xoIiELa2Zq%?d)rBA;;U0KB%rXn2SIY5wOqu@%lm=U$;@ z{vdiu>&M#igB_T03raj`QxOxap=B3NKf4{0t~9Hv3cXUm(B=16c~KNlvw z^5^Y}Ur-Oc=#wLRD5M1>VL3-(%>}5^QnkVT8#;If|L!M*6tAj+9kw0u+i26pTBXfxSaNr=~;Lc#C$dMf!u| z81|qdi_qkXFO&hs@BN`x(#$?Diz~jV{R5z#rFck%v*)#8OZ0hRN+b?0`h2pDV~L^Z zoy*WBQ8}uG<~7~mV7jKUZXKEj@Xj_#IjaHnO;qyrvOeM{qJ|>yPih$Bz157R z{&Z<*BS(q=Ghg-R7$lQoVWXnLAAnDg_HfUICdH8>^A! z`t%ICpNfxxF|05y*l8$qGZ&5r$-+t#Qd}+`0D!Yt(A~>@%qgx)KaXwp`MI5rrjUE9 z5I2J*xEn6_p)k%}04+}xm&J+nGXTMXNMNQ%OtzmI2**>^8Vpsm2YXzQd~wxMWEMrz zzLuTmQPlO_#rLr7UPepQ+rw|cmt}GIyye>r^C5#y(whpd5*fI2cF}mIh1L@>*E|6^ zg3d2a30-ec4m6uLp|RZQ3%`0;(XKC;4&GA zZn)ry+Fmk_2NfO|e}wn&W7_aA=;f`0cLCa`uDRQxUx(%=4ThhHS-WW9ZSJBH5b%K*hRCFaYj(5WAk4HHtdQ>?n7^`!NkPGYb2Jaf_A_94|D(w zvAK&B|M-o^q*7+=%)f;*%|0^1z7MnjGrzNM4h8c-i=0SCO)StwK6S?pC{RNU*`i*@ z{2gRqj-P!hiW?JL2B=_3E%ypH%g$ro@9WtnscC7WP`ea8`3Lr5RmmIVWzo>-S&{$Z zbF!VfnE5Vu_XX84Cski=p<=Nm=wvsZWw990zLr#_*(RUM5JmEO`&hrBI$tofqqTeQ zETPj*s$+(&f_^_xFfOymx*cI6Ti4XA7rP(gRf_pMcw2I2UHAT`=La}Pqva!qmjUpV3*YyBN!L4)1I@7jy9#C^>)?5N1?| zbL+x)JTGsjS$;fgpw6t0x;O`Z>@*ok_2puDR@ebyVb)6>_X**Qzu!Ksa%0H1}9bi*I)K$X3e zFtk~q_6L>?L{eX-OK;@e1l5Fb0W|4&K$Hpc3FnbM$f2tNO77e<=l^!3fE|P61wxIQ z!l)bv@o+1Z~t~%fvT+Nasrfusn^dt(B&(Uu?OpX zW?$S8jt85D%y_Ok0%F7%D@vyEcpd@IKkJWinxZ-R;wn&TdcyIq2AxJrjy1$EKikuI zJ^^{k9^ZQJS=)4zCU6JIjI(NsYRg{oQie1>`AK=EVz`~FoIt9hx+q&da8;!#C}Ls- zb}~hka!-&ZZq2I1Nq@Qor%e zbilJVz@9lvvcTc<>Y?38R5caea0H+%Xc>p`n0)8*VGw5M=dn){lbNQIyX>XtW)(QP zI$QHsd})2OBwsvJ&pHs)Qx#A72gJya@1uwM7`UX{K&CAdpL?h2lR_C0xlw_-m|zJG-5*Q~C$)R13i4}iscfZdX6xO(dJ#?}BF~0GQM331iR~W= zq)(6YdFKo$IGffzU6bEg@>e+bkZr=n2i0g+C)_Tp;(Y3;KoUOs zViXNRx6nvKRz^FH>oi^!T7hc;scfWLYDm@a8z5R>8xS6_l*ouuD4t|!Htc!iwLCL* z91ViaKzG}9u>+9`tjBY2CjKtuhUj6eS@!Tc-aM}tw!ttbKpB^MTeO()doyVKXs{wL z%nZz^96OGJGA_wfolp+1)^%8KwqB&s3_``F0T=Qa!Dx^_-FOnDI zeynU5e=I7{UmA0UT>1h%hI41MNk_)WDux=ZmLFeZ=w9wK1!;tS-lbm+Q~)7gT=igD zG>pp)Y3%emWZ7BaRWP{;9A-KyTUzjysUJ*jC#M zGH`5NpaKQ7q7>I|*W#YyeQ(twE){(+=ndP_Q&Li>1I|Fk{lt!h<#U2SQ2wpTMb+DF z-7b)~V7o&-s!hlF@<7jcFUK8THUR$-02w-DlA%yBL-J4RMRd)N4LeEi8(M&@IyRjp z3oX4)b5Rfz{Gxpw)$*GUq#U){9 zuj|gZRT9{mB{meWfkScGdatQ1=9m0K?Nm_YTlhG~Ar?S~G7kacWCFJ0d784Yp8X+p zJ_quJlL&Sl)H0}@Z=C%=-uC@;6INSu3Ud!QEz*n9w zCRO(nR~Tp^gyBY|TmWA#tOi&dvq4RY8A$PH2%rCSX-_aTTi&F_m%4R)@pp^Yk%}Y? zQ{T<#m;-|VmTX3%iF6&cN$p7>T;qVKeq4?ojEE$|qS^(=@_RD2rq{yRmVOi)w2NlvX@GEwt&0=)W6i%R!!hqswf z%2_*Sq0U8qa)hKEW7E4_`C32(s_B5NHw5?Ic~#^&bXQcMoI9|p$sitpRAfdTe*U(k zjWGo_Xnb5ObtJjnE~E3=V9;OcqLdvDB!q9orQZzTG2QRG3qG|k9XMrFkuP6D0RsFCUPw;OPzi!wi&duKpJPWJf!{Yqpn9nCJN+Sis|}t%3#fFVpa0OA zKs|fzT;%bLWph;*;IMr$8YQ5JN{mdu1 z;oV&lsz?*uP4xkmhv@ICCVwp9OWk`8bX`2)_eJ9AwI`61ivzr?iNRe8Cq(y9j{Xmw zI9Y@YY+1H>5pFB!1;{BLL}-XV`HUXLHL2wGX5grlX5-mEl8}K1O?PVzR7-HdWix@g z!H1zY{XFOM*pVhBuvPB`ja^R=zkaf#UaePYLc3%zopW%Crk}V5jHPEdHe=yV2vl9N zMRFPaj^}{LNePD_{UV%cJOso-dq&>Cgeu3?WbRMCV^$StD}B{ZJdh&O)sh;xxpeap zjAcBqa&y8s1*vR({v3G~Diyj3>eXQ7Dhn9n2wPHsQ(sWPWk7=^^KEc=VAnqP+QAlR zY{Z<#Yn{8z7@)ER8viQL*RXs5+?oqhFHn}N%sx?+Iz*L$5(vfxhiCf(gVvQ&S;->{ zKcE=_f?TmnJ#U-HS(Ke}-?EyWLslvH1_S8&pZ&DI4f+ALlEa^nlX0N$bslq5oyj6D z(Re`$uX21%kzdd1WRL2nn}juTzFr3*lgHUlcIVM0g5lEVUpXvh@uNd3W`r=*y-XHJ z?}?Mp9~(cvPqXKsw^-&}yI8X{cDF7A%6Cc!=49xBjJLrg6yc85 z<587yspw7zQdq~QG#e?LHWLb5WaDK>V#`u+2yCnL`#n7{|BWtxoZtM}>j-ybqV;NZ z2YCFesn=P}xlyjtW2BO9ntC<$+>5IXp&lNr12o?keO~x7SrWfM9X>XrgjP>Vv&i{c zJ~V|W9*J$dAOZJJflj75l-c3KzAj0E=+c-4^V6>X=%5cM7J;4xywa~GzHg`7=zZ9N zr$cTO^a|heq}u8LmL?YDxh`F#jw6j&)wM+b^|Xk*D^N6e^1Zq1#G2S{x2b@oBZqj6 zg+{nR7CaIK6Fh3>5(WBE9SsQ8AfXR}ZtGDA4hFheuBB#{8>)Ov5&0Qdy6s>`DbCNR zwhxU6eZ;i@3qV+WIPa2_Q2*SwJb1^iO3VhTxgUakErJHanN%9~UXkFr_FMDzuePtw-w- z*VK^4-S`&k1BMRJ6SjKAiEjcDNwsat%{yN6KE5Hdw41-SiX5N>>JTN z>5~b!XyossiZ&^bb-~-_zTJ+ydC2@5LT%K+b%Wzwb9|NOawECT9{7vPCp;$ps3`1q zf+MU0r_KDwhSHEw1J5hILxJfo4PAB=*J}4vtsr#yQXNv-UkDfeGZbCgR-QHq_XPp8 zcFzY12IzmFf(zY~mzC2|>yr|(0uG(yU{1XZT{Do} zakoARX$i}@G9kTml_qGJbL(odXT)|(2yr=NKEF51$RNh6mml6!@?rQV#-4f$ZJ&ENmv{` zTpCXIWhM+xyIX3}T1ySmGLRPo^g#bKNr(FJrKg1?!mcx?qO1oft8m^wWRkT}C~J>U zYxaA8%)8&!uy9=`TMvpDR9y#?RjuyQlAaCFmIlRuX7;g~LJ*MdccK|_=YBJZP=~lO z&L5w2lSMmI|G#`p(H8lClE+3K(-0SP4H6l|GFJiZOT-b zMRD?(M45ZkKR=>6KB9jpg&(QXLX1_Po zD&Mwel5(7<{o{3VK}0TjWL%kzHOIk&kE*y&j|_Or^mXm>t{SM%(c>yN7E1(6&;u2p zCIOSLlwp!^DZGm1%?)9b%3ZWv+C#-Ha^8_*OOOQNCi-Xy&?BRhvgk$*L9rm-4!l~p zuy@uS1-y3@z$4HkTs_L*n&aTozB2``T;iy6?&Gw^YZM3xrcu-qY9M8zBZh$dR+~Vz z^9s;3#7HFYTXD?x3_)*{H(DJO&vRb#+_fUS;DvbY_!^nycK+I$-G5Od9`=$Bysxf` z_-MnppK*b@CA|@;7zFQZY&W4e235=S0&~?}ok#_|_P)~Huc)qmvU?TTQ2SD zV8S!1b+SNQGB~hcEj7p)z~X8?yKrL^$j9>*hk@XhGlxtv5Ozf@$AMOV9_2WJY(mul zg*9h})9(hz!th-IS0>hy%-ewmXx^6}t*jQW0BL+8vF!}ObVC1ushEZ;j^$tGHB5*4 zF3NuteuYsM=DYGnAhGQ8nx!cw1a{HQP8{g_={A!ms@BnQ>nkVX&^l$Q=lYbJZGOjT6Rld}eMJMWNmPruD*Gop%>KdnjCInB|&ICcGx-RoH z^ygRnQn;w_y40)ZGgU3=KhOIQBsHfd0$mxt$#0V+NIr^Wvc$GxojDbtn~V}ee1=8+ zl;2L$`(K?gg&+f-7LBO>r>_BAOr}F~`^5V`iAV9IaV+fUa0GHsT{@X@Aq|~|F%V`$ z`^AmW`{hN&@W9`Qw!M7Iqx#?Z=Y>S6ouOihnBl295GoEdzNrD%F?>yNoNhoviNzp@ zF!gnrZ9ws*LO9$MA|0{MgGqRa@B8}-cDYm?&|gh$K>2Oi31PlzbRSxG6;9Tn?FkEG zs5<&!TIX&6ON6%<6TnVY2@>Y;RT-qtpYKmd~8i0zM1IFIhYK{r*6{e6I<>aqF>P z+HM$BH|W*r4(J59g*=I>X+*yA8;eu@#McUE2>qFxmu)y608bRt$@$L@CjPIClXw5v zDjeg@X(=KX zdaQfbqo1UJ?+8ddjLYwJy#FTX03ue)oT@YW?mh|NbW-rl4BJ7kr7@>S1+!s{Qxp(% z1819I;4J~BN$?Fbm|&PHJp6rL&ddnoly~vifdTWfU>DlMAJa}imnBI0Pq$!tr{@@^ z_#EF#ICpJ@p_m1LUw2iQaH;ngX*{dXh%5ym$vK~eYktg}hXmH^o^!_3ucivU<}?*g zZH2K}5y2-J-;j%evHD7mJX(ajH1K8q4G4`0*%Xs8UnhCX$f#{F11VXPw#0Q{b)`_jBop&Lyfn@GG+W|H2v6k@E zvkPjt;5{h)L*nSg$~c5XjK5)&MO4G@kG_zr_#xN>9GiWNCTwb*1?kq9p`fBW5L z)D7KK-*?vvyHoeG# z_9+t>9eX#Bo8#-$#(k$*Le}T#pDG&;TKI^ANh4dKejZIZ`}Xz?O@QGIHke>p?vT^} z;qnTJrq7XeG04RXiy@7C^LB2&3NAp;J@G&Kr>QxR8gG_C46x)2gE_edz%QA2-l=(i zh-3Mj`1*s8uS@gm&7zgW9l+&G$~B4yoml_9uvchX8jLamI}DZs@Cj8IL%S7Po`Ic^ z1nh4QQ#Hx+e%K5OSzdny0}p2yZ1QScG>$!KTF3pE046}!cN;onpX|!u%LmwM3tk?c z&~SVM7NZ1{ze{MX^ym_Vmq(W{#blVAHTUubP8h7BQu6AhV+TMu28dZWv2C>kQ+%Co z8A1*}SqtQXFj*M~6g%@QDuZYKK@Bkjs1){N2=|{m)XB~Sg~~>MK#)Li5-KkBuN5%r zsYx$m;CmLz+Y=2vbJQN4igI$B(wN{VEZV$&595R3epVufqw>?RO}Xo`CJAf<7yJ)u zkHhC2Kym>!c;516QVxy2$HNHb+0{}^@ttVbUg4+M_ym->eC1SNOtk$GjY(xS{C+ zZLR-P6IRVW<+a@x1XV+;J38~cH^_%hHvwA^#asvyZzqD_p$NBK|4iNSQN(g4T}`dv zB_A(OF!Xe2Fe4I=a-qGM-s_3bx$4G(en>MLiWUI=sN4pW;-KAO&=n3qm0s3tl z*1%4TCFM);!N_|s4SI&*0*Kf^L=GcsdmsD|0|^YA&OpU!2+YvRfiGi#sLGm90c{^B z4`F^9X4l{=1J)%2qoIb7CNoEN8#|%erT`#l7Y-;JWXR!kkO+E$=ly|qnL^)c*G4yQ z#?%ri9*tjz3Kv9*&@_UHe5h1V)p@+;NOza>nEuBi)~?FTs2$}Fm9cUM4B{pZ0PkD= zchdG)cFzkTy4jHM7~lo zH-g_~?}ASNWJFvo8?@b#!`A~%6>fYfR4Cga_CVK#!|;iu3Di%JU%MBx{((UnR`O$T zVFjfjW@Jh5$z|$F_WH41dW@1SW(2-~QyRd8G%bqE0*j32E&c6NQ;D?{uNex=BL+0` zXGEL?J;8Pn2%tt#*TJtnJ9Kwx5plZ-rtr8s9R}%hCRZCSsxn_uwWwZ7Mjz`Yw4M=h zXH5}Ot2xyG@k=2%z~x*09_3p{FiO-In3AdiKF~(L6N6M;7fS@oqWg5T%dy;^31_+N zkR%92{-%;I@fvaNEwGq+NjwSN;t6U2&kd!sf9xMR}Eln48Gwf9-Zge_v0v(f#9de!Xq%03g?5*WImOO z!mEsn<;})x3OuGPdC3O?A@mVn2nu)*v@1UpABOJAep;mXEHuL{Z{4dJ9wk9VR85EY zK7o`z7Y85K;oD<+8glUH7aG?1d0#@&B0h}ZQ%y1@w66!$^M?7iLuV6EgfIz#akOJ7 zL%#G54TJm{MW-@=N6=x(n*pS-p9U%Rgf~wemdSt$A4HDa)c8)wNBonjpofgWH$1>Y z=ldftQ#JEg!vcK)=x#iLm)C)BhS-%|u{X~G{g90;QZ*$E^33ZYd?5Ym=W73X6r(hb zUxF{?ykSn8lm%TKI~`&vbSbO5iF0LKXFJ4Vuc$!8K1(pTa|_|CXP1(xR}pN@&69r; zVW|0f@HqIOfIYmE-QFw&)hg$DD#;v!7#!Rqw?!I-Y75Ezb+?o>RGCj^z)2b0N5{EyVeN4#=K z0zAjBUbr2GFO<0k^Y* z$tJix)fROAb%P(vr^dk4wHP(hG`hHQM~ajB&Lzl95DGg^Bf1cB!C@E|Kb6-r&(>He z!Uw@r^n>wldW9(;5G1f#-U>QF#$hmW1j1?(J#q-7wh)Bl$I5P(ZK`-T`~8dGd5qbf znwlc0J<^9HZwlYGs|S6q(ItbfRzYwCWsE8UN=VzOq0gT`1Mk3mq)TaRm;Yh6qm%~* zSvb2_W4xCx1673Aj}zTMB~ouvfIg{<))PWVD%SkOh@*lQMo85UTl46o9T=8B)&FDaOW>*8+V&q*8fhM6=$r;6(IA<((p)qd zGd8M_c^>w5R4PRsp+rb3WQfce$|(nhIFigs+bo%F?Ekf%-TVFie&6qXzqh^iv!7=@ zYu)R9Q6WRShWE*mInG*-c)vgZG>2E+*XR1Z1UF!pjr^(rVCr&bF zngf7UvEB9x;+Yg>l<;Kp^?vzNRhKhxiUa-;b*8>#oVP_<0Pg!emRA7FBvE^A^~V-i zsE@FqE)?b%g>ERw2{+tA%8X?~@Fd$>rc>!GK!mrZp;%}M|2Ta4jG_!)qi4Tr=4uLVbFnRNjnLyDC(W4FMsyCsN6Yk8NLfa$N;lc07vF`F$m> zb_S(>>}ZXgCX_AUHKSyHzzi7@)SVnJ5uyxb9)6rrDP7hvly{hXk0=_%PifSpYue*2 z@z3rAx_obAC;T$_9>nE1pL#Dvpg>5K`PcoeDJQc4LRimgN4*~+iyED&yKsf{gWuhl z3;DChpPs`tff_@uNz=dLLp<*eE&`EN(2WO{jCy|E4}4~V2Ej2Cg+CqSb8KtR+qy5P zKpbH*`?Tdjsi1FbW>9AHfSA%sQ8%pW5ShW5TQPS=i3SLW%V^gQwXpyeWp=qDDuYMh zq)!9fF%z|R4l3j8_$C8c;5*7{bG?yrl!PRvg^cR0z0o{>4pJJRL`s8m#be2IKo9|1 zXfu1+8(T7_`ZA!uhUfwgHwZh6tK?8Tv@VS*5GS^bLAm1;0w3mdpMYC*tx6b@I#r@V z6suuw4=gK@>AJ8(lZZhY){`$Aag%2xNI&Fw1CZX^PBWnNKf*p#YEkU=5#H z`HE|QfbKai)m?_Gj=QLMDPI3!qw<=MnmKaNT}3>fpqk+OW7$3a)V?S4*;z5_X9s)# z{Ey$(1#-%FH+5itj|ZY`HagqjFdW-|h&(7uY{~9ag{J)B%QEI_sTstN$%`D1-v@>c zu#p3Eh{~q&L6np_D3$1aa-W>i8-?}H`F``xH4AU3pMNZjQv0tt zEHy~f=ro9;e%Sso{beHdRALTCC_bWFhYne=GoLwZVM*ANgX%1AG`^b_8-0SP&(p^#md7)l^3ojd9Gl(L{Qw;!b)*IkaTj zwtWI6;D^by!NcnYu0X7zPbVZ}<+V4(v&hf5qkdLO*4|l_TbSc$-}G+DVA8QhA@a%5 zKbHDo72@hg-naI3dl1iYWt@2gidVD_lL22&1fSbLzSU$zY*ITU!sjdw83YkZ{FZ$ATMyBJt~6M?##zraaaN z!h_@*`lBoq0H{C6;SQJ0fbDA1Gj2pySaAjK(PFADq5v+gC}DE@!|I6oELJ0g8lhGo z9K@6cdqw27-B#XY`|bV{G{Uo+m#?4K0kKKbrYu&Tk&NlH*AUmFsQFmx;GM-@UOM6z zKBpaS-7!a3(1KgmOd}sp)BQnIo}8qRH%}`;eV&%9GWqva|85<*lot2u0|Ysk^&L*8 zUmn1X{Ad!zyK1ukR_E%5`Vf=>!1d(RBu+kpKaM|^m5hDUIO`E~6?KgpNlhGRHowRd zQN8Q!fp22IG9gyo)&qBC5u`vViquG%xIRFcm^(fuxYCKSeb~hJGN68m zuWx{qU8}-D0qA=4S$mSjaImzlt|uK7IV~JsShU8Fu)=~lC@^j*&oxJ|a@s;58#Ul~ zch}+=|Kj9CFm`wUl|Xs{*c_qXFH*avbzfOTHtmFQV`7(*1FP@CxBS;V*FwvPFnO-({wy2@Vp^wd5D!ceU9)=)Xh;ummB5#Ji6T zL8T|#1>9~}KJwwyvAM0)FG+YV*Aw1{@|p?}`uT9}vzJ&c$18@J0RLZlu?CErV-(e3zrEGD37RB5wJ$ zC=Uq;t2t^ctJnt~n@%|cgzd21^w-y-a96=US}wAj?N@{Es+PMOwlPu~E~j$g6Hrv- zVd<&1b2@$rd$QM1T{au#6XTnF8?(4-gO2wy^Z%WIDm4~rIx@#4BT+}5R_Wog$aHmm z;5|_HkQ&$+`vBR?FRhqY=u!gq@>brYB=>PD5_YxZcsX&!^i-paxc@ zUSGX$!eWui?>FF{FFZ*yz$@pp6X}(6T>8i|tK%IeunniP#yj5gXQN6cR@xdC>GzaQ zgNi8}QSEHuhM@^Y+u?x+SmGd3g^}QF1@t?BCsnx*3khajGkBx$pFj|0qa5oO}OvfHR zB6}3~OX3skC4@o#CUZr7{!`@kL4>imp@9k0XL3C+)27pVj?KwGmc<2C9|ZE2HN2a; zea-+ELz&$F^ClELyI}>V6dHwFJh>fy?|M1Ku0gFd^a|w{MA_G-3H-ykX?FctHy$emcj<|V-(aTuKn_C4`L2oo+BSw zm)@W|c8Etn38Xh_@`C?OA#q^%h1-ezQc%7FoSeh!3Tfwn`Z5#spgV4^1mj`HPnOqs zlEmZg5HK}?dMWx4bXy|NZS+oPAuVA9c^4MgUp@C z1$Ahf)x>$VfCU(Q&X%^Ul)TjSy|(X;Z&;vD%5Y{uVC>0G`bo`DBeVhs^g#i)XI-!7 z>!YrKJzeAFA^ECDv?mbegz?K?b=nK z18MP71!V+vUVaV5F4#}-_uEEC^+7WY^8M*MuMbaW2XO`*0tg8(1c>b4#cPTd0+Io< z2A25Mhla~?!a;&C{@bw#mHbx&hk}rPa_a5#Q2GT@iwanOfB)~07D^B5PgYh|4y>v= zp{S(vH{cx{O==O(m~$yJ^NXQRUmvS)fhaF72F*6j>UrN#`mqKM;C}~QK}-T~2!Yq$ z=uf)Sf0a%nI;HrZ*iF!X*8R*!DP!O7&uP#VsVIM%$~`7d0&H&F#9GvVAf6x(steKu z9uzTi_}sRTSzwbH&n=A9Pyv$V_6`~4nA_iU6G!aZ_!^)HA8$EdFIM}4ngI2sbKx+& zo`%bM{s93af6|0_HML;8V`P06y5PF0(uZuL6}TBC0z~S}T4(Yv%oh*=K?y3^5l1$z zy+X(rG5{m;_nJzvY+7vA1IPwY$HtR);lO)!EUascS@Ph=wi13f2wy|bLm+70Hypgp z0#rKE`AeYQ9Gbh}J)_RmUa>Cy;_Nxp7b%GKz$R9Dw%11j^KGi)Jl;sem( z=8^#v)JA^-y=PVn=jOV=8@KkZM&$M4Ij#tJRJ*a5w(|JF`C|1QOrZuV~T-I+qt;x z-z>cTIg}eA9&h8ul9i| zwM8?~UYMG%)Tx|h%1KCJ^+I*Jy;+F*(q=b|2WjnhNh*r5I<10Y(O@v|Yn7>0+78Bg zUyGqhIYMNggvBaOgbv?lbC}0#--KUIq*%%Kk`02_`n+M;4bKb7#j%IIi0m(W9c9+~ zSNnI9*w0e<(#Gnzm|r2CPB|WHaZ{(OVU5o}@U^@K0g2a=6eTZnU8^^DsyQA*IWVT$ z&Nk0B;I$!A4#TOX`%xo16a^}4LrQHE`6>BzK)vWyctp~?iP_GA{AXU$uMm_=IM@hM zmb+ENL-6<}1Dk3e0$YWsjeRe^_4s6hRL}Q3qbzF`2i~br`zH5ff?YBnwQ|EnNQyaJ zXiCxQ14Q<>GgYt`LEF3V`pG5?hBj4(lyW9wE+Kg&{;{B+c$OY&1k_h4$Ozu? ziVE)YKO^&#EKhhO6+PNY6?}!gLAu1w!D06SPH@O;2{ADwub!1EQ)X7Mla zELu?-bTi8BpObKi%mgTYNCSg$_Oi)i-$)GD#i2!f)aPGnQEp4!YC1f}yan9AZb|7} zV4-r9kJ?GZ8|%`;9z80ltzF?%(a{n6CH>7?^7b{vw_NhJ8p7c?Te9dtdNT>1)Fx0*bkUMFY(G+1WiXb z1s1OlzuN0I<%=H#w0g8{E~4(joinKQK*BR}|ERY*Wwd90ew_Q2;RGwuecMh_tWGJ_ zpwtRI;N!?ecbD)P!^b0iCQ4Sc-;3a*vp?+Y!YLe_ zhtf{Q{IQ8r?>NosJZ6D&*tU%YCe^|?^T+|6l~g1Vc_Q_@uB^PDS7j}Y^%;tacaO*Z zvz?zh`sY$V_#cRa@Eyd?rbopp&b(YMxPtmw0n-lHC}Gx*po8z~f{%LPXvcp?!l{R@ zfPDf$y_0JeLdhHffrLaro?{zJO-XSqxWx2vt)(4{j}q3bal{$$^`;pDzXU7c9}$oU zPq3QVi9|E%b8F^O%~7$Bt|DMI&9zgHfW+tN8A{Zu!|=RuYy=js`R|R#!;&vcg7STP z^}(P;l#9f4jS#+;Q5`!MJ~?MBNC4c$d^7rc?gglv*Do9^OT2GAaC7|Z*1P9Zi z1sc`~CzR$ij4+vP#N@b3nIjx3elwj?Nd_U|vwx5B%Oax|@u3PwCuEU~G$@wuSCeNB zOS8Ks+e%2{jS(N_O+bfCgl>TGzK@P7+>8J5@V>FQE_bSrJH;%_4E4eh3f#tG7WGpd zRfCm)7ZTL6?FeAfq%*@|7!xA?BWig{rIYT*j0@W*Js6-!d<*5Lj0`{kEwa^~G1wIv z>@`uzrwTs{$?(MZ@#$dD0+Ys_8o{Z#3loX6`bB7J=+sf1-UL&FZ~GOO$P)H1VX27< z#TX?n4-skygcoItjtnSrRsX5;Or(2 zyp0inT%+2Fu|lYHBNHhkLp4|8J-jhfKcKv}@8G-{0_hEWRN{+I7Z76aD~h%@QHn(B zIQPjfUr=X-q$n_+>qhK6zV5VX{j%N>RxljEh{MwHvr3+Am3*WrjVZPP&YGQgn9z%% zr#4t|x!5~d5ry=5lL41fPZDBe>NTmc^ zCYTIV8ECpaIB)YtF5uzeb+XpKzC3#L2xOMK$iZ(Hq%s{$!@Tm7E-%eyPj*()`P1if z+u^g4t4!_kw6oi#Ao)bF^ZWPRU`f`9Qq9Nb79JHLJGM23P6fq|zri7oB_gEu@3uCs-F*Pe66B&78 zz4b{z(HVfia<>GidQvB3lJrbEQg&%6(?}_jP(b6nY?5jMAqI*0sUImFgoyIqLdAx! zJE|Hy1OJ%(`SZ?IIFFvFG=-}!|B&g?LBJR_#9SBO@>7{johZm}`rbK)jF0P1v&Q@Bovui@Z z@|&oFlG5oVVw6o2t5?AvzJRFEyASM$9BcI*b6Y!5gmVOKO3X}k%V+%?HM`@*V*zT{ zSRP>Gpc#Xg5_Gey!CiaHzA=kT3FHLyuA~z=CBfZ-0&svt4^stV zI3mGdJ6N|M?)Zq-GmSn`;A%ki<$xCT8jj3sCn8}|0&W1L&2SH9%illU8-MJpRxHQd z-)P%hAB)A5jW19p0;O=fICP#Yb}Ma_@Y|;}qN`1&$evJq+vmjF9FD$&19vTqqAAvq z((|bV-y36QTC0yF8FLciB);^G0I3&RM>WfX+<;_FJbPxF6pmaF`ro9@rZP1VLt!)6 zOmE=)oZ@Q$jJl#?+k?4+izu6i2&qt^{NmB-d-Gc1{t|dQfuFh@crXTmZri1yPm_o0 zp{*_@08Je7@K*sZ0lb9;GEm*OUhflH2@yF)n4(auU0uHCTO-TQ4+QkBtYAaXNJ>Cy z?P=UK)3Z_CRXLvBy(i!9fjcpMFa!{GCl04ljxBeKMQZIlY$If=H}g@;kn|}m)KFAa zHI~#Z&!`7&xwK2g9I~|+9p>P1n1$M+hJ>qcLVocN7w9gTEUrwKoGOBPip5X%ylrUJFvY%ah5t+Sz zhdHadly)yTOW0SV*zhVhdv&A*@NffT=J7URk-0NaOjd9eB@fY`E+n)}G;t@JxUwxy z&PajUFM;YYz4FKe{=hkmodgN}`UPN*c<4Hk0(;TjRKXqtR50purtJUPbgox8RXE%h zUdj=Rj5@^E(X}E?3uv6JO&Afks0w}zQZ~brY+@0f$9$MPH4-Hmcp&it5%Xl1X6Mkv+&_|HEs(3!iXERDR{2-k>wXH7 zzX=r5sFb1<6%_8Ewp=bU0AkUYw4!IvWaB6PK$Xz%pGMBl!g~I`>uV$9Ef=E@Dl3?e zvO{1G0pp8UM&?O)F{E3Llvuinj6I%=eE9TY38E^<1o*nVwY&eI^%G#pnV0p_Km z3-6XMr5*H9SU+@C)}zI~%W`qiXH}{I$&n=5=sd(XkoPzf#JKVx3Qh4ae~5(ZhEED8 z@dYCd!Pfu)YHzer6-8~)wC50BKHeaU;C*Q)p{L`h4gpe}tyxd`sJayu20H~QGOw_y zFmFBzmX6VxkQIkeq=g8D!dWexep!um*jh=cf5GR~tdc{$JxX)4J$!#=OOP7f&yFUF z{;Q!kt|>qP?TA6hJz?6g08TFFX^8}61ITlsdJk{}#DF?k#Af3R#putADj+U)s7?5k z$$F}}xmg`B#9Utuex-D!NV2z}BZkKyM^KK$X>g?h#34*y(eD13>_$fuF>!IgBV~e2u`jkoN-mt`gS$g|^<^2!zGUKIcfN?^j{CEET^p0Mr{?Ev6`+KKrncYw z-rjzl<)0u#Sdr3d6!jgqflw6g0r#sl78z1Rq3DfubW=n*gdrF4h0lOKVl`i2`i=U;-k=Ii#Tt!mL(vbeB~d?JM1KSScmPTPSgS}){0xVZ`VH24Vin+E*KDO$-9cJb)+A?jBqp%im}toFBNK1wDu7CqV)RJ;u01ITjy2N-2_ zOU1R%vpX!O@^R0pbX^doKA)`K89w-Jk#{|b<;4HyAq+zx(2P1`b9M72;nnu%00#l&gmEd@V=Wd`>*}!8Ys{Yx zz0yl0c5Pb%qo6OH-h0dfMLOA)3oKsh-?CUit-|ah0g4m7FuK(m_KPSCBS^rM(RSZ= z1)msNHvTrue_4?ug}Hg+s~BG{)c5!N&oF1283c<;NF;411iM+@P8q0@e5WKZ4$BvP zhL6gi5hP%}9v%LyM)bwQobHW&gE_TKIVKw4{^C*9Dj_P;D83qYu4nJSPmHomdyhGd zucqe|EGY1g?a&lOtI74p1M?~2H%>cL20m=#vO#H5?o#pn+LMk!^eN_)gL6qQPN&PC zPOZe04u%T}3R5!>lmDw<_%S4qxWLOqy@H(ux(A7eOfHQg6pG6w7NHGa^ z3yzDPIaOfAA>h$hf!_y@KpCn$)C20%^tJe9&57+^)5h~W#b+)NcDK-vXL=(nLk^=UeZx!LyYwust>JWu@LM>l}+It?L?6svD%J3}hm)Cr@W(dm*gI z?lnbJtXZXSA8ELISzI=AN>Z$$)}3V5n@a$w0(TN$Vv zohq5^-m3Y{1WvTyyFbZjU+@<|eLBMzen))VChsnxU@l*PH4{%e8b{0r^klc4?^%L4 z76g#Za7xc%BZ-MwNk0Lf$wysOrr)Lhi?#w{v4y26nerH*gWY;{05EcNV1Fs*fhZ&b z^?-Vx&n#RQ>2-9_k&4{5GW-D>9SD8V>gnH~b|gUe4EQHD zvY_sOBU8zGlrfv@*hJHHNY92iFBG^!`;<~Uf!GA;ak#~SQ_4&IE>8O~G+xe~C}*y` zy_cI?e1-v23Etu+ye_0nkeve{aAs4u*F+xQj^P9}Up|&vv(cB6wiX>BBm|k+SXoMqYj1b@;DrpO)vVo9s&rf{q^&NjUO~BErL#&{z~0qxnoy8e1J64TSTlhs zYrL2p1f{aZ_NbC0A^IU4W~U7Hm>(Gz>Q&5l9D03pz0F6QKcMV^7dBh3BG(jX3`FjO z<0Mp;&vl{rDVa?)1>qi=u2x)e$j)KBc2T)}Wo;O`Es`wPZ-C_9@z^KGvM(!@FwHwq zf4V;(JD8ILf&(&-=q{B!%>hRs*LeNn`+-yQ?aK-mmZ4(+95K|uTg;RxkeY)?@VcC#QV2;>TRc-syZy4R#i znMXk_9%rKuHYxIq&?}H7f-&ro86yK4te(~(U%`*mwSZ$>fz&DU8q5ckVFQ2KzY+^*_ z^zMuE@&>*M$JhoKDLiAm#3-Q-j?%Wt;MyNi^Vl#ui9NXI;Nr75uB%HXyMS_%{QA%w zBqPLc0iHxu(Dw7^<_~{(CjB)oKWzYsGWfyx*Q{$4B`h6MiuH>XXk5;1#dirwh|bM7 z^Hbt+I}bsd3JW9I7sq91-NFxesW{T{ug`=Dw#qYcZDaXvcNUK9&@S5rcuLfWV*Ndd z+LKDzzucskRtY&h+s0v97dB0ej^6iVQ`B0B?js03$LE=+KlQzYZ_-j}GO9%W7m6M+ z!7dk&oE@K>N}o1}##ZSGam=`l_!!X#@((jm&<5r2&Zs*l zyc}S-S2AoTfQ`ED{Bpo%J=o#(+}ho%P}=e}A-`0ftBKPN51Y7vVl70{o}}4WCJEJD zdO|3$n5vC=YXHd&y>I;rK*J+=XMSqtlR@-_?Qz>*P_-eMq5!OH=pqRb@Ag+JAY%kD(>|-(J2-bN+?>=4(Ckb<1Y4;W5WP^3Kyoy2?g90+NBT-MZ#=>BuSx z$x*A4?`UOlyn=}OBDE7fcc*#ekutBJ$l4I0{h)j_|BG@(YVhYBD2dPPP!=chH&ET2 z?-H@HdK*ppKyt*eXZS%UOG^Oh#|o75$oQKTZi@bIv{{(aFWSe%xGtwc*JjfFD}r?Q z!;@{hG>=M7Opz;10hMpQrSm@!I)(0*JtCwzpgIsBjq#y#JP;ShE*;wkVWXpM(hhqQ z5KciL0)hyun=fZ!`vI!A7on2n!N3uJIXsnXH=@HoE`ik!4KSa1lzu!I2f!n@SQ;wr zO{l)q?~OdK6W=X;cfCF8+@KNzn3B6>9JFNj_>OGq`LAqPfht&s^m)wc&%uc!e!fC9 z=iuOucOcJ*!2#Y4F^^qQ>I0dZF3!+fzxcuoLF&sFWgld?%&Tg-Nb^ZRF}tc5@uG!$ z2~@b_JN_`?Gq(a=%6vNlcl=}&%B$gTlr+|8mx1v(c0rh$3Du$} zuY5QqLruE8P&Llf5~YSnXI+^`%|D1uiPA9G{aSbRwr=c2RG?gxM!p%<$H1f4BfF;p_^o7NOR6#DdqM(DI4T;vZ zAwvyUbfqiY405PVH^>;PgAPvr{D#K*KuZ>Cm&3{4Z8z&R1QEffskJnbdszFl0Ke$+ zsT)n+Q4+ZT(rN`*uOxj!o7=|TiFltJm|euQ6B@#l&_fhp63)l_-(=1S%YdqFH zI7}K&0Op414G25X)`J59*hyCRtIy0nHaC}D{tZ+QuvK|<9;D5Qcsfe_H=?S`S-Lda zW$DJ*?R{as_Axw{6U}=g;>Y(uSrTEo_!KH^5Dv@i!sZ~nBxuiLqCk4bL!*Zkm;XkeVQVyzG<;aB$Gj-8b>*ZC~Gj zpEW6+47{S8`6T57Y2Aa;FC?*>#bsw>qaZjV)F-s2Ek27$#7mo7cn9USXsLk)zUb&U zWzTC04rd!GJ*Wo7!503Rj~cx-wFPB-9a95U#kImvgUW2B0seueZnaoeF13M;f9PuxlPyQYYUMwWx4me-8>O zXD``t9n{^#l}Dy8{Xg>@RI=9=%-Fp=hTZKO;Ax|rN&dQFV*L*5d2DYUtSR!2#~Is$ zasXtU(|6X_4*CL5C5?t~vlm0(|0cX$UnTO-JmCP7*(yM+8$-0GD*an4*3(qxJ?d;y@xwDq)N4gtHt)yQ6o+kaEnR0`v86G7ExWBVGYw`kpq%V>H$~wfuh< zNtQl@1q6TV>aKpm=H!WYNoeYt-m? zT|{96TyX^h-e4FEh{@my0wm2DnR*JsDo8ntBjqen)#F5<;uLxuf6&MCL4Axqhiq9( z>Bt&%JXLcb9~7jcA!&zmopG zT3>qU&dfnIDiWq0onw_lUat8zdBG(6DFL;>B$A`SU-CRoxfmhT2KXPzTM|m+YTX(% z54yW`YT#g+?kr99axC^p?~>+^zrT0|OIb&a>*1j^X41}23W2n9g;ha~T1Y?VJygQ1e89{bW<9DNk_6Ne zghdciN)`dCQ1K^>stgo@>11;~wkqb_t$bAkjyLL{S3hE-a3th9+Udfr354yYsGmo% zvq<-+9)_vAI^&@hA=?8`LRq`bR{H05WpF1_2FiCVtgy;yc`j{_kp6V&I!Smy6v$(o z@}k!MkGo#pn~8csA>RPff(!cz#9#QVVMoazmS7_j$Irc9hN-k(P13?LT*{L;E`NKI zR(|wSQhz=ikz~}@Qz3|y`oz}XZ}b#4#;-}X4?-0%_0OwuUvzqs((J(5+krL)dRwBx?|&n1boO6DKpp1A-DtR|0y>FLN<7=y zt)D@LlY~!$g90tyo|9Bw7!YRbODD(sQNBRlElRANt6M(`pntdh8FOj_d%A%PWht`|OW;9T-sHl`>DLm?Om39}p^ zB_cQQ+1_wjh0pSg&zI7O1Cc_%J+%{b&S25a{!39af%I`t4rQjHsb4XD^60y}a!z;T zy##kbL1iMe)7AoIFo866{^tYqHT>0oDZ#0z;VbQF-Tzn}oCPjQ2{OL;i!dN{H9^+~ z#!im~oZULqzBDOPSfby9>sft{=$ikoZqeg$s8vmRgoPKCX)LIC24DQSj6L-6!{}Gf z%1MzvY%7mpTaZ=S^Puw~QM zP}}i=GRdOj54Vp)KgikIG;1p2@8bWWl{5cR^MEg88rS;+LE8U{vOt7*{n|6@mzS4# znX^n}fROy> z-14}BLCgX~m=KBkn|Cjp=W38D!C%8o><`MNbtrHX_AT7^0~ui{?sX8H5uHeGAQ$Yu z0eS%b0ZHbMA&fjj1(Y|cEf+G?HogYY1+r}((^vFIE`uD6AsHirg-IwoByuI?Bg~|1 z1IP=q&+=*@%|p-Q0s9`DpQ_FMKPw*BnIwipciW0>(f#1Q!1OYQ_imnRV}pT zOgBQDLE3NCvfWpZN@i&NbDxd}7*BEQa-5GI=zQJMx@g7n755LDty!*VmSPr>=d9tn1EF=lZA%U)^PneY4R^bSVO^QE&P^U;Ei*lX>-A{+M~Ihq(ZR_H}|i z^}`aoM3>Uu&q{=fC1#uT-2f0Kc=As!6;K_lI%R7r!wEQwI($)~0oqGqu?D(q6zXjA zZx=0cUeM0`lcb5b*0s#*sC0>o`*ns~jKv}qKs`<&;_a=)-S6sd41^0g?9nNQW{(B# zsVE2}4pfs`qCHuMEok9KUKvg8oVm8bdK+)@ji*l8!Yf4A z35Eyx;hFbs6r|Bzt;6=RL0Q}Gt*$$~y#&3zY+MEhQ@ER9Sm00;(PgB3fyHN^H1)|c zxnmAfzrz1f^cJ5TeoSXbtx7Hl6NBs_(m)`#;7?t>yQc>MgMnKZ(pZZ$OT$-J^lheZ zpftTne^}yqO_yyOy+Wp4H`R-;^>=R3Y7JA*M-Mhh$*PW7dWqy23Wm#watn$i4n0zh z2$wSq-{771gnpwceG|`0g$B{P54AtOWH9wv`tJTw$F#4@O0V1VPp%uc8U2C=Qt90r zYrxeMrd^O125J!_ZI{vtTR){v8~c#w*FBGuCcb2x5Df-(N2N(=uyZF%e zwQ(sbR@3<5~xH44$oJ{)5V7X(e*e7X^)tk5G>hQoa$rma~Ih);kC-pv_?USs~Ti#yP(Y=Q; zr3tFkT^ofD0`yd?{qNOzbu*YB1IXm`m7pbk!O&~j;K`zHNH%IcbG<-*yg(B4yO3IY zY@~$GosRsT@36A2mVIFRF~_&~A>=-1Z5<#;Z%mK-M4t=tW0i)NPt{tFFJVs`msqbS zwW7#YW^9Y!Ba}CRA%v5o`1d9$%DOmN(K{^4@2N>&RQm8h+Bxni!1yRvI8Pr(X2q%O zF^@*vK$TNt|F*JOgN7BCGWI<^H`!by?S@i?KghhZ*7C?@A>!=R+KDHOuf_PUA8IP33)&g3@FGJ@8Is{9X^;@4Lw{SgZ3+(R)e~%gr5nz1IzB_x4L~t$nGJ zY}=f7zB30|w3&()_6->8w?41%iFRK9+U5>F&FgZItyW~yj6KK7vmZHsN?O$5`Y*Z7 z=Fb;Gf0X93amZ{~d}s^5YBT+2y89$l-tbnVCA)j4+i=M1 z!((Vn)z;Ouf?R?(YI>J`PZ6>o;@=%tJP>KEAL+X*Y8wQU$}zQ;JkpG~3c2Ues}Ph( z&@BzA9jNu_FUiK-<`3kyn~Rx`r2@IwpJfzGR{l&fb_cLPLq&J3NZE6;AUeKX8wMaO(@=Tt+9_$)UAno9f22s_fV^2RF?btTI5Mo`Y2_ zLj>?4G^}mN{`sN^lmk$TthZX|VeW!^crl*MZjF*&6E>h_0x1)xLu=*Li1c$!M%I>D z6-}O`4NQhYh5O`GA#Ji4j{q1U@`11}f;MI*DXI7K&^t+rP_G|5>IF zZz|1xn?@h(HeGL9&@N_)>G^W=fp-C(o}#ZB-)gsrpeK}E3UoaJ%xND&IizZOogtY!PB~`vC^FZ_WYiEj&}^^ zq8;y#BH;vpdsvnKk;jnN1?1|K(^KA^)#Vv=G0T3MlJ^vsyR)y4vE|`Mt%=b$Jlqi- zEy-vt1<~N+vES=pa{Ak|G;w-+z7wtSEKd?>MU!&ygYeL|5l>9!=zbhhJGkb=$o%$c zCd+C6*_fS~*vLDsnmsRpMU?Qz+(=32A2(*$RmLJ~O zggzZhf7Or@?kv9n27f!+UaqKf${T4F#PJF8Tz@i|!!I2^Er(0c9Glu7Y!Jr{e|5loX6{D%hH~Q_ua$mkU|<= z$PO$X{O1e9G2;KS|>3Vr_s@8b=y&< zd&AT@+9LG*ac2ypHa=T_)7lxhZlDFncVD@s2D#Xolb`b^$1@Hh7m4y?S^D-iLQNHA zPWGEcz4KhX!Ppfa>?%+w+)Q{RJBYKx3fg1^CnB%GRn7Fio?&?6fyhPeUgFC~2H`GZ zCA-jW*|}I6{b<6jLFOm#iYWUbsnOb8Y~_cm6g*9>_4AretnQv;F0L3t z>1R7A^>kG_l{z)nzK10dIIL5hii*qLnt*Ix?q)i9S7sOC+n~dzL`Zs0lWttvZ2V-B z5p%Q4**yJ;cJ}cRxQc$n!6*>(LuLdeD+;3HOWS>mqS@)&@m}Yd)ef)pl5r%$6`jOk zY4+c6^Uqw9@^a;J#^K+)>+9m1?|gCQe@&8b&1v|dO}_)@*RPO78qes1BfG;eyhEPY zl?}`%lRlE?$?IYaqlT6Dx-t+XK@MtgQ`x-2JH^0^t%$uy?cK`_z_2%F1-xO*udplY zZ~i&y)l?qS!~J+ZO>m|Izv^bGpo8zzdrEPa(8GOw7B@-NqZ%HtXK99l>HW_^jp|H? zW!P;^n~{6UDt73sJ3ewS=I!qv$Myu{_Lt*Dj>oCoyp6;FtU}m1*j1E7q(FZ!DQ4y? z^s7dk_p`suzOVGfQ$#7V1`95wpX3CeGw$#Dqa7J5c$6LF=k>cPhx61tEJMhTq8ro1 zT1LoS9(njR?Q%|ZR+7iBbm9-EP3YgT|EUtwSW~!JREXqc6-0kbC$FWM8Jymg-OH{V zmffmscS+ehZ|%vR;eX3xdJ%0c-gAmUFVdBSI;kY4WhIGwGBxZ(31#BgkPXt|Fm_58HNNQCDT)3R&=%S6#NmeH9akai&U9gg0z|rVZE=6 z$H_8j)X6)2g0smgR({=QbZoI{lD6Uf&%)uCxh=;2cxQ(IfG-2R)ze)cDuiIFm}p^a zfzZvO);yEg^UJRtX8Y+ms}ehfy)EdawwdR3$gamQGv62A4sNd-Qqx=$=)4Ga;`8&* zmzEGqJMad#C5@^-I58J4y32$NI5v&FeUq(h8p<#&o!iMzmV&thPVp4klEOFa_&wv7%*I-- z;Z&9j#=-~;k+?HC2FEf&0Mpj`S7CjIJ&6bIEo!gh?rUgbp57Y%Xmn^ZspXNoJ=u=I z1xtICp*54&t1NN5sK&pjYS~O&)?9k&k9e=Cl}T95Uc}y+l_H&riqpKZM~3Mf@hw`B zeMm&mXTj*084jso+Ts}!`H|>PKi!+gYN{{gYTa7YuKcW~i(wi1Lx(qFG};qgQ9sqh z^Z;cdcKhah$qMJs=-SW^5r&ES09dsz0c7i1{>&Qy1wT8HqvWn;sL%cL?X1CYc6RS{ zhJ;-2J~FxTs2m*hY|83!k=St4Em&=Jg|kW8$&YmiQp*Js>v|6UdAE5w8EAth`_w<`8hT3| zaiGW0SIDI7iuyen0>j}Jp+7iz0|<_H`Yph_PlD!vGb$gWnmC_ONG7X_3#}?ZVL2dw zya(IdBijsm_)e0GF|8TdR;I%PJG+P6$QJA;=NTVdP!fB-?yJwM>~zo+AtSj3(1xfA z?D(%{vK>hgWT<7;$#rSU|E2Awtsc-F6b)D>TVX=ZTtDeLuflTQO0*u-mO_HbWdSj% ze2R+`^`*z%)`J;!`fD%qf($k>f+t4LKJFEbR*ay8t{OJx4Gb(x?N%l`vMx_zQaNAv zHVW%@ll_{!_vT|zHAcq%aq2qw3_s6|KL(ni^6NfKOh0F0LutQEg)SZ-Z%v}})QNnF zuTWJ72)}GCx!_}s7I5kMeBajkkE|UDC;Mz++rZWQFL=qg9+E0o3Qw8b$52+e@7uNk z*Cyes*yBMa%%3wg_rMw)dWrz3Dqzrc}0^On4#te(s!ZK<@`J-cy_ z8{vix!x#2iBAK)A&jlvW$xyMGzJ*{wcradi=50$8P^(2? zZg+uXGs9x+q}}wXK4s_J^8XGGdyQYmgVabrtQP;kD#NA*9x%xyPt!*d@RstP0oXIf z9bNBR56g0sTmqXUM#nNyaZQ2o#|lpcCG_fz|qbbHq&&QIXr`4Q|{ z87g!yz9*6=-!d#5@z@c+?5QztUaR$-s=>*#BS2JMYi>a;$80Ighqwl>jH979US726Ak(&>zr~^+pnwn6lZa*Y7Xxqsz#zvK-gR$vHV|L;Qx=3XE%KuR@%OPwQpnGi{)h@sU^gt&A1yPfW zVakakS;3`@{ek3qUVE!v3BpnBlUnTwm{6ElF8)*2)<@7>r1N~4SYJG73{iV9m16mE z+A?T!%(iRheyG zRY~6EumTdi8~yA{ZS#8^+=Z*e$w0%(UWcdYR?H($FmczKA%fZc3JE)5)G{|gSn>!e$>QU0LKBU?j9J$=k5ec0 zhK2nfMQtJX?R-{FoWXcle&qfE@;>FE1_iHPZz*~8L+vgTDpurD`Zqzis6_d0dfa~Y zBl2*gOW5(R*}QFXguC+unXceVr%rND>O6s4g*gW}Btd{;%d-Th5A5*hpS_7(>*v)T z1#74e{7FKWf5!UWjX5}_Z0^}{aVDP1Hwi$9bI@G?vp2}k)TaY2$0~>+tDx?c09L`7 zwRppuMQ-aZ?Errcxdw^l7&uK0Fp+1-`BiwgFO(`7MmkSWnh6fuLgZyCFrYcO72<0z z_X`5J;=WbuOR@nS*s)Cyko8g z$dO2@tC3S%Sq43j0Q2hj@3E@kY!0jzkDN|^+WSddd;lqOggbi*XXck6h#@yu<3IB}3 z7W^_FAS8+k4z|*}0-(f}^SEfZor;L+j)&!XA{_|9NTmSz;b~uJ%quIX(bh|~EXn`b zK*;iV+@sc4hJuTon(0Jl$+9H0+a?thc%W6R&$dNl7ZICOF*oleSADwjeJkpYI;Bht zz_NiHJMXgpDkUBRh_)0cHgTTc|5#NqwtDqqrK+wG?xoPr!twrtsDIpUiD%=xgKP^{ z-RiQWxel$i@4wSC|D>VgFmZ%}WX}TD08JMjRv{L0^3%y5^qRV>6CeQiG`+U-@CrX4 z?4$mTp&viz^<;+VPTHZV6ro|NAbc>J=ldpM{0Z@QY;(DvFWnY8V?*22eNZEZ@P1pI zX(c8Eq$J?8YQ%q3<29($^;%9m<(x*ztGhP5|FV)cg-4}>PQy8^(#d~;^a_|K!!elh z(e*gg5_Ap6yapnI)7&M_rhXN;l|_uq=puBSne^{6SsqYSqdWQSGTyn?GlO5avE=V- zd*Dx})*89qKk_VfQI%bz*18iTn?KKhacPyWO&=IOtE76$6rOj%kJ-M3cp!){Ca9DF zVyzx~JHG6U1#Rri)J$Jzcjil_P+~v@U%o3^%fB&8{-v;E6Q{%lPWYDX zWRD(_6A6gOu;i4)nRdd5K|Tv-vmls%&GN$!^`wH*L}!@SmN^Uh7L{)fCihY(IQgb| zL7kIK(sIOOb)IWFxAGoATv@&|<2LO=dEDddwXI#NfR?K2n1ZF%Hf`9ga~g_FHut(i@UtyckBV9(C-E%adF_PtQ6`l1KZ znRv`T|C8OA;EHYJ9lzXlWA`_i8f9kx|Eudt1F6j0{;@$=d2u*;i5All%JMMCqAKrk@tTlrMqaoQZBabA*f8pAPbJ#pC+b#que z!fMMF*Qa z!GE9rd(m2@q(q)RDx3&f`put~u89C1xBighnz+E`GT#7wZaP61sMWP*X6@u0_E5QGH0>XN zS$RK2P`rVxBQXdp{M8nrI~p!OwKb?v50bgbBs ztt!8XR-C!?K!P~G8-zCa@FKZ+%K9t9^3?~<0+|Umtic0u^x%@P%L`ieR>TI24;6IE zm!39|{K!);|@jL>DB12gZB4yO!+$ zCL_qaZ-T|_N88b^(2zts84cjJ&Jt6UJ0}P|Qo8|uY(U40oUD0~v?(%ioVBd;^I)vQ zs%P6_J3V*VQAmkO+n*tpK5JAyIP@xKDx_#88U?eLw6S)ji(1>ig`MrE=Y*Z8maOz#3>wp)+YlCWn_kg)EdN(i5feyUHozswhD(vN=zFlu1**X+4yXqv&T>}@r!1ac%+j%9-%%H=%;VQ{%&ak8M$7no%+8kd zj1E_*i1ma{WV9vjzx5r8i|KX*#%#Uao_gZ%Xa@8*9`EY7mrsi24vIOIIV>j5wZyWNtGyO!FT2mu70 zcI=Mq$-oU=MZR}x7`jrpB-zyXWm0@V^>vi;uQ+-hpuQFuz3-%D}rj;1d z%`{wFCq6eGGsIe4lqtleegjYs7#yxqxb7ov#{Q235=DVeUde2N97<-H>`SiptG2Av zNXq8_;|J4;*35fi?C|}sDW@EOcMnMx-ZYM^+BTDP{?F-WS!&v%Q1T)#{ViEiDVMET zQ#*tAt`>0MLE;4RjusFm7v(k=yG1rl6i4o* zSyW|jb1#v4EV#}L+>>cgYbiyXhWmf!99E)n&uKEag0UL3fPO&dZZTmv!JF3#;o z-m{|idV+V%C1x+aWmZNc^v*XSit`6IttP>-+;^fNC|0#D}4U4lWVOfbcAivWmPvLWs{^Wh2#64dnzgkO_@7TAdKpP0m*(Xr0jnjDVQ!R56sa%WZj z7PYxgh>%au5N(5)sxWd=jeGCn*u#(kLbNl@fKw7D$hnk?vMIFl+xU8HnzCIy(p0@o zn9v2KCaq3x!;rV%7w;kT0qlpmFt|O_rWk>>5Hb$O4VBO~xm!ya%(}u*HRXxyOV1uS zxUV4u>2l;ZjiZC&$lw8-{Nc|>Lm2FBsRR((_MsT5=b6X|;9z1(=%6!?pL=>pt&%2*>x)F9di}i~3HcVgV`^Yi|(I_3@bA+X-2VB>TO@ zxe6&vuFR@8Fss*ozBaP(ngtTfO$OWqU}AW)*jI-|SyrOn5S!`bma4v)2<&Qaxg=6O zF)8|rU7|&VuC9z^Bv84sM7-tz+6zfTZ>-U7frDMIs~fy8<>!Gnmg@+mZ@XbTylh%2 z110m~K6wwP%Ha>b*D^u;8-dnZ$B?)oO1%a>#kE^93YHs@o3hy(cj-Z*9Y()E?#Kr-gL}ME$%%q)>m`|jW;|#<1qNJo@WD{w`>OL$L z5GF#~%8hr&7lgf|Z;-dM77-|#-6zL<5L{JW>RpUASqsv}Ueuk{eNu}!L8?6qXzGmg zQH-EQQ;|c*mzg3X)w=rf2F-@6H%PhDz2tZty%L;V)8F;?LdksaEJ4HyUA5;U+LoX& z?$-0?fYD&$))=;!G*{~p7EbFZ_47hEiyeTc2V?`MMzlm97P39g zP(ub4VyC^Y1^$MXPImXHv6DB{*AjwG3T5vsGY@h=AP+PBWzTw_8?PQ=+c43OpYIcI z5~`IkJX z<(tZpiVq=G00;U^bB`C9G!sEBnNZ6i5l3BncJHmg#aaJ_ELtGN>%+sJyRYJQz~KFK zMp~?llxN3oHZzOGv?8LjN?iW1^myu`*vgYRghut>2;|z5d?2PQOe=*mSe(qzcKyo% zU%KKV@(p0^eZM_QV`D@^b1#! zj3m=A3;|kyLulttXf6QhQ4f@BvCk^%uD3KKcqic;#oyD zq53e3Ov;*kr(@FLxnrf+b@&@W*1?N#a-Q?ik5)W zX5!brJu7__Q8}z`l->e8y`rV2-$+03n3*O^bBo}Z4$b~pmDl@-FxA~tgFTsT>t;!L z`l$H?DH!G#h??w`qkXvpj-ksqOLxL8C{Yl6&~S^rTNDzFomCU#t~zcOr(^_mViXKC z{24Pdu&!imMQS}ByvFPd_@scjhTc|<=_v4u!~7SF&6|^HR#F0~Y?WaCIQNM5eref6 zpUE9R&+G{5#NaddW@3Y+%ePr^-B<*REvsi0$}KO9zDgTe=hPwu%UQhVqg3?q_P)Dp zX1e6N)6SjXf5Rq$;RRez=1-AYlkO)NB8gEY<%2WF+T-B8q|=lr%<4^pu~6J+eiM>|`cuF#$gsI5-` zgAm?TF2r;;8L}aF6K)vXf}6QjV*X3PWIw9-W zlQ*Xe%9bx=b{tq%!KGT>B~qF*IGK2*BXH1rz)L%%*6&Id=E;KD4PAspp4bXW`CgV{zIxN&%7GL#9$A}9i{ z1X6iP?vU*hB>RzrN5yCHzNXyNx4dNs4EHHJ+dAL6X7zraG4E)Ls*l;aqnquEWJ{?> zJ}AlPXnynYUPLGBL`ZH#b82!^ZX|g0lbp04^QCFB5(1+;AQa-FKT|rirkPqjoGwIj z6>MS4ZEOMI6X3^P+eukUO-Zfe=Eq>Ohx>C_;rU@p=QLug;~75)_K^1z>XwE<(Q@x! z_DmAHmywSKXmCwY;|X39(JRhUJa1tys641^Db{ET>!Zws+Cx3Hnmad@Y!+Ph+4I%E zEH&2^+LXw#J``_I@-noY8Bd!61gf%Z zE-bhZ`{Rt$XAbIMUk!R!EL?J2iGp7DB_Lw+);jBonfu zzV-%tcT%g`iQD}7=ICIsEQA|kMS9vPE7$BjJlORMzbI`s!!H}{HD|2EsY{_>DQ$nJ ziY;H(9q9A>OR|aHVSsKb}x9e^3nC}$lSXo9pPdZD9T6iIglaPMBoVB zr9xJ}d3iDFZN37E*<3K1ag0EFylV_$B9j0d2hWoPi`cLU@WpS%eY^U-htcP&oBHoA zQ|E5MMe9txNUQhpx4nWk0C4FDTn*P81#UObfBhBng%TYr7@bMkA=O^1+W1CAXvWI0 zTm#psU;IVQmT5#1=u3xDm8b<8sE~9&^cVJrhK&G8!>BsTDx%A+k=1fhd&1fsio}=V zgajwtubl}e%FIiPEzuE(w$CZkO*>ifFOiSK%WtQ<>cqX)d?lIX*BD4n=AHiwm2|R) zt52OVOl~_pE-^J6<%`Cp|Gzjm2QUClJl)VuOwML}J8g8d%h6jDtmK%km6S=IWNkEl?VPURZ; zJJ?;wO@cnP5O2+R8M>X0F9+5o=qJFfh#L`e!l^fWcaqwhOGPz7knJ_~ZSW z4%zhz&0B9t&C!JfmvT0;DfYKemuY@0ZU!|(joW;F5jPtL4_QEK8XUHcWU9TY9u;$5 zYc|m^e;Tm|0weUo5Q})(reeWoiVRmu)W3Y+L5SCo57b?_B&ozN&zh4FF(xS$&2Og; zuw-2L3?=qtr>73X(I|>4`S6vt+$eQB4ShVpHCXM3pchcLl6~2l(LtMAxK7QOy6kmO zaC5KiP8CkAtN>Vd_c(3Bqai3&Xo=DO0=+Q+)FYxzgU7F(ROP+)qSBUKoQ;<7o}8OM zV$c?Rk4v1c$DGf;#r|nVGKO)TJ`+n+l0Zr<8(UBTf1m*$22|U(wVdczT;Fsewt{9o zndUG<#Nw|Cc^lxZkGop*_PFLm+4EBV57q7~>?j^dq z*rF5QCb7guW+kfrxgs-z>lb$7f2_` zeLX6N3`6(NGW0XJHZshr(jed z`T)ER?Ts1a<=(O9bGz-egA=+RC3MB64qa@sD$nPO`o*q#bv@gYJ-cDmx~&&6 z%_QDyHe*dNi1hisJgt#k_mG3!^qqfe^E*?T-o2lZMK`j#HRPhjQ>^t$S*Q3qKHzv+ z$Qx>9?v^;N6Gg$WzCxC;%sqkEFa1Cb)(>p+u8O|N>|Dc9-NicxdOYM4+7qfn`d`;& z*(0XTqozj;VWo9P*0zIFVz!p$4ZA$sVS_%mT&;tykpz|jsZ zsL->|K1Lm(pB7;*gbjV{;f;mGiGxHgl$i)(SlRf>+U(4 z6$CKQJd+v8=dPWxC0HDUd)+%zvGQ)o>Tm8S23EjDpH-Af{Pkl2*qhD>@ZF&u(LT=Y zb-fe0>g-HB7tlLPp%cTuho0fRBBZ#d{d5EO3yVp`bnEZBga+KT zT+m;B zyKow$YovERW#TL;$y8j0h(?E|p{us1oEtqF*uRk}m1hw<5)S%M*y@%y+BTm1lHyaA zXtd(WUx_T}ePAoD{SQ5GYevDF{n^y+b01r{l{At-+uJ^gf8{*0KR$Dr3sbAqAJ9-Z z%~+?fGw(1~;KB(mGSAISnC8yu7X0sd<`rq2r-wsPzmKe^UFldUp$Aen9;^Lw{w_P) z^`O@_P>9Ujn1S$RZ!7-oH=iQBswCu6>{Mg-C!8*sxU;7t%$m=YpObX%?JPi5>4&MQXu{Sf#LdUiQfhwwL+A$5G?ZrQu$-xr>LjX#;Nx5X zc5w6i=1w6faFXpBEoGMNliogs?QGLKNI@zYvo}DQeczKes24d5*ws)XE z+4a#c!GHP*;|Rq`q9?ygsH`}b;}9sqeNTAo$e+d2OBrBU9Q2OdFrdl1iE}Z{Y4ERl z-`DO?DDICAFvJ^Uocr60`jRIs0oS}Bn}R%wuc z`dL|V({ngfg4r>F#$Avvs!Kv}P$4oS3w$X*#x5{Z-%|&?ZXQ?i*5*iH-9>VYx`9Go zh*lye&cWHqNSS$se&K_MmPJPv9qw7n{ z(V!amLAW$<{8?SPhvfvX&GU6VYGAc5Hb!L7p9AUy2cOTWVki=-bVhf`A`ZfCUZPd& zF`#9$5KiF(t%)0ADLZBfRP(Ln6*LhTN`)WdYqyh*VKs2-dh*_06VCi5_gsn zerp^sPBAUi?fjx+vyaN=s+V>b@Gq(=_<2Q$O27^s!i%Z%XMIhY4c>a~Se5&0$}wOk zsNffm6&QTkuy8#3ZbQ2QsEYu-?v9l*4uUop=Sb03X5VtXU_s=MgY9f~r+W?JuZy7U zN_3Q8f<1t~?#*G{bgbZSgRscSN2j>?m7gwZ3P(nn0Gdc6rf=a;5Mlt=WE7Fo0OT>i zuFtC{hjkCjQ!<{4M3oaQxVn^_s+!XVjfp|wjp>n#zS>3!MA9g9h2eYE?t&47vWy<6 zfY;rFjF_a)$2UNYz>@q_)!Ub=lgQ1A(`#UWizN*7_s}m$r05vD`xXR>uu~|VKsri0 z)U-0OaVSg^&h0oDi-mm`3ve;IWd9pYgza2eK4Uij32p8QBBVa|xhoyZpUoi3<)P7> z3DC4Y^S>IRDlUe?<8u#AQGmgw*7IJ*4BGH>xk)@tUtndo(|o=z7zHJ|y#NIjTVF%`%bb($i6RQO^kitM!79fq>Ttg$X;Y$%bpMuVldfb zFxhwBb9CSCp67Qzzt`{m|KI<6yvI?;(cN{=T<3XR=jZ$`pYP{;a`%S168VX!(LAA#*LfK9?tGJ zon2V3$;q+0xH;R}JK8`XJ_AYGb~@VAv{Gxs%U4w+f*-3oYn>)x)w&uLOcQ&N>)dfF zm56gM`%i1Wr&drPWN3L2aWpzQ_}*zvZnDS|Ux~-hrACLOMMQqw8u3qa=&f3tY~L;J zlb*_5N-F9kt|K9Psw|)>6ik+Nm6`fmNOMEe$T;tn+b7PuK&VKHtk1dcuoFPmgCr#{ zv(*vTKnQ$Lq$H3!wZu9YWB4BB%++Tm1i^n0)Oo~;s*(iLK(6>jU(JAAQ6vaXiod80 zc|i!VXtS~$gWTYOSX@F*q9DOZ>(BfNAjXf+ohEn@3t>HF6Cn?|DFMmt)QePr7{DMj zwyF(ckgq%t*fo7yWyqUSNL>dNX(5D!3*Q*x7v`e>{WGC@$(tE?my1 zn&JXTJu8$fKQE?m+n9vMl~`Y&mzksE8r@|YVfYbv!WG^RzR%9^-xj3&w)6=Cc@cXW ztafR~r-LHDqeD2lgurj}@r&C+sgccL@Lql_RYE4HK6I^ve|{!8*;GVUJuWct7ZtQAoK^ zZo)~D-*Z&mK29jxLUIazZXkldGSYxOnuP9I==!<$%7iDQ&a<{g&}g!$#tJF(X+`R= z>MAoWO1T{o3pY`$h?Qu8@ZZ@Et5V{7e7jIn>I+%1T={E}vwtY2l76=K;7f?1eUbRt z?cG_ad%`cLKIhz|9Eg#YYlhW*rzaL@aD?$T)n9!p%6|vv2frk5=AY zvFd#5O?dLQLfvs;Lhq}raAsL$gX;$BUzK08GM*Qt9yl&?{OBK=A1<*ydae5IVg>a^ z1HC0bU$}uXH+%2Nm{U+H{|LDTk@F|-%JE++e{kg5+gCc>C%YB9owV4exw8@lmCv$2 zY8|k4C?pnp%*}eaC3HM#ynb9@oMHUjqVb6&%PX$0*0o1Dv_9TaIWs{uAvi%~6VETJ zo0gz8^!lX^wNQ-F)x4J_S_PWnuTKejCWy6%&|Xtb&r5xRI(w7u=9+!McaI9{p(o7h zai8_O+oqMi$9+G#>`g`av!j9uiB0Vv^-5GECg81thXHZdRnss0zeAxJ@XOz#J zbq%;mxB^bcemwC+<;le-3|vs1tn7^J;cQafCEYfim@G5xzI~ zz-lvS+hs|-4$roG?W`m7D#&o>E|FpV^?D7tSDG*0nvYdAv65PyDe~by=Rlql=6PGF z;o{r&{s1qo%J?0o$|$Mqte0YP0xAOfMOsUbZDokZ;$7FDd#E3u;>e6!3W^-2!Eugq zGGqu|FXyu{amaoe_tYRqIY%=`30p0kKbM)5QLRvExOJOCwprzp$t5Mbn2uvfQ`-I7 zLzyQsY1O2}SMqtY9nu+IR?2(VI%UqsC)bz-Ctr8Y9QLiA+GGrb?&$0wHZxb0$P9>z z$j%d?$vWsI8--k*C7Gbj=mkwV)A}oN>F_7;xRF0%%ey4gdKB*~Qr@#~G8sERhG;@G zrClVtc?&3Ye`n= znKu((iO~5ah>}3Koh6-JX=8r<{G;keffa%Gcch5P!}}?6n2eZ$+~2v;i0g?DCv-fY zmkDfKemV`cGw7u1{342f_dS2`}y#}luv>woXLg)?w{*hVg6x5^G_mmr7fjx8uiY}=Ajrl zB>SHAp5I;3piIBEhCP$SuO0JlfCz%T=ukV%$>YD6YulR z-E5hCkauovD!wDO!=57aM$q@(&&2RacvO85*`?6%Q2j8OXqO0svt4{~Lf&12a_$)U zP5Gzt!-;l$8e#=JDneY+*}S(T2Sh3aD`c8@Z4E#57$g~7b5vy%G zePZnNvCCn4M7fe>ktJM+9*vwi{kZ&~>WhAA4!6zTf4uX)n7{!`s(WH0G!Q%3H)pD0 z05!xE!#Bf{7^F|%jm)P()5V=ee&rbSnP3Qa{#g5L@bSB>oQ87iPTOwV>B(W9WMhh_ z*>RI`6i(#*1MkQ&dhg~|Ru$0DxONV7Wk`u!d0MSxS(Qy@Ye&0;jLs{aWt~kO;rZ^N zVtC)WQ`bC$f7PnsY!BMFYHdflwFa$ApsUtn+henJZL4TaCjV~s^!n2+glV2S!ga3s zX!p?_Vs?s>zVa_UOx;pCS~0zsu}T!pEH%4HIByCTb^a8Cx1xXnd0H>YW+(+g3ueP(BxwH1t(j(rL9H zn7)I0WVhIPFX>5AU=Z)dpanX$#DB`F19OYg8COB&G19HD7d0hOCDCEBSmv=Aa0JB@tBd^eT33zu6$mZkY8@%P(JThUF>hWHqDSM(lNAL?Yls`*V~gUCN)&O ziSK0+D%QuVdh0pmtr{${E26w|2I);q^hd03r&3NXq!Q zz^xo?JXkGl?ChPS*l?A#Y^?UyQf&G{YA`hyIh&jI%6@J(+J5RfR(=jvV%BUj(kCQ+ z#K8mz8xJ_E55m#OUED{CZGT>IaE<>M%Er2Xi-&_0n=Jl;tOjZ~Smm7EY*>YOg?X%C z!Xm69V!V7pA|iq!+^qaCJ|QSf04f0E;TI4Wgoz9Cv;O&qP5K15l614S71xqi{PS_( z|D@P%dU&{qL!sW@-n`xdyv}ZRP(Cp+F(`~5%FoXO?%;9vb@G7w@Hn}%|D5EndE{-} zt=#NgJnWsFSn>11EuB3*q}bT-4?6hs=eiIs2M=;`{}VfaBGd=&0_Ed{LH}uxwbj8G z7f(0G{a3TLg4#IRAZ(mG+`%}$e;fxORZ}|{`j3x8ApUWryN7}oSizr&{NqS>9bXq4 zsFsbpv!|Ptje-|=6ZU_4d3O&jo4>(5#8lw+f1JqWuTtip-|OJ({|KXpz3qQ^5&Sp* zahQ#d{eKvS|K{hgeVh8Z)sg^paXB{|xQDZwj5`c?Z35tpQ^`KuS`!5gDaI*)Y5bpTPJ@~a)gNMPbgl)xyEQNVM z;1c73!3BkQMEOMcc!WiTV4~JGFk3iW@UKVw!!-ZpQOfr2!0vp1u}5H)`@v4z#`Rx6 z{p$-y`~9Hh0(Wz_!TYon+h0z75akYn7=FxtYydBg4|md5ctf`VaR1Avf4|nj$UleK z|JUt290Wh-|7G}peuno=8>j!yQGQOff7gGW#NFA}!yE2qBWnjNupqw(kCm{PFpr>sARq85F#%wXg2Ezv)}mt8Fsr|| z`Ty}Y{2FQgzuShtt=Q@&+{wj#& zpyWZM5|?v!baqp7wziQLkc9rz$G^<^_t&7capPxaL)u+S=_>0r1vz0pF=1gIeqO#` zr}^c+U*^G+e{sOAK%UE46BuCGE4kW@41Am}=`2YSn^w+QdeW>Ao zKlInH|9$A+BXynZJ*4^neA>T#{L3hOh4K?rZBUil;FtFAAN~8?H|%|E91Y~{0W)^T zGd%pV{v7r1U;X1y!~Z<=m#_XD`qNE+sh{`F;^(KJItRi4^gtQ-#bORv<^TSRzt{A? zf9``Be?Q~^io;y|hU+jW9o+L9t^+6zbMYIl!=Q9<&u_R6pg7FMZ@3PF(!o8y;W~ig zFc-h!It)q&_xy(I0E)w0{D$i=C>`AM8?FN=4s-DvuEU^oaL;eJ4xl*9#c#L{gVMo0 zzu`K7;xHG#;W`XT2lxDj>i~+wT>OUXFen||^Bb-MC=PS+8?M8kba2maxDKE=%*AiG z4ujIcJ-^{PfZ{M0zu`IzN(cA+hU)-|!(9A^>o6!C-18f*11Ju2@f)tgpmcE0Z@3Pi zILyUwxDJET!9Bm>I)LIZ7r)^;3`z(0{D$iQio;y|hU+jW9o+L9t^+6zbMYIl!=Q9< z&u_R6pg7FMZ@3PF(!o8y;W~igFc<$VTqpkiS$i8N@B{YV;3w(plP+(9pTcLgQr1#~ zK>RL1Ai*IJ$i^P{cNzlmgh3!P77&PdJOo1L{Ls8n5dv8tza}rM<1;Y3<6CyV-xRZf zWOs`i6u{MdsV-g3&Mz*VaS}GXsu+BanmyzK=^LsR5^f^)v{=Ly%PSG^GjAFpR!#Kv z5#cHHos=?UOQ^5+niUJ1^+?}XALmhX$eW+Zu2{1jKfATMCC8LyY}#XLET50-TN=z% z&6{cY%hEZ!iyImmT8e|3cQ`vcS1z+bAk%CwjzV0BlLJ@# zn_kL=izlg1ot|*p2%sXgl|@m+wM|5^v1O;yENyJ_QMX1#`~m;E94N!05PMP~mQl=2 zp1UofqO-cNaPh9?Tl%JR(vfVMx0<8yLN{Tlw>di1;wRr8#TEUQ&nN*KCCz-;mA?$Qv{h#-yQlA|h@k zTzsO^NJp)yBcvstdi~;9WU5J2YD+Ox znst^*&}$v;WbP*_bZ#5lM!bnq=h@m}k#3swwlgag-E&?`Y;4I>cc*-{#Xd^45{0}c z#R)C4xovOAq)=0HjxRZLjV~3xeLE7FDj%u*nj^A+Lq&q5z69snM_#}FdaR<;Ln4VX zKR0*#7!N6g6~GUZC8#ptmC?!5OW}B;GUZ-ASdq#R#g?kTcB|T~)3ef4O}?Q7@fMM& znCP-l|L|xwTj9=jin(sf)I`JGQ__6c5+k6O8-l*{`aZ_RmCo+Z4!ycKwsw6tg1Ba%%^@<&05 z{PBVObi^6qmeDFByvNZLv1C9y>veMfOkVqdkd43lhQmS`#4QrEky_F78?at z>2R60(47e9&#owZ^X5j6XP}{cgDayeV|dtAu7NqPi47UvmblCg8IHw?ya@^>@pn)* zTlz35%vwQW#>QQ<+P#sI{JpN;-lx~F1Q3$%kl?I$m6iVadO1>>ym8Q*30_=@ZF#mj zo>~2ly2l75OZ=DoAyCGegce8-E9^5q;QJ@|2X7QST-Hp?T!2S0&4<0m*%^ z;~OlN8kQUyI&UR-amTeGpxoTt(-k!|gtzJ4-;kfN-P|^NRFLbwH+idgb2X_xynwI< z+3q?7HF&!67*3x;zr3|)k3h_wF(QLpxk3!N*ASHBnWe>R{zY)k=dFQcmi@p{Qc}{K zihPkkOr`THkqeQksPPP~hw#9AbE%zWQlM)^*sy8mD71<5Dw|Dg>@%|pI&Gp-* zAartinm4z^BnsG;M`#O{>f8;6{zX$M6-f57B$p1 zjd2YD{%v6OKi}p_2UIW-W0KQjHtZ;@@eVy#81bfA6gK?1hMTSJ+4V`Ac3bUeCW+ov zN!%$O)4e?}@g$_3aj&@(h>Y)O_Tkc=!3U?FTPtrH8yoxda(eV1w@uWC!C%AaRWea8 zZwI%M&#F=T8cN6c_kUb06r`iA0Bf^EBKLOua#oPJ1qIqZ6#Mpdttr)1yG($3D#8g6cGXBcm& zmwztKwy(fkrlh83R}REXW5#B>iHt6BPtSZEJz{SU)*=GhiK;V;IF2J8u;mp*@H8 z3M)(3YP8<2OT0U|zbN*T3SAmP3GL3OV^QTM>PwOu19QUUM?cwHAcIu4FFW+iTt2O^ zF?XwYU^Q^>k$!>cR!UoXH?B(s*kY}fhRlv&H3-`;yBwv<)KE1YuzSE6RN{oj zwK>V;j@RFim%qbGg;~20nHsn4EZKPqv!C#JsPiqrno>R8>_mrxd!5_j$T!*k@<7@}sVK!mMizzUk2@IHI3UCHv2pybRECbaYhC z((!9u^Z)T)wwsdLW$*UTT3};`a@lO_EJhLXL z0amV{IU4MNLQ3#}$BSvw*w9y{ah%lE4Nseeu1;OQ$lmYhB`Uu60iyAmg%VwI8eP)H zwy4|I))wRJ=(t?~Ow%Y6y;F~W`RW}rocqsxv*1NII#xM(damHcmo~x!G0-)?YRSf- zZgLE<+}isWjA0`%>t+J<+783+4r#sLHf}O&NWMl=NQUH&#HKSP(XwbAW+r7LU^lGR zAJ&55`ZBirEF&vY*-XAfVZ2t#ceKH{u-~z-aBeSPWv0g*jUL*uGCGd8UKlExS!&Y? z_#EbGtF`)Ojrr_~pRdt{epcTfmv+|{#OLXD|A32bS1z3_Q<}C&41@dBJKmuq+7J!S z$>zeZ_C!kC#Q8_9gH)qq-_(OOI+idBV9j}GV`GfRcndZAj~$J99Ikdx)qJzNpzA6= z>IG-%bJH?NNJz+-X6czf)$BoZE@cln#o>mx$VOb>MDHcxwni)F$z>%;?u=k+QPk%5 zWLk)2S(B%Rmc2(UJ+reZ>irqNjUj7bWzE_+R}Hunl+C>I-Mi(lGXn#a^{ndw0Oh_; z>7`Gd10`y*WcUXC;GrR}LY!W(7K{ znhUvW2KN)hqjaq-5FH9?i}i8>1BDZ%vay@D!fymA!6k?NxGBb?$Hlq}I1`jM|VYmKcQFA4ADFWDR<57o(rzKab>B zb5Ef?f@fIwcpewz<_36In;~taQ)Du+n~DW}X>hai5w);;0I%q=2rxP4w5z5tD<^ct z;!?g~N~?94e;s~FQ%m)A>NkGh+cVG|&VkF!s5Hfz`6YfNl1xLcY}0)jb0= zvF!o8YdljODSMh$uyu=4^bQhmqFtHju^-@<%7R6d`L~CLs{?&nP`Hx1hXL6~0=M?;O?Q4Hk~8aakCZ zne`Iiv&%$n)Oz>ZG2vavMIs^N2`w${?&gON9~@LB7IV-QX1KL2cZMbfYf=9Bt(vbT zJo#9Pz0xV)`dZ>oXk0U?TxE84{mjBb3P_(ID1_|{Pw~C;9+*S1x%v6~Z-rAbi7&&e zhjwKKDt4VKhUU#(6!&vP7%eSbios3cT3=V!g-Q=onHkXzH4)4bmFQ@SXh5^V;Amc+ zfdEBS8H{Wq6vG6i9T@9c^L>66gJ-sjg(kvVh1!|wJ8NO~yE)jtg1xAn>S|=CAt9bd zx@QB!6``Z8@wD;X@DIQsKMAVo_N|nYk1u$vh(H_G4NOb|EUm1@l!Vm$Ju^|9 z?QOj`iI0{8M5bqm*BI?mTv-EMtHUIY&$=3z?$6E5r8o*E7U0(W97+&T2(#0%lEuq6T(GSl7_46G9a!n6ufa_wmz}^DEn+e z2EJINeV$m{<=N+X+O$tZN5QwR+&7li3rs<4rx z)n-r`2Zy#X>P3!7e96MCYSL#jARf$cq!NMVrzZ<@=v&&TdaRQEjL8%kR9R9mFx-L6?N5 zjn67jd~0k_IA_{-x*lc?qbF19i)u`ziBizi_00HC!AEaR7E4a;yR|_|7xa0lYHw{R zWor;Kx8j#Fc5gqy(D3l`O1~{GUeOM{I`R-(A!OPoP(^tE}kk?{a zOpS~-nH4mn@$3^$FL3%JFuRvC71#<7@nCrc&GR7mj)?+XoJg>ZmYQzmgDxI{Ga-JN z+9zX)DG^S)!>hf$0$W>Kd!tJ!d)u9EwEI>f1sL-EG^Lnbw{)$IoElsKpsw58p^H9J zzKkt~8Wa|fDbzz_>itYkOAmVo4%GXVqIn`(7J$H@9V4^5j+vt*LT=+XktxG|-koTF zPNtiJXQxU^OOb^J+(E^l0w}t1Uy5P^B%-J)!>1SE^xY$ni(I)NmyPryicaqZcdZL~WGr5iObeYkA>`!5=dl$Cy-h!14ut{6d)F4={hJ(<0 zNq(Qg_-O|R2lFC4`9_%TvG-;j1(`nHIGlpXZ>+n!n*qSy=P90)DwZCK8NI=*uM(a4 zwutF$u;EF<+hjx`V7;!&&?hSON8C~Lz5eFI8asDE<+Pp_$Gg++l(~d7D|eend83YZ zvO1Hm0|S0bU{);P(3f|Dkhf~XKfoU2Lj(6)gzifn8e7#LXNf${{#!0D$(6U*#RX~0F)v>BVJ>tP(LL=Y&IeuzGeLS$p= z%>?1?R%?FkHD3=?^OLbGVu72>3=&BlILWm^_u2v6nv9ahKCoQrTK4wt?n_D_-PIWP zt<0%s3V5cRi)32}+zYdGjvo754g@DmN``%;eZs?5>sKbcQ5p7H+_V=G+tr6&SL9;> zD~~1ac)pSBRJv6&m2ct;{rSQTmx0~4%Y_ful)d>8Qjrh6KE^y*gNp+EWg3ez(Pf4- z@sJ#uUh(V3uE?VVIiIL#OJooCu7pKwmr_0#Tap@5pGq+YyF~{=p03{+{D#SMf{1(n z{(TlhLqp%nd~6$v_S0Bi*Qn=282pXUS;+ZMc;&@dNB4FK zle7Ie>^pSr!k~NS4*$=0l>a(B9MrbCxhdgkig{jQF~7CzVk}q>Yw$D`r-5kEhK=wj z^#F;=x(Ma&zZq}?x%I5b4%6*airkq2vkX|ke8QNCfd9bU62mE~7d}r;BAw}*qVuuZ z0?X72T`uni3%RG)wshdc!F*z(*=D?P=?2^?FK;}!`VuCTHq%MGf^?ajeO8OM_ommx zJ0eV+fq|hw!sGjABu=#BnrUBVMz;oiSUrO*O0cnzTl^H{;&+J(3{JHc!$SKwDzi?b z?X{n=b2Iyl^Sp<6?Z)7f%Y>eIdNgfdY8to#D3kvkR#8yTW=?L^FIB-|B4l!sooNCD z7I&(@zV?$&Nr^o1qm%BDKYnGk1wg}D0N{VDET;ch#d54Afhp?FPKmzj`&^R80Q*3Pbrjwwp8b;1or z^(BrbmZf(6TL{u0gqLdb?GJQ8cbSg?m|U9Q2RQg|WfndA6aus={%M>@Wa^0Es?WQ@ zwLtNc?D||!&d0UgMW9D3c#T&GIDZ71R}AHo5gi2#qyEGX)Eix6oHS~&eM?EX+8w<45pIO>-0WuQm| zU|6D8eUmTcV^!$GW@#bkIY^G2AtSO(m?TGGNJBr!7ia_8Xh?W^PXsJk2zbbuFusNNk`xmQ!xMxo_YzY%PKLb%vJPKd%QWhl}qOCO1EOaR3=4VWHa{!=}gAN*nF?le0_Tg zqtfUNOv0jrNz84W>GI{v$Y#NIt4B3)zEp%F$J{L*GBOrx(=O8$#0O~s5V;G) zfa%7B!`C0=V^8gvQW3iYZxU21xmMp0=B}#?L7l7f5+{rm`TNB45{a*UOt8GQ>f>@l$E zW8X$StHR(BHim@m*WYnO%Io$>aA`dOHPPwUV{d>z{~&mb+p5p{0hz~%cjMN=v0uif zagU5{bht7$x;`HRx{Mu5;Bpu6oSvN8ZQwq&N*YIi{|I~sWMQwY>t>Z%-%9bd;h;}T zlCF$OL?O2go+>DKT@Ur=ya8lf>!SXVWwch-`5GK$W^FUJmtZi3l_ZSPf6Q~nRkr*Z z*-Vqg1L|0%-oC!Q#*ZIAGNhCTY+N$V>kXg21{j$Iks|_8?TZN9QBtcH6>7zP8sn^W zh1o@MT~FL%T8OT%$gn>Nw1V7b*`X!LAZYLwAZ|a_I4Nd8+_ntyD?qnLwd6!I$?WQ4 z85mf=W=pTh0iJ-`$BM?t$jGn1x3{;F2IRLSFMmrUW-PB8tU)wd4)R`yc$v;PB&ZaG z%K&L8P?sVbM_np~(It(pj0=ItGAE|Gp}qX3TGf#ESAY&JPrOo+udgufIR!OYznrIs zO+l_sXCm*I5dx3NkjL{?zvb@*G*OMtSP${WYk?`mmkTALqlAjxv zX`cZ40x=wSBZqRw)n=$^#d1Ph9CR-P z6gJ4<{pVc#`qrv>xEu%rdZvB)OwG@}S~w+4!s4|_NQ=DcQ!eW2;sfkk1CYdl9lzzq zhJEg+=lwPuQURy0f01uL&`_<9SxZMvKgY+|S|c;V4Sm2gr-E862PE+E*HD-eFpbHk zCKlwF$m){6k)?`Xx8uO)ibg`NI>l7M_Mpo+4i}obskBs}_CT?C@pm+2u!yeJee;(ifv* zBsag8BB!Iww}}DeWJz5EJUp0B6qIWcdiwg9;akN|FD>l&CVR`l>;`yj4JF;A=RI>G zQzKG2>Zxnp%DH8!A`s|$7EtWNnd*{`t(47domUSmMHs(DY~2Mi+qSuCTU+fCo_Wrd zAQG3E@#q`@8Xgqg-@j4LO2*<)Z1n7!KN47eNIBmBKB-+vX>gYscBt0MQZ6^4#jH7# z%q%2$eh_!DlMQ^>_u5-L=^>yup;2^uK)cgNuHu7XNb*r&KUyA(T>;$(yg*fm-|~0OTe(X&$m8c<0MalAlGL6(s6T}8 zNn*Hb&TH?^{=6sO0rhntFE4LRG}^sz$SLR1^6+S4z#TExQ%s*Ji98iFfx?;9a|=Ah zCs~AS#`We}U<$>tdDhqZIXxrNGmldD3$;XzB zgx73eRC>1q=sehW-|>~SbfPDE&*;uWQH|QH0o;M+#4*5av~qeHmL%VDJCx1QXn(s+ zM>*rl_r-_vny`de=~slQvtD)&OYQPkE6naEpgk=Cc#u~L2!PeBS=iZ4<$=h$r`)&F zer*6HNhSv>HGCQPiag$1ljeRvV)45p{=9LI06L%+q#^{*axy4o)|epv@dSfJ35E;Y z7@h0qf7Z%z%8HEW49G^HjH&^M;Wh{DWVUuL`ez;mPx8(%D@c*>10|<5|FeQ#&uZ8; z91q1L2(DAU9<(lXWhDlxOG~fu8lfJbnCmq%&wCkS&YtYOT|iRrQs$0N5kxE3d|moB z7Oz+BO>S>2nFr2ouB7aA)77qDUOo#9=*LO*=b{VlcySVFTixfY3w#nBYB6+up5^C= z%`Bd-(ZyuGpIY(-T9y`XTq7VXr`OBs7>n$Kp-X}0e$~kaPcJ-CQNTx|(+^Refu8oR z3v_fq9{1mz&a4F*mPvXa-Vu)K>FJpj7Zok3*5FclJp(O+L_$~}ytk+`o2+Bnt+_52 zTeg*+J>$~um##(K=ZSyNjZ#G78;S_9*zRc$kg`BC1-!zAlunHXe=JZ?seK#!i|k}R z-W134k2+9a2NmdW1-89HB+QuTMCwTrl5@nycg6WW%-x$vBPg{lfwPu^?~X< zCOYan{AP6P{LlCHwV$S=0}4|U3b_$iU?<1~5~#ZaXd?;Xt8+)g-jQnq;_a@CfINbV z)4+~mP~k>Wugd&2JQ)Dljr|Ye%=gWwN9tkcbFFwAkLis?;ud-xAKj=-MbAwtjN5=l z_RjYeY#(v~gosgSmla5!@s}wjf7Hq3>E&QR3&}=n_z`XkH8I)lS4>;T)IK>aIHo zo~29%dVi^DpOg$|+9aHwBD2W&Y2$ZV7fKq1b1!P;(bh$VKy}sPdJ^W)%hPtU*|lLU za6vNWO?3Gv(1t2afmMH;DAdW(<)mq z-e*UTgFeUy*!fqOb3jgX=pD2Fv2b?C2WpQJRM6G6U9sS;kpXncv=8uf&^!Y*QuA&g zz5xy7Kx1bA?S(eH*qfQ+JNXRrsuWl$tOwr~iGK3rNh#%4G=plW^OiGj(ur;gln_r#|R$ZN>I`~9|l)0Mcc@@vrb@yj`$rB;pi+d z>;&GHr`PB?1V1(#VqCiLfVzldSZ1gMfyawCu337nx(_=6+B~H)tLtMLGF!7nGWbdZ zP$m3hxb`^=kOGq`@|`t4Y{A7PU}qy!spz$DqC|5Y61?iAu|S$wi^~||a~KupbCqp0 z%Db45aesy21u($gl`&BB>;=5+-+o%V9#!jeRO%Sj=i7I^cDFay=uU(>13#ZlHwa;+ zs)xPm!;+Ii36!DAT8&bs3xew)<^gyDFm%nXVd&Cl8LhLdpPu{JCm_&ZMSwD^@WwG< z{b#(yjighMJL%2LmFplUFCc(Wh`hf4_Nk6ePBm~)0w+DSNt*Ghv`WFuh(-f@1Vmm( zj!2Q_^BBv$?-wUGVpl-c+&@Y%T9F^0Ax4I)uOpi}5M zBmfBN#*Sk3E(7SmEz7*h#P2rsKGg@uHON85B09eYtR563z`;jFDbgnENSt4cy@>8= zn{cboLyy)4xtyq)^364D{z6)RgRH*nWyE0Fnz}RAFHMJc&zn+e`Lk8Z#wR;72SdH^ zgBmOi7&7ZeQCOSuDO)%mspLVgJHVZ(`WX$d=0;=4C&oIwhAr#h8Ld8zwUj zzYeW%N|xOb&^}1{;u#1Z#DVpWycTPG7`#B_ah+B{^VLrR1|ti{_8iB?d%@`=0nuP{ zd-TTJ&$utf$;olsD^G7LAyazk8B50glqd1JR2&fYj6F?Ru#>OA5c>qjB%a6IF<)jlllCQE60B(Sms`be>0+^7l}6wY2)3S5CSU=&w+J*i1hHDAPI`&#rPuL}IY+d&yCi#A4 zw8YzMxT2^cJ!s&ta7(5js!`8iu zvg_Tbk)bv&1n&Q~98`$l2nd7BN~=Z?REiL|fIMi-k5DddZ09srmzE>YAee;HnL{42 zGmF_R2lzDx#&u4C1f>e}0bnwm4WQ9YMM&s%U9is+FkDa*bvp{D>u}wltM-s!$}{1u zUFcGcW8NLVB(tH9FYv>`cDiVQWp#thwpWd_-!%38H?h-`(IDRS31jM#nWTi%T58lAGaDwr#fX;W}jR}?tLuk+@;uG zMP(tN8;+y8J@?T9X7PoQ^_3MFjY6!W@a3QV2Oy6DE=l^S@pjT~^LaN@ zeSkb@v;gEmVe)l23Uv3d4i2?%KRX7sU6Nk7i+kaXUw{%XXs8iyTpdaDH0^8hO>?FX z^AKkO2XGiXlX&NbvP9oX{m@`Kog8)P@xY$<9Z>@QaNQ zjzR(zLr$`&ceSq5Z=0oevNQr`u;kfQfE;~m1Z7Rr#?sVY5dwXwGO6S1{F^{boO-5Q zsF%gZ8!P_1KkIZY4x00z!|8>1YIE1d@T3B@LoXj6Mgv4YZ*Kajn-vH&@P*9IWT~wz zaKLQ0-j3INm)ws8WHphm{r$dJAn;XsnmR`>1%d#>J$n_*UU7rfsb))>*$_$_h37=B zj6eY3ZaWD!VJr7kFP}QG63U@C0q+d$o8VA^m8Z>w{;i&wzJNt=<_x_|0qBanwP<=^ zuI@lR?6G<#`%aL#Wak(WMf`_gTIc>_fbGF2W_?mT+X|gHV0458MWsif(sk9QBr7{GOHxwC&wlK0jg*`lIa=^eS9NKBO2w^~^?3-$Q(F16`HZ zVj_-p9^oE}T?)omivyKJ`W5**&oPZ*@^b zX7msItCMHv=Yav!qqjbT_=oUZVUcl}{3MgE4}hMm!`p~WQUCT`AZ9mvo&s$;AdTz$ zRWpNNKz6+T!Gq|^O6oLQf{V&D3YLaXXXoaw<@B62emkI^sm(pUgq#`)XW3<~J_6n@ z?L_F+-7Y1T9Z<6sfSj@6T1DLFX@f+X8|BgBrwjK8eh_U>>%8W-v+4O3;m?hG4AZ#XVsX+u6H~ayE?Z;r^z}c9$r{U( zxe>MhW;7Dugh(ei@Ufi%#N08Tz|N(x&~HGWlnXwiB-&g}$ofI-qA3+2VC3Ti#SWH+ zy#~W_nM1s~A17FRf4nE&BbUMh70N5U{*6U9rw92NoK*wU)WgO%SFk`>Hh6U6ljc_y ze1xnn;#6$*J$i3UQb*${56Xsj%lFP$k~Lze2*ssv6n`kfIEepzM(mon`61?-64>@uR7KpIS*uEE7@ zuOQHR;&ZF5AV~xT`1)>~UM8_sdwQ{s+g>=4G8wcf&0`#p?m&g;xz)NK2;=4KcZb8_ z92xc$rLvK0pF2RT<)C?WgCin4TZiz{i=*rt`Je;=i31!>17~B-Zus8>)zfN6M=dxi ziUSpI%@5LjK>z5+cQim0DfnI?y~sJYKrVpJBp}Rt_u(lx&eLiQC^sOn0hl<@AB#*? z21Vo5m#q8BPb%;lK+g$lImNw$?N4F0fR(FMo5d-n@h!TS_svZ*v`_kXJXV>K2Ki|h z;dcyp|5dyUSPFu(H|S}_;+&boUIN}U?NxbQmrvH*;gyWiOEQxa9pV);Z3l*a`QXDq&9bIK%M`T69-Np0lqO2$Lv2lEwj_2r2d&5L}9DN^|2Up zW-_91lRl>D=h;3+arszFw6KUiB7@H`YjlcI zdCxiLAZD|b=h|O*%7|1fe6O%i7!{3vo$Xhjup*kP{l;-wNB6=Ds|=syOF-|b?^@QB zCR`3Q_oyxo7#UUA5ddVcF{ofo%#V=JgB6^XTY7)$l6nBp3}#-NOOPC~FE#>|Uir%- zF4`o~a(+gr7G2#ZtR(YW{@f%-0Lh_I06nDhiS2kX8MLWGufoS2l&8*6%EO{SM+_v5 z8eAJ_l(p1qi_dv&fSLliFk93Kq?KX^q;iZ!0<3^s+a@q@rx>({*D2?`{KrQIT^Y-R zb}B&a^g%3`j(i%m8U(t(C_&(OE%WpFCbdDPMtM&So;G?9MQjG!5XJYn`@`s4K+OvJ zxv{lDKnJ2=8ra+c;khPcxK40{S}da}mR)K1ZB^BFzE`Cw^UAR%7#Yxuzy>i9d{an2 z^DdP`rJ4kP)@c8T21s;;{ke}-E&xdlY*K#(Pr6s83ybZ>wy>HuFc-zNb3L~_K|MF* zl=%j9spc);wg@`{Gq~pW?b9=)5pNvkt5a#QW{nel7zie>P}6UQ8S7M4{8^SY^|qoj zXZ({d;d`zh8&8jrh8{UK2^r*TcC`ip8hDsrwRCs57BE%wES=rn5K5_c3vXkBmJ7NW zzysE%KZKGZAGA-@q@$2ji&<=UOpBqRWMPlbwbhGScIOeFd-vwCoRTo^E>{exwO19 zaGb%ka2DTjnLlKI^z4{9zp57mMNB>xZw|gkT=e0W^Mb+IDfs{v(8vJa!0ExUKVS)w z8TR#ED*@7-f<6%FCR0ClCG1?a0eAFlF$ixTU{yYVBPH*C%9Pnr+X+fJfdi}ZR=G^e zFx8o~MOlJKDMt9e^7dQUoqTFT-9a$eb3YTemBobAI ze3znwAzvuI)O{Fs53USk6?XsEQx*AuB`gTob!^vaAXkep8Kp)n8A>3@I;8=jN)mK5 zYr8yme!Pa#1AQ7$MWAUbLCUbW2k$apT#n?Ah4Y#fG1JAjfssuGEUeUn~pA1FR#UUrllruw6oX7e!K z`~CAvO#AC+ELCn3{g{^`;Qh@k{?d8UuzrDG=)!%f#Tvgs1~o)uGGAs=f5{Z8YAkvr zUt?my%gY7M)q&tb)|cD4#KF)0J>E`}<{CJj)462r>Z&P-@L*U42h>LgHV5`nZFT1M z1=V3t`uGe3+GDJzr;o|OZYrx1NJO&(DnwTQ;uPe%%rfp?@$EG#wQc1;iS~RjJQducbCs)!OXrTO+DKOe*+%iuHxnJQGkp zlR*E(bV%LKdddpWnS_k*6}%+~)N#nk4YO^cgiztcq<&4$ z4`MFtfK@b(fm32F%wNtlv1;e(A-BI|q1rQ(H$PFL_SXVrv7@!1JAgUkDGE+M0lpEh zMZ)?77cW@9ksHI&3M2P3Jl9Wp}u%#!DAgXiccZb6#T#SVhBLe0Z}VG{X(m)l6%|DvtdSDPli24 zK7@wGAh*W~(l5al9ge64Yf69?Z0#r8q5=jR(X~KxUYWzd}iaRmIhPuAVc7nwq-3^5&JZ33x)MgwkCk>3UmFjzY~YXIT) zYl)O6LZS8uv>is)KQsi+(JHNOS)xmTqx8vaeosRxQU(V{KU>BtepjeB6j6doZo}Zv zO$h?L=&%c=8AMs)MW)zUmAQMy;hy_dyea?>*bttH0XhRP!)={n3O+tnmjt9l!0>=x z7P~Vn8lCpFWakGs01|kQ8F+@k1fF5PhzJ;at=KV!_yW)YYOgq?PA96b_|&9X#eJYA zp-b>p>h5k007|uTs0MBwcoHK*X%;L&@u{%37FuoQ{O;UPA+Jfr^4H91f_q04Mjuot zF@q?3O!mh*NBxEcjLNi6slx0ygn)03K{2v(s2mi_+9L|qWwm~Qc7YrVPQF+xWIFX_ zO8OAYn^ch-(}!sl^eJi8zjtL+kXJYLn|<54Gp!`;cFzpFwl;RZ5}5Lhxp&LRzpB}{ zatJh70P+IVpHC~CQGZE!C%CDGc>jC}_}Lq9>i(y$F%HD^2{$WJEKS7G`MIEio?D

5V4VblJk(dytNV}puRdMAMYop7qT^hv{usBd_j4;-H3iUXT zlG)c~_D_cOf)*x_lBfvT=jsJXNskomZVft>0!9md(uUnvtu)4srWJI9L&dtMf?d1*nR)?92 znm}+;gc1pg=YS7*A&FG^1p9a8dwSf$ye3ua=HL)bxn}56NRK3e#q(4SOR0r>B=t9d z2nY-wED8Kb)-Bb{uNY=!#VUUd6M8qA0Wjn0Z2w+^hd0${zGoxb+Z&f8=2oXQt1}R2 zU`w6MQ;>^OsR@=>>>hhAkJ|r8o=Vqy9)su&9tW}$;Ci6y;gH)L!Ank=T3PSp@qTjG z6?7OcfvUJwa|rgfSuFV4W82S`9zpj+R5@wg#@>@YP*)aFbGI(i9;gUf2gj6<+4e?v z@#b&kJ>8wIVer(`^6^Lj5{Z1J9S`}Y{^r=(i=5j>m$;H`#AY4@EsQp5pf-WW4~}f7 zrE!tag)iylTmXlG)*%-oQ#o=kBx~BQIt+v!yX||rkCUIw${N!Sj!$hZG_&lT%%eOa z-ZZ*ztq3%o(&-NJ`Ug8Zfq(?wCI4=JjAvD!1$jk7K>pO{+a91x0#*n@h|(g{jBoPt z*3KNh>(7s$Lvd!`QLz;<_qd)@5*-Cum3?UJB;+E`t5f4m4BaRyeb-Fq+sjwoK^!nE z5P5j#9-uFL^o)M93(Yc0*pT4pSfB4N!*|uF2+e^^Ix_`WE_l@ODM-c`!26ttVpsE} zxgXbSk4!HHB43>w0@^J-S+ogoyiEH*s?V|6q;$O7vh-od9zaM#-GQ>dBzcyl&V*QA z(ZEjN)&IrPb%53QzTxkbWJD-RT1b&p6eTS*DJd1DX_mHzc84g^KpIMemP++QdlQAG zh9fQQrP7j?{`Z&vxh~hm<(zN4-}^o9^W4vU-*0Nl=R+2ys{mUe^dm>mQ^`@A{I(ou zzA{p{k|jYLwMkByi_pA1xx8>84^j(omK)AgI=*_ad!!}vit>9(l%|7;!}cOnqkQp4 z5UWBRJ3E!RXKOQMR}H8%lDx|F2en=~rtCXc_xnGk4=>-qw&Li}=92yA--*T>-`u!V zlhtAc%H_^xjs|cV?At0tQ0oVokvDMnVntxl8~xP9#kVF8xITnkk-k1UFB`y2h&ZpJ zHeZ{ES}*uhT`7=+DG@fEKqHS)Ke2??VP5*MJ5{+@D7fT#*+) zdjHyIO4I{cZolX8QnyZ(&~<|)rE+}EMD%0m$%L^Zo`3`D$n&Q!qw>g;!oUkTyp=j4+$VZ8jb4?Jj%CNi+HpY%UAH6-(v?ZNfo< zPjgzVtfeVx9(PZ=afo*WcaInoZ+PmJ$7NbZoYtgTq{@{PzZ#d)=UL%U7*8^tENyI- zCQ)2)DM|vwy=_m3*?Wie4XkZ>8IWU>G>aENBpz(iiLcL&FIkesJ^7q+6Ru^s{v7xc zw-lK^9H8mGx}2dCOYW84KH=)IH{vs0G_9mcs!nu&uJMM`VyzSG^1bS z`G_1biQS;fk;(26Lt2>9<{CZw^(KA7DJc=(&CJ++R!r^h$W7W@*B~Uk>c^-TP5JPn z^x?U8APUNzOV<8M&er`7qpf*MvBPt}@w8H#c3`yk*wvkQ^If8xmea6hfznza%A|Y* zzq7bn6TKLeDZOf71DGk~qV+A?!-=)CuwzW@`g)T@de`qAap6(6p8pvSo;P{@e&=AD z^$ou5ZFo1NT?gwc%v?6q^|yXN7r$<1F(cdRNYjxAV8V#9|5tmM`lR^D#fS+@M2DkM z`a-3b{FLhp+N_6|zZlSTEGzSW)s-=OJqQ(uy?820%46=VwvI=Zc|>vyBl=RQO`B>b z&729bZ9S{>-`w}8yE(gVa9qXpdr4|w_ips#Mw-!d4I+PBV>$M-Fv~!2@dsv3;-WrS zGBuQ{>HvdlY?f>81*k_b8^7pUuU5>v3eW=Z#$;cIme-AzhvxHy+UrITu!y*apg(EBa#^aCi{^i!KW zEzvDnmnn^0BK@4up+N4#6TYbPVt!jRF9VL2Es33!s=`d=V^3Dfv=^(tSzJ9(>ae^n zu{DUF@k&FkYk2?TmCCMmyqn=aoEbASr zY`Lep*wZ)elyKqkqS4(~?){!Gss|kL>SQWQ-TsT*MGk{;KBk-}3QnJ2(EcLI!~s^U zs+e@e+n9%6$5r06C1Vtma%&!SOC}eeLgFyu6e>`bNNR8>ocQ`gAFCXq>QZe zbW=P==8J)<0lN{GqTXZHx9j~GpCf$oh}p)vt0J*)Jh(-{e7}kEqIfK6PAc09ErZ{Y zLEJ}vpVIfpzHEN0nQZodg+g<&?UX>1=lZhKs0c~@Dp%=yhH+;P05T!y6(T26P(T)s ziU-7V$vLhSH%TqjVdt{m`M+EN4}49-^&jwm4A8&?Vpn8LJP&Syj?QH}J3Hq9)m}%} zqE;wERf<3AX}J@L-5*EcLg zjXFtvAnD^{^ktHlpFm!Vxywohi*A8a3ww*2Qj)l83fj^E!di1s{P#)rF0&`d8a}2L zR9#ecV}d4o{H`~=P{tS>>)cx_xi`cQixw8kF(enESc9YcRSl@Y9(6}c=q6Je231hk zD8)S-nwFfL~Y@P>@O?RI0-b=g0?FyJj71sUdFONZy!!% zD(~oi_y;t+25~40EnBuM>wb`T`TNxTatZTsKFmNJWK(>I2MPGG$Vg+_)r4u~5oWoG zk6v>kUVr89U;)ih9#l^#;gNfXwmPpBv1VX*nbnRdk4cHu} zCTpJIe$KeZ&6{J2ZI%*7NEB{5+BH z2qcFu;sF~k`Kw3lZ0(Lcui)qug*Q4Cs@wfUKO1B$>8wr zuxJ5p4##P{8kOx>WcTyWct?YHngSUCu8{fj!k_Ua$kY0EzdP&}MXz!O)Pj(Z&3gL! zkFMucO?7O%qtIGB($Fd!G|3rm2*N2Yly?;d1UfD08-L%$)OW3L@Nb56L-zUboNh$M zMxSSxj-YmnOH#FgSLqoTJpS-OFy?v(f|!bxqMTFG%gfT)n%@cZK38vOfKrEC_}TgSL|d?|5mPd4!ONNM%Zq5C? zp3M!v_ms(9#TBRA2ng?xA=JJe;h=DtyY)O&{%c>-(V;eZ5Cy9tt@+eC`RBl-$qDeL z`BLLks;pzZ!O({>dQoF2H&idr94+w*e{Q$JA%5Uz^P3U`yo=_e3G8PrC78TdGCl~$ zd`G_xw23E)9z2~-vm>iL8AQG27Cna?qah2{(KL4I8Ti5z_hI#b$~Or8w*mOyrq-aA zR@>FJxyUGJ+e+fzX;wwD54SuAq>;lw#9RPd4011lW(ezfMhQ<2q z*VrIscAw-??^9^-MPAlRW(xorjrC#4b&SW2I!yl7I4}$)&>*$2YB@C$D8zJJFq>XQ z`l$rRx_2&XI%wMS+(F{>4xm2zij=yGF&)RGcihu}+(lSKWGT)SIp7w7*luC8u+qpz z`Jj_j^1fDo5=QtjC_`1arIe<7u}AvD1C_hmI`X3W93L@?5NPP2=0uEX8jH+Mmq0$X z==FuXn1qHT4KIZ5CiA6Dgs2=P(Lnf3pLB1eL~%xiy>-&oy6!Ir=C;Tew%L6TXP_WQAQ#{456)9(WKJK#Rbm+*q@EK&*Mx{G@%`9_k>FHt3_Y!(#H&Q}2GP z3ZF0XQ8Q)mtbLuz=&K zJ~!tGc=($k((3D#O=8VN>j{Hqnd-5-$k}C}mzy`U!cpFjcUO27G+o=5<_)H$+}+tg1CCHYtgCy*gMrZ6uYj7#a3+RBCJ2mEH+vwL*0F7 z+ceKP2wOHIc*VH=UzpQ|d?t(G=`vJ^p1(Q9b069?=*%5^CV$a?d;G;i^PvwQu0s$( zDQ%viobi)WcEKX^5XECO3Vqr|kg^gS-O0Pq&(aUkN>(`Tf4cD06TQ>jBi@&QO+E>D|pHBKt>~{H-+!fcL)>bytkY_%b|=?A~^q@xfgLiKc5h2c9qm?B7Y* z2-aw@h~Ex${F2j^A*VmEbvklA52XH>5gwzMJ4Ke@93VARD@~4}Su1<= zK1Q5tz!B( z9Pkv}ik6@!LvWhhPSp|&ZT~Mn`!|O^R+5tvxuovu1|)8czxlzEdwGQMfwL{r(3dg- zvGy!ayRvk2bkf_1QN86>nU-NtJL+qa%X-WLBT$aY?JtiZjaDyJyv$?TwTg~ooBRb# zgO-%)qw??n&`XtEBu170g?YQDjYe@D!&WgR;6LGoo@+b;X+e-8vh(ROQ6@#2<8Pcl zcUeyx?ZX?tb0l(qwjRB03}?kdr-G>OkG8_kBBpXN3&|3#{`-olWSunKoY2s)j{Rtn znHTw>sjIg|!kX9wq0qM(_jhzs9n)`AHCdIuP+DgW?r5*xvkz}|NwxM3x;gP9ppcEB zl@tASoY9*!#qp~oR19Qez`=S)$4T)h9El12!-2)&!TT9BAjlfeEx@GJ%8YAk35^m}d~^_=<%eP3X^3EA4Dg zxv>H=?_&qiDbx` z)ofzqg|52Zi`=TiC0lyetIMmxdmS<9Tr@B2q_wGu6;{M85)Z_I{i8!&U#}(DAx2JuyjSDtFyg{; zz!^r4-P4PJ5WrVCY0ecx-fqe6OK&i4r^n643U6OmZvdpGvaImqMUCn+ICMkGYEk#? zqOO<%l;JBKz~fz(*GSrLPs{F)SkJpplNB@&C<1w>U8T021Q*Z|itvuVJ;nwswqQLtl9ig8no;bzILEkrI0vGyXa#(^I7Dm=p1!?;u(? zfOpJgS4g@HH4Yj-%UV5fF$aWoj*8#p z8ye^wt$dtb#gJ7ImA2X4)lB`_RvI2EwY}Ht&BQO6o2!O~hJwITmsgAavot&XgM8-{ zH~p4_!2Ldh!@#a65F1pB7wPY-{(OOke9ci{R5HDE)JWk~YF05Sz)HF$&m8$20mZFW z@<&uEB=aZFpwGbV?X)c3bHDW_d>ste#f#NF`J;`7%&(h&{zn*O>0cgvkDQCS zo8k|cXz-LHZ8`x<*rMP*9O4PCt#~I>*-3N57f-&gH)({*@UO}B_YEycyGmgugjoyP z&t(jlxO(@~eD7h|odbkY_3M|~N%{S+n{B>-`{vAr18Ep0Xz3Xn8F|i|h^k!X33oPJ zrO`m~mpZrKRo3O5=+lVl{*;sx{|Z;I){}o`9Z98d5#@FsHArSd@G;^j6#tG`tGXhSQw?SlPpi#aKfRcwwWSjlE!=j zhYK!Tt!E53FZg#QXVO4Y!?SLs(gCDD61<9308xWSPxdYH4~$ww-;?<8=6 z--k3LV<)LhrIP5PBjRRlE%NsCx_`@V#gxL!^z>pWCm(+gmFcFa-NNG9F?Juf&RNo69TtRi`XnCD zVl*kNRb}iP78C+7IAeTr)pM=qWilxI?&Di zS#eakelsn>9Jo|XyBel6 z33j?GjI1MqU5y{~RdwiR{q{i}9f->*i4Ul*UP^Bnreo4KQcv#&dU@4&bpLG!?z=>|f;pRi<%=Pjl{L5PPIAJL_DVd1 zGp_f-Qr6v)2mRMAnm*i*itZaTi!wR8FmuO^P>zy%r!Ds#s#ZaX2;BZl*Dbqb%Yv`N zy%L8c+K=vKsk6CGDkB{XXUvudm*q|N2Ev+ci)G6duDMlUA_B;`@I4=np$S^gE-DGN z%iQ$6K=41oZOWW>GMt$rQ%_YK#}`rE)!wudHXf(XNOs~H#4fXzHrjIfZk5{uUpc9$ zn)g3lqEVOt#X1z3V6P-R)2zF$RD0Por- z@5baZz(Db+8G$K*JMh6oJMWg^X{{qSDZJHG>y|@VVp?W2FhYQ!Kj-!o=U9hC$zcOl z1MAA{k;ZI8sFM?rR?nb|^^L6X8rAwFMG^fDrnvs#>pK%uHm#tb%1T7Som|a6I^&v$0%<9b zDytkABRMXXZDk|iVUT#6F8m@V1vc2*~v^Z#u|Z1gOty|blM4TAanHsN)4Y-$Zpzu9sJMPGfR zCw1-CF{zX52(`#%57YRG4IQE@pTI^5^oah_arC>(#j8W&gCd<2j}z=+%Z)|ItHsN? zjMo01nGwj|q6(lNrB}^X?oc9J*W9|`%9FmY4^fEtQ)SD*nfJt%%%fyq*zYL)!TaDP z;)>>m6)a~5EbE~L!4jgM_VvB&OC454An_7l6;aw;-5 z@;t~6<|Qi=vqoSMa_%rmatEC70IqBBL|a6>7G+BGqzhjQ;v$MYL7nqSAz()j=L3Sc z*YP$E@I;XZ9lcbR1))~pObd(8OHYMeWpT;rx)Up=wvzH)a2F$0d1x~pi2IhQD^0B5 zDU@`C>82i>>2O0E~-0Jsc`+?tf#e!$?fbt6kV_9)d+)Czc40nLe@AWG7PtE0+} zjBSst-JsrZ0?=A=K(xk%5KyhePD7doX082^N8B3b9W5=9-!9mSJSM9t@sv9n17oa& z+7V2`O2q)z#p(|iULBQ>*@!hZ*+04c zRb2Qz=t!6X{m=6Y;388S`8D)_`RD#gR^LeZz;4oZhpF+H0K(#{-*`Uh|$3-aMB3)-iL#kN00<97(pOswmftsdxmeK_hIFqA~e?1 zhiiVWvX?H3S~D8#yYDMe_1f9w4I~oJcYo?DI??Ocq(9;{Fyk7tGgjj!psJD>-Cif9 zTFQ_1VmX;wh*SQxLrGl)^ogvBV2XFKjeUE&=QjlI@QIl(h9AknhOsf2x?*;w0>=HhFtJHUcwthQ^RyhI_&}@Au4jS$K*6=Fv$Z}vtku$ zBT?;kb+aIEr)75PGPVKhQHh1wA03g5AQAV#EPyRTj*+Ujh1oW7zhAqV_+KYjgxz^- zVjDu0efw47kS9R=D_B=X4ikKAjX*3wA*7Hh;N7m$45$K`%3KS1TsXgj|M%Aon&b*U z?>v~DZ2TQ0r2oLrnho zvD>wt7Wd)99SNBm2~!$Em8~&PEPe|){w<#?X9kAxV9Kuhi#<^~@Lngq`vh%#z*&0Y z&%0~Gc6TezzJIgMEbJ3H37A1gH`K8ceaG~@qpS=&zO}Y4Jk-0E`egM0`O1AUb64h& zxT|f=NT}`o8lw@ND@;dJHO#b=HDJZ#J0I?h37hHPqsp2dl92EMKd3rBsPa2bnkkjb z2bpC1cV?nb)h}+K#rKee*JFGyqMARhpCAf2R;a-h+|W#nJAa8LH;%Zmm3OYX^U$ec=6^H_)F;2YpL@?!BtT90Nhbv96#n zM+(TZ`&@|NZ)l-Z$$);bRT0Je&xwhNZ2&+rUxCu3*mI0SU>)w3s`q1FmiA_K-uB*h zI-;IS@$BZ_PAnm`O?4HQz_0?%sqI+3026}&LA-I;sj!<Vci{=K87) znH9mZ!poP=;`7Wox+2R;4I*X?uJ!intO16!U$+y@chSw=g)LQ~GHtq7T==Z^!XXg)E)=e$t~Ih1+-uTDgUAeFc;DuoAlhRblP9GhfnFGkF>lSN4O zCOWDfS`h3$4?lZX!F!K`Zf+GRYj2)e%t9{mwP71U@OQ4fOO@4M&W*s>FlQp1SDRN~ zSnuv-HCgv8xrNPRh;9yT`wnQeX8D2!=ZnJ=KUJ?KOsAOGQQ+hMYOOe@y%u%iZiZ@kmG1BYi{vI=e`lL^C{fEy=jWwvks0H2g#h93X z{QFKN8GdhWf0D9~mdGCq=Tjxwu=%0*Ad8l4cAvL441`Iacid$?|Di;8?`EJOA&kUK zDXMv;FL1Fk{=~Ls*4*4TdG!Ekw%d-v7iR>bU=_KMfhetltO`~6b}dU-^pk79FE3%s zjE-?jHp|j_tDNbfnwlE7RRgOn4n=lEP3`BZ*w`RGYef)Mh0BHZ86n|>Y&boVa=m_L zV&*O0FK&K`$Z8}e0%k5StPH)pPrze;mR01J6+~%k+row&!3USy`RK`H>n^5}kb@WpocP6q_gwt64k_vxaK z>F&_Wc4z2_a0{66nX)%t$U6xeh}5R8qjE(2#~HB3_KsUT-p{U0lQ?9gEMKBbdM1Vq z=bv~~0WQpaLBeEo8D9yyWp~4c`BhF%g~tlQU!EgwAiDn(`uQ3g>c);H=(oLAo4iTR zj6)v~5FUsGt-MiSDEOd*Qu!JiE`*tn5TZNKLC@;_-mEITMdB63!@^>?f~!K+0V`qh zCW26l%QZ2`pq(sauNBJDL{zD6&b`+$i{{YQ{*lQnUaebPNJAF)T zL$QspVaHX&ji0!y}BH^|0jtsTATzC(! z;3TLm27AGNZZ^hGd$aWS!Bw7dju15^*NKAWY%8efy3CXNX)z)b$Y91Pa$4dn=C8r(>)VLOw7mLNC86TT7@>%0#tX z!t-C}PbksN?Q0yG4od`|U3gM3SLORhA41+7az3&)Lm-KE1K<;6 zbufOr=hYE<8LbqPOxO}WMJfsVp0LVYPE|%Zk;dl?7T}^g&ldb^t3Y2$es1ocx%i+# z$r=NlXAA`INa?n-E*%{mVb(cE9_}0(pSR3ud7YC(N-a@}cXAR}cw>|H`@zcckAy(4 z*s1rc=CIc8WqqjpNE&-p{4`xd$oo;gIU4@6NkIPvR|RgP(qE&lVNdDBFRz6_LNlQ4 zdJK@#$O}OAbFp0Mjw2=c`M)FZSQfWa3>6~QuFV*ocD)yxgI2m)Lyr*5UIC@RF#tLl z$a6s?M9U1t6Yva3X-N(v>#lKHfOpPtK6rdcF_%@gn|!u24^T{$P~5=HLtOpj&+tUk z7Xh%ASUpgiN*nH|H_u^_f%qU<%?tbANC5i*fk;skp^_A{qT>K?>)B}-<6E!#Ij$kJ zzR~Y_>j48a(3iUs(7(d^X`zGFn}IspnV$|}PhmD+n5uQk-Ly5WQ z8`7UHmNS?V$O5tH&aw$onQzZ{^%=AR-DTyN2G9&V*CUXgn4SWqu9VPd-+VEG08WD} zwq)B1${8BPBoMj(vMWhXmnA$K!@@$1Z_G}Fd0M5@yT%!L^&%IX@{kNHYe!zEreel6 zJF&`5x}Ga2y2N(TMB0_xcg%a|pqd^d@g4+HlEs0>Id_gxMv0a}IY(WE{)fe(rqtn( z6TKOH9y>(?+%oA1QH^IZ!b48K1fuzQGWG{4_W*;XYF;XaC>e;>Vo&SJsf2_C3Fz6Q zdiFnQUr+8L^k$#%h6p~e5BBX7DYCYv2Mq1yQeTm26$#&Ee8hy(T45<>yg!UWp76Z> zt3Rh7Rru$)QON(TIt2fkJ4(Ume$_~D1Tgj^cEATdr~R50zl&P!BuYDci+b23;!4+f zE2Qz}SnH19TQm>=@0aO?ejsljuLdL5T`X)O`~W$Mozm&%;IozkyX-u^Aj}6$WPJMI zA%9Q9zV?b(64`mMXQ`uorFDS1r&P~%F@pcd3@@3eC1Di@>~383#*;#-JYY3o)LYY>e>4_B*9Ynh7h@2h5tFjLVl;T$DUJQ zoSMFS=!>1UJ&%ig2R3LZXuq~kz(tMbnEY?}>_29YydQq(_}$gy zXE&FZ%gmZ)Zpm*~nF#;?A!e_%z*8h@VE>U3Czr{(z9Ux-#nOhSwG(p$ipG`?V&Zz+ zG`wDX=JVNVOZnOtL#OJaN~fI2!&AE@?=iP4Y36DQpZ$7OIMywCrYw5w?4wa4F=u%+ zy39xMMmkklC!OKw6`y&?FolGfHse-gpjC0wV$Je*Oy(3cAD>WqiT6<4o+1-j9eskl zN&aHm|IeP69$C&i`c;i#3VZfM@UGevgBTqmOI|?f6#5&$Q5_-*`d#i|D;h6{D|B<% zAR!VId)egf{r`sH7a+P_#p}V>e+|1nFN-U5aFGlWaFI^=cbV33?0KJ>mzv`0%1;NM z1X>V7v9ZQPI0(W0E>B&SZ*?2IGXlH$WUDjTuMmxrqKzG_^A+tI!5)s2h}9W!B@WTXES*KQ@BB9XhR zc5+jU}%EB$GQ*=0Uj-S_T>nw2~%@E|^yLf%y`{>`i0H1Sa7g6IO7 z5XvI7>J>tjg1a0Gb2ssj3mQH--7ZF5O|3GtYV0#bJ7p-}@%{UpGWg}HTp~8!uNH;!DO0T}f+q@wyg&D_@Sa+B;_DZd@qEa}Z!@<_H=MAY32VAV92f_xm-VwR68PR2wX-PX`Nt-UvP|m6kIgdYH@ik_7`i<_2-RARfV0XTiUb9ouq~AT}fm92Nj<6oH z8|*&!M2421P!)59-6mt-Vz7U12*}2Yz{mRRql&d<4^Rx_Qz|nlEi!(&p;v2gm|r&b zBx}^E(bEs%r-R*(@M#t6m|@B2lO32jD`H&yH0B$%7~3_~i!$x|M77q_?b=qkd?kI# z(tjc^$z!S85*AwQae;mg_NX)bGFH@oXOZ93d|>eVmA!jYZKA(Xu`AG0BwrBRX@KM7 z^FT#jL&~`CdUFN;fmq{W_rqf@?O);pK`sB5t^WV($a(zrSy%=(J345G>6s==a(`4; zI^f>gQkP@0XiS&?6I_Ca@fNnQO_8e6=j6%Wf=7Kof4=b5jpTWFU&%*J@%{qqzau=C zYLup1vVG9!Wd4ukKx*QNoD*+}Yn6fUjq#JKt#U$DPMvaQ;M{OW6+bnXI00|b{E9;<+G`pTjqRyA+hO+X0(dDvDqQx zs2<<@&+qDk0-bA`(UMR6Hvj2<^2bzhI(XIJ3w+*hKkJ)e zkoQ{rH`NpVix>Sqeu|f6|NoVk-=jSfNQS}JhYN#M-{@Vjr4+fr7v^1j%oXx5#Dc7#=b#-;=VzZQxM#c;OzdNhsm&ai-ej(Yx$&zB&kl78-W4_sEoE4`3KS)ab zIbWmv+GBPzo9j*{%x7rbIL+=u=FRwMg_TQ9ok*DY_ziEK{$d259Mk`ADeec}Vfy#j ztLfe=hN%m$oMn@lH=VV)`tP-RknPOn`lz8*>`B$V;erAIW$wDOy1Lxjti866)?I2j z^#6k-W51(u$APHq_T6EhnFFL_!}j*xhw09vOWmTb%d>?&M3jkzhfg@9az?sgA(|c% z09W2m%o|rwNJt6e|4=n0969$a?KADeV%cvfhhV2Ou zBG()x*%%GW6*AHD8Vonc-77^C7*qIMo+O9mjDpXSzm4GY-5l0zEIys<#6BoBQyGQn^+7})*^hB^nH+ROF<*kI~+Vk}S{^-gIg zQE=r3>_lfgyqp&UY!sjn`ip~Ryc1;wOG8_*&B3Fn-@={0!0D{l(&+kVy^}*_6%1YQ zqNTb(^xZQ#+ZhDg4wGnDIlNsk`EzQBzthB-ZT7SGbtsIE!qL&r@W!9uDQ!8VZcj+s zo#q9ak)lpytzi)?=!!iLep!C@);yJRX8}#swqt=jn@w1?x1DpFIIVxP58bVqFw zt}*V*gXMntSr~*MD4YQSh*NDeb6P1&-|fj=K9)L*tnai;JhqJvWFC>IDD6@3#SDy2 z^`GW*FYn*4gy;c)BG4Lxe;96p1;jQy9n`-sPa6n_oi7P4 zxuHyv39C-uHEr6xW|TZu{5@}%JcBC%E`CQ5lRkg{9sxF>>1js7L`y+a+`u>5VAsr% zz4@NX6hkEKk9b0pdI_p3*e@7+(z-;AU+ip4LcmG9$dI7naiOCOmUdzhJ;CqO?{ZJ~ zI@`SyB|5{g(M{LiQ92KV+}Mr)7t zW+X_fx?tqNMg23tA(%IDFc6|*%j-#uMBl=KZi5WCNmBw->%`LEd$iQ5{B zY5{HOh;;J^(o+OhM)5hW49I)w^1CJRd|3*N!7i>CI}!^f`c74bDRc=P4bhDzcES*z z)CISV&JO)l3#D?9ID+V5rN3aY~IpcwmbKoGb``Qp*65 zT4E-6r~ErXF9M&HJatJ&k|sgyWUf9yL6Z~tT<`V6Bv}vS zHtf?ozE4ZkC?$oQ8DLGrc%`a}o`~lUSkM>TtWnbAIGXUc7MYdQCyBBx7UYj7U9d~a zRv-H+YZXxW9$>H#M#Ql>>t@u{NEBs%(!@vp$S^vLtp9CZaAtgq1zrjHh|WS%_SDZ8 zU*Yy0yePt4CC*3xP-L6?r==y&zug6jh_~dozL-^All1aBR^hVv^3D`q=p8!>3P{(} z5+;JG@Zo&gE?vt*kA`skhhcw%#)dpjzvUtx+%gcBIN7=2!|OTnJ&W0E5U$Qts}|Qc z&6aJoH{oaTJkfki3L9^vo;`auCOqVaCp->HJfqy!5%SMBbzomw@1B`So(?N{GP`>a z4`vxOYYm(=9bTgTa?_1xO6K43VsBGS-}afcGG47;QQmgAEzV8PlrOcve#t_5*Zx|2 zY+jq;w`4pRg}CHa8-m!x5)>;CeqXHVT3X}SOcfUGEd4arJKM>4_3D`=^uHXmr_9G^ zgEeGl(=WXGGj(%=%!I@lp)E18?BDlQN{z!dt}^q^w6nS^Jb-d?l&iN9@;ly%s)@(m zI5MJ@pzo5|G3GIlPDd_UnGJ)i!LBfu)j9@Gi`;3wZtN5B8*%yDzEn`h}y z$EJ-SnZ9Syklufg+#HCc0E5g8JaXX?F89+b)xBYr{fa&5^m7U3e{2crYy)`5h=OQ5 z$CotC+;?}G!$L6XCUEd^38a1EOvB{HA2wE2R$bn%g+Jc{Ky{E>cuf$aAgZDj3x~0u z^o$Snvus6yMc%qcn87B9*jxD8bGFZ!w`Q-xyLTopeg3AN z*$lYxGdNSRL`-eT9Cm(Y;cB$cdkW$vXhvYb{z;$^t#5L_K~k&o@{!xohQL-wdD@yg z-QKt9q&8!fRIL-Ys-J-Y)W>Rz%B$#+^ZfZ!A@}zto0?ZlZY;M=Q9o(sRxQFz{D|IV zcyeD@C0G71HM+X@8$F)ResJi>_yJ+9wMhMKZy4~)syBQa(~(~E&{A&PtoRUt5HQH~ z=^>pf;}l|aZzX>oTYidQCDKnFMb(#&g~*GTvA}vVG(D%_GwE$n)vqr7+|wjrPT|g! zYmsnZH*j+95(R=fzh>ZGZ?~vmo1=Wt^A~Ceo@%#X!hLUZONYpgiMPz2zkH2yZ_G-6 zZ)@`yBgd%2W46+mdnxtasv*0}H

UUD6$UC<->tdA8PRNsV(`3QoVA=O@ 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

<1N>bM;KKgRSg=u3~yJG^ynu+8y?WpH`;Er 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 literal 1685 zcmeAS@N?(olHy`uVBq!ia0vp^Cm0wQbvW37thQf&_AxN9#dx|nhE&XXd)MDzDq80F zL*t5{{xK7>axU-q_ILjhy;;k0V#ong(>C9n;y>TcRBr!{9U9FC7hEZgUAKPy>9oz2N3ZUcz5BDX>_Uy5{^px& zg8BbUzw-L)sT?!y&p-d%@P4`G|8xI;tv+g#2^mm8;lT*mq2z`OVFPiJklx^Z2ehb`4aO7_j`|1VjJ98Nzq ziu+lm`TqWX{lgDGJh=bdUuA;roqPA}ZZ?O6hN?~Wl#tSm3gzYH zOV_>LbT_a5_xFn#AwX}hUh{UXu!r~Sue)mg|Eqnz@%HW8MU|B|SN8rY+pQbFzphru zUDN)WwG5y4{`>ZaPTdomt{+uZQITP@?QUMVok0J~ou*5FAGU`l*UXnso-Fwo5gI!8 z)`IDu{>-}c_o4i{q^(!pe?MJgcmL<2`zb~*QxwwE(}C_#KhDnl7p?U$vM_s-&JKO7W#)iK^>&8k&}e6QcVTUU7J?g~$9 zrcBM=Svq3fa~G@qt=nJo=f}qd#*u&P>Ys~hE>_cbR?yulX?n9%_4Sh{Pc){7-0qph z@ucz_G)*P&U%Iqk*0i#s!s3+L-{-SteN%Mvy(Q@34K!&__4mB><))GAuRs0#{5<>q zGru1mZdaFnw`$d?pFb-fr^ZQtDP!5xelus=_1EmDpMHAqe`40|yVIT)?flGWFV|nb z&(66TV$br3>K!|GX3CVme{>}$_ImH*clo(SKOrvcm4Wx>weSTSu&S_fqj2p zZT21J-s8#AU&ZDmZG95!`_Bhn2y6j!K>=x!_3883)w?B5fE+SAe0S5;K8J4`-mf^B zZObuxUw`1_|9%SWCNg}dj~+dG(B9#Adb3QQ10OJ@{bX5T?xO&AO**5VSog8Q^2KUz zIaF@snC-V?EZZ7o+BfS;>D`^Zzs?6vj(xT3-`mM~=CjXc>%H|YoM~QR^WyuR<`vzG z)lNSCy*YcqM0uNL7W?^zyDV1z7BZXdyYls}k25C*ty;Z$b>81s&3uJjtfPe(JPIi& c&j0YFdh=h>L~KMCQA_Y%b8IS)MMAAr-gY-aPBYY$(EZ!OqXP?+0_%mf3yAGxq!! z-ud3iC{y!#!M;?5ZswhwPpoCwPcpL58a#P7VaoNO^5ttRk2WnSetX8Uwdu*NxiVav zzBykDm~e?-bD_f5E|!)vm)D(1RJ^>;SC|6>oZwYF{Ktgv^LJ~8lo@p?GmdHL?(I_z zYp?LBe4UtcG3u`Ur_$5RESDyCr@s%$Ui_razc){Mw>jXErLzpB2CM zTVulZ8xwYZ&G>7>BLD6A`(>Pw^ZwnnE?V9|W8iX5|L`K`HM@Hz=>j88wZt`|BqgyV z)hf9t6-Y4{85mmV8XD*tn1mRbSeY7G8Chr>7+4t?Xm8rkjiMnpKP5A*61Rq^_Z;Rx w4U!-mg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4hf>INMr>mdKI;Vst021=#+5i9m literal 1776 zcmeAS@N?(olHy`uVBq!ia0vp^Cm0wQbvT%SEY2C(FM*U~iEBhjaDG}zd16s2gJVj5 zQmTSyZen_BP-l zO)W`OsL0L9E4HezRRWu91!RMS^_3LBN=mYAl^~9a@C{IK&M!(;Fx4~DO*S+!QZTpF zGc+@>G&0dqFfuSS*EcZNH#F8YFtjo-vof|+fC43;ZAB?*RzWUqP`iLUTcwPWk^(Dz z{qpj1y>er{{GxPyLrY6beFGzXBO_g)3fZE`@j@w*YQzUNJP7fB~jokyxN_sAmB35=^15FMg%Dxp39RB|)hO_hL1; zIHa;5RX-@TIKQ+g85nVC${?!>telHd6HD@oLh|!-V4)b0kzbNuoRMFk;OwlR5uTZs zl3!k|30CgwYvq|&T#}fVoa*Ufs{}MaFEca6%FM*V$kfEu*x1s*($LV=!qLdW)zQSn z+1SzCz{Sbc5~kNBKe;qFHLnDwHwB^B1gBn5O2{n$+U$~Alv$RV;#QQOs{r<~RVHq? z7~(V!sy799yBiO3djj(>Wj6+5Kc& z-Lg|`mm~G2%U{@X#JgdGfY>3XL`9tz)q1Rudu9rqma}8`ceyFN<)w9o)+i&OooDey! z*KM&u`n=~Kw~9Utl2{k2yZ+T`gDi;~a<5riL@yjTRCVChyk%@+*POTMvu&%aZ@ZdR zx-;h1r$E0+N0T1j=QZ4MrzSzug{Y8fVdeiIW*^}@>4?Fhtb5jfmfQ{M*bsO5Wr@*- z4YR_hEp2SQx@VEbs#R4}GY_vweDLF5Yy9MsD%ZNhW-ZJ8#}#({YuWC*IcD!>{V_Te z{q^^|9&*r<0+{(ORQ@Dylq8^mODt z#fC-{YMuVH{NCLS_4nVF9qJ0c;qtt)&PHy2&B9fywB~%TvHKk+u@)G_aqHKfUWb`> jI8G1w)b;;eKLay^g>(Pw(m4fvpgPFY)z4*}Q$iB}QrLNN diff --git a/src/Ombi/wwwroot/images/favicon/apple-touch-icon.png b/src/Ombi/wwwroot/images/favicon/apple-touch-icon.png index 1e6943041824a47d37fc45aac84e1400fc5af52b..069c32adb48d915543551fba5f73cad0fdbf5f4b 100644 GIT binary patch delta 2269 zcmZuyc{J3E9v^WVh7{S?v2S6Fv5h@rUoMiJhG8t(%_LL#$&w01?;^#s8zMxMVF))A zl8i}eq8eft`&jS2bJctAyz|a^-|so!?eqPd&!3-E!F_=|1OUa@xjTbE1Z5B?H3I}X z03G#35C{(gff#5I2zeg_5{)Y%xtSaRXI-51fAlt#XWN3fBc_~uoEk)*uJSE`47I=Qu*Lll|v_A z?&sWu2ADDnGZ-4xsuxaIf39^ulkTdFotfBC`U3axrlV!_^Smi3+O47K%3245E1mLI z`YWFB)Fh>dCIO}ILciTB=5K$uN|d^A&3^iVdAMwMjtuZnQ&Cb)(n5a*?e)H!^J`_$Oh ze$)d}NM0Ukn3@QxFY|Qf5#_n5Ply<@VwF6?u0WzE#Kw9=UizC~(__fkOrBY^(+J^( z>W-S?9{@@{ns;clq?!vK?3~6bAE&ffQv6gEv?9JK(GPk0cti6&74)$K+WJy>doiT~ zTj!G^d~-dtIzPg4bIv4^?AyC%N?NrNdv3 zARFC8mfls&NlZG%5||cS?z;v?TTgt&4LB=fSs4TdVrq08MwDHWr(My{U6sOrXK>R1 z-K~};iC=0tcjy?06aIRLEMEnQMQ$wj3H(RXhjvtDzD*b@#CDpSH7are_p z_#yZ{bQN+JRIi)aiwt*mwx0h~l>ju|g<$T>sZzXkn{US>YxLhfAeb1+{Ym9eAB(C> z%C@wt^P9fZUv+$Q-5eEL@=E0^h>VOPSO;TL??ghe$yVOY)T0Tz$y4}uFWTp#3UCsBmwTmQ(R!MEt)Sg zABUN!;L!7*9%5oY57h(7yTWwDN%coXssY^#?F+2WNS3n=ku}HR@+-$?C^<&CZS@Ly zA@Ac-Q(eWK-$LyvbKuI3$8Ic%)yBh=W;~C$z(BDv=DAGG)cC>PaLvNIeJk)K_pGza z`9)b5Kef>|o;r?kJ#<}bf^(6K(cD^|W0O_K%Lrk)99gv{R0a@K5jehUt+_*$;g;4u zh1Gg);v@~rniGGPv{7-f(~|6ik=%U0r_*3doVuSle3AS5;83}9Z5f4l!-A2gmhlye zFlbv9l#2X%j<~YH{|ptgU@D=E9%UWY3`!Rvqm51Y1CVksiiGbx&Ha5sTV>1s5^6uL zo)u?SqxHVt8gTty;TPO{eOUWqdxa-KT;$L!&2F0iuuv?QSxbtIOY077-Y|2d`Dhjv zwe0%4vKzTWCd{4J?G5Cr)%7afR*wxK!%{!Bh(+y*^Y>|ciL(m_ti2t zH5L-eH#Eq z7jWVcr~!i^VK9x>mhAswM8}3iV3U6`{x89IjoaXe O0J67sK{cI4r~M7Vb0Nk6 delta 2267 zcmV<12qgFU5r7hqG=BkeQb$4nuFf3k0000#P)t-s0@k8l zF9co$TIvX5r?~tJ&;a66+_})luBd}P`9)|26uA&~4z$8qp?(266FEoiRA|}a0@2DF zp{2%!Lf;0haDTy2w?LP1MvmC%cV$jrsx__ku#$KyC%p-M~8tjWGYPkDu^vfHCsd-51Aau*8pqolL_DQRaL%v@Cu9UZF z(2~#I*p|f{O^rFl{f3%m7F%T!v>sQ!hcFA>Gh#Od!_^@*O?(qN1zPe$8JJVHW^{O0 zX3RdIHgAX|$}JP1H9rW0I1x5%GBE$9c7gkv@PDMx3DAM&=mcnXg7YFmJ?eS6-6+*g z0};2!UGW%N9Ss6j)rpS!9J|z%HM3v*Hll777+N0-?V#5Z?ov$cDyOz50o%=3?%zf zuYai5N>j`Hf+^j?pk)}k-dcB$dQf$S9+??zsFomT%}II zK+v3=st}<5aYt06(u9lJpNbNkL@G@rNa-jgR@Cf^y7Cn58y7u|?}`9uMaobQl1@$& zO8}UODi6J;rh=@ghq2ilkub8_6J7(Y0j>JfJ!Po=nW-!Ds9?jayBYIHQd|RV1~7kG ztf{Jby#65bze+VNubD`J*4IG$T7Qp#v(}&Wth@L_(8tv8_40|2hXV$1S)zJvsL7T(vPHn5DNB`ls&q5*eNl_p)`8IJL4=zNuCuA* zq5o}!i>+Je2Axo!w%9@b8_$z^R@&@yva}`;Xq^Y`JpH0uY67H&Od+UDXy#_!5%H+; zmO5?3JDkhQWOnl&6&Gao^?&%xOt|0Mek=jw4_&Ap3a|Dsn4-cW0TYD0+=jyS;3Hbb{rUFKYLQsSX+) zzy8P_q4Pb&by_b<(}2N!GUIqkX$xkII$ zYl_NKzgBZ(X+ocz)ho)xirR}lP3V*Jr_rU#U1&WGSIX zY{W6f1P#b&eH`1>s4c}vDFBP8_W5X!BJN;cX#UC{VM}4@03~=g1*z%<*v}b zk2)&vL~Pax{eOp;duT04SS=J&6sVmd6ZI%u{MLg0arbo`m)tigmC1-Tb%8HzW-DM=9&VS55Efc++mp3~*)3;O|e8k91 z^<5&Ty3_#fu0T@tLfPvhxw|sy>&tylfK*}duG}{T6#hmA`+W@EhP~@be<#B=-+dvy zxS;vx3p*a@2l|12pdaW5`hk9+ALs}AfqtMbiT?m`yz=e`v<2P(0038dR9JLUVRs;K za&Km7Y?0jzQ2= Date: Fri, 5 Oct 2018 13:04:47 +0100 Subject: [PATCH 32/74] Date and times are now in the local users date time --- .../app/issues/issueDetails.component.html | 2 +- .../app/requests/movierequests.component.html | 8 ++++---- .../requests/music/musicrequests.component.html | 4 ++-- .../requests/tvrequest-children.component.html | 2 +- .../app/requests/tvrequests.component.html | 4 ++-- .../app/search/moviesearch.component.html | 6 +++--- .../app/search/moviesearchgrid.component.html | 4 ++-- .../app/search/music/albumsearch.component.html | 2 +- .../app/search/seriesinformation.component.html | 2 +- .../ClientApp/app/search/tvsearch.component.html | 4 ++-- src/Ombi/ClientApp/app/shared/shared.module.ts | 3 +++ .../usermanagement/usermanagement.component.html | 15 +++++++++++---- 12 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/Ombi/ClientApp/app/issues/issueDetails.component.html b/src/Ombi/ClientApp/app/issues/issueDetails.component.html index 33157cd09..44be113c6 100644 --- a/src/Ombi/ClientApp/app/issues/issueDetails.component.html +++ b/src/Ombi/ClientApp/app/issues/issueDetails.component.html @@ -63,7 +63,7 @@

{{comment.comment}}

- +
diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index a35ede06c..6167fbac0 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -58,7 +58,7 @@ -
{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate | date: 'mediumDate'} }}
-
{{ 'Requests.DigitalRelease' | translate: {date: request.digitalReleaseDate | date: 'mediumDate'} }}
-
{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | date}}
+
{{ 'Requests.TheatricalRelease' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}
+
{{ 'Requests.DigitalRelease' | translate: {date: request.digitalReleaseDate | amLocal | amDateFormat: 'LL'} }}
+
{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}

diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html index f6ad4bb2a..caa1ca2d6 100644 --- a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html @@ -97,8 +97,8 @@
-
{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | date: 'mediumDate'} }}
-
{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | date: 'mediumDate'}}
+
{{ 'Requests.ReleaseDate' | translate: {date: request.releaseDate | amLocal | amDateFormat: 'LL'} }}
+
{{ 'Requests.RequestDate' | translate }} {{request.requestedDate | amLocal | amDateFormat: 'LL'}}

+ + {{u.lastLoggedIn | amLocal | amDateFormat: 'l LT'}} + + + Not logged in yet! + + Local User From 293aa8823457c6acd79c07aca659fa647a73699d Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Sat, 6 Oct 2018 23:09:02 +1300 Subject: [PATCH 33/74] Hides Sonarr options on tv requests page if only 1 option available. --- src/Ombi/ClientApp/app/requests/tvrequests.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.html b/src/Ombi/ClientApp/app/requests/tvrequests.component.html index e4d10d72e..801998c71 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.html @@ -50,7 +50,7 @@ View
-
+
@@ -66,7 +66,7 @@
-
+
From f8cda54002a33f5e37a3a902b14b4e25f4aa8bd5 Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Sat, 6 Oct 2018 23:30:32 +1300 Subject: [PATCH 34/74] Hides Radarr options on movie requests page if only 1 option available. --- src/Ombi/ClientApp/app/requests/movierequests.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index 6167fbac0..05d0ca14f 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -129,7 +129,7 @@ -
+
@@ -145,7 +145,7 @@
-
+
From 42eb5ca9117bc801575fddafea0f4028d560648b Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Sun, 7 Oct 2018 02:06:30 +1300 Subject: [PATCH 35/74] Changes language selector to always show native language name. --- src/Ombi/ClientApp/app/app.component.html | 22 +++++++++++----------- src/Ombi/wwwroot/translations/da.json | 13 ------------- src/Ombi/wwwroot/translations/de.json | 13 ------------- src/Ombi/wwwroot/translations/en.json | 13 ------------- src/Ombi/wwwroot/translations/es.json | 13 ------------- src/Ombi/wwwroot/translations/fr.json | 13 ------------- src/Ombi/wwwroot/translations/it.json | 13 ------------- src/Ombi/wwwroot/translations/nl.json | 13 ------------- src/Ombi/wwwroot/translations/no.json | 13 ------------- src/Ombi/wwwroot/translations/pl.json | 13 ------------- src/Ombi/wwwroot/translations/pt.json | 13 ------------- src/Ombi/wwwroot/translations/sv.json | 13 ------------- 12 files changed, 11 insertions(+), 154 deletions(-) diff --git a/src/Ombi/ClientApp/app/app.component.html b/src/Ombi/ClientApp/app/app.component.html index 953e2a9a2..d695ac61c 100644 --- a/src/Ombi/ClientApp/app/app.component.html +++ b/src/Ombi/ClientApp/app/app.component.html @@ -117,37 +117,37 @@ diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index 0229b3603..7786d69ad 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -57,19 +57,6 @@ "Welcome": "Velkommen til {{username}}", "UpdateDetails": "Opdater loginoplysninger", "Logout": "Log af", - "Language": { - "English": "Engelsk", - "French": "Fransk", - "Spanish": "Spansk", - "German": "Tysk", - "Italian": "Italiensk", - "Danish": "Dansk", - "Dutch": "Hollandsk", - "Norwegian": "Norsk", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Åbn mobilapp", "RecentlyAdded": "Recently Added" }, diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index c3646221d..2dccedb1f 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -57,19 +57,6 @@ "Welcome": "Willkommen {{username}}", "UpdateDetails": "Update-Details", "Logout": "Ausloggen", - "Language": { - "English": "Englisch", - "French": "Französisch", - "Spanish": "Spanisch", - "German": "Deutsch", - "Italian": "Italienisch", - "Danish": "Dänisch", - "Dutch": "Niederländisch", - "Norwegian": "Norwegisch", - "BrazillianPortuguese": "Portugiesisch (Brasilien)", - "Polish": "Polnisch", - "Swedish": "Schwedisch" - }, "OpenMobileApp": "Mobile App", "RecentlyAdded": "Kürzlich hinzugefügt" }, diff --git a/src/Ombi/wwwroot/translations/en.json b/src/Ombi/wwwroot/translations/en.json index 93f6c39f0..2d6d81e94 100644 --- a/src/Ombi/wwwroot/translations/en.json +++ b/src/Ombi/wwwroot/translations/en.json @@ -60,19 +60,6 @@ "Welcome": "Welcome {{username}}", "UpdateDetails": "Update Details", "Logout": "Logout", - "Language": { - "English": "English", - "French": "French", - "Spanish": "Spanish", - "German": "German", - "Italian": "Italian", - "Danish": "Danish", - "Dutch": "Dutch", - "Norwegian":"Norwegian", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish":"Polish", - "Swedish":"Swedish" - }, "OpenMobileApp":"Open Mobile App", "RecentlyAdded":"Recently Added" }, diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index dc7637e79..a9f8c2b5e 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -57,19 +57,6 @@ "Welcome": "Bienvenido {{username}}", "UpdateDetails": "Detalles de la actualización", "Logout": "Cerrar sesión", - "Language": { - "English": "Inglés", - "French": "Francés", - "Spanish": "Español", - "German": "Alemán", - "Italian": "Italiano", - "Danish": "Danés", - "Dutch": "Holandés", - "Norwegian": "Norwegian", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Open Mobile App", "RecentlyAdded": "Recently Added" }, diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index c20dffdb0..bf6e26a93 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -57,19 +57,6 @@ "Welcome": "Bienvenue {{username}}", "UpdateDetails": "Détails de la mise à jour", "Logout": "Déconnexion", - "Language": { - "English": "Anglais", - "French": "Français", - "Spanish": "Espagnol", - "German": "Allemand", - "Italian": "Italien", - "Danish": "Danois", - "Dutch": "Néerlandais", - "Norwegian": "Norvégien", - "BrazillianPortuguese": "Portuguais brésilien", - "Polish": "Polonais", - "Swedish": "Swedish" - }, "OpenMobileApp": "Ouvrir l'application mobile", "RecentlyAdded": "Ajouts récents" }, diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 0ade93919..bfa026be0 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -57,19 +57,6 @@ "Welcome": "Benvenuto {{username}}", "UpdateDetails": "Aggiorna i tuoi dati", "Logout": "Logout", - "Language": { - "English": "Inglese", - "French": "Francese", - "Spanish": "Spagnolo", - "German": "Tedesco", - "Italian": "Italiano", - "Danish": "Danese", - "Dutch": "Olandese", - "Norwegian": "Norvegese", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Apri l'applicazione mobile", "RecentlyAdded": "Recently Added" }, diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index 18a1d319d..24ce67966 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -57,19 +57,6 @@ "Welcome": "Welkom {{username}}", "UpdateDetails": "Update gegevens", "Logout": "Logout", - "Language": { - "English": "Engels", - "French": "Frans", - "Spanish": "Spaans", - "German": "Duits", - "Italian": "Italiaans", - "Danish": "Deens", - "Dutch": "Nederlands", - "Norwegian": "Noors", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Open Mobiele App", "RecentlyAdded": "Recently Added" }, diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index 80cd9e31a..6f84943a8 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -57,19 +57,6 @@ "Welcome": "Velkommen {{username}}", "UpdateDetails": "Oppdater detaljer", "Logout": "Logg av", - "Language": { - "English": "Engelsk", - "French": "Fransk", - "Spanish": "Spansk", - "German": "Tysk", - "Italian": "Italiensk", - "Danish": "Dansk", - "Dutch": "Nederlandsk", - "Norwegian": "Norsk", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Åpne mobilapp", "RecentlyAdded": "Recently Added" }, diff --git a/src/Ombi/wwwroot/translations/pl.json b/src/Ombi/wwwroot/translations/pl.json index 3aba4da41..b53f7f1c1 100644 --- a/src/Ombi/wwwroot/translations/pl.json +++ b/src/Ombi/wwwroot/translations/pl.json @@ -57,19 +57,6 @@ "Welcome": "Witaj {{username}}", "UpdateDetails": "Podaj szczegóły", "Logout": "Wyloguj", - "Language": { - "English": "Angielski", - "French": "Francuski", - "Spanish": "Hiszpański", - "German": "Niemiecki", - "Italian": "Włoski", - "Danish": "Duński", - "Dutch": "Holenderski", - "Norwegian": "Norweski", - "BrazillianPortuguese": "Brazylijski portugalski", - "Polish": "Polski", - "Swedish": "Swedish" - }, "OpenMobileApp": "Otwórz aplikację mobilną", "RecentlyAdded": "Ostatnio dodane" }, diff --git a/src/Ombi/wwwroot/translations/pt.json b/src/Ombi/wwwroot/translations/pt.json index eaf45cac9..199f06593 100644 --- a/src/Ombi/wwwroot/translations/pt.json +++ b/src/Ombi/wwwroot/translations/pt.json @@ -57,19 +57,6 @@ "Welcome": "Bem-vindo, {{username}}", "UpdateDetails": "Detalhes da Atualização", "Logout": "Sair", - "Language": { - "English": "Inglês", - "French": "Francês", - "Spanish": "Espanhol", - "German": "Alemão", - "Italian": "Italiano", - "Danish": "Dinamarquês", - "Dutch": "Holandês", - "Norwegian": "Norueguês", - "BrazillianPortuguese": "Português (Brasil)", - "Polish": "Polonês", - "Swedish": "Swedish" - }, "OpenMobileApp": "Abrir aplicativo do celular", "RecentlyAdded": "Recentemente adicionado" }, diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index 8d2e8374a..6da35d413 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -57,19 +57,6 @@ "Welcome": "Välkommen {{username}}", "UpdateDetails": "Uppdatera information", "Logout": "Logga ut", - "Language": { - "English": "Engelska", - "French": "Franska", - "Spanish": "Spanska", - "German": "Tyska", - "Italian": "Italienska", - "Danish": "Danska", - "Dutch": "Holländska", - "Norwegian": "Norska", - "BrazillianPortuguese": "Brazillian portugisiska", - "Polish": "Polska", - "Swedish": "Swedish" - }, "OpenMobileApp": "Öppna Mobil App", "RecentlyAdded": "Nyligen tillagda" }, From dcab7773be13fcb78b168666d76087ec3840ed44 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 20:51:30 +0100 Subject: [PATCH 36/74] New translations en.json (Danish) --- src/Ombi/wwwroot/translations/da.json | 70 +++++++++++++-------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index 0229b3603..b60fb6c7e 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -12,8 +12,8 @@ "Common": { "ContinueButton": "Fortsæt", "Available": "Tilgængelig", - "PartiallyAvailable": "Partially Available", - "Monitored": "Monitored", + "PartiallyAvailable": "Delvist tilgængelig", + "Monitored": "Overvåget", "NotAvailable": "Ikke tilgængelig", "ProcessingRequest": "Behandler anmodning", "PendingApproval": "Afventer godkendelse", @@ -48,7 +48,7 @@ "Requests": "Anmodninger", "UserManagement": "Brugeradministration", "Issues": "Problemer", - "Vote": "Vote", + "Vote": "Stem", "Donate": "Donér!", "DonateLibraryMaintainer": "Donér til vedligeholder af bibliotek", "DonateTooltip": "Sådan overbeviser jeg min kone om, at jeg skal bruge min fritid på at udvikle Ombi :)", @@ -66,27 +66,27 @@ "Danish": "Dansk", "Dutch": "Hollandsk", "Norwegian": "Norsk", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" + "BrazillianPortuguese": "Brasiliansk portugisisk", + "Polish": "Polsk", + "Swedish": "Svensk" }, "OpenMobileApp": "Åbn mobilapp", - "RecentlyAdded": "Recently Added" + "RecentlyAdded": "Senest tilføjet" }, "Search": { "Title": "Søg", "Paragraph": "Ønsker du at se noget, som er utilgængeligt? intet problem, bare søg efter det nedenfor og anmod om det!", "MoviesTab": "Film", "TvTab": "Tv-serier", - "MusicTab": "Music", + "MusicTab": "Musik", "Suggestions": "Forslag", "NoResults": "Beklager, vi fandt ingen resultater!", - "DigitalDate": "Digital Release: {{date}}", - "TheatricalRelease": "Theatrical Release: {{date}}", + "DigitalDate": "Digital udgivelse: {{date}}", + "TheatricalRelease": "Biografudgivelse: {{date}}", "ViewOnPlex": "Se på Plex", "ViewOnEmby": "Se på Emby", "RequestAdded": "{{title}} er anmodet med succes", - "Similar": "Similar", + "Similar": "Lignende", "Movies": { "PopularMovies": "Populære film", "UpcomingMovies": "Kommende film", @@ -116,15 +116,15 @@ "Paragraph": "Herunder kan du se dine og alle andre anmodninger, samt status for download og godkendelse.", "MoviesTab": "Film", "TvTab": "Tv-serier", - "MusicTab": "Music", + "MusicTab": "Musik", "RequestedBy": "Anmodet af:", "Status": "Status:", "RequestStatus": "Status for anmodning:", "Denied": " Afvist:", - "TheatricalRelease": "Theatrical Release: {{date}}", - "ReleaseDate": "Released: {{date}}", - "TheatricalReleaseSort": "Theatrical Release", - "DigitalRelease": "Digital Release: {{date}}", + "TheatricalRelease": "Biografudgivelse: {{date}}", + "ReleaseDate": "Udgivet: {{date}}", + "TheatricalReleaseSort": "Biografudgivelse", + "DigitalRelease": "Digital udgivelse: {{date}}", "RequestDate": "Dato for anmodning:", "QualityOverride": "Tilsidesæt kvalitet:", "RootFolderOverride": "Tilsidesæt rodmappe:", @@ -140,20 +140,20 @@ "GridStatus": "Status", "ReportIssue": "Rapportér problem", "Filter": "Filter", - "Sort": "Sort", + "Sort": "Sorter", "SeasonNumberHeading": "Sæson: {seasonNumber}", - "SortTitleAsc": "Title ▲", - "SortTitleDesc": "Title ▼", - "SortRequestDateAsc": "Request Date ▲", - "SortRequestDateDesc": "Request Date ▼", + "SortTitleAsc": "Titel ▲", + "SortTitleDesc": "Titel ▼", + "SortRequestDateAsc": "Dato for anmodning ▲", + "SortRequestDateDesc": "Dato for anmodning ▼", "SortStatusAsc": "Status ▲", "SortStatusDesc": "Status ▼", "Remaining": { - "Quota": "{{remaining}}/{{total}} requests remaining", - "NextDays": "Another request will be added in {{time}} days", - "NextHours": "Another request will be added in {{time}} hours", - "NextMinutes": "Another request will be added in {{time}} minutes", - "NextMinute": "Another request will be added in {{time}} minute" + "Quota": "{{remaining}}/{{total}} anmodninger, der er tilbage", + "NextDays": "En anden anmodning vil blive tilføjet i {{time}} Dage", + "NextHours": "En anden anmodning vil blive tilføjet i {{time}} Timer", + "NextMinutes": "En anden anmodning vil blive tilføjet i {{time}} Minutter", + "NextMinute": "En anden anmodning vil blive tilføjet i {{time}} Minut" } }, "Issues": { @@ -180,18 +180,18 @@ "FilterHeaderAvailability": "Tilgængelighed", "FilterHeaderRequestStatus": "Status", "Approved": "Godkendt", - "PendingApproval": "Pending Approval" + "PendingApproval": "Afventer godkendelse" }, "UserManagment": { - "TvRemaining": "TV: {{remaining}}/{{total}} remaining", - "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", - "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", - "TvDue": "TV: {{date}}", - "MovieDue": "Movie: {{date}}", - "MusicDue": "Music: {{date}}" + "TvRemaining": "Tv: {{remaining}}/{{total}} Resterende", + "MovieRemaining": "Film: {{remaining}}/{{total}} Resterende", + "MusicRemaining": "Musik: {{remaining}}/{{total}} Resterende", + "TvDue": "Tv: {{date}}", + "MovieDue": "Film: {{date}}", + "MusicDue": "Musik: {{date}}" }, "Votes": { - "CompletedVotesTab": "Voted", - "VotesTab": "Votes Needed" + "CompletedVotesTab": "Stemt", + "VotesTab": "Nødvendige stemmer" } } \ No newline at end of file From 12d37511eef4c7c30e4d6358cad7ee116c2e67d7 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 20:55:38 +0100 Subject: [PATCH 37/74] Potentially fix the user profiles issue --- .../Controllers/External/RadarrController.cs | 34 ++++++++----------- .../Controllers/External/SonarrController.cs | 1 + 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/Ombi/Controllers/External/RadarrController.cs b/src/Ombi/Controllers/External/RadarrController.cs index 128b1a9e2..882f55283 100644 --- a/src/Ombi/Controllers/External/RadarrController.cs +++ b/src/Ombi/Controllers/External/RadarrController.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Caching.Memory; using Ombi.Api.Radarr; using Ombi.Api.Radarr.Models; using Ombi.Attributes; @@ -13,9 +12,9 @@ using Ombi.Settings.Settings.Models.External; namespace Ombi.Controllers.External { - [Authorize] - [ApiV1] - [Produces("application/json")] + [Authorize] + [ApiV1] + [Produces("application/json")] public class RadarrController : Controller { public RadarrController(IRadarrApi radarr, ISettingsService settings, @@ -24,6 +23,7 @@ namespace Ombi.Controllers.External RadarrApi = radarr; RadarrSettings = settings; Cache = mem; + RadarrSettings.ClearCache(); } private IRadarrApi RadarrApi { get; } @@ -61,15 +61,12 @@ namespace Ombi.Controllers.External [HttpGet("Profiles")] public async Task> GetProfiles() { - return await Cache.GetOrAdd(CacheKeys.RadarrQualityProfiles, async () => + var settings = await RadarrSettings.GetSettingsAsync(); + if (settings.Enabled) { - var settings = await RadarrSettings.GetSettingsAsync(); - if (settings.Enabled) - { - return await RadarrApi.GetProfiles(settings.ApiKey, settings.FullUri); - } - return null; - }, DateTime.Now.AddHours(1)); + return await RadarrApi.GetProfiles(settings.ApiKey, settings.FullUri); + } + return null; } /// @@ -80,15 +77,12 @@ namespace Ombi.Controllers.External [HttpGet("RootFolders")] public async Task> GetRootFolders() { - return await Cache.GetOrAdd(CacheKeys.RadarrRootProfiles, async () => + var settings = await RadarrSettings.GetSettingsAsync(); + if (settings.Enabled) { - var settings = await RadarrSettings.GetSettingsAsync(); - if (settings.Enabled) - { - return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); - } - return null; - }, DateTime.Now.AddHours(1)); + return await RadarrApi.GetRootFolders(settings.ApiKey, settings.FullUri); + } + return null; } } } \ No newline at end of file diff --git a/src/Ombi/Controllers/External/SonarrController.cs b/src/Ombi/Controllers/External/SonarrController.cs index 94d1e3edf..a1e90e96f 100644 --- a/src/Ombi/Controllers/External/SonarrController.cs +++ b/src/Ombi/Controllers/External/SonarrController.cs @@ -20,6 +20,7 @@ namespace Ombi.Controllers.External { SonarrApi = sonarr; SonarrSettings = settings; + SonarrSettings.ClearCache(); } private ISonarrApi SonarrApi { get; } From 8bb3b1888d2b8a6f98f0f6e797c33b18267c50db Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 20:57:03 +0100 Subject: [PATCH 38/74] !wip poweruser --- src/Ombi/Controllers/External/RadarrController.cs | 2 ++ src/Ombi/Controllers/External/SonarrController.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Ombi/Controllers/External/RadarrController.cs b/src/Ombi/Controllers/External/RadarrController.cs index 882f55283..623322704 100644 --- a/src/Ombi/Controllers/External/RadarrController.cs +++ b/src/Ombi/Controllers/External/RadarrController.cs @@ -59,6 +59,7 @@ namespace Ombi.Controllers.External /// /// [HttpGet("Profiles")] + [PowerUser] public async Task> GetProfiles() { var settings = await RadarrSettings.GetSettingsAsync(); @@ -75,6 +76,7 @@ namespace Ombi.Controllers.External /// /// [HttpGet("RootFolders")] + [PowerUser] public async Task> GetRootFolders() { var settings = await RadarrSettings.GetSettingsAsync(); diff --git a/src/Ombi/Controllers/External/SonarrController.cs b/src/Ombi/Controllers/External/SonarrController.cs index a1e90e96f..c3401736d 100644 --- a/src/Ombi/Controllers/External/SonarrController.cs +++ b/src/Ombi/Controllers/External/SonarrController.cs @@ -55,6 +55,7 @@ namespace Ombi.Controllers.External /// /// [HttpGet("Profiles")] + [PowerUser] public async Task> GetProfiles() { var settings = await SonarrSettings.GetSettingsAsync(); @@ -70,6 +71,7 @@ namespace Ombi.Controllers.External /// /// [HttpGet("RootFolders")] + [PowerUser] public async Task> GetRootFolders() { var settings = await SonarrSettings.GetSettingsAsync(); From 7763ecca4f844cffc7bd840754c22570cf6b059c Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:24 +0100 Subject: [PATCH 39/74] Fixed lidarr newsletter bug --- src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs index 9f5e58482..88125b7c6 100644 --- a/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs +++ b/src/Ombi.Schedule/Jobs/Ombi/NewsletterJob.cs @@ -98,7 +98,7 @@ namespace Ombi.Schedule.Jobs.Ombi // Get the Content var plexContent = _plex.GetAll().Include(x => x.Episodes).AsNoTracking(); var embyContent = _emby.GetAll().Include(x => x.Episodes).AsNoTracking(); - var lidarrContent = _lidarrAlbumRepository.GetAll().AsNoTracking(); + var lidarrContent = _lidarrAlbumRepository.GetAll().Where(x => x.FullyAvailable).AsNoTracking(); var addedLog = _recentlyAddedLog.GetAll(); var addedPlexMovieLogIds = addedLog.Where(x => x.Type == RecentlyAddedType.Plex && x.ContentType == ContentType.Parent).Select(x => x.ContentId); From 730bb52e01d0096de5f9c270346d1fe8bd25b33c Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:30 +0100 Subject: [PATCH 40/74] New translations en.json (Danish) --- src/Ombi/wwwroot/translations/da.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/da.json b/src/Ombi/wwwroot/translations/da.json index b60fb6c7e..05214732e 100644 --- a/src/Ombi/wwwroot/translations/da.json +++ b/src/Ombi/wwwroot/translations/da.json @@ -57,19 +57,6 @@ "Welcome": "Velkommen til {{username}}", "UpdateDetails": "Opdater loginoplysninger", "Logout": "Log af", - "Language": { - "English": "Engelsk", - "French": "Fransk", - "Spanish": "Spansk", - "German": "Tysk", - "Italian": "Italiensk", - "Danish": "Dansk", - "Dutch": "Hollandsk", - "Norwegian": "Norsk", - "BrazillianPortuguese": "Brasiliansk portugisisk", - "Polish": "Polsk", - "Swedish": "Svensk" - }, "OpenMobileApp": "Åbn mobilapp", "RecentlyAdded": "Senest tilføjet" }, From 20fa3813a86fa2719e28f7e5dfb0f7e2da152502 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:31 +0100 Subject: [PATCH 41/74] New translations en.json (Dutch) --- src/Ombi/wwwroot/translations/nl.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/nl.json b/src/Ombi/wwwroot/translations/nl.json index 18a1d319d..24ce67966 100644 --- a/src/Ombi/wwwroot/translations/nl.json +++ b/src/Ombi/wwwroot/translations/nl.json @@ -57,19 +57,6 @@ "Welcome": "Welkom {{username}}", "UpdateDetails": "Update gegevens", "Logout": "Logout", - "Language": { - "English": "Engels", - "French": "Frans", - "Spanish": "Spaans", - "German": "Duits", - "Italian": "Italiaans", - "Danish": "Deens", - "Dutch": "Nederlands", - "Norwegian": "Noors", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Open Mobiele App", "RecentlyAdded": "Recently Added" }, From eed92e7ed5524aca926ff70ba0a0476a436a2d12 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:32 +0100 Subject: [PATCH 42/74] New translations en.json (French) --- src/Ombi/wwwroot/translations/fr.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index c20dffdb0..bf6e26a93 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -57,19 +57,6 @@ "Welcome": "Bienvenue {{username}}", "UpdateDetails": "Détails de la mise à jour", "Logout": "Déconnexion", - "Language": { - "English": "Anglais", - "French": "Français", - "Spanish": "Espagnol", - "German": "Allemand", - "Italian": "Italien", - "Danish": "Danois", - "Dutch": "Néerlandais", - "Norwegian": "Norvégien", - "BrazillianPortuguese": "Portuguais brésilien", - "Polish": "Polonais", - "Swedish": "Swedish" - }, "OpenMobileApp": "Ouvrir l'application mobile", "RecentlyAdded": "Ajouts récents" }, From b5df13aaeba24501276fa30e569d978dda377a99 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:34 +0100 Subject: [PATCH 43/74] New translations en.json (German) --- src/Ombi/wwwroot/translations/de.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/de.json b/src/Ombi/wwwroot/translations/de.json index c3646221d..2dccedb1f 100644 --- a/src/Ombi/wwwroot/translations/de.json +++ b/src/Ombi/wwwroot/translations/de.json @@ -57,19 +57,6 @@ "Welcome": "Willkommen {{username}}", "UpdateDetails": "Update-Details", "Logout": "Ausloggen", - "Language": { - "English": "Englisch", - "French": "Französisch", - "Spanish": "Spanisch", - "German": "Deutsch", - "Italian": "Italienisch", - "Danish": "Dänisch", - "Dutch": "Niederländisch", - "Norwegian": "Norwegisch", - "BrazillianPortuguese": "Portugiesisch (Brasilien)", - "Polish": "Polnisch", - "Swedish": "Schwedisch" - }, "OpenMobileApp": "Mobile App", "RecentlyAdded": "Kürzlich hinzugefügt" }, From c89b02a89f7e364661cf7b1dbae8250eb8900b7d Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:35 +0100 Subject: [PATCH 44/74] New translations en.json (Italian) --- src/Ombi/wwwroot/translations/it.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/it.json b/src/Ombi/wwwroot/translations/it.json index 0ade93919..bfa026be0 100644 --- a/src/Ombi/wwwroot/translations/it.json +++ b/src/Ombi/wwwroot/translations/it.json @@ -57,19 +57,6 @@ "Welcome": "Benvenuto {{username}}", "UpdateDetails": "Aggiorna i tuoi dati", "Logout": "Logout", - "Language": { - "English": "Inglese", - "French": "Francese", - "Spanish": "Spagnolo", - "German": "Tedesco", - "Italian": "Italiano", - "Danish": "Danese", - "Dutch": "Olandese", - "Norwegian": "Norvegese", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Apri l'applicazione mobile", "RecentlyAdded": "Recently Added" }, From 2e0b112a17fdf4f1f22d7db93cdcda9a23c93fc6 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:36 +0100 Subject: [PATCH 45/74] New translations en.json (Norwegian) --- src/Ombi/wwwroot/translations/no.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/no.json b/src/Ombi/wwwroot/translations/no.json index 80cd9e31a..6f84943a8 100644 --- a/src/Ombi/wwwroot/translations/no.json +++ b/src/Ombi/wwwroot/translations/no.json @@ -57,19 +57,6 @@ "Welcome": "Velkommen {{username}}", "UpdateDetails": "Oppdater detaljer", "Logout": "Logg av", - "Language": { - "English": "Engelsk", - "French": "Fransk", - "Spanish": "Spansk", - "German": "Tysk", - "Italian": "Italiensk", - "Danish": "Dansk", - "Dutch": "Nederlandsk", - "Norwegian": "Norsk", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Åpne mobilapp", "RecentlyAdded": "Recently Added" }, From bd9e5aa1057c9bd429629406e1f313c6a96fae86 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:37 +0100 Subject: [PATCH 46/74] New translations en.json (Polish) --- src/Ombi/wwwroot/translations/pl.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/pl.json b/src/Ombi/wwwroot/translations/pl.json index 3aba4da41..b53f7f1c1 100644 --- a/src/Ombi/wwwroot/translations/pl.json +++ b/src/Ombi/wwwroot/translations/pl.json @@ -57,19 +57,6 @@ "Welcome": "Witaj {{username}}", "UpdateDetails": "Podaj szczegóły", "Logout": "Wyloguj", - "Language": { - "English": "Angielski", - "French": "Francuski", - "Spanish": "Hiszpański", - "German": "Niemiecki", - "Italian": "Włoski", - "Danish": "Duński", - "Dutch": "Holenderski", - "Norwegian": "Norweski", - "BrazillianPortuguese": "Brazylijski portugalski", - "Polish": "Polski", - "Swedish": "Swedish" - }, "OpenMobileApp": "Otwórz aplikację mobilną", "RecentlyAdded": "Ostatnio dodane" }, From c4399b2915ac4b8e73391333601f679de6615c12 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:39 +0100 Subject: [PATCH 47/74] New translations en.json (Portuguese, Brazilian) --- src/Ombi/wwwroot/translations/pt.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/pt.json b/src/Ombi/wwwroot/translations/pt.json index eaf45cac9..199f06593 100644 --- a/src/Ombi/wwwroot/translations/pt.json +++ b/src/Ombi/wwwroot/translations/pt.json @@ -57,19 +57,6 @@ "Welcome": "Bem-vindo, {{username}}", "UpdateDetails": "Detalhes da Atualização", "Logout": "Sair", - "Language": { - "English": "Inglês", - "French": "Francês", - "Spanish": "Espanhol", - "German": "Alemão", - "Italian": "Italiano", - "Danish": "Dinamarquês", - "Dutch": "Holandês", - "Norwegian": "Norueguês", - "BrazillianPortuguese": "Português (Brasil)", - "Polish": "Polonês", - "Swedish": "Swedish" - }, "OpenMobileApp": "Abrir aplicativo do celular", "RecentlyAdded": "Recentemente adicionado" }, From 5a15a52ffd5859c2cf60c7331e153c0371a975ca Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:40 +0100 Subject: [PATCH 48/74] New translations en.json (Spanish) --- src/Ombi/wwwroot/translations/es.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/es.json b/src/Ombi/wwwroot/translations/es.json index dc7637e79..a9f8c2b5e 100644 --- a/src/Ombi/wwwroot/translations/es.json +++ b/src/Ombi/wwwroot/translations/es.json @@ -57,19 +57,6 @@ "Welcome": "Bienvenido {{username}}", "UpdateDetails": "Detalles de la actualización", "Logout": "Cerrar sesión", - "Language": { - "English": "Inglés", - "French": "Francés", - "Spanish": "Español", - "German": "Alemán", - "Italian": "Italiano", - "Danish": "Danés", - "Dutch": "Holandés", - "Norwegian": "Norwegian", - "BrazillianPortuguese": "Brazillian Portuguese", - "Polish": "Polish", - "Swedish": "Swedish" - }, "OpenMobileApp": "Open Mobile App", "RecentlyAdded": "Recently Added" }, From ebef2950618a9e4cbd191bcbab659f621b276d25 Mon Sep 17 00:00:00 2001 From: Jamie Date: Sun, 7 Oct 2018 21:01:41 +0100 Subject: [PATCH 49/74] New translations en.json (Swedish) --- src/Ombi/wwwroot/translations/sv.json | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Ombi/wwwroot/translations/sv.json b/src/Ombi/wwwroot/translations/sv.json index 8d2e8374a..6da35d413 100644 --- a/src/Ombi/wwwroot/translations/sv.json +++ b/src/Ombi/wwwroot/translations/sv.json @@ -57,19 +57,6 @@ "Welcome": "Välkommen {{username}}", "UpdateDetails": "Uppdatera information", "Logout": "Logga ut", - "Language": { - "English": "Engelska", - "French": "Franska", - "Spanish": "Spanska", - "German": "Tyska", - "Italian": "Italienska", - "Danish": "Danska", - "Dutch": "Holländska", - "Norwegian": "Norska", - "BrazillianPortuguese": "Brazillian portugisiska", - "Polish": "Polska", - "Swedish": "Swedish" - }, "OpenMobileApp": "Öppna Mobil App", "RecentlyAdded": "Nyligen tillagda" }, From 324c02a58d13516d8a05ae55bdbd97021e53fe01 Mon Sep 17 00:00:00 2001 From: Anojh Date: Sun, 7 Oct 2018 15:09:05 -0700 Subject: [PATCH 50/74] users can now remove their own requests --- .../app/requests/movierequests.component.html | 13 ++++++++----- .../app/requests/movierequests.component.ts | 7 +++++++ .../app/requests/music/musicrequests.component.html | 13 ++++++++----- .../app/requests/music/musicrequests.component.ts | 7 +++++++ .../app/requests/tvrequest-children.component.html | 4 +++- .../app/requests/tvrequest-children.component.ts | 8 ++++++++ .../app/requests/tvrequests.component.html | 2 +- .../ClientApp/app/requests/tvrequests.component.ts | 2 ++ 8 files changed, 44 insertions(+), 12 deletions(-) diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.html b/src/Ombi/ClientApp/app/requests/movierequests.component.html index 05d0ca14f..a1dd5cadc 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.html +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.html @@ -166,11 +166,7 @@
-
- -
+
+
-
- -
+
+
+
+ +
diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts index 19e941e9d..ff451e5b5 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts @@ -10,6 +10,7 @@ import { NotificationService, RequestService } from "../services"; export class TvRequestChildrenComponent { @Input() public childRequests: IChildRequests[]; @Input() public isAdmin: boolean; + @Input() public currentUser: string; @Output() public requestDeleted = new EventEmitter(); @@ -116,4 +117,11 @@ export class TvRequestChildrenComponent { this.childRequests.splice(index, 1); } } + + public isRequestUser(request: IChildRequests) { + if (request.requestedUser.userName === this.currentUser) { + return true; + } + return false; + } } diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.html b/src/Ombi/ClientApp/app/requests/tvrequests.component.html index 801998c71..7c5e13479 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.html @@ -99,7 +99,7 @@
- +

diff --git a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts index 144de0206..924fccd1f 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequests.component.ts @@ -20,6 +20,7 @@ export class TvRequestsComponent implements OnInit { public searchChanged = new Subject(); public searchText: string; public isAdmin: boolean; + public currentUser: string; public showChildDialogue = false; // This is for the child modal popup public selectedSeason: ITvRequests; public defaultPoster: string; @@ -48,6 +49,7 @@ export class TvRequestsComponent implements OnInit { private readonly platformLocation: PlatformLocation) { this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); + this.currentUser = this.auth.claims().name; if (this.isAdmin) { this.sonarrService.getQualityProfilesWithoutSettings() .subscribe(x => this.sonarrProfiles = x); From 5e0f04a2f67a177fd25fb73dac6cb5794ea2e9da Mon Sep 17 00:00:00 2001 From: Anojh Date: Sun, 7 Oct 2018 15:28:50 -0700 Subject: [PATCH 51/74] !wip - fix lint errors --- .../app/requests/movierequests.component.ts | 13 +++++++------ .../app/requests/music/musicrequests.component.ts | 13 +++++++------ .../app/requests/tvrequest-children.component.ts | 13 +++++++------ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/Ombi/ClientApp/app/requests/movierequests.component.ts b/src/Ombi/ClientApp/app/requests/movierequests.component.ts index 950743dc4..19fc989f2 100644 --- a/src/Ombi/ClientApp/app/requests/movierequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/movierequests.component.ts @@ -218,6 +218,13 @@ export class MovieRequestsComponent implements OnInit { }); } + public isRequestUser(request: IMovieRequests) { + if (request.requestedUser.userName === this.auth.claims().name) { + return true; + } + return false; + } + private filterActiveStyle(el: any) { el = el.toElement || el.relatedTarget || el.target || el.srcElement; @@ -366,10 +373,4 @@ export class MovieRequestsComponent implements OnInit { ("url(" + "https://image.tmdb.org/t/p/w1280" + req.background + ")"); } - public isRequestUser(request: IMovieRequests) { - if (request.requestedUser.userName === this.auth.claims().name) { - return true; - } - return false; - } } diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts index 360b68c27..e70d44a38 100644 --- a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.ts @@ -198,6 +198,13 @@ export class MusicRequestsComponent implements OnInit { this.loadInit(); } + public isRequestUser(request: IAlbumRequest) { + if (request.requestedUser.userName === this.auth.claims().name) { + return true; + } + return false; + } + // public subscribe(request: IAlbumRequest) { // request.subscribed = true; // this.requestService.subscribeToMovie(request.id) @@ -349,10 +356,4 @@ export class MusicRequestsComponent implements OnInit { ("url(" + req.cover + ")"); } - public isRequestUser(request: IAlbumRequest) { - if (request.requestedUser.userName === this.auth.claims().name) { - return true; - } - return false; - } } diff --git a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts index ff451e5b5..bc792a55b 100644 --- a/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts +++ b/src/Ombi/ClientApp/app/requests/tvrequest-children.component.ts @@ -111,6 +111,13 @@ export class TvRequestChildrenComponent { }); } + public isRequestUser(request: IChildRequests) { + if (request.requestedUser.userName === this.currentUser) { + return true; + } + return false; + } + private removeRequestFromUi(key: IChildRequests) { const index = this.childRequests.indexOf(key, 0); if (index > -1) { @@ -118,10 +125,4 @@ export class TvRequestChildrenComponent { } } - public isRequestUser(request: IChildRequests) { - if (request.requestedUser.userName === this.currentUser) { - return true; - } - return false; - } } From bfdc9b84f0867bc63d333b65a94b6ee7f06a3f70 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 8 Oct 2018 10:21:25 +0100 Subject: [PATCH 52/74] New translations en.json (French) --- src/Ombi/wwwroot/translations/fr.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index bf6e26a93..f7b37855e 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -12,8 +12,8 @@ "Common": { "ContinueButton": "Continuer", "Available": "Disponible", - "PartiallyAvailable": "Partially Available", - "Monitored": "Monitored", + "PartiallyAvailable": "Partiellement disponible", + "Monitored": "Suivi", "NotAvailable": "Non disponible", "ProcessingRequest": "En cours de traitement", "PendingApproval": "En attente d'approbation", @@ -65,7 +65,7 @@ "Paragraph": "Vous voulez regarder quelque chose qui n'est pas disponible actuellement ? Pas de problème, recherchez-le ci-dessous et demandez-le !", "MoviesTab": "Films", "TvTab": "TV", - "MusicTab": "Music", + "MusicTab": "Musique", "Suggestions": "Suggestions", "NoResults": "Désolé, nous n'avons trouvé aucun résultat !", "DigitalDate": "Sortie numérique: {{date}}", @@ -103,7 +103,7 @@ "Paragraph": "Vous pouvez voir ci-dessous vos demandes et celles des autres, ainsi que leur statut de téléchargement et d'approbation.", "MoviesTab": "Films", "TvTab": "Émissions", - "MusicTab": "Music", + "MusicTab": "Musique", "RequestedBy": "Demandé par :", "Status": "Statut :", "RequestStatus": "Statut de la demande :", From 891b12be091f426340ef1c80477cde92d2cb0a72 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 8 Oct 2018 10:30:44 +0100 Subject: [PATCH 53/74] New translations en.json (French) --- src/Ombi/wwwroot/translations/fr.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index f7b37855e..6da397625 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -136,11 +136,11 @@ "SortStatusAsc": "Statut ▲", "SortStatusDesc": "Statut ▼", "Remaining": { - "Quota": "{{remaining}}/{{total}} requests remaining", - "NextDays": "Another request will be added in {{time}} days", - "NextHours": "Another request will be added in {{time}} hours", - "NextMinutes": "Another request will be added in {{time}} minutes", - "NextMinute": "Another request will be added in {{time}} minute" + "Quota": "{{remaining}}/{{total}} demande(s) restante(s)", + "NextDays": "Une autre demande sera ajoutée dans {{time}} jours", + "NextHours": "Une autre demande sera ajoutée dans {{time}} heures", + "NextMinutes": "Une autre demande sera ajoutée dans {{time}} minutes", + "NextMinute": "Une autre demande sera ajoutée dans {{time}} minute" } }, "Issues": { From 5e30c50add04a14a14b993d90010b764014cbc6f Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 8 Oct 2018 10:41:01 +0100 Subject: [PATCH 54/74] New translations en.json (French) --- src/Ombi/wwwroot/translations/fr.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index 6da397625..5f1c59ece 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -1,6 +1,6 @@ { "Login": { - "SignInButton": "Connexion", + "SignInButton": "Se connecter", "UsernamePlaceholder": "Nom d’utilisateur", "PasswordPlaceholder": "Mot de passe", "RememberMe": "Se souvenir de moi", @@ -109,7 +109,7 @@ "RequestStatus": "Statut de la demande :", "Denied": " Refusé :", "TheatricalRelease": "Sortie en salle: {{date}}", - "ReleaseDate": "Released: {{date}}", + "ReleaseDate": "Sortie : {{date}}", "TheatricalReleaseSort": "Sortie en salle", "DigitalRelease": "Sortie numérique: {{date}}", "RequestDate": "Date de la demande :", @@ -170,15 +170,15 @@ "PendingApproval": "En attente de validation" }, "UserManagment": { - "TvRemaining": "TV: {{remaining}}/{{total}} remaining", - "MovieRemaining": "Movies: {{remaining}}/{{total}} remaining", - "MusicRemaining": "Music: {{remaining}}/{{total}} remaining", - "TvDue": "TV: {{date}}", - "MovieDue": "Movie: {{date}}", - "MusicDue": "Music: {{date}}" + "TvRemaining": "TV : {{remaining}}/{{total}} restant(s)", + "MovieRemaining": "Films : {{remaining}}/{{total}} restant(s)", + "MusicRemaining": "Musique : {{remaining}}/{{total}} restant(s)", + "TvDue": "TV : {{date}}", + "MovieDue": "Film : {{date}}", + "MusicDue": "Musique : {{date}}" }, "Votes": { - "CompletedVotesTab": "Voted", - "VotesTab": "Votes Needed" + "CompletedVotesTab": "Voté", + "VotesTab": "Votes nécessaires" } } \ No newline at end of file From 0b7d09b410b06e7df94311b674581c17ce4bcda5 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 8 Oct 2018 10:50:47 +0100 Subject: [PATCH 55/74] New translations en.json (French) --- src/Ombi/wwwroot/translations/fr.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ombi/wwwroot/translations/fr.json b/src/Ombi/wwwroot/translations/fr.json index 5f1c59ece..013dc9865 100644 --- a/src/Ombi/wwwroot/translations/fr.json +++ b/src/Ombi/wwwroot/translations/fr.json @@ -37,10 +37,10 @@ "OnlineParagraph": "Le serveur média est actuellement en ligne", "PartiallyOnlineHeading": "Partiellement en ligne", "PartiallyOnlineParagraph": "Le serveur média est partiellement en ligne.", - "MultipleServersUnavailable": "Il y a {{serversUnavailable}} serveurs hors ligne sur {{totalServers}}.", - "SingleServerUnavailable": "Il y a {{serversUnavailable}} serveur hors ligne sur {{totalServers}}.", - "OfflineHeading": "Actuellement hors ligne", - "OfflineParagraph": "Le serveur média est actuellement hors ligne.", + "MultipleServersUnavailable": "Il y a {{serversUnavailable}} serveurs hors-ligne sur {{totalServers}}.", + "SingleServerUnavailable": "Il y a {{serversUnavailable}} serveur hors-ligne sur {{totalServers}}.", + "OfflineHeading": "Actuellement hors-ligne", + "OfflineParagraph": "Le serveur média est actuellement hors-ligne.", "CheckPageForUpdates": "Consultez cette page pour voir les mises à jour du site." }, "NavigationBar": { From d9e25522e16f2be5ba8a1f7b95ff330f59f7b35a Mon Sep 17 00:00:00 2001 From: TidusJar Date: Mon, 8 Oct 2018 14:00:52 +0100 Subject: [PATCH 56/74] Fixed the issue where we could not delete users #2558 --- .../ClientApp/app/login/login.component.html | 2 +- .../usermanagement-user.component.html | 7 +- .../usermanagement.component.html | 2 +- .../createadmin/createadmin.component.html | 2 +- .../mediaserver/mediaserver.component.html | 2 +- .../app/wizard/welcome/welcome.component.html | 2 +- src/Ombi/cypress.json | 4 + src/Ombi/cypress/fixtures/example.json | 5 + src/Ombi/cypress/integration/login.spec.js | 38 ++ .../integration/usermanagement.spec.js | 48 ++ src/Ombi/cypress/integration/wizard.spec.js | 21 + src/Ombi/cypress/plugins/index.js | 17 + src/Ombi/cypress/support/commands.js | 25 + src/Ombi/cypress/support/index.js | 20 + src/Ombi/cypress/tsconfig.json | 12 + src/Ombi/package.json | 6 +- src/Ombi/yarn.lock | 597 +++++++++++++++++- 17 files changed, 776 insertions(+), 34 deletions(-) create mode 100644 src/Ombi/cypress.json create mode 100644 src/Ombi/cypress/fixtures/example.json create mode 100644 src/Ombi/cypress/integration/login.spec.js create mode 100644 src/Ombi/cypress/integration/usermanagement.spec.js create mode 100644 src/Ombi/cypress/integration/wizard.spec.js create mode 100644 src/Ombi/cypress/plugins/index.js create mode 100644 src/Ombi/cypress/support/commands.js create mode 100644 src/Ombi/cypress/support/index.js create mode 100644 src/Ombi/cypress/tsconfig.json diff --git a/src/Ombi/ClientApp/app/login/login.component.html b/src/Ombi/ClientApp/app/login/login.component.html index bb6afc1dd..5d5032aef 100644 --- a/src/Ombi/ClientApp/app/login/login.component.html +++ b/src/Ombi/ClientApp/app/login/login.component.html @@ -33,7 +33,7 @@ include the remember me checkbox
- + diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html index 6f3ae63ee..1dfba95dd 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html @@ -1,5 +1,8 @@ 

Create User

User: {{user.userName}}

+ + +
@@ -65,7 +68,7 @@
- +
@@ -208,7 +211,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html index 8bbc5b45a..26faac5cb 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html @@ -2,7 +2,7 @@ - +
diff --git a/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.html b/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.html index bdb992a6e..2a7facaa5 100644 --- a/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.html +++ b/src/Ombi/ClientApp/app/wizard/createadmin/createadmin.component.html @@ -19,7 +19,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/wizard/mediaserver/mediaserver.component.html b/src/Ombi/ClientApp/app/wizard/mediaserver/mediaserver.component.html index 0973a2ad6..e6768328e 100644 --- a/src/Ombi/ClientApp/app/wizard/mediaserver/mediaserver.component.html +++ b/src/Ombi/ClientApp/app/wizard/mediaserver/mediaserver.component.html @@ -16,7 +16,7 @@
-
diff --git a/src/Ombi/ClientApp/app/wizard/welcome/welcome.component.html b/src/Ombi/ClientApp/app/wizard/welcome/welcome.component.html index 98cca17ef..8207583b6 100644 --- a/src/Ombi/ClientApp/app/wizard/welcome/welcome.component.html +++ b/src/Ombi/ClientApp/app/wizard/welcome/welcome.component.html @@ -9,7 +9,7 @@ we are just going to run though the initial Ombi setup!
- Next + Next
diff --git a/src/Ombi/cypress.json b/src/Ombi/cypress.json new file mode 100644 index 000000000..9310964db --- /dev/null +++ b/src/Ombi/cypress.json @@ -0,0 +1,4 @@ +{ + "projectId": "gms1wj", + "baseUrl": "http://localhost:3577" +} diff --git a/src/Ombi/cypress/fixtures/example.json b/src/Ombi/cypress/fixtures/example.json new file mode 100644 index 000000000..da18d9352 --- /dev/null +++ b/src/Ombi/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/src/Ombi/cypress/integration/login.spec.js b/src/Ombi/cypress/integration/login.spec.js new file mode 100644 index 000000000..92f7eface --- /dev/null +++ b/src/Ombi/cypress/integration/login.spec.js @@ -0,0 +1,38 @@ +/// + +describe('Login Page', function () { + it('Invalid Password', function () { + cy.visit('/'); + cy.contains('Sign in'); + + cy.get('#inputEmail').type('automation'); + cy.get('#inputPassword').type('incorrectpw'); + + cy.get('[data-test=signinbtn]').click(); + cy.get('.ui-growl-title').should('be.visible'); + cy.get('.ui-growl-title').next().contains('Incorrect username') + }); + + it('Invalid Username', function () { + cy.visit('/'); + cy.contains('Sign in'); + + cy.get('#inputEmail').type('bad username'); + cy.get('#inputPassword').type('incorrectpw'); + + cy.get('[data-test=signinbtn]').click(); + cy.get('.ui-growl-title').should('be.visible'); + cy.get('.ui-growl-title').next().contains('Incorrect username') + }); + + it('Correct Login', function () { + cy.visit('/'); + cy.contains('Sign in'); + + cy.get('#inputEmail').type('automation'); + cy.get('#inputPassword').type('password'); + + cy.get('[data-test=signinbtn]').click(); + cy.url().should('include', '/search') + }); +}) \ No newline at end of file diff --git a/src/Ombi/cypress/integration/usermanagement.spec.js b/src/Ombi/cypress/integration/usermanagement.spec.js new file mode 100644 index 000000000..8c15f4593 --- /dev/null +++ b/src/Ombi/cypress/integration/usermanagement.spec.js @@ -0,0 +1,48 @@ +/// + +describe('User Management Page', function () { + beforeEach(function () { + cy.request({ + method: 'POST', + url: 'http://localhost:3577/api/v1/token', + body: { + username: 'automation', + password: 'password', + } + }) + .then((resp) => { + window.localStorage.setItem('id_token', resp.body.access_token) + }); + + cy.visit('/usermanagement'); + }); + + it('Loads users table', function () { + cy.contains("User Management"); + cy.contains("Add User To Ombi"); + }); + + it.only('Creates basic user', function(){ + cy.get('[data-test=adduserbtn').click(); + cy.url().should('include','/user'); + + // Setup the form + cy.get('#username').type("user1"); + cy.get('#alias').type("alias1"); + cy.get('#emailAddress').type("user1@emailaddress.com"); + cy.get('#password').type("password"); + cy.get('#confirmPass').type("password"); + + // setup the roles + cy.contains('Roles').click() + cy.get('#labelRequestTv').click(); + cy.get('#labelRequestMovie').click(); + + // submit user + cy.get('[data-test=createuserbtn]').click(); + + cy.get('.ui-growl-title').should('be.visible'); + cy.get('.ui-growl-title').next().contains('has been created successfully') + }); + +}) \ No newline at end of file diff --git a/src/Ombi/cypress/integration/wizard.spec.js b/src/Ombi/cypress/integration/wizard.spec.js new file mode 100644 index 000000000..d1487c8ca --- /dev/null +++ b/src/Ombi/cypress/integration/wizard.spec.js @@ -0,0 +1,21 @@ +describe('Wizard Setup Tests', function() { + it('Setup Wizard User', function() { + cy.visit('http://localhost:3577/'); + cy.url().should('include', '/Wizard') + + cy.get('[data-test=nextbtn]').click(); + + // Media server page + cy.contains('Please choose your media server'); + cy.get('[data-test=skipbtn]').click(); + + // Create user + cy.contains('Create the Admin account'); + cy.get('#adminUsername').type('automation'); + cy.get('#adminPassword').type('password'); + + // Submit user + cy.get('[data-test=createuserbtn]').click(); + cy.contains('Sign in'); + }) + }) \ No newline at end of file diff --git a/src/Ombi/cypress/plugins/index.js b/src/Ombi/cypress/plugins/index.js new file mode 100644 index 000000000..fd170fba6 --- /dev/null +++ b/src/Ombi/cypress/plugins/index.js @@ -0,0 +1,17 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +module.exports = (on, config) => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +} diff --git a/src/Ombi/cypress/support/commands.js b/src/Ombi/cypress/support/commands.js new file mode 100644 index 000000000..c1f5a772e --- /dev/null +++ b/src/Ombi/cypress/support/commands.js @@ -0,0 +1,25 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/src/Ombi/cypress/support/index.js b/src/Ombi/cypress/support/index.js new file mode 100644 index 000000000..d68db96df --- /dev/null +++ b/src/Ombi/cypress/support/index.js @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') diff --git a/src/Ombi/cypress/tsconfig.json b/src/Ombi/cypress/tsconfig.json new file mode 100644 index 000000000..756a92b35 --- /dev/null +++ b/src/Ombi/cypress/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "allowJs": true, + "baseUrl": "../node_modules", + "types": [ + "cypress" + ] + }, + "include": [ + "**/*.*" + ] + } \ No newline at end of file diff --git a/src/Ombi/package.json b/src/Ombi/package.json index 5fac0c31a..4c5302d6c 100644 --- a/src/Ombi/package.json +++ b/src/Ombi/package.json @@ -7,7 +7,8 @@ "lint": "tslint -p .", "publish": "gulp publish", "restore": "dotnet restore && yarn install", - "clean": "gulp clean" + "clean": "gulp clean", + "cypress": "cypress open" }, "dependencies": { "@angular/animations": "^6.0.7", @@ -90,5 +91,8 @@ }, "resolutions": { "@types/tapable": "1.0.0" + }, + "devDependencies": { + "cypress": "^3.1.0" } } diff --git a/src/Ombi/yarn.lock b/src/Ombi/yarn.lock index 68381bf9d..1debb9f2d 100644 --- a/src/Ombi/yarn.lock +++ b/src/Ombi/yarn.lock @@ -100,6 +100,22 @@ dependencies: url "^0.11.0" +"@cypress/listr-verbose-renderer@0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@cypress/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#a77492f4b11dcc7c446a34b3e28721afd33c642a" + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +"@cypress/xvfb@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.3.tgz#6319afdcdcff7d1505daeeaa84484d0596189860" + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + "@ng-bootstrap/ng-bootstrap@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-2.2.0.tgz#acd514e878a1412f39d50eff691095ecc0882bf3" @@ -128,20 +144,84 @@ dependencies: tslib "^1.9.0" +"@types/blob-util@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@types/blob-util/-/blob-util-1.3.3.tgz#adba644ae34f88e1dd9a5864c66ad651caaf628a" + +"@types/bluebird@3.5.18": + version "3.5.18" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.18.tgz#6a60435d4663e290f3709898a4f75014f279c4d6" + +"@types/chai-jquery@1.1.35": + version "1.1.35" + resolved "https://registry.yarnpkg.com/@types/chai-jquery/-/chai-jquery-1.1.35.tgz#9a8f0a39ec0851b2768a8f8c764158c2a2568d04" + dependencies: + "@types/chai" "*" + "@types/jquery" "*" + +"@types/chai@*": + version "4.1.6" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.1.6.tgz#1eb26c040e3a84205b1008ad55c800e5e8a94e34" + +"@types/chai@4.0.8": + version "4.0.8" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.0.8.tgz#d27600e9ba2f371e08695d90a0fe0408d89c7be7" + "@types/core-js@^2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.0.tgz#35cc282488de6f10af1d92902899a3b8ca3fbc47" +"@types/jquery@*": + version "3.3.12" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.3.12.tgz#e83229bcbf09dc668f9d4c1894864199521ff258" + dependencies: + "@types/sizzle" "*" + +"@types/jquery@3.2.16": + version "3.2.16" + resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.2.16.tgz#04419c404a3194350e7d3f339a90e72c88db3111" + +"@types/lodash@4.14.87": + version "4.14.87" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.87.tgz#55f92183b048c2c64402afe472f8333f4e319a6b" + "@types/mini-css-extract-plugin@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@types/mini-css-extract-plugin/-/mini-css-extract-plugin-0.2.0.tgz#afb037dbbd76f6c13803927c2751d194188b5d47" dependencies: "@types/webpack" "*" +"@types/minimatch@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + +"@types/mocha@2.2.44": + version "2.2.44" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e" + "@types/node@*", "@types/node@^10.5.1": version "10.5.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.1.tgz#d578446f4abff5c0b49ade9b4e5274f6badaadfc" +"@types/sinon-chai@2.7.29": + version "2.7.29" + resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-2.7.29.tgz#4db01497e2dd1908b2bd30d1782f456353f5f723" + dependencies: + "@types/chai" "*" + "@types/sinon" "*" + +"@types/sinon@*": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-5.0.4.tgz#a765b390b373cf01a3b19b0c97f9eb4bb2a168b1" + +"@types/sinon@4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.0.0.tgz#9a93ffa4ee1329e85166278a5ed99f81dc4c8362" + +"@types/sizzle@*": + version "2.3.2" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" + "@types/tapable@*", "@types/tapable@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.0.tgz#b76254453021be05681f6213416766bac9afb99c" @@ -356,6 +436,15 @@ ajv@^4.9.1: co "^4.6.0" json-stable-stringify "^1.0.1" +ajv@^5.1.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + ajv@^6.1.0: version "6.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" @@ -400,7 +489,7 @@ ansi-cyan@^0.1.1: dependencies: ansi-wrap "0.1.0" -ansi-escapes@^1.1.0: +ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" @@ -612,6 +701,12 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" +async@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.4.0.tgz#4990200f18ea5b837c2cc4f8c031a6985c385611" + dependencies: + lodash "^4.14.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -648,10 +743,18 @@ aws-sign2@~0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + aws4@^1.2.1: version "1.7.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" +aws4@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -668,7 +771,7 @@ babel-polyfill@6.23.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-runtime@^6.22.0: +babel-runtime@^6.18.0, babel-runtime@^6.22.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -731,6 +834,10 @@ block-stream@*: dependencies: inherits "~2.0.0" +bluebird@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" + bluebird@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -861,6 +968,10 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: caniuse-db "^1.0.30000639" electron-to-chromium "^1.2.7" +buffer-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + buffer-from@^1.0.0, buffer-from@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" @@ -921,6 +1032,12 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +cachedir@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-1.3.0.tgz#5e01928bf2d95b5edd94b0942188246740e0dbc4" + dependencies: + os-homedir "^1.0.1" + caller-id@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/caller-id/-/caller-id-0.1.0.tgz#59bdac0893d12c3871408279231f97458364f07b" @@ -984,7 +1101,7 @@ chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: +chalk@2.4.1, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -1000,6 +1117,10 @@ chardet@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + check-types@^7.3.0: version "7.4.0" resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" @@ -1048,6 +1169,10 @@ chrome-trace-event@^1.0.0: dependencies: tslib "^1.9.0" +ci-info@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" + cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" @@ -1076,12 +1201,29 @@ clean-css@4.1.x: dependencies: source-map "0.5.x" +cli-cursor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987" + dependencies: + restore-cursor "^1.0.1" + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" dependencies: restore-cursor "^2.0.0" +cli-spinners@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" + +cli-truncate@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" + dependencies: + slice-ansi "0.0.4" + string-width "^1.0.1" + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" @@ -1188,12 +1330,16 @@ colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" -combined-stream@^1.0.5, combined-stream@~1.0.5: +combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" dependencies: delayed-stream "~1.0.0" +commander@2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + commander@2.15.x, commander@~2.15.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -1206,6 +1352,12 @@ commander@~2.13.0: version "2.13.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" +common-tags@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" + dependencies: + babel-runtime "^6.18.0" + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1218,6 +1370,14 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" +concat-stream@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" @@ -1345,7 +1505,7 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.5: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" dependencies: @@ -1475,6 +1635,51 @@ cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" +cypress@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-3.1.0.tgz#b718ba64289b887c7ab7a7f09245d871a4a409ba" + dependencies: + "@cypress/listr-verbose-renderer" "0.4.1" + "@cypress/xvfb" "1.2.3" + "@types/blob-util" "1.3.3" + "@types/bluebird" "3.5.18" + "@types/chai" "4.0.8" + "@types/chai-jquery" "1.1.35" + "@types/jquery" "3.2.16" + "@types/lodash" "4.14.87" + "@types/minimatch" "3.0.3" + "@types/mocha" "2.2.44" + "@types/sinon" "4.0.0" + "@types/sinon-chai" "2.7.29" + bluebird "3.5.0" + cachedir "1.3.0" + chalk "2.4.1" + check-more-types "2.24.0" + commander "2.11.0" + common-tags "1.4.0" + debug "3.1.0" + execa "0.10.0" + executable "4.1.1" + extract-zip "1.6.6" + fs-extra "4.0.1" + getos "3.1.0" + glob "7.1.2" + is-ci "1.0.10" + is-installed-globally "0.1.0" + lazy-ass "1.6.0" + listr "0.12.0" + lodash "4.17.10" + log-symbols "2.2.0" + minimist "1.2.0" + progress "1.1.8" + ramda "0.24.1" + request "2.87.0" + request-progress "0.3.1" + supports-color "5.1.0" + tmp "0.0.31" + url "0.11.0" + yauzl "2.8.0" + d@1: version "1.0.0" resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" @@ -1487,6 +1692,10 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +date-fns@^1.27.2: + version "1.29.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" + date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" @@ -1501,7 +1710,7 @@ debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^3.1.0: +debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" dependencies: @@ -1669,6 +1878,10 @@ electron-to-chromium@^1.2.7: version "1.3.50" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.50.tgz#7438b76f92b41b919f3fbdd350fbd0757dacddf7" +elegant-spinner@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -1821,6 +2034,18 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +execa@0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -1833,6 +2058,16 @@ execa@^0.7.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +executable@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + dependencies: + pify "^2.2.0" + +exit-hook@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -1925,6 +2160,10 @@ extend@^3.0.0, extend@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" +extend@~3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + external-editor@^2.0.1: version "2.2.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" @@ -1960,6 +2199,15 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@1.6.6: + version "1.6.6" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.6.tgz#1290ede8d20d0872b429fd3f351ca128ec5ef85c" + dependencies: + concat-stream "1.6.0" + debug "2.6.9" + mkdirp "0.5.0" + yauzl "2.4.1" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -1992,6 +2240,19 @@ fastparse@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" +fd-slicer@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" + dependencies: + pend "~1.2.0" + +figures@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e" + dependencies: + escape-string-regexp "^1.0.5" + object-assign "^4.1.0" + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -2159,6 +2420,14 @@ form-data@~2.1.1: combined-stream "^1.0.5" mime-types "^2.1.12" +form-data@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" + forwarded@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" @@ -2180,6 +2449,14 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" +fs-extra@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.1.tgz#7fc0c6c8957f983f57f306a24e5b9ddd8d0dd880" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" @@ -2270,6 +2547,12 @@ get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" +getos@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.1.0.tgz#db3aa4df15a3295557ce5e81aa9e3e5cdfaa6567" + dependencies: + async "2.4.0" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2319,6 +2602,17 @@ glob2base@^0.0.12: dependencies: find-index "^0.1.1" +glob@7.1.2, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^4.3.1: version "4.5.3" resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" @@ -2338,17 +2632,6 @@ glob@^6.0.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@~3.1.21: version "3.1.21" resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" @@ -2357,6 +2640,12 @@ glob@~3.1.21: inherits "1" minimatch "~0.2.11" +global-dirs@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445" + dependencies: + ini "^1.3.4" + global-modules-path@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb" @@ -2428,7 +2717,7 @@ graceful-fs@^3.0.0: dependencies: natives "^1.1.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.2: +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -2507,6 +2796,10 @@ har-schema@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + har-validator@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" @@ -2523,6 +2816,13 @@ har-validator@~4.2.1: ajv "^4.9.1" har-schema "^1.0.5" +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -2533,6 +2833,10 @@ has-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -2689,6 +2993,14 @@ http-signature@~1.1.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -2752,6 +3064,10 @@ indent-string@^2.1.0: dependencies: repeating "^2.0.0" +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -2874,6 +3190,12 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-ci@1.0.10: + version "1.0.10" + resolved "http://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" + dependencies: + ci-info "^1.0.0" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -2964,6 +3286,13 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-installed-globally@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" + dependencies: + global-dirs "^0.1.0" + is-path-inside "^1.0.0" + is-my-ip-valid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" @@ -3160,6 +3489,12 @@ json5@^0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" +jsonfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" + optionalDependencies: + graceful-fs "^4.1.6" + jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" @@ -3201,6 +3536,10 @@ kind-of@^6.0.0, kind-of@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -3224,6 +3563,53 @@ liftoff@^2.1.0: rechoir "^0.6.2" resolve "^1.1.7" +listr-silent-renderer@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e" + +listr-update-renderer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.2.0.tgz#ca80e1779b4e70266807e8eed1ad6abe398550f9" + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + elegant-spinner "^1.0.1" + figures "^1.7.0" + indent-string "^3.0.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + strip-ansi "^3.0.1" + +listr-verbose-renderer@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" + dependencies: + chalk "^1.1.3" + cli-cursor "^1.0.2" + date-fns "^1.27.2" + figures "^1.7.0" + +listr@0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/listr/-/listr-0.12.0.tgz#6bce2c0f5603fa49580ea17cd6a00cc0e5fa451a" + dependencies: + chalk "^1.1.3" + cli-truncate "^0.2.1" + figures "^1.7.0" + indent-string "^2.1.0" + is-promise "^2.1.0" + is-stream "^1.1.0" + listr-silent-renderer "^1.1.1" + listr-update-renderer "^0.2.0" + listr-verbose-renderer "^0.4.0" + log-symbols "^1.0.2" + log-update "^1.0.2" + ora "^0.2.3" + p-map "^1.1.1" + rxjs "^5.0.0-beta.11" + stream-to-observable "^0.1.0" + strip-ansi "^3.0.1" + load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -3348,6 +3734,10 @@ lodash.mergewith@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" +lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" @@ -3394,20 +3784,37 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.0.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10: +lodash@4.17.10, lodash@^4.0.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10: version "4.17.10" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" +lodash@^4.14.0: + version "4.17.11" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + lodash@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/lodash/-/lodash-1.0.2.tgz#8f57560c83b59fc270bd3d561b690043430e2551" -log-symbols@^2.1.0: +log-symbols@2.2.0, log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" dependencies: chalk "^2.0.1" +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + dependencies: + chalk "^1.0.0" + +log-update@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" + dependencies: + ansi-escapes "^1.0.0" + cli-cursor "^1.0.2" + loglevelnext@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.5.tgz#36fc4f5996d6640f539ff203ba819641680d75a2" @@ -3584,12 +3991,22 @@ mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" +mime-db@~1.36.0: + version "1.36.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" + mime-types@^2.1.12, mime-types@~2.1.18, mime-types@~2.1.7: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: mime-db "~1.33.0" +mime-types@~2.1.17: + version "2.1.20" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" + dependencies: + mime-db "~1.36.0" + mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -3687,6 +4104,12 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" +mkdirp@0.5.0: + version "0.5.0" + resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz#1d73076a6df986cd9344e15e71fcc05a4c9abf12" + dependencies: + minimist "0.0.8" + "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -3981,7 +4404,7 @@ number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" -oauth-sign@~0.8.1: +oauth-sign@~0.8.1, oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -4067,6 +4490,10 @@ once@~1.3.0: dependencies: wrappy "1" +onetime@^1.0.0: + version "1.1.0" + resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -4095,6 +4522,15 @@ opn@4.0.2: object-assign "^4.0.1" pinkie-promise "^2.0.0" +ora@^0.2.3: + version "0.2.3" + resolved "http://registry.npmjs.org/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" + dependencies: + chalk "^1.1.1" + cli-cursor "^1.0.2" + cli-spinners "^0.1.2" + object-assign "^4.0.1" + orchestrator@^0.3.0: version "0.3.8" resolved "https://registry.yarnpkg.com/orchestrator/-/orchestrator-0.3.8.tgz#14e7e9e2764f7315fbac184e506c7aa6df94ad7e" @@ -4111,7 +4547,7 @@ os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" -os-homedir@^1.0.0: +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -4129,7 +4565,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -4303,11 +4739,19 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + performance-now@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" -pify@^2.0.0: +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^2.0.0, pify@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4618,6 +5062,10 @@ process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" +progress@1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -4690,6 +5138,10 @@ qs@~6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" +qs@~6.5.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" @@ -4705,6 +5157,10 @@ querystring@0.2.0, querystring@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" +ramda@0.24.1: + version "0.24.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857" + randomatic@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923" @@ -4915,6 +5371,37 @@ replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" +request-progress@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-0.3.1.tgz#0721c105d8a96ac6b2ce8b2c89ae2d5ecfcf6b3a" + dependencies: + throttleit "~0.0.2" + +request@2.87.0: + version "2.87.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + "request@>=2.9.0 <2.82.0": version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" @@ -5006,6 +5493,13 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2: dependencies: path-parse "^1.0.5" +restore-cursor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" + dependencies: + exit-hook "^1.0.0" + onetime "^1.0.0" + restore-cursor@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" @@ -5054,6 +5548,12 @@ rx@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" +rxjs@^5.0.0-beta.11: + version "5.5.12" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" + dependencies: + symbol-observable "1.0.1" + rxjs@^6.0.0, rxjs@^6.1.0, rxjs@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.1.tgz#246cebec189a6cbc143a3ef9f62d6f4c91813ca1" @@ -5237,6 +5737,10 @@ slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -5439,6 +5943,10 @@ stream-shift@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" +stream-to-observable@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.1.0.tgz#45bf1d9f2d7dc09bed81f1c307c430e68b84cffe" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -5518,6 +6026,12 @@ style-loader@^0.21.0: loader-utils "^1.1.0" schema-utils "^0.4.5" +supports-color@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.1.0.tgz#058a021d1b619f7ddf3980d712ea3590ce7de3d5" + dependencies: + has-flag "^2.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -5546,6 +6060,10 @@ svgo@^0.7.0: sax "~1.2.1" whet.extend "~0.9.9" +symbol-observable@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" + tapable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" @@ -5574,6 +6092,10 @@ text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" +throttleit@~0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" + through2@^0.6.1: version "0.6.5" resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" @@ -5608,6 +6130,12 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tmp@0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + dependencies: + os-tmpdir "~1.0.1" + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -5646,7 +6174,7 @@ to-string-loader@^1.1.5: dependencies: loader-utils "^0.2.16" -tough-cookie@~2.3.0: +tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: @@ -5822,6 +6350,10 @@ unique-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-1.0.0.tgz#d59a4a75427447d9aa6c91e70263f8d26a4b104b" +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -5869,7 +6401,7 @@ url-loader@^1.0.1: mime "^2.0.3" schema-utils "^0.4.3" -url@^0.11.0: +url@0.11.0, url@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" dependencies: @@ -6214,6 +6746,19 @@ yargs@^7.0.0: y18n "^3.2.1" yargs-parser "^5.0.0" +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + dependencies: + fd-slicer "~1.0.1" + +yauzl@2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.8.0.tgz#79450aff22b2a9c5a41ef54e02db907ccfbf9ee2" + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.0.1" + yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" From 8764ec3a78e6a452690528730edcf14817c61258 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Mon, 8 Oct 2018 16:11:01 +0100 Subject: [PATCH 57/74] More UI tests !wip --- .../usermanagement-user.component.html | 2 +- .../usermanagement.component.html | 2 +- src/Ombi/cypress/integration/login.spec.js | 6 +- .../integration/usermanagement.spec.js | 114 +++++++++++++++--- src/Ombi/cypress/support/commands.js | 34 ++++++ 5 files changed, 133 insertions(+), 25 deletions(-) diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html index 1dfba95dd..c25102ba2 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html @@ -214,7 +214,7 @@
- + diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html index 26faac5cb..b8a89c2b3 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement.component.html @@ -129,7 +129,7 @@ Emby User - Details/Edit + Details/Edit diff --git a/src/Ombi/cypress/integration/login.spec.js b/src/Ombi/cypress/integration/login.spec.js index 92f7eface..af40f21e7 100644 --- a/src/Ombi/cypress/integration/login.spec.js +++ b/src/Ombi/cypress/integration/login.spec.js @@ -9,8 +9,7 @@ describe('Login Page', function () { cy.get('#inputPassword').type('incorrectpw'); cy.get('[data-test=signinbtn]').click(); - cy.get('.ui-growl-title').should('be.visible'); - cy.get('.ui-growl-title').next().contains('Incorrect username') + cy.verifyNotification('Incorrect username'); }); it('Invalid Username', function () { @@ -21,8 +20,7 @@ describe('Login Page', function () { cy.get('#inputPassword').type('incorrectpw'); cy.get('[data-test=signinbtn]').click(); - cy.get('.ui-growl-title').should('be.visible'); - cy.get('.ui-growl-title').next().contains('Incorrect username') + cy.verifyNotification('Incorrect username'); }); it('Correct Login', function () { diff --git a/src/Ombi/cypress/integration/usermanagement.spec.js b/src/Ombi/cypress/integration/usermanagement.spec.js index 8c15f4593..866542129 100644 --- a/src/Ombi/cypress/integration/usermanagement.spec.js +++ b/src/Ombi/cypress/integration/usermanagement.spec.js @@ -2,18 +2,12 @@ describe('User Management Page', function () { beforeEach(function () { - cy.request({ - method: 'POST', - url: 'http://localhost:3577/api/v1/token', - body: { - username: 'automation', - password: 'password', - } - }) - .then((resp) => { - window.localStorage.setItem('id_token', resp.body.access_token) - }); - + cy.login('automation', 'password'); + cy.createUser('userToDelete', 'password', [{ + value: "requestmovie", + Enabled: "true", + }]); + cy.visit('/usermanagement'); }); @@ -22,9 +16,9 @@ describe('User Management Page', function () { cy.contains("Add User To Ombi"); }); - it.only('Creates basic user', function(){ + it('Creates basic user', function () { cy.get('[data-test=adduserbtn').click(); - cy.url().should('include','/user'); + cy.url().should('include', '/user'); // Setup the form cy.get('#username').type("user1"); @@ -40,9 +34,91 @@ describe('User Management Page', function () { // submit user cy.get('[data-test=createuserbtn]').click(); - - cy.get('.ui-growl-title').should('be.visible'); - cy.get('.ui-growl-title').next().contains('has been created successfully') + + cy.verifyNotification('has been created successfully'); + + // Also check if the user is in the table + cy.contains('alias1'); }); - -}) \ No newline at end of file + + it('Tries to create user without roles', function () { + cy.get('[data-test=adduserbtn').click(); + cy.url().should('include', '/user'); + + // Setup the form + cy.get('#username').type("user1"); + cy.get('#alias').type("alias1"); + cy.get('#emailAddress').type("user1@emailaddress.com"); + cy.get('#password').type("password"); + cy.get('#confirmPass').type("password"); + + // submit user + cy.get('[data-test=createuserbtn]').click(); + + cy.verifyNotification('Please assign a role'); + + }); + + it('Tries to create user when passwords do not match', function () { + cy.get('[data-test=adduserbtn').click(); + cy.url().should('include', '/user'); + + // Setup the form + cy.get('#username').type("user1"); + cy.get('#alias').type("alias1"); + cy.get('#emailAddress').type("user1@emailaddress.com"); + cy.get('#password').type("password"); + cy.get('#confirmPass').type("pass22word"); + + // submit user + cy.get('[data-test=createuserbtn]').click(); + + cy.verifyNotification('Passwords do not match'); + }); + + it('Delete a user', function () { + cy.get('#edituserToDelete').click(); + cy.contains('User: userToDelete'); + cy.get('[data-test=deletebtn]').click(); + cy.contains('Are you sure that you want to delete this user?'); + cy.contains('Yes').click(); + cy.verifyNotification('was deleted'); + }) + + + it.only('Creates user with request limits', function () { + cy.get('[data-test=adduserbtn').click(); + cy.url().should('include', '/user'); + + // Setup the form + cy.get('#username').type("user2"); + cy.get('#alias').type("alias2"); + cy.get('#emailAddress').type("user2@emailaddress.com"); + cy.get('#password').type("password"); + cy.get('#confirmPass').type("password"); + + // setup the roles + cy.contains('Roles').click() + cy.get('#labelRequestMovie').click(); + + cy.contains('Request Limits').click(); + cy.get('#movieRequestLimit').clear().type(2); + cy.get('#musicRequestLimit').clear().type(3); + cy.get('#episodeRequestLimit').clear().type(4); + + // submit user + cy.get('[data-test=createuserbtn]').click(); + + cy.verifyNotification('has been updated successfully'); + + // Verify that the limits are set + cy.get('#edituser2').click(); + cy.contains('Request Limits').click(); + cy.get('#movieRequestLimit').should('eq', 2); + cy.get('#musicRequestLimit').should('eq', 3); + cy.get('#tvRequestLimit').should('eq', 4); + + }); + + +}); \ No newline at end of file diff --git a/src/Ombi/cypress/support/commands.js b/src/Ombi/cypress/support/commands.js index c1f5a772e..cc2110b3e 100644 --- a/src/Ombi/cypress/support/commands.js +++ b/src/Ombi/cypress/support/commands.js @@ -23,3 +23,37 @@ // // -- This is will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) + +Cypress.Commands.add('login', (username, password) => { + cy.request({ + method: 'POST', + url: '/api/v1/token', + body: { + username: username, + password: password, + } + }) + .then((resp) => { + window.localStorage.setItem('id_token', resp.body.access_token) + }); +}); + +Cypress.Commands.add('createUser', (username, password, claims) => { + cy.request({ + method: 'POST', + url: '/api/v1/identity', + body: { + UserName: username, + Password: password, + Claims: claims, + }, + headers: { + 'Authorization': 'Bearer ' + window.localStorage.getItem('id_token'), + } + }) +}) + +Cypress.Commands.add('verifyNotification', (text) => { + cy.get('.ui-growl-title').should('be.visible'); + cy.get('.ui-growl-title').next().contains(text) +}) \ No newline at end of file From 148e39489629f1ac25ccfc58c0a5add977243397 Mon Sep 17 00:00:00 2001 From: Jamie Date: Mon, 8 Oct 2018 22:55:57 +0100 Subject: [PATCH 58/74] Added more UI test !wip --- src/Ombi/ClientApp/app/app.component.html | 4 +- .../updatedetails.component.html | 2 +- .../usermanagement-user.component.html | 6 +- .../integration/usermanagement.spec.js | 116 ++++++++++++++---- src/Ombi/cypress/support/commands.js | 2 +- src/Ombi/cypress/tsconfig.json | 12 -- src/Ombi/tsconfig.json | 4 + 7 files changed, 106 insertions(+), 40 deletions(-) delete mode 100644 src/Ombi/cypress/tsconfig.json diff --git a/src/Ombi/ClientApp/app/app.component.html b/src/Ombi/ClientApp/app/app.component.html index d695ac61c..9f6d00a7e 100644 --- a/src/Ombi/ClientApp/app/app.component.html +++ b/src/Ombi/ClientApp/app/app.component.html @@ -94,13 +94,13 @@
- +
diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html index c25102ba2..3f44a808b 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html @@ -80,7 +80,7 @@
- +
@@ -132,7 +132,7 @@
-
@@ -213,7 +213,7 @@
- + diff --git a/src/Ombi/cypress/integration/usermanagement.spec.js b/src/Ombi/cypress/integration/usermanagement.spec.js index 866542129..0dcd53a6c 100644 --- a/src/Ombi/cypress/integration/usermanagement.spec.js +++ b/src/Ombi/cypress/integration/usermanagement.spec.js @@ -8,6 +8,11 @@ describe('User Management Page', function () { Enabled: "true", }]); + cy.createUser('userToEdit', 'password', [{ + value: "requestmovie", + Enabled: "true", + }]); + cy.visit('/usermanagement'); }); @@ -86,20 +91,8 @@ describe('User Management Page', function () { }) - it.only('Creates user with request limits', function () { - cy.get('[data-test=adduserbtn').click(); - cy.url().should('include', '/user'); - - // Setup the form - cy.get('#username').type("user2"); - cy.get('#alias').type("alias2"); - cy.get('#emailAddress').type("user2@emailaddress.com"); - cy.get('#password').type("password"); - cy.get('#confirmPass').type("password"); - - // setup the roles - cy.contains('Roles').click() - cy.get('#labelRequestMovie').click(); + it('Add request limits to a user', function () { + cy.get('#edituserToEdit').click(); cy.contains('Request Limits').click(); cy.get('#movieRequestLimit').clear().type(2); @@ -107,18 +100,99 @@ describe('User Management Page', function () { cy.get('#episodeRequestLimit').clear().type(4); // submit user - cy.get('[data-test=createuserbtn]').click(); + cy.get('[data-test=updatebtn]').click(); - cy.verifyNotification('has been updated successfully'); + cy.verifyNotification('successfully'); + + // Verify that the limits are set + cy.get('#edituserToEdit').click(); + cy.contains('Request Limits').click(); + cy.get('#movieRequestLimit').should('have.attr', 'ng-reflect-model', '2') + cy.get('#musicRequestLimit').should('have.attr', 'ng-reflect-model', '3') + cy.get('#episodeRequestLimit').should('have.attr', 'ng-reflect-model', '4') + + }); + + it('Add notification preferences to user', function () { + + cy.get('#edituserToEdit').click(); + + cy.contains('Notification Preferences').click(); + cy.get('[data-test=Discord]').clear().type("Discord"); + cy.get('[data-test=Pushbullet]').clear().type("Pushbullet"); + cy.get('[data-test=Pushover]').clear().type("Pushover"); + cy.get('[data-test=Telegram]').clear().type("Telegram"); + cy.get('[data-test=Slack]').clear().type("Slack"); + cy.get('[data-test=Mattermost]').clear().type("Mattermost"); + + // submit user + cy.get('[data-test=updatebtn]').click(); + + cy.verifyNotification('successfully'); + + // Verify that the limits are set + cy.get('#edituserToEdit').click(); + cy.contains('Notification Preferences').click(); + cy.get('[data-test=Discord]').should('have.attr', 'ng-reflect-model', "Discord"); + cy.get('[data-test=Pushbullet]').should('have.attr', 'ng-reflect-model', "Pushbullet"); + cy.get('[data-test=Pushover]').should('have.attr', 'ng-reflect-model', "Pushover"); + cy.get('[data-test=Telegram]').should('have.attr', 'ng-reflect-model', "Telegram"); + cy.get('[data-test=Slack]').should('have.attr', 'ng-reflect-model', "Slack"); + cy.get('[data-test=Mattermost]').should('have.attr', 'ng-reflect-model', "Mattermost"); + + }); + + it('Modify roles', function () { + + cy.get('#edituserToEdit').click(); + + cy.contains('Roles').click(); + cy.get('#labelRequestMovie').click(); + cy.get('#labelRequestTv').click(); + + // submit user + cy.get('[data-test=updatebtn]').click(); + + cy.verifyNotification('successfully'); // Verify that the limits are set - cy.get('#edituser2').click(); - cy.contains('Request Limits').click(); - cy.get('#movieRequestLimit').should('eq', 2); - cy.get('#musicRequestLimit').should('eq', 3); - cy.get('#tvRequestLimit').should('eq', 4); + cy.get('#edituserToEdit').click(); + cy.contains('Roles').click(); + cy.get('#createRequestMovie').should('have.attr', 'ng-reflect-model', 'true'); + cy.get('#createRequestTv').should('have.attr', 'ng-reflect-model', 'true'); + cy.get('#createDisabled').should('have.attr', 'ng-reflect-model', 'false'); }); + it('Update local users info', function () { + + cy.get('#userDropdown').click(); + cy.get('#updateUserDetails').click(); + + cy.url().should('include','/updatedetails'); + + cy.get('#emailAddress').clear().type("user11@emailaddress.com"); + cy.get('#currentPassword').type("password"); + + cy.get('[data-test=submitbtn]').click(); + + cy.verifyNotification('All of your details have now been updated'); + }); + + it('Update local users info with bad password', function () { + + cy.get('#userDropdown').click(); + cy.get('#updateUserDetails').click(); + + cy.url().should('include','/updatedetails'); + + cy.get('#emailAddress').clear().type("user11@emailaddress.com"); + cy.get('#currentPassword').type("password32113123123"); + + cy.get('[data-test=submitbtn]').click(); + + cy.verifyNotification('Your password is incorrect'); + }); + }); \ No newline at end of file diff --git a/src/Ombi/cypress/support/commands.js b/src/Ombi/cypress/support/commands.js index cc2110b3e..b44a1e2b5 100644 --- a/src/Ombi/cypress/support/commands.js +++ b/src/Ombi/cypress/support/commands.js @@ -56,4 +56,4 @@ Cypress.Commands.add('createUser', (username, password, claims) => { Cypress.Commands.add('verifyNotification', (text) => { cy.get('.ui-growl-title').should('be.visible'); cy.get('.ui-growl-title').next().contains(text) -}) \ No newline at end of file +}); diff --git a/src/Ombi/cypress/tsconfig.json b/src/Ombi/cypress/tsconfig.json deleted file mode 100644 index 756a92b35..000000000 --- a/src/Ombi/cypress/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "allowJs": true, - "baseUrl": "../node_modules", - "types": [ - "cypress" - ] - }, - "include": [ - "**/*.*" - ] - } \ No newline at end of file diff --git a/src/Ombi/tsconfig.json b/src/Ombi/tsconfig.json index 1256bed93..bd114dcdb 100644 --- a/src/Ombi/tsconfig.json +++ b/src/Ombi/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { "target": "es5", "lib": [ + "es2015", "es2017", "dom" ], @@ -27,6 +28,9 @@ } ] }, + "types": [ + "cypress" + ], "include": [ "ClientApp/**/*", "typings/**/*", From 0b2b3d2f30d24d6e8a012ba352d95701c9306c2b Mon Sep 17 00:00:00 2001 From: Anojh Date: Tue, 9 Oct 2018 00:26:31 -0700 Subject: [PATCH 59/74] New role to enable users to remove their own requests --- src/Ombi.Helpers/OmbiRoles.cs | 1 + src/Ombi.Store/Context/OmbiContext.cs | 10 ++++++++++ src/Ombi/Attributes/UserAttribute.cs | 14 ++++++++++++++ .../requests/music/musicrequests.component.html | 2 +- src/Ombi/Controllers/IdentityController.cs | 1 + src/Ombi/Controllers/MusicRequestController.cs | 2 +- src/Ombi/Controllers/RequestController.cs | 6 +++--- 7 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 src/Ombi/Attributes/UserAttribute.cs diff --git a/src/Ombi.Helpers/OmbiRoles.cs b/src/Ombi.Helpers/OmbiRoles.cs index 1d584d57f..e0cfc5398 100644 --- a/src/Ombi.Helpers/OmbiRoles.cs +++ b/src/Ombi.Helpers/OmbiRoles.cs @@ -14,5 +14,6 @@ public const string RequestMusic = nameof(RequestMusic); public const string Disabled = nameof(Disabled); public const string ReceivesNewsletter = nameof(ReceivesNewsletter); + public const string ManageOwnRequests = nameof(ManageOwnRequests); } } \ No newline at end of file diff --git a/src/Ombi.Store/Context/OmbiContext.cs b/src/Ombi.Store/Context/OmbiContext.cs index 66631dfc6..54682e24e 100644 --- a/src/Ombi.Store/Context/OmbiContext.cs +++ b/src/Ombi.Store/Context/OmbiContext.cs @@ -145,6 +145,16 @@ namespace Ombi.Store.Context SaveChanges(); } + var manageOwnRequestsRole = Roles.Where(x => x.Name == OmbiRoles.ManageOwnRequests); + if (!manageOwnRequestsRole.Any()) + { + Roles.Add(new IdentityRole(OmbiRoles.ManageOwnRequests) + { + NormalizedName = OmbiRoles.ManageOwnRequests.ToUpper() + }); + SaveChanges(); + } + // Make sure we have the API User var apiUserExists = Users.Any(x => x.UserName.Equals("Api", StringComparison.CurrentCultureIgnoreCase)); if (!apiUserExists) diff --git a/src/Ombi/Attributes/UserAttribute.cs b/src/Ombi/Attributes/UserAttribute.cs new file mode 100644 index 000000000..3ab4cef49 --- /dev/null +++ b/src/Ombi/Attributes/UserAttribute.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Authorization; +using Ombi.Helpers; + + +namespace Ombi.Attributes +{ + public class UserAttribute : AuthorizeAttribute + { + public UserAttribute() + { + Roles = "ManageOwnRequests"; + } + } +} diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html index 28734f5a3..c4c903da2 100644 --- a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html @@ -186,7 +186,7 @@
-
+ diff --git a/src/Ombi/Controllers/IdentityController.cs b/src/Ombi/Controllers/IdentityController.cs index d138e973e..61b3f06d8 100644 --- a/src/Ombi/Controllers/IdentityController.cs +++ b/src/Ombi/Controllers/IdentityController.cs @@ -240,6 +240,7 @@ namespace Ombi.Controllers await CreateRole(OmbiRoles.RequestTv); await CreateRole(OmbiRoles.Disabled); await CreateRole(OmbiRoles.ReceivesNewsletter); + await CreateRole(OmbiRoles.ManageOwnRequests); } private async Task CreateRole(string role) diff --git a/src/Ombi/Controllers/MusicRequestController.cs b/src/Ombi/Controllers/MusicRequestController.cs index 0d763cd86..3ab99dc0f 100644 --- a/src/Ombi/Controllers/MusicRequestController.cs +++ b/src/Ombi/Controllers/MusicRequestController.cs @@ -88,7 +88,7 @@ namespace Ombi.Controllers /// The request identifier. /// [HttpDelete("{requestId:int}")] - [PowerUser] + [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] public async Task DeleteRequest(int requestId) { await _engine.RemoveAlbumRequest(requestId); diff --git a/src/Ombi/Controllers/RequestController.cs b/src/Ombi/Controllers/RequestController.cs index 25270f9dd..a1a61c9f9 100644 --- a/src/Ombi/Controllers/RequestController.cs +++ b/src/Ombi/Controllers/RequestController.cs @@ -95,7 +95,7 @@ namespace Ombi.Controllers /// The request identifier. /// [HttpDelete("movie/{requestId:int}")] - [PowerUser] + [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] public async Task DeleteRequest(int requestId) { await MovieRequestEngine.RemoveMovieRequest(requestId); @@ -269,7 +269,7 @@ namespace Ombi.Controllers /// The request identifier. /// [HttpDelete("tv/{requestId:int}")] - [PowerUser] + [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] public async Task DeleteTvRequest(int requestId) { await TvRequestEngine.RemoveTvRequest(requestId); @@ -380,7 +380,7 @@ namespace Ombi.Controllers /// /// The model. /// - [PowerUser] + [Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")] [HttpDelete("tv/child/{requestId:int}")] public async Task DeleteChildRequest(int requestId) { From 7e49366999b42989ba04476d2b5a10a89536008f Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 19:30:58 +0100 Subject: [PATCH 60/74] !wip --- src/Ombi/cypress/integration/usermanagement.spec.js | 2 +- src/Ombi/package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ombi/cypress/integration/usermanagement.spec.js b/src/Ombi/cypress/integration/usermanagement.spec.js index 866542129..3e6b83e28 100644 --- a/src/Ombi/cypress/integration/usermanagement.spec.js +++ b/src/Ombi/cypress/integration/usermanagement.spec.js @@ -109,7 +109,7 @@ describe('User Management Page', function () { // submit user cy.get('[data-test=createuserbtn]').click(); - cy.verifyNotification('has been updated successfully'); + cy.verifyNotification('has been created successfully'); // Verify that the limits are set cy.get('#edituser2').click(); diff --git a/src/Ombi/package.json b/src/Ombi/package.json index 4c5302d6c..ceb7fc621 100644 --- a/src/Ombi/package.json +++ b/src/Ombi/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "vendor": "gulp vendor", + "main": "gulp main", "lint": "tslint -p .", "publish": "gulp publish", "restore": "dotnet restore && yarn install", From 65233cb3b7ded3fcdbc2436eaa8fe312407c4de9 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:01:46 +0100 Subject: [PATCH 61/74] Fixed the issue where user preferences was not being inported into some notifications --- src/Ombi.Notifications/BaseNotification.cs | 20 ++++++++++++++++++- .../NotificationMessageCurlys.cs | 3 ++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Notifications/BaseNotification.cs b/src/Ombi.Notifications/BaseNotification.cs index d351c8283..53d6d5d9d 100644 --- a/src/Ombi.Notifications/BaseNotification.cs +++ b/src/Ombi.Notifications/BaseNotification.cs @@ -170,6 +170,24 @@ namespace Ombi.Notifications.Interfaces { return new NotificationMessageContent { Disabled = true }; } + + if (model.UserId.IsNullOrEmpty()) + { + if (model.RequestType == RequestType.Movie) + { + model.UserId = MovieRequest.RequestedUserId; + } + + if (model.RequestType == RequestType.Album) + { + model.UserId = AlbumRequest.RequestedUserId; + } + + if (model.RequestType == RequestType.TvShow) + { + model.UserId = TvRequest.RequestedUserId; + } + } var parsed = Parse(model, template, agent); return parsed; @@ -184,7 +202,7 @@ namespace Ombi.Notifications.Interfaces protected UserNotificationPreferences GetUserPreference(string userId, NotificationAgent agent) { return UserNotificationPreferences.GetAll() - .FirstOrDefault(x => x.Enabled && x.Agent == agent && x.UserId == userId); + .FirstOrDefault(x => x.Agent == agent && x.UserId == userId); } private NotificationMessageContent Parse(NotificationOptions model, NotificationTemplates template, NotificationAgent agent) diff --git a/src/Ombi.Notifications/NotificationMessageCurlys.cs b/src/Ombi.Notifications/NotificationMessageCurlys.cs index 1b655b84d..b178f0545 100644 --- a/src/Ombi.Notifications/NotificationMessageCurlys.cs +++ b/src/Ombi.Notifications/NotificationMessageCurlys.cs @@ -19,7 +19,7 @@ namespace Ombi.Notifications LoadIssues(opts); if (pref != null) { - UserPreference = pref.Enabled ? pref.Value : string.Empty; + UserPreference = pref.Value; } string title; @@ -268,6 +268,7 @@ namespace Ombi.Notifications {nameof(IssueUser),IssueUser}, {nameof(UserName),UserName}, {nameof(Alias),Alias}, + {nameof(UserPreference),UserPreference}, }; } } \ No newline at end of file From 2fc0ab5fa8c632603943b45b449f3f610be59b82 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:05:43 +0100 Subject: [PATCH 62/74] updated typescript !wip --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 862993a21..2d18fdcbb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,7 @@ configuration: Release os: Visual Studio 2017 environment: nodejs_version: "9.8.0" + typescript_version: "3.0.1" install: # Get the latest stable version of Node.js or io.js From 48705f2ea09d744d001ffab07a1c9eef78557a5d Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:20:06 +0100 Subject: [PATCH 63/74] another change !wip --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 2d18fdcbb..d8265e6f6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,9 @@ environment: install: # Get the latest stable version of Node.js or io.js - ps: Install-Product node $env:nodejs_version + + - cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\2.2;%path% + - cmd: tsc -v build_script: - ps: ./build.ps1 --settings_skipverification=true From 873c0d7da750fa45c8bbb9304ad6ec7105f1807b Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:24:42 +0100 Subject: [PATCH 64/74] tsc to ver 3.0.1 !wip --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d8265e6f6..d22d50f67 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ install: # Get the latest stable version of Node.js or io.js - ps: Install-Product node $env:nodejs_version - - cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\2.2;%path% + - cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\3.0.1;%path% - cmd: tsc -v build_script: - ps: ./build.ps1 --settings_skipverification=true From 9f933eb611f41d5b0d34ce92181d783b85547a37 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:26:52 +0100 Subject: [PATCH 65/74] 3.0.1 didn't exist :S !wip --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d22d50f67..99ec1e669 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ install: # Get the latest stable version of Node.js or io.js - ps: Install-Product node $env:nodejs_version - - cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\3.0.1;%path% + - cmd: set path=%programfiles(x86)%\\Microsoft SDKs\TypeScript\3.0;%path% - cmd: tsc -v build_script: - ps: ./build.ps1 --settings_skipverification=true From fed8ac9213b31bbebfc22d1a23e0f95706edd1de Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:48:46 +0100 Subject: [PATCH 66/74] updated ts !wip --- src/Ombi/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/package.json b/src/Ombi/package.json index ceb7fc621..df35ffbf8 100644 --- a/src/Ombi/package.json +++ b/src/Ombi/package.json @@ -78,7 +78,7 @@ "ts-node": "^7.0.0", "tslint": "^5.10.0", "tslint-language-service": "^0.9.9", - "typescript": "2.7.2", + "typescript": "3.1.1", "uglify-es": "^3.3.9", "uglifyjs-webpack-plugin": "^1.2.7", "url-loader": "^1.0.1", From f0e15670958b54d3b99fb72f88d37862c3626719 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Tue, 9 Oct 2018 20:54:24 +0100 Subject: [PATCH 67/74] put pkg back !wip --- src/Ombi/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ombi/package.json b/src/Ombi/package.json index df35ffbf8..ceb7fc621 100644 --- a/src/Ombi/package.json +++ b/src/Ombi/package.json @@ -78,7 +78,7 @@ "ts-node": "^7.0.0", "tslint": "^5.10.0", "tslint-language-service": "^0.9.9", - "typescript": "3.1.1", + "typescript": "2.7.2", "uglify-es": "^3.3.9", "uglifyjs-webpack-plugin": "^1.2.7", "url-loader": "^1.0.1", From 909180d0991dba0e5064f227c926ae3954ca7f93 Mon Sep 17 00:00:00 2001 From: Jamie Date: Tue, 9 Oct 2018 21:50:21 +0100 Subject: [PATCH 68/74] Update LidarrAvailabilityChecker.cs --- src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs index d5ba14a6d..5708dad6c 100644 --- a/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Lidarr/LidarrAvailabilityChecker.cs @@ -43,7 +43,7 @@ namespace Ombi.Schedule.Jobs.Lidarr var cachedAlbum = await _cachedAlbums.FirstOrDefaultAsync(x => x.ForeignAlbumId.Equals(request.ForeignAlbumId)); if (cachedAlbum != null) { - if (cachedAlbum.Monitored && cachedAlbum.FullyAvailable) + if (cachedAlbum.FullyAvailable) { request.Available = true; request.MarkedAsAvailable = DateTime.Now; @@ -70,4 +70,4 @@ namespace Ombi.Schedule.Jobs.Lidarr } } } -} \ No newline at end of file +} From f2b78384e7288e068c1b8c2b3b4a0b816d9a9659 Mon Sep 17 00:00:00 2001 From: Victor Usoltsev Date: Thu, 11 Oct 2018 00:59:38 +1300 Subject: [PATCH 69/74] Fixes untickable mass email checkboxes in Safari. --- .../ClientApp/app/settings/massemail/massemail.component.html | 2 +- .../ClientApp/app/settings/massemail/massemail.component.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html index e66d83a18..5c51c68ca 100644 --- a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html +++ b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.html @@ -39,7 +39,7 @@
- +
diff --git a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts index a80f7adfe..91693103f 100644 --- a/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts +++ b/src/Ombi/ClientApp/app/settings/massemail/massemail.component.ts @@ -38,10 +38,6 @@ export class MassEmailComponent implements OnInit { this.users.forEach(u => u.selected = !u.selected); } - public selectSingleUser(user: IMassEmailUserModel) { - user.selected = !user.selected; - } - public send() { if(!this.subject) { this.missingSubject = true; From fed7708a0d0e24bdef99d9d4915532fb4ba03d34 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 10 Oct 2018 15:36:37 +0100 Subject: [PATCH 70/74] fixed the build. Thanks Matt! --- src/Ombi/package.json | 3 ++- src/Ombi/tsconfig.json | 3 --- src/Ombi/yarn.lock | 10 +++------- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Ombi/package.json b/src/Ombi/package.json index ceb7fc621..fa476f66b 100644 --- a/src/Ombi/package.json +++ b/src/Ombi/package.json @@ -91,7 +91,8 @@ "zone.js": "^0.8.26" }, "resolutions": { - "@types/tapable": "1.0.0" + "@types/tapable": "1.0.0", + "cypress/**/@types/sinon": "4.3.3" }, "devDependencies": { "cypress": "^3.1.0" diff --git a/src/Ombi/tsconfig.json b/src/Ombi/tsconfig.json index bd114dcdb..1e41f53b9 100644 --- a/src/Ombi/tsconfig.json +++ b/src/Ombi/tsconfig.json @@ -28,9 +28,6 @@ } ] }, - "types": [ - "cypress" - ], "include": [ "ClientApp/**/*", "typings/**/*", diff --git a/src/Ombi/yarn.lock b/src/Ombi/yarn.lock index 1debb9f2d..77c0b08f6 100644 --- a/src/Ombi/yarn.lock +++ b/src/Ombi/yarn.lock @@ -210,13 +210,9 @@ "@types/chai" "*" "@types/sinon" "*" -"@types/sinon@*": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-5.0.4.tgz#a765b390b373cf01a3b19b0c97f9eb4bb2a168b1" - -"@types/sinon@4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.0.0.tgz#9a93ffa4ee1329e85166278a5ed99f81dc4c8362" +"@types/sinon@*", "@types/sinon@4.0.0", "@types/sinon@4.3.3": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-4.3.3.tgz#97cbbfddc3282b5fd40c7abf80b99db426fd4237" "@types/sizzle@*": version "2.3.2" From 870a07de9d181abb3663eee25e13815e14382717 Mon Sep 17 00:00:00 2001 From: TidusJar Date: Wed, 10 Oct 2018 15:47:50 +0100 Subject: [PATCH 71/74] revert, no idea how this happened --- src/Ombi.DependencyInjection/IocExtensions.cs | 1 + src/Ombi.Schedule.Tests/IssuesPurgeTests.cs | 85 +++++++++++++++++++ src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs | 9 ++ src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs | 63 ++++++++++++++ .../Settings/Models/IssueSettings.cs | 3 + .../Settings/Models/JobSettings.cs | 1 + .../Settings/Models/JobSettingsHelper.cs | 5 ++ src/Ombi.Store/Entities/Requests/Issues.cs | 1 + .../ClientApp/app/interfaces/ISettings.ts | 3 + .../music/musicrequests.component.html | 2 +- .../app/settings/issues/issues.component.ts | 6 +- .../app/settings/jobs/jobs.component.html | 9 +- .../app/settings/jobs/jobs.component.ts | 1 + .../usermanagement-user.component.html | 12 +-- src/Ombi/Controllers/SettingsController.cs | 3 +- 15 files changed, 193 insertions(+), 11 deletions(-) create mode 100644 src/Ombi.Schedule.Tests/IssuesPurgeTests.cs create mode 100644 src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs create mode 100644 src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index eac6812bc..30ccb6973 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -192,6 +192,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } } } diff --git a/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs new file mode 100644 index 000000000..932022cd8 --- /dev/null +++ b/src/Ombi.Schedule.Tests/IssuesPurgeTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Ombi.Core.Settings; +using Ombi.Schedule.Jobs.Ombi; +using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; +using System.Threading.Tasks; + +namespace Ombi.Schedule.Tests +{ + [TestFixture] + public class IssuesPurgeTests + { + + [SetUp] + public void Setup() + { + Repo = new Mock>(); + Settings = new Mock>(); + Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings()); + Job = new IssuesPurge(Repo.Object, Settings.Object); + } + + public Mock> Repo { get; set; } + public Mock> Settings { get; set; } + public IssuesPurge Job { get; set; } + + [Test] + public async Task DoesNotRun_WhenDisabled() + { + await Job.Start(); + Repo.Verify(x => x.GetAll(),Times.Never); + } + + [Test] + public async Task Deletes_Correct_Issue() + { + var issues = new List() + { + new Issues + { + Status = IssueStatus.Resolved, + ResovledDate = DateTime.Now.AddDays(-5).AddHours(-1) + } + }; + + Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); + Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); + await Job.Start(); + + Assert.That(issues.First().Status, Is.EqualTo(IssueStatus.Deleted)); + Repo.Verify(x => x.SaveChangesAsync(), Times.Once); + } + + [Test] + public async Task DoesNot_Delete_AnyIssues() + { + var issues = new List() + { + new Issues + { + Status = IssueStatus.Resolved, + ResovledDate = DateTime.Now.AddDays(-2) + }, + new Issues + { + Status = IssueStatus.Resolved, + ResovledDate = DateTime.Now.AddDays(-6) + } + }; + + Settings.Setup(x => x.GetSettingsAsync()).ReturnsAsync(new IssueSettings { DeleteIssues = true, DaysAfterResolvedToDelete = 5 }); + Repo.Setup(x => x.GetAll()).Returns(new EnumerableQuery(issues)); + await Job.Start(); + + Assert.That(issues[0].Status, Is.Not.EqualTo(IssueStatus.Deleted)); + Assert.That(issues[1].Status, Is.EqualTo(IssueStatus.Deleted)); + Repo.Verify(x => x.SaveChangesAsync(), Times.Once); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs new file mode 100644 index 000000000..fbd1e3aaf --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/IIssuesPurge.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public interface IIssuesPurge : IBaseJob + { + Task Start(); + } +} \ No newline at end of file diff --git a/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs b/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs new file mode 100644 index 000000000..92ca31071 --- /dev/null +++ b/src/Ombi.Schedule/Jobs/Ombi/IssuesPurge.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Ombi.Core.Settings; +using Ombi.Settings.Settings.Models; +using Ombi.Store.Entities.Requests; +using Ombi.Store.Repository; + +namespace Ombi.Schedule.Jobs.Ombi +{ + public class IssuesPurge : IIssuesPurge + { + public IssuesPurge(IRepository issuesRepo, ISettingsService issueSettings) + { + _issuesRepository = issuesRepo; + _settings = issueSettings; + _settings.ClearCache(); + } + + private readonly IRepository _issuesRepository; + private readonly ISettingsService _settings; + + public async Task Start() + { + var settings = await _settings.GetSettingsAsync(); + if (!settings.DeleteIssues) + { + return; + } + + var now = DateTime.Now.AddDays(-settings.DaysAfterResolvedToDelete).Date; + var resolved = _issuesRepository.GetAll().Where(x => x.Status == IssueStatus.Resolved); + var toDelete = resolved.Where(x => x.ResovledDate.HasValue && x.ResovledDate.Value.Date <= now); + + foreach (var d in toDelete) + { + d.Status = IssueStatus.Deleted; + } + + await _issuesRepository.SaveChangesAsync(); + } + + private bool _disposed; + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + _issuesRepository?.Dispose(); + _settings?.Dispose(); + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/IssueSettings.cs b/src/Ombi.Settings/Settings/Models/IssueSettings.cs index e025c82d1..d7a35c0d9 100644 --- a/src/Ombi.Settings/Settings/Models/IssueSettings.cs +++ b/src/Ombi.Settings/Settings/Models/IssueSettings.cs @@ -4,5 +4,8 @@ { public bool Enabled { get; set; } public bool EnableInProgress { get; set; } + + public bool DeleteIssues { get; set; } + public int DaysAfterResolvedToDelete { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettings.cs b/src/Ombi.Settings/Settings/Models/JobSettings.cs index 48c721e29..8b283cdf7 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettings.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettings.cs @@ -14,5 +14,6 @@ public string RefreshMetadata { get; set; } public string Newsletter { get; set; } public string LidarrArtistSync { get; set; } + public string IssuesPurge { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs index 0f8fec5fd..4491ca27a 100644 --- a/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs +++ b/src/Ombi.Settings/Settings/Models/JobSettingsHelper.cs @@ -57,6 +57,11 @@ namespace Ombi.Settings.Settings.Models return Get(s.LidarrArtistSync, Cron.Hourly(40)); } + public static string IssuePurge(JobSettings s) + { + return Get(s.IssuesPurge, Cron.Daily()); + } + private static string Get(string settings, string defaultCron) { return settings.HasValue() ? settings : defaultCron; diff --git a/src/Ombi.Store/Entities/Requests/Issues.cs b/src/Ombi.Store/Entities/Requests/Issues.cs index b1021e362..9fbc6a83e 100644 --- a/src/Ombi.Store/Entities/Requests/Issues.cs +++ b/src/Ombi.Store/Entities/Requests/Issues.cs @@ -29,5 +29,6 @@ namespace Ombi.Store.Entities.Requests Pending = 0, InProgress = 1, Resolved = 2, + Deleted = 3, } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/app/interfaces/ISettings.ts index 5f1b255a4..f1934a994 100644 --- a/src/Ombi/ClientApp/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/app/interfaces/ISettings.ts @@ -144,11 +144,14 @@ export interface IJobSettings { newsletter: string; plexRecentlyAddedSync: string; lidarrArtistSync: string; + issuesPurge: string; } export interface IIssueSettings extends ISettings { enabled: boolean; enableInProgress: boolean; + deleteIssues: boolean; + daysAfterResolvedToDelete: number; } export interface IAuthenticationSettings extends ISettings { diff --git a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html index f86d3351d..c4c903da2 100644 --- a/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html +++ b/src/Ombi/ClientApp/app/requests/music/musicrequests.component.html @@ -59,7 +59,7 @@

- {{request.title}} + {{request.title | truncate: 36}}

diff --git a/src/Ombi/ClientApp/app/settings/issues/issues.component.ts b/src/Ombi/ClientApp/app/settings/issues/issues.component.ts index 005202f1e..cfe0bd65c 100644 --- a/src/Ombi/ClientApp/app/settings/issues/issues.component.ts +++ b/src/Ombi/ClientApp/app/settings/issues/issues.component.ts @@ -21,8 +21,10 @@ export class IssuesComponent implements OnInit { public ngOnInit() { this.settingsService.getIssueSettings().subscribe(x => { this.form = this.fb.group({ - enabled: [x.enabled], - enableInProgress: [x.enableInProgress], + enabled: [x.enabled], + enableInProgress: [x.enableInProgress], + deleteIssues: [x.deleteIssues], + daysAfterResolvedToDelete: [x.daysAfterResolvedToDelete], }); }); this.getCategories(); diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html index a4dcd6fb3..1365710f0 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.html @@ -85,12 +85,19 @@
-
+
The Newsletter is required
+ +
+ + + The Issues Purge is required + +
diff --git a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts index 756d6ba89..d8ce106ae 100644 --- a/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts +++ b/src/Ombi/ClientApp/app/settings/jobs/jobs.component.ts @@ -35,6 +35,7 @@ export class JobsComponent implements OnInit { newsletter: [x.newsletter, Validators.required], plexRecentlyAddedSync: [x.plexRecentlyAddedSync, Validators.required], lidarrArtistSync: [x.lidarrArtistSync, Validators.required], + issuesPurge: [x.issuesPurge, Validators.required], }); }); } diff --git a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html index bf0e53895..3f44a808b 100644 --- a/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html +++ b/src/Ombi/ClientApp/app/usermanagement/usermanagement-user.component.html @@ -147,7 +147,7 @@
-
+
@@ -165,7 +165,7 @@
-
+
@@ -182,7 +182,7 @@
-
+
-
+