From ee4b6c94427c61c0716df288b12068b67f904f39 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 28 Jul 2011 00:21:49 -0700 Subject: [PATCH 01/15] ExternalNotifications enabled (Xbmc only right now). - Grab, Download, Rename Notification Settings for Xbmc uses definitions. --- NzbDrone.Core/CentralDispatch.cs | 7 +- .../Datastore/Migrations/Migration20110726.cs | 30 ++++++ NzbDrone.Core/NzbDrone.Core.csproj | 7 +- NzbDrone.Core/Providers/DiskScanProvider.cs | 22 ++++- NzbDrone.Core/Providers/DownloadProvider.cs | 8 +- ...derBase.cs => ExternalNotificationBase.cs} | 35 +------ .../Providers/ExternalNotification/Xbmc.cs | 66 +++++++++++++ .../XbmcNotificationProvider.cs | 92 ------------------- .../Providers/ExternalNotificationProvider.cs | 68 ++++++++++---- .../Providers/Jobs/PostDownloadScanJob.cs | 2 +- .../ExternalNotificationDefinition.cs | 17 ++++ .../Repository/ExternalNotificationSetting.cs | 17 ---- .../Controllers/SettingsController.cs | 15 ++- 13 files changed, 211 insertions(+), 175 deletions(-) create mode 100644 NzbDrone.Core/Datastore/Migrations/Migration20110726.cs rename NzbDrone.Core/Providers/ExternalNotification/{ExternalNotificationProviderBase.cs => ExternalNotificationBase.cs} (53%) create mode 100644 NzbDrone.Core/Providers/ExternalNotification/Xbmc.cs delete mode 100644 NzbDrone.Core/Providers/ExternalNotification/XbmcNotificationProvider.cs create mode 100644 NzbDrone.Core/Repository/ExternalNotificationDefinition.cs delete mode 100644 NzbDrone.Core/Repository/ExternalNotificationSetting.cs diff --git a/NzbDrone.Core/CentralDispatch.cs b/NzbDrone.Core/CentralDispatch.cs index 6d9f15f4b..5ac60dd4e 100644 --- a/NzbDrone.Core/CentralDispatch.cs +++ b/NzbDrone.Core/CentralDispatch.cs @@ -62,9 +62,9 @@ namespace NzbDrone.Core _kernel.Get().SetupDefaultProfiles(); + BindExternalNotifications(); BindIndexers(); BindJobs(); - BindExternalNotifications(); } private static void BindKernel() @@ -111,8 +111,9 @@ namespace NzbDrone.Core private static void BindExternalNotifications() { - _kernel.Bind().To().InSingletonScope(); - var notifiers = _kernel.GetAll(); + _kernel.Bind().To(); + + var notifiers = _kernel.GetAll(); _kernel.Get().InitializeNotifiers(notifiers.ToList()); } diff --git a/NzbDrone.Core/Datastore/Migrations/Migration20110726.cs b/NzbDrone.Core/Datastore/Migrations/Migration20110726.cs new file mode 100644 index 000000000..c0ed06601 --- /dev/null +++ b/NzbDrone.Core/Datastore/Migrations/Migration20110726.cs @@ -0,0 +1,30 @@ +using System; +using System.Data; +using Migrator.Framework; + +namespace NzbDrone.Core.Datastore.Migrations +{ + + [Migration(20110726)] + public class Migration20110726 : Migration + { + public override void Up() + { + Database.RemoveTable("ExternalNotificationSettings"); + + Database.AddTable("ExternalNotificationDefinitions", new[] + { + new Column("Id", DbType.Int32, ColumnProperty.PrimaryKeyWithIdentity), + new Column("Enable", DbType.Boolean, ColumnProperty.NotNull), + new Column("ExternalNotificationProviderType", DbType.String, ColumnProperty.NotNull), + new Column("Name", DbType.String, ColumnProperty.NotNull) + }); + } + + + public override void Down() + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index 31072b292..0eb47a119 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -176,6 +176,7 @@ + @@ -203,8 +204,8 @@ - - + + @@ -224,7 +225,7 @@ - + diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs index 7c04d7eb4..49dd9bc8f 100644 --- a/NzbDrone.Core/Providers/DiskScanProvider.cs +++ b/NzbDrone.Core/Providers/DiskScanProvider.cs @@ -18,18 +18,22 @@ namespace NzbDrone.Core.Providers private readonly EpisodeProvider _episodeProvider; private readonly MediaFileProvider _mediaFileProvider; private readonly SeriesProvider _seriesProvider; + private readonly ExternalNotificationProvider _externalNotificationProvider; + private readonly SabProvider _sabProvider; [Inject] public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider, - SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider) + SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider, + ExternalNotificationProvider externalNotificationProvider, SabProvider sabProvider) { _diskProvider = diskProvider; _episodeProvider = episodeProvider; _seriesProvider = seriesProvider; _mediaFileProvider = mediaFileProvider; + _externalNotificationProvider = externalNotificationProvider; + _sabProvider = sabProvider; } - public DiskScanProvider() { } @@ -142,7 +146,7 @@ namespace NzbDrone.Core.Providers return episodeFile; } - public virtual bool MoveEpisodeFile(EpisodeFile episodeFile) + public virtual bool MoveEpisodeFile(EpisodeFile episodeFile, bool newDownload = false) { if (episodeFile == null) throw new ArgumentNullException("episodeFile"); @@ -163,6 +167,18 @@ namespace NzbDrone.Core.Providers episodeFile.Path = newFile.FullName; _mediaFileProvider.Update(episodeFile); + //ExternalNotification + var parseResult = Parser.ParsePath(episodeFile.Path); + parseResult.Series = series; + + var message = _sabProvider.GetSabTitle(parseResult); + + if (newDownload) + _externalNotificationProvider.OnDownload(message, series); + + else + _externalNotificationProvider.OnRename(message, series); + return true; } diff --git a/NzbDrone.Core/Providers/DownloadProvider.cs b/NzbDrone.Core/Providers/DownloadProvider.cs index 9a32b609f..e4abe5342 100644 --- a/NzbDrone.Core/Providers/DownloadProvider.cs +++ b/NzbDrone.Core/Providers/DownloadProvider.cs @@ -11,14 +11,18 @@ namespace NzbDrone.Core.Providers private readonly SabProvider _sabProvider; private readonly HistoryProvider _historyProvider; private readonly EpisodeProvider _episodeProvider; + private readonly ExternalNotificationProvider _externalNotificationProvider; + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); [Inject] - public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider, EpisodeProvider episodeProvider) + public DownloadProvider(SabProvider sabProvider, HistoryProvider historyProvider, + EpisodeProvider episodeProvider, ExternalNotificationProvider externalNotificationProvider) { _sabProvider = sabProvider; _historyProvider = historyProvider; _episodeProvider = episodeProvider; + _externalNotificationProvider = externalNotificationProvider; } public DownloadProvider() @@ -55,6 +59,8 @@ namespace NzbDrone.Core.Providers } } + _externalNotificationProvider.OnGrab(sabTitle); + return addSuccess; } } diff --git a/NzbDrone.Core/Providers/ExternalNotification/ExternalNotificationProviderBase.cs b/NzbDrone.Core/Providers/ExternalNotification/ExternalNotificationBase.cs similarity index 53% rename from NzbDrone.Core/Providers/ExternalNotification/ExternalNotificationProviderBase.cs rename to NzbDrone.Core/Providers/ExternalNotification/ExternalNotificationBase.cs index d21d8bf35..811713e22 100644 --- a/NzbDrone.Core/Providers/ExternalNotification/ExternalNotificationProviderBase.cs +++ b/NzbDrone.Core/Providers/ExternalNotification/ExternalNotificationBase.cs @@ -6,16 +6,14 @@ using NzbDrone.Core.Repository; namespace NzbDrone.Core.Providers.ExternalNotification { - public abstract class ExternalNotificationProviderBase + public abstract class ExternalNotificationBase { protected readonly Logger _logger; protected readonly ConfigProvider _configProvider; - protected readonly ExternalNotificationProvider _externalNotificationProvider; - protected ExternalNotificationProviderBase(ConfigProvider configProvider, ExternalNotificationProvider externalNotificationProvider) + protected ExternalNotificationBase(ConfigProvider configProvider) { _configProvider = configProvider; - _externalNotificationProvider = externalNotificationProvider; _logger = LogManager.GetLogger(GetType().ToString()); } @@ -24,35 +22,6 @@ namespace NzbDrone.Core.Providers.ExternalNotification /// public abstract string Name { get; } - public ExternalNotificationSetting Settings - { - get - { - return _externalNotificationProvider.GetSettings(GetType()); - } - } - - public virtual void Notify(ExternalNotificationType type, string message, int seriesId = 0) - { - if (type == ExternalNotificationType.Grab) - OnGrab(message); - - else if (type == ExternalNotificationType.Download) - { - throw new NotImplementedException(); - var series = new Series(); - OnDownload(message, series); - } - - - else if (type == ExternalNotificationType.Rename) - { - throw new NotImplementedException(); - var series = new Series(); - OnRename(message, series); - } - } - /// /// Performs the on grab action /// diff --git a/NzbDrone.Core/Providers/ExternalNotification/Xbmc.cs b/NzbDrone.Core/Providers/ExternalNotification/Xbmc.cs new file mode 100644 index 000000000..2533fc54e --- /dev/null +++ b/NzbDrone.Core/Providers/ExternalNotification/Xbmc.cs @@ -0,0 +1,66 @@ +using System; +using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Repository; + +namespace NzbDrone.Core.Providers.ExternalNotification +{ + public class Xbmc : ExternalNotificationBase + { + private readonly XbmcProvider _xbmcProvider; + + public Xbmc(ConfigProvider configProvider, XbmcProvider xbmcProvider) + : base(configProvider) + { + _xbmcProvider = xbmcProvider; + } + + public override string Name + { + get { return "XBMC"; } + } + + public override void OnGrab(string message) + { + const string header = "NzbDrone [TV] - Grabbed"; + + if (_configProvider.XbmcNotifyOnGrab) + { + _logger.Trace("Sending Notification to XBMC"); + _xbmcProvider.Notify(header, message); + } + } + + public override void OnDownload(string message, Series series) + { + const string header = "NzbDrone [TV] - Downloaded"; + + if (_configProvider.XbmcNotifyOnDownload) + { + _logger.Trace("Sending Notification to XBMC"); + _xbmcProvider.Notify(header, message); + } + + UpdateAndClean(series); + } + + public override void OnRename(string message, Series series) + { + UpdateAndClean(series); + } + + private void UpdateAndClean(Series series) + { + if (_configProvider.XbmcUpdateLibrary) + { + _logger.Trace("Sending Update Request to XBMC"); + _xbmcProvider.Update(series); + } + + if (_configProvider.XbmcCleanLibrary) + { + _logger.Trace("Sending Clean DB Request to XBMC"); + _xbmcProvider.Clean(); + } + } + } +} diff --git a/NzbDrone.Core/Providers/ExternalNotification/XbmcNotificationProvider.cs b/NzbDrone.Core/Providers/ExternalNotification/XbmcNotificationProvider.cs deleted file mode 100644 index 458efcec3..000000000 --- a/NzbDrone.Core/Providers/ExternalNotification/XbmcNotificationProvider.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using NzbDrone.Core.Providers.Core; -using NzbDrone.Core.Repository; - -namespace NzbDrone.Core.Providers.ExternalNotification -{ - public class XbmcNotificationProvider : ExternalNotificationProviderBase - { - private readonly XbmcProvider _xbmcProvider; - - public XbmcNotificationProvider(ConfigProvider configProvider, XbmcProvider xbmcProvider, - ExternalNotificationProvider externalNotificationProvider) - : base(configProvider, externalNotificationProvider) - { - _xbmcProvider = xbmcProvider; - } - - public override string Name - { - get { return "XBMC"; } - } - - public override void OnGrab(string message) - { - const string header = "NzbDrone [TV] - Grabbed"; - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcEnabled", false))) - { - if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnGrab", false))) - { - _logger.Trace("Sending Notification to XBMC"); - _xbmcProvider.Notify(header, message); - return; - } - _logger.Trace("XBMC NotifyOnGrab is not enabled"); - } - - _logger.Trace("XBMC Notifier is not enabled"); - } - - public override void OnDownload(string message, Series series) - { - const string header = "NzbDrone [TV] - Downloaded"; - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcEnabled", false))) - { - if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnDownload", false))) - { - _logger.Trace("Sending Notification to XBMC"); - _xbmcProvider.Notify(header, message); - } - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnDownload", false))) - { - _logger.Trace("Sending Update Request to XBMC"); - _xbmcProvider.Update(series); - } - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnDownload", false))) - { - _logger.Trace("Sending Clean DB Request to XBMC"); - _xbmcProvider.Clean(); - } - } - - _logger.Trace("XBMC Notifier is not enabled"); - } - - public override void OnRename(string message, Series series) - { - const string header = "NzbDrone [TV] - Renamed"; - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcNotifyOnRename", false))) - { - _logger.Trace("Sending Notification to XBMC"); - _xbmcProvider.Notify(header, message); - } - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcUpdateOnRename", false))) - { - _logger.Trace("Sending Update Request to XBMC"); - _xbmcProvider.Update(series); - } - - if (Convert.ToBoolean(_configProvider.GetValue("XbmcCleanOnRename", false))) - { - _logger.Trace("Sending Clean DB Request to XBMC"); - _xbmcProvider.Clean(); - } - } - } -} diff --git a/NzbDrone.Core/Providers/ExternalNotificationProvider.cs b/NzbDrone.Core/Providers/ExternalNotificationProvider.cs index 4cba27051..749c12875 100644 --- a/NzbDrone.Core/Providers/ExternalNotificationProvider.cs +++ b/NzbDrone.Core/Providers/ExternalNotificationProvider.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Ninject; using NLog; +using NzbDrone.Core.Model; using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Repository; using PetaPoco; @@ -13,10 +15,13 @@ namespace NzbDrone.Core.Providers private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private readonly IDatabase _database; - [Inject] - public ExternalNotificationProvider(IDatabase database) + private IEnumerable _notifiers; + + [Inject] + public ExternalNotificationProvider(IDatabase database, IEnumerable notifiers) { _database = database; + _notifiers = notifiers; } public ExternalNotificationProvider() @@ -24,51 +29,54 @@ namespace NzbDrone.Core.Providers } - public virtual List All() + public virtual List All() { - return _database.Fetch(); + return _database.Fetch(); } - public virtual void SaveSettings(ExternalNotificationSetting settings) + public virtual void SaveSettings(ExternalNotificationDefinition settings) { if (settings.Id == 0) { - Logger.Debug("Adding External Notification settings for {0}", settings.Name); + Logger.Debug("Adding External Notification definition for {0}", settings.Name); _database.Insert(settings); } else { - Logger.Debug("Updating External Notification settings for {0}", settings.Name); + Logger.Debug("Updating External Notification definition for {0}", settings.Name); _database.Update(settings); } } - public virtual ExternalNotificationSetting GetSettings(Type type) + public virtual ExternalNotificationDefinition GetSettings(Type type) { - return _database.SingleOrDefault("WHERE NotifierName = @0", type.ToString()); + return _database.SingleOrDefault("WHERE ExternalNotificationProviderType = @0", type.ToString()); } - public virtual ExternalNotificationSetting GetSettings(int id) + public virtual IList GetEnabledExternalNotifiers() { - return _database.SingleOrDefault(id); + var all = All(); + return _notifiers.Where(i => all.Exists(c => c.ExternalNotificationProviderType == i.GetType().ToString() && c.Enable)).ToList(); } - public virtual void InitializeNotifiers(IList notifiers) + public virtual void InitializeNotifiers(IList notifiers) { Logger.Info("Initializing notifiers. Count {0}", notifiers.Count); + _notifiers = notifiers; + var currentNotifiers = All(); - foreach (var feedProvider in notifiers) + foreach (var notificationProvider in notifiers) { - ExternalNotificationProviderBase externalNotificationProviderLocal = feedProvider; - if (!currentNotifiers.Exists(c => c.NotifierName == externalNotificationProviderLocal.GetType().ToString())) + ExternalNotificationBase externalNotificationProviderLocal = notificationProvider; + if (!currentNotifiers.Exists(c => c.ExternalNotificationProviderType == externalNotificationProviderLocal.GetType().ToString())) { - var settings = new ExternalNotificationSetting() + var settings = new ExternalNotificationDefinition { - Enabled = false, - NotifierName = externalNotificationProviderLocal.GetType().ToString(), + Enable = false, + ExternalNotificationProviderType = externalNotificationProviderLocal.GetType().ToString(), Name = externalNotificationProviderLocal.Name }; @@ -76,5 +84,29 @@ namespace NzbDrone.Core.Providers } } } + + public virtual void OnGrab(string message) + { + foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnGrab(message); + } + } + + public virtual void OnDownload(string message, Series series) + { + foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnDownload(message, series); + } + } + + public virtual void OnRename(string message, Series series) + { + foreach (var notifier in _notifiers.Where(i => GetSettings(i.GetType()).Enable)) + { + notifier.OnRename(message, series); + } + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs index dbf84db30..509c9bea5 100644 --- a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs @@ -84,7 +84,7 @@ namespace NzbDrone.Core.Providers.Jobs } var importedFiles = _diskScanProvider.Scan(series, subfolder); - importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file)); + importedFiles.ForEach(file => _diskScanProvider.MoveEpisodeFile(file, true)); //Delete the folder only if folder is small enough if (_diskProvider.GetDirectorySize(subfolder) < 10.Megabytes()) diff --git a/NzbDrone.Core/Repository/ExternalNotificationDefinition.cs b/NzbDrone.Core/Repository/ExternalNotificationDefinition.cs new file mode 100644 index 000000000..64a3abba1 --- /dev/null +++ b/NzbDrone.Core/Repository/ExternalNotificationDefinition.cs @@ -0,0 +1,17 @@ +using PetaPoco; + +namespace NzbDrone.Core.Repository +{ + [TableName("ExternalNotificationDefinitions")] + [PrimaryKey("Id", autoIncrement = true)] + public class ExternalNotificationDefinition + { + public int Id { get; set; } + + public bool Enable { get; set; } + + public string ExternalNotificationProviderType { get; set; } + + public string Name { get; set; } + } +} \ No newline at end of file diff --git a/NzbDrone.Core/Repository/ExternalNotificationSetting.cs b/NzbDrone.Core/Repository/ExternalNotificationSetting.cs deleted file mode 100644 index b6d946b69..000000000 --- a/NzbDrone.Core/Repository/ExternalNotificationSetting.cs +++ /dev/null @@ -1,17 +0,0 @@ -using PetaPoco; - -namespace NzbDrone.Core.Repository -{ - [TableName("ExternalNotificationSettings")] - [PrimaryKey("Id", autoIncrement = true)] - public class ExternalNotificationSetting - { - public int Id { get; set; } - - public bool Enabled { get; set; } - - public string NotifierName { get; set; } - - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/SettingsController.cs b/NzbDrone.Web/Controllers/SettingsController.cs index 5223e4945..5c2a36436 100644 --- a/NzbDrone.Web/Controllers/SettingsController.cs +++ b/NzbDrone.Web/Controllers/SettingsController.cs @@ -9,6 +9,7 @@ using NzbDrone.Core.Model; using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Providers; using NzbDrone.Core.Providers.Core; +using NzbDrone.Core.Providers.ExternalNotification; using NzbDrone.Core.Providers.Indexer; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; @@ -30,12 +31,15 @@ namespace NzbDrone.Web.Controllers private readonly NotificationProvider _notificationProvider; private readonly DiskProvider _diskProvider; private readonly SeriesProvider _seriesProvider; + private readonly ExternalNotificationProvider _externalNotificationProvider; public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider, QualityProvider qualityProvider, RootDirProvider rootDirProvider, AutoConfigureProvider autoConfigureProvider, NotificationProvider notificationProvider, - DiskProvider diskProvider, SeriesProvider seriesProvider) + DiskProvider diskProvider, SeriesProvider seriesProvider, + ExternalNotificationProvider externalNotificationProvider) { + _externalNotificationProvider = externalNotificationProvider; _configProvider = configProvider; _indexerProvider = indexerProvider; _qualityProvider = qualityProvider; @@ -140,7 +144,7 @@ namespace NzbDrone.Web.Controllers { var model = new NotificationSettingsModel { - XbmcEnabled = _configProvider.XbmcEnabled, + XbmcEnabled = _externalNotificationProvider.GetSettings(typeof(Xbmc)).Enable, XbmcNotifyOnGrab = _configProvider.XbmcNotifyOnGrab, XbmcNotifyOnDownload = _configProvider.XbmcNotifyOnDownload, XbmcUpdateLibrary = _configProvider.XbmcUpdateLibrary, @@ -262,7 +266,6 @@ namespace NzbDrone.Web.Controllers return new JsonResult { Data = "failed" }; } } - [HttpPost] public ActionResult SaveIndexers(IndexerSettingsModel data) @@ -395,7 +398,11 @@ namespace NzbDrone.Web.Controllers if (ModelState.IsValid) { - _configProvider.XbmcEnabled = data.XbmcEnabled; + //XBMC Enabled + var xbmcSettings = _externalNotificationProvider.GetSettings(typeof(Xbmc)); + xbmcSettings.Enable = data.XbmcEnabled; + _externalNotificationProvider.SaveSettings(xbmcSettings); + _configProvider.XbmcNotifyOnGrab = data.XbmcNotifyOnGrab; _configProvider.XbmcNotifyOnDownload = data.XbmcNotifyOnDownload; _configProvider.XbmcUpdateLibrary = data.XbmcUpdateLibrary; From 23681167e4cd2fc023153725c963586060013509 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 28 Jul 2011 18:02:21 -0700 Subject: [PATCH 02/15] Fixed DownloadProviderTest (wasn't providing a mock for ExternalNotificationProvider) --- NzbDrone.Core.Test/DownloadProviderTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/DownloadProviderTest.cs b/NzbDrone.Core.Test/DownloadProviderTest.cs index e0d956236..353f42c71 100644 --- a/NzbDrone.Core.Test/DownloadProviderTest.cs +++ b/NzbDrone.Core.Test/DownloadProviderTest.cs @@ -55,10 +55,12 @@ namespace NzbDrone.Core.Test mocker.GetMock() .Setup(c => c.MarkEpisodeAsFetched(12)); - mocker.GetMock() .Setup(c => c.MarkEpisodeAsFetched(99)); + mocker.GetMock() + .Setup(c => c.OnGrab(It.IsAny())); + mocker.Resolve().DownloadReport(parseResult); mocker.VerifyAllMocks(); From 559011ba828393a93cb2c1c81ee336e7848fd56b Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 28 Jul 2011 18:03:24 -0700 Subject: [PATCH 03/15] SeriesPathExists compares paths in lower-case now. GitHub Issue #9 --- NzbDrone.Core.Test/SeriesProviderTest.cs | 125 ++++++++++++++++++++++ NzbDrone.Core/Providers/SeriesProvider.cs | 2 +- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/NzbDrone.Core.Test/SeriesProviderTest.cs b/NzbDrone.Core.Test/SeriesProviderTest.cs index 63796e6ea..d2b271695 100644 --- a/NzbDrone.Core.Test/SeriesProviderTest.cs +++ b/NzbDrone.Core.Test/SeriesProviderTest.cs @@ -10,6 +10,7 @@ using NzbDrone.Core.Providers.Core; using NzbDrone.Core.Repository; using NzbDrone.Core.Repository.Quality; using NzbDrone.Core.Test.Framework; +using PetaPoco; // ReSharper disable InconsistentNaming namespace NzbDrone.Core.Test @@ -350,5 +351,129 @@ namespace NzbDrone.Core.Test series.QualityProfile.Should().NotBeNull(); series.QualityProfileId.Should().Be(fakeQuality.QualityProfileId); } + + [Test] + public void SeriesPathExists_exact_match() + { + var mocker = new AutoMoqer(MockBehavior.Strict); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var path = @"C:\Test\TV\30 Rock"; + + var fakeSeries = Builder.CreateListOfSize(10) + .WhereAll() + .Have(c => c.QualityProfileId = 1) + .WhereTheFirst(1) + .Have(c => c.Path = path) + .Build(); + var fakeQuality = Builder.CreateNew().Build(); + + db.InsertMany(fakeSeries); + db.Insert(fakeQuality); + + //Act + mocker.Resolve(); + //mocker.GetMock().Setup(s => s.Fetch(It.IsAny())).Returns( + //fakeSeries.ToList()); + + var result = mocker.Resolve().SeriesPathExists(path); + + //Assert + result.Should().BeTrue(); + } + + [Test] + public void SeriesPathExists_match() + { + var mocker = new AutoMoqer(MockBehavior.Strict); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var path = @"C:\Test\TV\30 Rock"; + + var fakeSeries = Builder.CreateListOfSize(10) + .WhereAll() + .Have(c => c.QualityProfileId = 1) + .WhereTheFirst(1) + .Have(c => c.Path = path) + .Build(); + var fakeQuality = Builder.CreateNew().Build(); + + db.InsertMany(fakeSeries); + db.Insert(fakeQuality); + + //Act + mocker.Resolve(); + //mocker.GetMock().Setup(s => s.Fetch(It.IsAny())).Returns( + //fakeSeries.ToList()); + + var result = mocker.Resolve().SeriesPathExists(path.ToUpper()); + + //Assert + result.Should().BeTrue(); + } + + [Test] + public void SeriesPathExists_match_alt() + { + var mocker = new AutoMoqer(MockBehavior.Strict); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var path = @"C:\Test\TV\The Simpsons"; + + var fakeSeries = Builder.CreateListOfSize(10) + .WhereAll() + .Have(c => c.QualityProfileId = 1) + .WhereTheFirst(1) + .Have(c => c.Path = path) + .Build(); + var fakeQuality = Builder.CreateNew().Build(); + + db.InsertMany(fakeSeries); + db.Insert(fakeQuality); + + //Act + mocker.Resolve(); + //mocker.GetMock().Setup(s => s.Fetch(It.IsAny())).Returns( + //fakeSeries.ToList()); + + var result = mocker.Resolve().SeriesPathExists(@"c:\Test\Tv\the sIMpsons"); + + //Assert + result.Should().BeTrue(); + } + + [Test] + public void SeriesPathExists_match_false() + { + var mocker = new AutoMoqer(MockBehavior.Strict); + var db = MockLib.GetEmptyDatabase(); + mocker.SetConstant(db); + + var path = @"C:\Test\TV\30 Rock"; + + var fakeSeries = Builder.CreateListOfSize(10) + .WhereAll() + .Have(c => c.QualityProfileId = 1) + .WhereTheFirst(1) + .Have(c => c.Path = path) + .Build(); + var fakeQuality = Builder.CreateNew().Build(); + + db.InsertMany(fakeSeries); + db.Insert(fakeQuality); + + //Act + mocker.Resolve(); + //mocker.GetMock().Setup(s => s.Fetch(It.IsAny())).Returns( + //fakeSeries.ToList()); + + var result = mocker.Resolve().SeriesPathExists(@"C:\Test\TV\Not A match"); + + //Assert + result.Should().BeFalse(); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/SeriesProvider.cs b/NzbDrone.Core/Providers/SeriesProvider.cs index 8daeb1c89..038e1c5ab 100644 --- a/NzbDrone.Core/Providers/SeriesProvider.cs +++ b/NzbDrone.Core/Providers/SeriesProvider.cs @@ -178,7 +178,7 @@ namespace NzbDrone.Core.Providers public virtual bool SeriesPathExists(string cleanPath) { - if (GetAllSeries().Any(s => s.Path == cleanPath)) + if (GetAllSeries().Any(s => s.Path.ToLower() == cleanPath.ToLower())) return true; return false; From 35abb7499322437c1cfef9970d61f558bc4c3d2d Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Mon, 1 Aug 2011 22:10:05 -0700 Subject: [PATCH 04/15] Fixed parsing of House - S06E13 - 5 to 9 Episode Title was being picked up as part of the numbering. Lost ability to parse some oddly named scene titles. --- NzbDrone.Core.Test/ParserTest.cs | 5 ++++- NzbDrone.Core/Parser.cs | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/NzbDrone.Core.Test/ParserTest.cs b/NzbDrone.Core.Test/ParserTest.cs index d7073507a..12f82054f 100644 --- a/NzbDrone.Core.Test/ParserTest.cs +++ b/NzbDrone.Core.Test/ParserTest.cs @@ -41,6 +41,7 @@ namespace NzbDrone.Core.Test [TestCase("Adventure.Inc.S03E19.DVDRip.\"XviD\"-OSiTV", "Adventure.Inc", 3, 19)] [TestCase("Hawaii Five-0 (2010) - 1x05 - Nalowale (Forgotten/Missing)", "Hawaii Five-0 (2010)", 1, 5)] [TestCase("Hawaii Five-0 (2010) - 1x05 - Title", "Hawaii Five-0 (2010)", 1, 5)] + [TestCase("House - S06E13 - 5 to 9 [DVD]", "House", 6, 13)] public void ParseTitle_single(string postTitle, string title, int seasonNumber, int episodeNumber) { var result = Parser.ParseTitle(postTitle); @@ -131,7 +132,7 @@ namespace NzbDrone.Core.Test [TestCase("Two.and.a.Half.Men.103.104.720p.HDTV.X264-DIMENSION", "Two.and.a.Half.Men", 1, new[] { 3, 4 }, 2)] [TestCase("Weeds.S03E01.S03E02.720p.HDTV.X264-DIMENSION", "Weeds", 3, new[] { 1, 2 }, 2)] [TestCase("The Borgias S01e01 e02 ShoHD On Demand 1080i DD5 1 ALANiS", "The Borgias", 1, new[] { 1, 2 }, 2)] - [TestCase("Big Time Rush 1x01 to 10 480i DD2 0 Sianto", "Big Time Rush", 1, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10)] + //[TestCase("Big Time Rush 1x01 to 10 480i DD2 0 Sianto", "Big Time Rush", 1, new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 10)] [TestCase("White.Collar.2x04.2x05.720p.BluRay-FUTV", "White.Collar", 2, new[] { 4, 5 }, 2)] [TestCase("Desperate.Housewives.S07E22E23.720p.HDTV.X264-DIMENSION", "Desperate.Housewives", 7, new[] { 22, 23 }, 2)] [TestCase("S07E22 - 7x23 - And Lots of Security.. [HDTV].mkv", "", 7, new[] { 22, 23 }, 2)] @@ -203,6 +204,8 @@ namespace NzbDrone.Core.Test [TestCase("peri.od", "period")] [TestCase("this.^&%^**$%@#$!That", "thisthat")] [TestCase("test/test", "testtest")] + [TestCase("90210", "90210")] + [TestCase("24", "24")] public void Normalize_Title(string dirty, string clean) { var result = Parser.NormalizeTitle(dirty); diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index dc4a7e441..7c5e49b9c 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -19,11 +19,11 @@ namespace NzbDrone.Core RegexOptions.IgnoreCase | RegexOptions.Compiled), //Multi-Part episodes without a title (S01E05.S01E06) - new Regex(@"^(?:\W*S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?\d{1,2}(?!\d+)))+){2,}\W?(?!\\)", + new Regex(@"^(?:\W*S?(?\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s){1,2}(?\d{1,2}(?!\d+)))+){2,}\W?(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), //Single episodes or multi-episode (S01E05E06, S01E05-06, etc) - new Regex(@"^(?.+?)(?:\W+S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)", + new Regex(@"^(?<title>.+?)(?:\W+S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s){1,2}(?<episode>\d{1,2}(?!\d+)))+)+\W?(?!\\)", RegexOptions.IgnoreCase | RegexOptions.Compiled), //No Title - Single episodes or multi-episode (S01E05E06, S01E05-06, etc) @@ -381,6 +381,8 @@ namespace NzbDrone.Core /// <returns></returns> public static string NormalizeTitle(string title) { + if (title == "90210" || title == "24") + return title; return NormalizeRegex.Replace(title, String.Empty).ToLower(); } From ec78c5be3e02e7e70ac07d8225f3884ed637d6af Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Mon, 1 Aug 2011 22:15:41 -0700 Subject: [PATCH 05/15] Added todo to remove hack to handle shows that contain numbers only (added in last commit). --- NzbDrone.Core/Parser.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NzbDrone.Core/Parser.cs b/NzbDrone.Core/Parser.cs index 7c5e49b9c..21e5f736d 100644 --- a/NzbDrone.Core/Parser.cs +++ b/NzbDrone.Core/Parser.cs @@ -381,6 +381,7 @@ namespace NzbDrone.Core /// <returns></returns> public static string NormalizeTitle(string title) { + //Todo: Find a better way to do this hack if (title == "90210" || title == "24") return title; return NormalizeRegex.Replace(title, String.Empty).ToLower(); From c32346e6eae65f7a88a9da037d86422c1d82769f Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 3 Aug 2011 00:26:39 -0700 Subject: [PATCH 06/15] New series ignore editor on Series/Details. Styling works, backend saving not yet implemented. --- NzbDrone.Web/Content/Images/ignored.png | Bin 0 -> 3537 bytes NzbDrone.Web/Content/Images/notIgnored.png | Bin 0 -> 5898 bytes NzbDrone.Web/Controllers/SeriesController.cs | 5 +- NzbDrone.Web/Models/EpisodeModel.cs | 1 + NzbDrone.Web/NzbDrone.Web.csproj | 3 + NzbDrone.Web/Scripts/seriesDetails.js | 83 +++++++++++++++ NzbDrone.Web/Views/Series/Details.cshtml | 48 +++++++-- NzbDrone.Web/Views/Settings/Test.cshtml | 106 +++---------------- 8 files changed, 142 insertions(+), 104 deletions(-) create mode 100644 NzbDrone.Web/Content/Images/ignored.png create mode 100644 NzbDrone.Web/Content/Images/notIgnored.png create mode 100644 NzbDrone.Web/Scripts/seriesDetails.js diff --git a/NzbDrone.Web/Content/Images/ignored.png b/NzbDrone.Web/Content/Images/ignored.png new file mode 100644 index 0000000000000000000000000000000000000000..06b5934d61068f17b36cf16a9ea4032b6ff14036 GIT binary patch literal 3537 zcmV;?4KDJDP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800009a7bBm000XU z000XU0RWnu7ytkYIcY;fP*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-ozocT`l@7KhKh zcY2}CFalDB-n+m6(tDF$MPZm3U?>9)Gc>Uwq5=^`M4BQ<P{e@IP$X7Fu@Dqgum>av zC@~mCR4i{s){CyJy!Z0*`{S%{?X&l}`|Q2XS{DG4r!SY621@~u$`kN|Je=tfkx_K) z0Du7=V1OwAOjbs^U$A=!5XsBUg`OdD0$&6H@OoIh0&vsNGk{J9|DU8;>3o6cm;e!* zvpE?o5f_L!B}<Slw$F-~8ka0)7$s)DC_xd+i8*1B4L<uUvZI*UsXVrb#W|<5Q`utf z74f!AAxFfBuZRmXxtuH!-x9G#iZGQc;y2>hR1Px(02E1V7jRgKA~q2*i60W=BI4x$ z;7AEyaokrd;A9KLmvTu<&*5_u5(RV}mM-1Y+L}T4<Yc9A1OmeVCM%iAXVYEMQq!5d z8~_&Y{8=*M+R{ZuwluZ2v@|p`GX1Jh|8?_kO5EE;dKvh+XArIbi|w!c(i#9@%!z8V z^NTI63V_Zn0Lb0@V$(PPfTXBz9oN6Q4^2ETi2^~ojj?f7R+bT$!!i;V^bh?{hrgk( zzGoyp?vL`&T{-bgVTyn*t{W>YB~8euXQVS(9J=A3hxi`{{&gM(L7aFFpTiSHgo&n% z%S#Zoo5$t~xM@5(m-nBV_z%PWq{X=wiPHEHP-BdM)O9LAe(eV+3K1aD`^8=Vqi??W zFd%+;;VP4hbN}x*{b#|Y;w6Kd@Hx&UD1^=u@-r9r#Lp6-0Rcz?Dv$@tKpp4+LtqB1 zfGuzYZonJ(gAfo2Rs$AD1gU@zvOpf#1PVbh*a`N4YETCnK{IFt$3Z7J1<r#@;2O9A zZiD+^6ik3;U>3Xv3lIchAu>dP<RKME6VivwAO>U)xk0{A5EKc;LJ1HL5<+><W~dk{ zgQ}r=s2TbmItiVJu0Vs(U1$uNhJJ_M!3a!(sjw2P1slT**a`N5L*P|#0=x#!g$v*v za3$OTAB8*M^YAtJHarSHgXa){5D^-piWnjc#0Bw3qL6rmkE}<EkP4&$X+^q`KIA4c zip(HyQ5X~jMMoK;Y*C)5P!tQb2DKhlf~rOxMs=WiQ8!Uzs9DrUG#Rak)<@f*z0eWp z1aua<5M6<8LbszYqHm)o(62EV3=N}=vBG#_A~8vrwU`pjK1>_t9A*$Rj+w(^vGQ1b ztR2=L%ft$>h1e?WQS4dl5OxCl21mrH;LLFDxF{SCmyfH!9l@Q!4dEtn3wSBKCf)|` zk7wg^@TK@hd^i3&egeNhkS1so><A%*B*G>_C83pYk??@<l1L<~5v_@VL@se7v66U< zc$qjxd`prc>5*JW(Ig>h2k8*$9O*9UC7DdtB0G|!$O7^Xax?h?`4Rbz1VzF~!b^fJ zu|c9nqC;Xx;<+SVQd81Na<ycxWQF7j$?K9cQg|sXDHkcG)Ox8JsgqJ8QghN2X;W!` z=~U?w=_Artq^D%CGFmcjGHjUwnS(MHWX357MV;b8VNo_y8Yvek6I2XUo9abPq83xX zqYhAKWo2ZS$%e^h%ht%AmK~)bG%cDJErnJ}J5C#>y<4KR#Ayj<$@V3!ONN%r%Pp02 zl;g-1$+gMdmU|~pmv@s-mft1cDgRIbrJ$z}sF0<wU*V#{%u?!7+oi0fMN5w_y{8B( z>L~^(u2np!*snOJq^#tjl&(~zbU|rGnWpThoTOZ?d`5X%g`#4w!c{3(Iji!NE=zZ! zr_d|uz4TdCMO9B#p=!PAfa-#pwpyrKzFM2wLv?~WLp@%-T)jtqRzpR@Pa{vGMdO|( zUX!7jsJU0OPjg;NTPs{^t5&Dhl(w9<mv*jpi}nK@vW|n!8l486Te=ushHkR%KHVF7 zu%3lpqF#;Ob$wXhQlG28Pk+z=ZD4J{GiWdvF(er}8D<)`7>*gyjC_sqjXI5<8*3Ox z8SgUgGyZ5|VUl9fXma0F#?;$1-?ZEGcQZXRmRXJ2EpxKDyZHw5F7p@5^p|m#?O%4s zf@0xkvDKo-;)A7?CEv2ua@tD6D%PsjYJ@>$1Tab%m#xv(&ej{OPg%dUv9uA`9Jl$+ z*3dTD_K5A&a_!}u<&De7?bPg;cJ+3n_H_GL`vdl)4yq1JhX#koj_QtV$0o-~Ctar` zr=w2KolTti&h5_gE;cUfT+X>7t{$#Mt^;l|ZlP|~Zjap6+!Nee+-E&3Jl1-g^F(|4 zc<%BX@lx_)c{O{@dRuv~^X~N_`2_n^`#kp5^X2<?`@w$Re!Kl1tk7J+ThZwc{Js2l z`#%iO4p<ZLV<09lAh0IzNswt!UQk~!B{(MdNbu_r=aABn`=L6a!qA>D$*}0K=CJv2 z*YL9N(Fo&+brIJh6(YHjT~XMmu&Ab}xs`4!_pF?Vwuml_9$uxrDtpzH)e5UqR-cZM zjA6!{h(*VS#~z7&&-7<DFz4dD;%eh&SuU)-tY>UTb~$^RW5+4uOvc;Am&H#d*d^>v zm`-#^tVo>Ux^SzxFOocy>XPP@{gV$Re@Y2YX-mbW#-^U+$?%eSy=ls6*=d96`ssz~ zqibx|>{&C*_u)5XKpCqtx&&0w&s4uqN4P~emT8|^lldkqEbBzJbT%)$KSwWTd(LF8 zd+xVuQEORid-7ECHsy`2b6Quw9$Fu_zGs8_hJp<f8$C82-bC1xyy>TWll-#$SDV8( zcNZuXY%Cbx;<2TrP@<4uII`7tYuz@~Htx28?dIF7wtp;Q7hNqjDXu7fU&1Q6`i<!~ zmEU~Y5x--g)UtH{PRvf;&f#4SyAGF0m*tj?@Ali>QBE%}Du1;nX3v$1WfgUM344Wm zM=O0RyQ(y*c2>QwPOQFN<6P5Lt600ec77jw-_U-?{jGIMb;Wh>4sZ|LsrRVwXwYh? zIEXozdGJYNSYzL}jBlHp6q<^gJ{;m58a*6zxVPD=x%r6Vk<u2pMc6WZH2Ua`?_9s@ zYBg-FZ=<yp9|OmP$DV#4`~C27pW{6z7$;hP(EOpcozh;^0d?eb%yuSrj&?<M4W9Hq z+0$*?-TtG|kB3gFo~k)5d%E-t;Y`7qPiJ$_&Yep?H*-GW{CH1H&;1J#7jE?i_6}V1 zxp?`K`=twgPJL%D+g(0&#pcS%tBk9i*DSAf^jq|||77t~`+(&@$91dgT{o<6bPp~c zJTv4lbpEF6%}ckuZe6>*;`ZQh=<uD9=#kMo>^oC;Q|`XFmw9jD{>BIB2SpF19#%Y3 zeAMu>?$2$bmZPV~T*vw!2S2_)&KiIAOU5tnCkmdBpHxh$Og2xMO`V!{pT6;Q<<p60 z>CYBs3V)UUwf4Er^B;b5{H=dBVs_#M|HY@@OJ2&qJoIYWtDd=lxks;4UoXrTy^()& z_$}jY-@EX4lM7kzvF|HC=zi$_==1Txr_@iM{sjY=^Zb#(TH62s00d`2O+f$vv5tKE zQIh}w03c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(`>R<o>I+y?e7jKeZ#YO-C0|H4z zK~#9!V*LOAKf^!*fB<4*7+?SpKm+aofB+hB2LJ@nfI9#nfCk(F00G33nwm<KLPJAC ze<0-m!i<cJ2H4a9bJPJKbpePEoIihlBQfRz1P}{8Nqv2Nejrr<!vFsL6ClDo2PiH8 z>iqzuCxGz9ix)rQ(f|-ZIHN#EN2mVp-@gxl7B_$_2Vw>iF-T7X$PAEKxa<K4AdC>u z)YQ}m2Jr%*W&<ikGbrR5w6wGWfKili{rdHb$dUj7gc1Vk>gw@8iw}S-C*V7fngpPM z4j>F-L&fmv0hxIKWCyaH009IK0TmS$kmU=A@KM6;+qV~@i5&#ug{rEmAj20B2#p0m zyBO}=xdS!>Ab`LjproV(wj3B=4EQj}r&uh91CSg@4H4!n0NDxB0}w!r&CSiAIKM!I z&ksC!@W7FnNLNr$*bmJOcmXi_3;+U%1?ZX$#25-nn8<utSy@oZZh&$d9zTBE2$KW( z5R^8EFm?k#0D&B!KxAru0Aol=Ny&q<7?K=_4R-)2>k(<D0zd$<fZ~Km@Bx{PPag?p zG5`b+6A{@5jDd#2%Y|ppo_&NCP%!xhGzTaUSy&wa2p~`t1VGDhLIqC(D67FZuU@@s zL=JXj0LlqOc>V$~T?GIH5YwACZ$3f`lM4h~>mVQ?5Kpk!1F8Y3C1BPCkewjA0RjkI z5WRc%?jt`xzXK5=v4EGCm*Mm0&sfU>pd2xU8OY}jAAtbG0SF*?NeB`E8US(xC<_JP za|AaxcLKD;gQa#*P68Fj4g|9D1|UxO^yw44b_56@l*$O`Gmu0eCnsk<v??W3?UDit zzJLEd5nT`<fUvd)KngiHI5t2-!-0~j5a{CrAYcFZ@#7&jd4K@I*&YCC`1$jvBQ!xB zAfr71(&GR!1DE9>01!YdgnaPl&mT}t=g7>=EDuc^ploXZt=UitDrlVoisuc$vfv>x zAp{UWj04>>0tlc1ZxsUs(11GtAb<wk0RRCs;0^!?paFLPK!5=N6L@<N7M?Qv00000 LNkvXXu0mjfh3;0E literal 0 HcmV?d00001 diff --git a/NzbDrone.Web/Content/Images/notIgnored.png b/NzbDrone.Web/Content/Images/notIgnored.png new file mode 100644 index 0000000000000000000000000000000000000000..4ead4b480bc90f4635b7da458a6ed6d111819a4a GIT binary patch literal 5898 zcmcIo2{@Ep`+r1sQbHxYV@MJ+X6(~gXKdNBg|v;C$CxlPX2x2)R6@KY*&{8ot0-kn zl9CX;DN9sHc<Id&C4A3}_|(_;{jcx4uK!%KopbK{{{7DH+~+>$dZPAPn~Df-5d;80 z#M}&T2mQj>AAVlwn;8@|1pos56bEOPvy~-=Nb^PzNHjMP5#UXS#sL7UA3!G%JwX=C z4J1>jy6}&c_uw!JNf+*<VWnzC$AKOcvmgd&A7t%74DuxEkl^}yg4h5I6u=u~5nut{ zUQ{L~Ko`!5i-G>LyOD4h$Asmn3pZo~!kn%4!f-SO2vbL(5oi^ns-_xDLk*#(f!0K; zE5p#Ls@h0ZEu@;N3Q8M;LSs<cu<tLpo*)d%Ah~1g@Fw5kpq4J&gT<m_kVt=je}ump zg2o^tQ93$0NL4fvjaGpyRG5KO79l`|%9LY=Sc-uMnM4MK&Z5w$Fm_CW8_k!c3x`AF zu<ya?9yAt>=|Q8fKv0?ARfF_Ht_&kl2vy{2M{_!hs1E7Ep5}@#ghk|#W3oud)fOCg zj(R@?ArV(x>Anmvjw^|X1ie6Skji30UZ@}JAv`+@*@MMgar=R70D;Z{VsLDJ_WaL2 ztLFy6SXupNb8qh-!ZTUMevq=?SLv6em=1w-5NQW8X}%01XzT|~P>ut-qA><fV1YOW z2z^5-iB?6as;Hu%_fJ-k1&IQIv2%e8MezdN2}BS9E!R(mhQ2Hh8iTo)#-y-Q07^~< z3x=mKKq8CA;MlHSB8)ke1$ucwgT4eWQ;^DDRyqYTB{K+1ks(<yR#3i^2rL4d>MD+E z4rmlc4UN&z*`<oYsH$?Jb7X|VGFb#F5j4l^!l51ng+juh47JcWEwr(rmWejr7=_YC zqj1LRMo^!znkHUrwL6|h^kpZ*vODR&bGLzVfkGvC{ZC=o8wrN}K_t%d&=@!xwA+X* zbE-RyvzdOhp%{`FI0`FJ7rvK4Ap3$GHDLd0vzJICK`Np&Rlh6t3mY7Z0mZ=ufkY3O z9Z2_e^P&)c8PC|8K=E>-F`y#kNig_DV*DK3k@4O97m0*inmUPyS=vjS=2B5H^kOZw zmYQA^b|t}h5vXKneN{+MGWvS4;CdXPD1IPR2fe*%R3^-j#bQw0d^yF)nnqP&_k#=~ zh2RCVror$agW^YEK@LnTl3lC3DBm$Ttz}Rw@<&Qos7Yk+7#d?0MSIZaM~ZreUSI%$ zN&*=$3knk|FMhuQS$5-qtd7b?A^o$Ku0mSo^}XQ!J24zQC`=giW_f@x3K6Qt?hKkY z4BBL?HZRl%E{!r_Opt|u{fgo;9*5%3l-M+xzHUdMExd}N5rO=Z7;FYV247xEy_F6B z<M>i*IT8naH4qz^MS<qB3U-<4|Dnu}dHPQc*b@AT!*W;-hks&Xp~|v!GGH8;1TyFV zGQ64pJ)8fpr2V(-*u1T*FlH1cl)-_k2ruX2ihP_h1A}o68Vpo=I0c<a@B>Lp)nIvu z^1qf_sMIY5_xAvyvts4=`DJNeF(!II8=rk#A=x(}_Q?QWsvY0ccX=LI<iE^xX?a)O zQK0EB^+9(Z=-P!`xpzU0m75z#g}@lly^ItTlL+1E1n6ciOaKrPXMebWv<sU6K+uMQ z!|mO>e6@j@<8UxKgXT{00s$bPE5n}bU_UIWJJUO5WDyy9-hyT)!4I=DiVocvub?C= zAhst`_EM*W?fvz}#yrxGvm&`;V?$#kY?X!22)^PQki8sx@=D~HCv$I)UGeOwnt9*6 zaQmg+P~l`oX)9kXzi^V7nyp5taGue&^>0o-s&9BZfG`LXl&1q?{G}vW=DZviFdKr! z?t<6y)c{-p&xQDbTC4P0)vXbW>qd-H4{(K^;;LoEYg_P#ZUhX1VvVi=1}0pg87T_( zKo$>hq>1R(5A0O|j_mmT{c|8RV>a~|7vOqcR)Q-l9)O91k$AvkH&EER?~F0vtO{&& zx2V?vUa0`8=1%Trz>PaVZHt&t3BWH5sM<z`?*w>`0Y@Io$prwXQh|*o6HeGqyNbnz zlpv*2OR(kgI>uqH{3<?tPELqziY?|+yEbYP))3MR5Osm6o6%wF>)uR01%Rx0323&H z^8qcQ#VswGv1Ou%RYvadF2dd1`WFUU3+M&_@Rk+QwxEnG-4eQ+H`IIK^7e5ak3&LP zzBAG8CF=}s16jjePLp)DHpZ9EmUngz4h+27Q*Y!-c;XbiK(0Gn>oo86B?S9zX8KLd zJNYp5!7x*v**A4hr!2F#ww@C><<|2)&h*<Ip~Y`9&*AmvZspEO8~bfH_{8GVVkXhq z@F=5mJ0EUuUnITjQ=a42>(c;k9M$7G7O#?fkjVYnVuzSBtM=Ut08CWU?*FOC&mHP< z@@=dC;z#{?)2!`4sJr<&9{@Onmq$1}Db;_(4*>YAFr^!Y(o^?0DcACDy}!2SzR<TL znx~B9>+6ifj08!cTNy`o7M?ItJl%XlM(GIhPXihCT3e6P33O3Jo#S0mUHZDO1pdtX zidA6(Jcf_?#R;-qkz8(PoaJKqrBcIZW$&BuY>1YJHAZf<g<HgHn4#>>IKUjuWF~YO zYjh$Gm{i2?ehi>bd^=rbiaH-wVypY7@NHaquGW@QCYObtkyxm-ILWN^XN+50bYnCx z4LvLHSl1P&hkK-2`(YEGTD_MlqTzwjO>Jd?1Y*-oc%g7X!<HNS3b`wzriraL{dqQo z8P^JE^7tFU2-^(JoGqQLUzz2?w#uuo?-I}w;67zrw*#J#YjI1VV*Qu;O>SsZgtM8l zT*rnuairL>NL;;^{DvqTdPn)$HRd<3I<#+SH)*HZ$qg&#rK_85kxOXoB6*hZ>6}-F z?Rp$OkTLLJKy5&JKz71aFvHEj=jyEeJ4L%ENB3+V6dP0@<ONgEhK^U#>|W<ya#*hs zx8JDfQkmU#+lX9ob>B3d=97}<7THCY6Q6JKkn)(JT>rqTSpPb4n|1Otr}m~{(+|lX zxTpMuMNUhcd=yDm+?>9NZxea5|EB9Hme)2vkKX*qK~)PinlhBSE|uo!tW>6STq6F7 zVB(%cg+ys3q(ff*wfx?EA;(F_CWpAZL-xVNCXQ<M{dod^CKWj5XY3udm$oP8?aqzJ zC+E@}^sk1v%t!IMJg|IVgS%>*b@On4RRc_jxVbbyS=Lje;Iwa;M!mw#=Fa1ON|h<| zGL_M~`FWRgaB6$hoJ#E`&%5jM^{4pEUSL@Z3@PR$PllZ7-S~0myPeY4)Gf<V-~rG4 zq~s*$0<!|!0@KlI&Em0~jBC}#l`eB(qK1$5>^QK)lpNQxHe<-X)BbgiV2-4fE@rwI zk?)x;eW?=fe~+3oo|0K}C^XZOmeU(pJ@j>JFmm2u-ur9Lw5hN&Z>g|6Z?|yEChS8E z9~xE$`KbGoE$-k016(#Ck&yiMRD5|G_R0&BKTOueP#O;O%lCUXcsE>8;8l=Su&^4j z8c(ycD!;b#+HT9;mXDgBHeYV;NYzN!MZH81r4OaQOz*pMWPiYZ<+9weyP$c=!2WA4 z<7HLU@clY2+0Ksp50;t~WfX1is4S|?S)WsGonT#%*+G7A<5O8<S>Bz^HwItvb{Cg< zmjx5ZZc^ks4~rj+CsZc{R|NktugfPA(J5LWvtK5Jd5f_Tvk%jo)^cH7KltHP(lC<j z+$z@kO0B<4ylH1WCBr`bxhYwST+Tc>85tdAaiR&4tcOUKZg~B;dSDN0+UMZgp!VsE z^wRXYvE1(K<C^1?@ra&_Pt`B(%ydy>K6~&O_r`XxO<(9@qN-<KVTcXRCX9T0NDfIv z#yb_gym2SnKYxbsQA+Q#9^+!aUO!f=7v1Y}_^DRyoyT{YY#z`u4~O^RewSM$Ee0*N z0Xd=Dxyam6VTNI@YvyKyj?3PQ)yH&$pG!K&3}dp&4J8cYP86LOyk8{i@%XQ^MY1zP zDJ}6W6w&a#As;%P@e$q=q923^?+A|wcRH;fOOJHk(uPXb@NetEG5hde@k#jJbTZ0D z=eo)s4JEyN#8GUQR)u<negneY<>?FO3}<sMi>*2crD9F0S54oyvB`*c8SjMpk4dE< zA0MO#UTUeE3eS<uLu^pSBpFynT5F&G+fYm?S2AY%gt-6qSi!(LnL08Y*{0bR`3iSH zX`A(gnUYn|xxeD8Gy>%xs%0w=mY>>@cect>#PW4ICQ~xa59W>5#Z;qCo{K;0W0Wsz zl~6_=A-{L`Ybpf8OBNndqwgTMCGs_TmYywK>!Ko9W_0a?!+yn=N_-_`(8>K?bJP=^ zq=L*{&0|kXpLCzqy72Mh;@F0|4R6ntWuLZiQju|fUtM@qrv&R0*e%#Ev3A$#eY}O( zyJyOjykFQ9j5#G2hg2&BS$T#Xy8q<F{o7ogqe6~?gW<uWJuk-&+BhR!`fd}xp3acg zlZZM~ys=v<S>pFsiah~?(h;;L_fmV#-^wefFDJFSx4RF&?^Vfk6-~-dexEE#73u7{ zCDOO=*4XrC<8EYfvtoDU$ueB|m3!E`RbWnIOY?4hhpP@#4qqKK$J<}uCcK=bwv9_4 ztNN_|*Nbk~s+oDc#+q(NE=Q{u?k~VO^SRO){o<(n;n}1)?}J6w-accGxZAns`Q${) zUY7s)LZ)5U!7i?2*QlxJqu**4GCumNh-iv9{&At<og5t>I50dT&^NP91k>dgbFA<! z-}B%6WXLkeqef{HX(s9RX>+b_t`#;<_1Zc{dV{{1cK^2Sf;!eEaL=SeBc*Hj#PbC5 zL~Be&Vn%QX;!Dqw?#pGzhKMbFN7vClR*13Qwi_=-5ACkn-Ev^!E^DHsdr;izu2b4| z%wg{l)(CbX;knJD$_tfg#pMS)%g2MNZy$4?8J5n}YI!|X9r#IdvExOij_&K~$spRy z!<p$m$=4x_9rG<sm7V@O#*3<Kst!*Xj2U*~UPrvpcU|at^|9yoqqY87lWjewGrZv^ zTNmP|<JRHU#-+wJofbJgnyIa)qw`LmF+cn*2iHn!RmhZDxV+F*EG3BynSC+(vZXDh z?NahYa>70D<Jt2gwWFJ_cQ@2EY^!MuP(vM>+BY|L-@nIS=Hr#Vbv6UXVZSRbdVIM# zZT~E1E@5**!XgiM%H%hqno2ix--Hp(?5qGFXgdIeo&<m|i_q^d0Qjl`z{n8*z@z|x z6z$yMhb929#>5<N=n&BLmy^{|xUJa2$h2!p_^pP*HSK@Mp9;;=F}xriqjiIpr4`BK z9^wt+9^&~da!Xe1gv*o|eaNQYNb6vtRa~Ucl#ZZF%-Xfr^JYeZrzdMJo8$jZy!`h= z1}~l0;F|Pr+qTtk=K{H6wt?N-)_>v>24?3&7GJgjd$P}oWW{^DuYKbV><9K6isAXV z6(yr>fGzm*ItmZrXzpi0At4I{o;M2%HF&%?FesjU3<mFQYO?s-34opnXaFiFbAtis z*-wJvLEuok++0apKydKb@ilZN)7}E!x1*P?{g;<oPj7Dra7*OLyXxv{U!ZnPsL`dp z;_+pXN0Qe#%k(HDG|E4AFO^kunwiaaFYTmbGI$5Ht)n7&dE@5@n?lrfmW@_a2M?N| zZ9AU!XarrC9&8+%v;oo@$&>T*!7m1%_DIH`3F|I)cYk>!`hkA*=%{BzYPx5cLa0Qj zb}SBeu{gGWR?N+%$UkFu?T(FVb5CP}w><iEM#|)hZT-_`jblviP|tS>slgQ$6(!=L zeWuuj0IcTGx&sJu`zdaCz8G-e(4j41V(V#|>ArqrjU=?faWw4m6B0UVAD+}*hKl@h zyhZ|#$<VC9p1J%%Tq5T=Ww-3rd;y8;)xvU03YQV<11G;=yL3d52*gOEqHeajWb{Lu zexD0_vM<O>ClaaK;TI#T1{q)GE^hTG`t5dyZqe<nxw=Ku{f!f3m!vD_^LJ$j;nlap wJD=s*<>#EwcTF1HME>xN%;NpKk9&3#@U4p(V1Bre{frwhH@3zX8yz|E4_EC^A^-pY literal 0 HcmV?d00001 diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index c1d0835be..195fce36b 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -227,8 +227,6 @@ namespace NzbDrone.Web.Controllers var episodePath = String.Empty; var episodeQuality = String.Empty; - - if (e.EpisodeFile != null) { episodePath = e.EpisodeFile.Path; @@ -252,7 +250,8 @@ namespace NzbDrone.Web.Controllers Path = episodePath, EpisodeFileId = episodeFileId, Status = e.Status.ToString(), - Quality = episodeQuality + Quality = episodeQuality, + Ignored = e.Ignored }); } diff --git a/NzbDrone.Web/Models/EpisodeModel.cs b/NzbDrone.Web/Models/EpisodeModel.cs index 2384031d9..867066f99 100644 --- a/NzbDrone.Web/Models/EpisodeModel.cs +++ b/NzbDrone.Web/Models/EpisodeModel.cs @@ -15,5 +15,6 @@ namespace NzbDrone.Web.Models public String Status { get; set; } public string AirDate { get; set; } public String Quality { get; set; } + public bool Ignored { get; set; } } } \ No newline at end of file diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index e158a9177..e204fe659 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -183,6 +183,8 @@ <Content Include="Content\Blueprint\ie.css" /> <Content Include="Content\Blueprint\screen.css" /> <Content Include="Content\Blueprint\liquid.css" /> + <Content Include="Content\Images\ignored.png" /> + <Content Include="Content\Images\notIgnored.png" /> <Content Include="Content\Images\x_16.png" /> <Content Include="Content\jQueryUI\images\ui-bg_diagonals-thick_30_a32d00_40x40.png" /> <Content Include="Content\jQueryUI\images\ui-bg_flat_0_065efe_40x100.png" /> @@ -250,6 +252,7 @@ <Content Include="favicon.ico" /> <Content Include="Global.asax" /> <Content Include="Scripts\AutoComplete.js" /> + <Content Include="Scripts\seriesDetails.js" /> <Content Include="Scripts\Plugins\jquery.livequery.js" /> <Content Include="Scripts\settingsForm.js" /> <Content Include="Scripts\Plugins\doTimeout.js" /> diff --git a/NzbDrone.Web/Scripts/seriesDetails.js b/NzbDrone.Web/Scripts/seriesDetails.js new file mode 100644 index 000000000..cc18320cb --- /dev/null +++ b/NzbDrone.Web/Scripts/seriesDetails.js @@ -0,0 +1,83 @@ +var notIgnoredImage = '../../Content/Images/notIgnored.png'; +var ignoredImage = '../../Content/Images/ignored.png'; + +$(".ignoreEpisode").live("click", function () { + var toggle = $(this); + var ignored = toggle.hasClass('ignored'); + + if (ignored) { + toggle.removeClass('ignored'); + toggle.attr('src', notIgnoredImage); + } + + else { + toggle.addClass('ignored'); + toggle.attr('src', ignoredImage); + } + + if (toggle.hasClass('ignoredEpisodesMaster')) { + var seasonNumber = toggle.attr('id').replace('master_', ''); + + toggleChildren(seasonNumber, ignored); + } +}); + +function toggleChildren(seasonNumber, ignored) { + var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); + + if (!ignored) { + ignoreEpisodes.each(function (index) { + $(this).addClass('ignored'); + $(this).attr('src', ignoredImage); + }); + } + + else { + ignoreEpisodes.each(function (index) { + $(this).removeClass('ignored'); + $(this).attr('src', notIgnoredImage); + }); + } +} + +function grid_rowBound(e) { + var dataItem = e.dataItem; + var ignored = dataItem.Ignored; + var episodeId = dataItem.EpisodeId; + + var ignoredIcon = $('#' + episodeId); + + if (ignored) { + ignoredIcon.attr('src', ignoredImage); + } + + else { + ignoredIcon.attr('src', notIgnoredImage); + ignoredIcon.removeClass('ignored'); + } +} + +function grid_dataBound(e) { + var id = $(this).attr('id'); + var seasonNumber = id.replace('seasons_', ''); + var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); + var master = $('#master_' + seasonNumber); + var count = ignoreEpisodes.length; + var ignoredCount = 0; + + ignoreEpisodes.each(function (index) { + if ($(this).hasClass('ignored')) { + ignoredCount++; + } + }); + + if (ignoredCount == count) { + master.attr('src', ignoredImage); + master.addClass('ignored'); + } + + else { + master.attr('src', notIgnoredImage); + master.removeClass('ignored'); + } +} \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index 76b412db7..21d1ae8aa 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -4,6 +4,26 @@ @section TitleContent{ @Model.Title } + +<script src="../../Scripts/seriesDetails.js" type="text/javascript"></script> + +<style> + .ignoreEpisode + { + width: 18px; + height: 18px; + padding-bottom: -6px; + } + + .ignoredEpisodesMaster + { + width: 18px; + height: 18px; + padding-left: 18px; + padding-right: -18px; + } +</style> + @section ActionMenu{ @{Html.Telerik().Menu().Name("SeriesMenu").Items(items => { @@ -50,11 +70,11 @@ .TableHtmlAttributes(new { @class = "Grid" }) .Columns(columns => { - columns.Bound(o => o.EpisodeId) + columns.Bound(o => o.Ignored) + .Title("<img src='../../Content/Images/ignored.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_" + season + "' />") .ClientTemplate( - "<input type='checkbox' name='checkedEpisodes' value='<#= EpisodeId #>' />") - .Title("") - .Width(1) + "<img src='../../Content/Images/ignored.png' class='ignoreEpisode ignoreEpisode_" + season + " ignored' id='<#= EpisodeId #>' />") + .Width(20) .HtmlAttributes(new { style = "text-align:center" }); columns.Bound(c => c.EpisodeNumber).Width(0).Title("Episode"); @@ -68,7 +88,7 @@ "<a href='#Rename' onClick=\"renameEpisode('<#= EpisodeFileId #>'); return false;\">Rename</a>"); }) .DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>")) - .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(true)) + .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(false)) .Footer(true) .DataBinding( d => @@ -78,12 +98,16 @@ c => c.Custom().Text("Rename Season").Action("RenameSeason", "Series", new { seasonId = season }) .ButtonType(GridButtonType.Text)) + .ClientEvents(clientEvents => + { + clientEvents.OnRowDataBound("grid_rowBound"); + clientEvents.OnDataBound("grid_dataBound"); + }) .Render();} </div> } @if (Model.Seasons.Any(s => s == 0)) { - <br /> <h3> Specials</h3> @@ -92,11 +116,11 @@ .TableHtmlAttributes(new { @class = "Grid" }) .Columns(columns => { - columns.Bound(o => o.EpisodeId) + columns.Bound(o => o.Ignored) + .Title("<img src='../../Content/Images/ignored.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_0' />") .ClientTemplate( - "<input type='checkbox' name='checkedEpisodes' value='<#= EpisodeId #>' />") - .Title("") - .Width(1) + "<img src='../../Content/Images/ignored.png' class='ignoreEpisode ignoreEpisode_0 ignored' id='<#= EpisodeId #>' />") + .Width(20) .HtmlAttributes(new { style = "text-align:center" }); columns.Bound(c => c.EpisodeNumber).Width(10).Title("Episode"); @@ -106,7 +130,7 @@ columns.Bound(c => c.Status).Width(10); }) .DetailView(detailView => detailView.ClientTemplate("<div><#= Overview #> </br><#= Path #> </div>")) - .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(true)) + .Sortable(rows => rows.OrderBy(epSort => epSort.Add(c => c.EpisodeNumber).Descending()).Enabled(false)) .Footer(true) .DataBinding( d => @@ -134,6 +158,8 @@ } }); } + + </script> } diff --git a/NzbDrone.Web/Views/Settings/Test.cshtml b/NzbDrone.Web/Views/Settings/Test.cshtml index 27792a291..03660916e 100644 --- a/NzbDrone.Web/Views/Settings/Test.cshtml +++ b/NzbDrone.Web/Views/Settings/Test.cshtml @@ -1,94 +1,20 @@ -<style> -/*body{ -font-family:"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif; -font-size:12px; -} -p, h1, form, button{border:0; margin:0; padding:0;}*/ -.spacer{clear:both; height:1px;} -/* ----------- My Form ----------- */ -.myform{ -margin:0 auto; -width:400px; -padding:14px; -} +<div id="image" value="5"> + <img src='../../Content/Images/watched.png' class='ignoreEpisode watched' value='5' /> +</div> -/* ----------- stylized ----------- */ -#stylized{ -border:solid 2px #b7ddf2; -background:#ebf4fb; -} -#stylized h1 { -font-size:14px; -font-weight:bold; -margin-bottom:8px; -} -#stylized p{ -font-size:11px; -color:#666666; -margin-bottom:20px; -border-bottom:solid 1px #b7ddf2; -padding-bottom:10px; -} -#stylized label{ -display:block; -font-weight:bold; -text-align:right; -width:140px; -float:left; -} -#stylized .small{ -color:#666666; -display:block; -font-size:11px; -font-weight:normal; -text-align:right; -width:140px; -} -#stylized input{ -float:left; -font-size:12px; -padding:4px 2px; -border:solid 1px #aacfe4; -width:200px; -margin:2px 0 20px 10px; -} -#stylized button{ -clear:both; -margin-left:150px; -width:125px; -height:31px; -background:#666666 url(img/button.png) no-repeat; -text-align:center; -line-height:31px; -color:#FFFFFF; -font-size:11px; -font-weight:bold; -} -</style> +<script type="text/javascript"> + $(".ignoreEpisode").live("click", function () { + var toggle = $(this); -<div id="stylized" class="myform"> - <form id="form" name="form" method="post"> - <fieldset> - <h1>Sign-up form</h1> - <p>This is the basic look of my form without table</p> + if (toggle.hasClass('watched')) { + toggle.removeClass('watched'); + toggle.attr('src', '../../Content/Images/unwatched.png'); + } - <label>Name - <span class="small">Add your name</span> - </label> - <input type="text" name="name" id="name" /> + else { + toggle.addClass('watched'); + toggle.attr('src', '../../Content/Images/watched.png'); + } + }); +</script> - <label>Email - <span class="small">Add a valid address</span> - </label> - <input type="text" name="email" id="email" /> - - <label>Password - <span class="small">Min. size 6 chars</span> - </label> - <input type="text" name="password" id="password" /> - - <button type="submit">Sign-up</button> - <div class="spacer"></div> - </fieldset> - </form> -</div> \ No newline at end of file From c88ead05850da964064fcf185d3f77bda458d5be Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 3 Aug 2011 18:45:45 -0700 Subject: [PATCH 07/15] Wired in the season and episode ignore saving. Converted setSeasonIgnore & setEpisodeIgnore to use inline SQL. Added tests for setSeasonIgnore & setEpisodeIgnore. --- NzbDrone.Core.Test/EpisodeProviderTest.cs | 112 +++++++++++++++++++ NzbDrone.Core/Providers/EpisodeProvider.cs | 27 ++--- NzbDrone.Web/Content/Images/ignored.png | Bin 3537 -> 3485 bytes NzbDrone.Web/Controllers/SeriesController.cs | 15 ++- NzbDrone.Web/Scripts/seriesDetails.js | 73 ++++++++++-- NzbDrone.Web/Views/Series/Details.cshtml | 2 +- 6 files changed, 198 insertions(+), 31 deletions(-) diff --git a/NzbDrone.Core.Test/EpisodeProviderTest.cs b/NzbDrone.Core.Test/EpisodeProviderTest.cs index 03e12b013..8e3e5672f 100644 --- a/NzbDrone.Core.Test/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/EpisodeProviderTest.cs @@ -991,5 +991,117 @@ namespace NzbDrone.Core.Test mocker.VerifyAllMocks(); } + + [Test] + public void IgnoreEpisode_Ignore() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .Have(c => c.Ignored = false) + .Build().ToList(); + + episodes.ForEach(c => db.Insert(c)); + + //Act + mocker.Resolve<EpisodeProvider>().SetEpisodeIgnore(1, true); + + //Assert + var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => e.Ignored).Should().HaveCount(1); + + mocker.VerifyAllMocks(); + } + + [Test] + public void IgnoreEpisode_RemoveIgnore() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .Have(c => c.Ignored = true) + .Build().ToList(); + + episodes.ForEach(c => db.Insert(c)); + + //Act + mocker.Resolve<EpisodeProvider>().SetEpisodeIgnore(1, false); + + //Assert + var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => !e.Ignored).Should().HaveCount(1); + + mocker.VerifyAllMocks(); + } + + [Test] + public void IgnoreSeason_Ignore() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .Have(c => c.Ignored = false) + .Build().ToList(); + + episodes.ForEach(c => db.Insert(c)); + + //Act + mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, true); + + //Assert + var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => e.Ignored).Should().HaveCount(4); + + mocker.VerifyAllMocks(); + } + + [Test] + public void IgnoreSeason_RemoveIgnore() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .Have(c => c.Ignored = true) + .Build().ToList(); + + episodes.ForEach(c => db.Insert(c)); + + //Act + mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, false); + + //Assert + var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => !e.Ignored).Should().HaveCount(4); + + mocker.VerifyAllMocks(); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 416def223..0e8f7ea4f 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -296,26 +296,23 @@ namespace NzbDrone.Core.Providers public virtual void SetSeasonIgnore(long seriesId, int seasonNumber, bool isIgnored) { Logger.Info("Setting ignore flag on Series:{0} Season:{1} to {2}", seriesId, seasonNumber, isIgnored); - var episodes = GetEpisodesBySeason(seriesId, seasonNumber); - using (var tran = _database.GetTransaction()) - { - foreach (var episode in episodes) - { - episode.Ignored = isIgnored; - _database.Update(episode); - } - - //Shouldn't run if Database is a mock since transaction will be null - if (_database.GetType().Namespace != "Castle.Proxies" && tran != null) - { - tran.Complete(); - } + _database.Execute(@"UPDATE Episodes SET Ignored = @0 + WHERE SeriesId = @1 AND SeasonNumber = @2", + isIgnored, seriesId, seasonNumber); Logger.Info("Ignore flag for Series:{0} Season:{1} successfully set to {2}", seriesId, seasonNumber, isIgnored); - } + } + + public virtual void SetEpisodeIgnore(int episodeId, bool isIgnored) + { + Logger.Info("Setting ignore flag on Episode:{0} to {1}", episodeId, isIgnored); + _database.Execute(@"UPDATE Episodes SET Ignored = @0 + WHERE EpisodeId = @1", + isIgnored, episodeId); + Logger.Info("Ignore flag for Episode:{0} successfully set to {1}", episodeId, isIgnored); } public IList<Episode> AttachSeries(IList<Episode> episodes) diff --git a/NzbDrone.Web/Content/Images/ignored.png b/NzbDrone.Web/Content/Images/ignored.png index 06b5934d61068f17b36cf16a9ea4032b6ff14036..41423bad28f74f9981c8f84d70433918c11b2798 100644 GIT binary patch delta 713 zcmV;)0yh288=V`lxe5XT&Xc<eEq}6P$r7RzN=i!l11Se6Ho&3m01#gQ(a)bh-$;zP z00G2;Pf|icf*(i~fG{w-3J_r)%z*_!y&r(|1Q34l;>AZ?8UO+aXB3EuiPiu6_wNDF z;s%iApz?x54ARp8G6Q55E_(n12qOeUMMd?2)B>Ppa4=J$paO+l1JK+6Ab*|k`t|FJ z$dUj7gc1TGA|mlX3lD%SC*V7fngm#ogV<0pe0o4;9st>aY$ref!9zeuNC;&40wR2r z@b>N7g=k_2fq0>?urSE*1q4E40njdnckkYT%>W1>a0mzr3W6;M#uo!V4Du-!%i#bd z2U0_XISW8`g7g3c5aZ^}n}7A8S%(lf@ZrM;N1`HKKwv*KdI$jnfB<3wx@H5BsTw6{ z`T6<lf%rcZqoi_>S|ZKd01!YR2PhD!_yNoUUS3{#X!eB3HJ~}*0g+}Z00a;VC{Bn3 zACTGj^pRjD13&;V5s`i1*$AfT^XJbW34{Q#p?UxyfEd@VUCR$G!+#A3CBkptz7drO zxwyE9Nra$;?*I@$Oh7|Hz5x{`7YMl4frEo1o{;4rHBj^LnFX@T0c1Bo0D;Sa@87?J z9RZBH3xx77J3Bj0M}XvrC{MtagX{$90SF*?N%-T(kB>kD9H7}P0FNgZu(7cvKubJW z&QJiAkRXEz1keUXMt{ZxkX<kVfB-_NjDG(736cn8Wo6BWR;7fhT~a{7@87>Cq6-29 z5Z3A#q!5~t4-hI=i70kKxl93@<sbkMKseh2fByV=2*i%i1a*Ln_5eta1IP@JS-9*0 z2p|?h;Rf^tsHSsdVqyZ7egV+*VF0b!&<m;y(0JYe)c25>csc<HAjX02836>)fVYYP v0%*V;01!X}?f`%Q8gK^y1kivx03g5sx?4p)vDl?L00000NkvXXu0mjfH)<h2 delta 765 zcmV<Z0s{S=8_^rExe5XU0+YK6Eq{`lno5*HLqkJ<AmsqUjEsy1*wg@X)BzxM0f-Nr zKYxBBG3EjU5DPv@eSLj?AXNau|Ni|GAi_KcC@ui%{Q#sVfbhkO7eC_C01!Ypqd-ST zr~dEXzYl;GH-Ib$Vg?d1NKXUE43Js4>;VWMj1bV&)YJzC@dBV`11d!`D1YP{w6wGW zfKili{rdHb$dUj7gc1Vk>gw@8iw}S-C*V7fngpPM4j>F-L&fmv0hxIKWCyaH009IK z0TmS$kmU=A@KM6;+qV~@i5&#ug{rEmAj20B2#p0myBO}=xdS!>Ab`LjproV(wj3B= z4EQj}r&uh91CSg@4H4!n0DsvD(gP4cjLpr>pg6xkgwGE=c<{iHm`GPpP}mR64R`@C z`V0U9hz01H4a67<N|?xeSy@?7%5H#i8y-J?+z68c`4E&gh%j~oKmdUppg?45egI=g zNlD3rvKW#ahz)lDDC-evrUF0!v4G-)NbmufjZYs5W-<T-5EBvE2Y-x#hQiB*XV0E} zgceXR`35uxC=gj#9RLU*P!t3}%Wy&kPXZ{b!8os8y=p`bc4Pp`2}F4Q0x(?#00a=z zn>TMhLJN}%1YGMNARrJ=u-F5t0jVWm)&-EAAiDtq2wV`od-v`mKR>?%5h1aFmzS5} z^XJc4%L1SrF@+h(=YI|#fdIq-2q1V#2oeAq0CEH<3kBeF1UEN#0<^?~rFKwG0u{#& z1hVo5AWr!7=@Yzm1PCCM$_VH)kVGISCucsiDkW6yk^&08fB!xaT@WCEu(k(43OP79 zHb6tefs(2a=;H$*U;p^=;~_SAfB?eT9sp_h`SYhEG(jC8qi#I_(&GR!1DE9>01!Yd zgnaPl&mT}t=g7>=EDuc^ploXZt=UitDrlVoisuc$vfv>xAp{UWj04>>0tlc1ZxsUs v(11GtAb<wk0RRCs;0^!?paFLPK!5=N6L@<N7M?Qv00000NkvXXu0mjf11=)b diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index 195fce36b..59566e3bb 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -148,12 +148,17 @@ namespace NzbDrone.Web.Controllers } [HttpPost] - public JsonResult SaveSeason(int seriesId, int seasonNumber, bool monitored) + public JsonResult SaveSeasonIgnore(int seriesId, int seasonNumber, bool ignored) { - if (_episodeProvider.IsIgnored(seriesId, seasonNumber) == monitored) - { - _episodeProvider.SetSeasonIgnore(seriesId, seasonNumber, !monitored); - } + _episodeProvider.SetSeasonIgnore(seriesId, seasonNumber, ignored); + + return new JsonResult { Data = "ok" }; + } + + [HttpPost] + public JsonResult SaveEpisodeIgnore(int episodeId, bool ignored) + { + _episodeProvider.SetEpisodeIgnore(episodeId, ignored); return new JsonResult { Data = "ok" }; } diff --git a/NzbDrone.Web/Scripts/seriesDetails.js b/NzbDrone.Web/Scripts/seriesDetails.js index cc18320cb..36544f364 100644 --- a/NzbDrone.Web/Scripts/seriesDetails.js +++ b/NzbDrone.Web/Scripts/seriesDetails.js @@ -1,5 +1,8 @@ var notIgnoredImage = '../../Content/Images/notIgnored.png'; var ignoredImage = '../../Content/Images/ignored.png'; +var seriesId = 0; +var saveSeasonIgnoreUrl = '../Series/SaveSeasonIgnore'; +var saveEpisodeIgnoreUrl = '../Series/SaveEpisodeIgnore'; $(".ignoreEpisode").live("click", function () { var toggle = $(this); @@ -15,17 +18,31 @@ $(".ignoreEpisode").live("click", function () { toggle.attr('src', ignoredImage); } + var seasonNumber = 0; + + //Flip the ignored to the new state (We want the new value moving forward) + ignored = !ignored; + if (toggle.hasClass('ignoredEpisodesMaster')) { - var seasonNumber = toggle.attr('id').replace('master_', ''); + seasonNumber = toggle.attr('id').replace('master_', ''); toggleChildren(seasonNumber, ignored); + saveSeasonIgnore(seasonNumber, ignored); + } + + else { + //Check to see if this is the last one ignored or the first not ignored + seasonNumber = toggle.attr('class').split(/\s+/)[1].replace('ignoreEpisode_', ''); + var episodeId = toggle.attr('id'); + toggleMaster(seasonNumber, ignored); + saveEpisodeIgnore(episodeId, ignored); } }); function toggleChildren(seasonNumber, ignored) { var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); - if (!ignored) { + if (ignored) { ignoreEpisodes.each(function (index) { $(this).addClass('ignored'); $(this).attr('src', ignoredImage); @@ -37,9 +54,26 @@ function toggleChildren(seasonNumber, ignored) { $(this).removeClass('ignored'); $(this).attr('src', notIgnoredImage); }); - } + } } +function toggleMaster(seasonNumber) { + var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); + var ignoredCount = ignoreEpisodes.filter('.ignored').length; + var master = $('#master_' + seasonNumber); + + if (ignoreEpisodes.length == ignoredCount) { + master.attr('src', ignoredImage); + master.addClass('ignored'); + } + + else { + master.attr('src', notIgnoredImage); + master.removeClass('ignored'); + } +} + +//Functions called by the Telerik Season Grid function grid_rowBound(e) { var dataItem = e.dataItem; var ignored = dataItem.Ignored; @@ -55,6 +89,9 @@ function grid_rowBound(e) { ignoredIcon.attr('src', notIgnoredImage); ignoredIcon.removeClass('ignored'); } + + if (seriesId == 0) + seriesId = dataItem.SeriesId } function grid_dataBound(e) { @@ -63,13 +100,7 @@ function grid_dataBound(e) { var ignoreEpisodes = $('.ignoreEpisode_' + seasonNumber); var master = $('#master_' + seasonNumber); var count = ignoreEpisodes.length; - var ignoredCount = 0; - - ignoreEpisodes.each(function (index) { - if ($(this).hasClass('ignored')) { - ignoredCount++; - } - }); + var ignoredCount = ignoreEpisodes.filter('.ignored').length; if (ignoredCount == count) { master.attr('src', ignoredImage); @@ -80,4 +111,26 @@ function grid_dataBound(e) { master.attr('src', notIgnoredImage); master.removeClass('ignored'); } +} + +function saveSeasonIgnore(seasonNumber, ignored) { + $.ajax({ + type: "POST", + url: saveSeasonIgnoreUrl, + data: jQuery.param({ seriesId: seriesId, seasonNumber: seasonNumber, ignored: ignored }), + error: function (req, status, error) { + alert("Sorry! We could save the ignore settings for Series: " + seriesId + ", Season: " + seasonNumber + " at this time. " + error); + } + }); +} + +function saveEpisodeIgnore(episodeId, ignored) { + $.ajax({ + type: "POST", + url: saveEpisodeIgnoreUrl, + data: jQuery.param({ episodeId: episodeId, ignored: ignored }), + error: function (req, status, error) { + alert("Sorry! We could save the ignore settings for Episode: " + episodeId + " at this time. " + error); + } + }); } \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index 21d1ae8aa..2a4a44ddc 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -159,7 +159,7 @@ }); } - + seriesId = @Model.SeriesId; </script> } From 4f68d07c9a723b26467352f57c923fac0b5df347 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 3 Aug 2011 22:44:24 -0700 Subject: [PATCH 08/15] Fixed issue with missing episodes incorrectly using include specials. Added tests for missing episodes. --- NzbDrone.Core.Test/EpisodeProviderTest.cs | 115 ++++++++++++++++++ NzbDrone.Core/Providers/EpisodeProvider.cs | 13 +- NzbDrone.Web/Controllers/MissingController.cs | 5 +- 3 files changed, 125 insertions(+), 8 deletions(-) diff --git a/NzbDrone.Core.Test/EpisodeProviderTest.cs b/NzbDrone.Core.Test/EpisodeProviderTest.cs index 8e3e5672f..029f134e6 100644 --- a/NzbDrone.Core.Test/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/EpisodeProviderTest.cs @@ -1103,5 +1103,120 @@ namespace NzbDrone.Core.Test mocker.VerifyAllMocks(); } + + [Test] + public void IgnoreSeason_Ignore_Half() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .WhereTheFirst(2) + .Have(c => c.Ignored = false) + .AndTheRemaining() + .Have(c => c.Ignored = true) + .Build().ToList(); + + episodes.ForEach(c => db.Insert(c)); + + //Act + mocker.Resolve<EpisodeProvider>().SetSeasonIgnore(10, 1, true); + + //Assert + var episodesInDb = db.Fetch<Episode>(@"SELECT * FROM Episodes"); + + episodesInDb.Should().HaveCount(4); + episodesInDb.Where(e => e.Ignored).Should().HaveCount(4); + + mocker.VerifyAllMocks(); + } + + [Test] + public void EpisodesWithoutFiles_no_specials() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var series = Builder<Series>.CreateNew() + .With(s => s.SeriesId = 10) + .Build(); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) + .WhereTheFirst(2) + .Have(c => c.EpisodeFileId = 0) + .Build().ToList(); + + var specials = Builder<Episode>.CreateListOfSize(2) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 0) + .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) + .Have(c => c.EpisodeFileId = 0) + .Build().ToList(); + + db.Insert(series); + db.InsertMany(episodes); + db.InsertMany(specials); + + //Act + var missingFiles= mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(false); + + //Assert + missingFiles.Should().HaveCount(2); + missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(2); + + mocker.VerifyAllMocks(); + } + + [Test] + public void EpisodesWithoutFiles_with_specials() + { + var db = MockLib.GetEmptyDatabase(); + var mocker = new AutoMoqer(); + mocker.SetConstant(db); + + var series = Builder<Series>.CreateNew() + .With(s => s.SeriesId = 10) + .Build(); + + var episodes = Builder<Episode>.CreateListOfSize(4) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 1) + .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) + .WhereTheFirst(2) + .Have(c => c.EpisodeFileId = 0) + .Build().ToList(); + + var specials = Builder<Episode>.CreateListOfSize(2) + .WhereAll() + .Have(c => c.SeriesId = 10) + .Have(c => c.SeasonNumber = 0) + .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) + .Have(c => c.EpisodeFileId = 0) + .Build().ToList(); + + db.Insert(series); + db.InsertMany(episodes); + db.InsertMany(specials); + + //Act + var missingFiles = mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(true); + + //Assert + missingFiles.Should().HaveCount(4); + missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(4); + + mocker.VerifyAllMocks(); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 0e8f7ea4f..40c0475c2 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -159,12 +159,15 @@ namespace NzbDrone.Core.Providers public virtual IList<Episode> EpisodesWithoutFiles(bool includeSpecials) { - var episodes = _database.Query<Episode>("WHERE (EpisodeFileId=0 OR EpisodeFileId=NULL) AND AirDate<=@0", + var episodes = _database.Query<Episode, Series>(@"SELECT Episodes.*, Series.Title FROM Episodes + INNER JOIN Series + ON Episodes.SeriesId = Series.SeriesId + WHERE (EpisodeFileId=0 OR EpisodeFileId=NULL) AND AirDate<=@0", DateTime.Now.Date); - if (includeSpecials) + if (!includeSpecials) return episodes.Where(e => e.SeasonNumber > 0).ToList(); - return AttachSeries(episodes.ToList()); + return episodes.ToList(); } public virtual IList<Episode> GetEpisodesByFileId(int episodeFileId) @@ -298,8 +301,8 @@ namespace NzbDrone.Core.Providers Logger.Info("Setting ignore flag on Series:{0} Season:{1} to {2}", seriesId, seasonNumber, isIgnored); _database.Execute(@"UPDATE Episodes SET Ignored = @0 - WHERE SeriesId = @1 AND SeasonNumber = @2", - isIgnored, seriesId, seasonNumber); + WHERE SeriesId = @1 AND SeasonNumber = @2 AND Ignored = @3", + isIgnored, seriesId, seasonNumber, !isIgnored); Logger.Info("Ignore flag for Series:{0} Season:{1} successfully set to {2}", seriesId, seasonNumber, isIgnored); } diff --git a/NzbDrone.Web/Controllers/MissingController.cs b/NzbDrone.Web/Controllers/MissingController.cs index 6b94dce08..581c3f541 100644 --- a/NzbDrone.Web/Controllers/MissingController.cs +++ b/NzbDrone.Web/Controllers/MissingController.cs @@ -26,10 +26,9 @@ namespace NzbDrone.Web.Controllers [GridAction] public ActionResult _AjaxBinding() { - //TODO: possible subsonic bug, IQuarible causes some issues so ToList() is called - //https://github.com/subsonic/SubSonic-3.0/issues/263 + var missingEpisodes = _episodeProvider.EpisodesWithoutFiles(false); - var missing = _episodeProvider.EpisodesWithoutFiles(true).Select(e => new MissingEpisodeModel + var missing = missingEpisodes.Select(e => new MissingEpisodeModel { EpisodeId = e.EpisodeId, SeasonNumber = e.SeasonNumber, From 412a3fc476d4690a138bba244ce867d76acc143c Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 3 Aug 2011 22:56:42 -0700 Subject: [PATCH 09/15] EpisodesWithoutFiles now skips ignored episodes, Tests updated. --- NzbDrone.Core.Test/EpisodeProviderTest.cs | 22 ++++++++++++++++++---- NzbDrone.Core/Providers/EpisodeProvider.cs | 2 +- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/NzbDrone.Core.Test/EpisodeProviderTest.cs b/NzbDrone.Core.Test/EpisodeProviderTest.cs index 029f134e6..a4f078c5f 100644 --- a/NzbDrone.Core.Test/EpisodeProviderTest.cs +++ b/NzbDrone.Core.Test/EpisodeProviderTest.cs @@ -1151,8 +1151,11 @@ namespace NzbDrone.Core.Test .Have(c => c.SeriesId = 10) .Have(c => c.SeasonNumber = 1) .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) + .Have(c => c.Ignored = true) .WhereTheFirst(2) .Have(c => c.EpisodeFileId = 0) + .WhereSection(1, 2) + .Have(c => c.Ignored = false) .Build().ToList(); var specials = Builder<Episode>.CreateListOfSize(2) @@ -1161,6 +1164,10 @@ namespace NzbDrone.Core.Test .Have(c => c.SeasonNumber = 0) .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) .Have(c => c.EpisodeFileId = 0) + .WhereTheFirst(1) + .Have(c => c.Ignored = true) + .AndTheRemaining() + .Have(c => c.Ignored = false) .Build().ToList(); db.Insert(series); @@ -1171,8 +1178,8 @@ namespace NzbDrone.Core.Test var missingFiles= mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(false); //Assert - missingFiles.Should().HaveCount(2); - missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(2); + missingFiles.Should().HaveCount(1); + missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(1); mocker.VerifyAllMocks(); } @@ -1193,8 +1200,11 @@ namespace NzbDrone.Core.Test .Have(c => c.SeriesId = 10) .Have(c => c.SeasonNumber = 1) .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) + .Have(c => c.Ignored = true) .WhereTheFirst(2) .Have(c => c.EpisodeFileId = 0) + .WhereSection(1,2) + .Have(c => c.Ignored = false) .Build().ToList(); var specials = Builder<Episode>.CreateListOfSize(2) @@ -1203,6 +1213,10 @@ namespace NzbDrone.Core.Test .Have(c => c.SeasonNumber = 0) .Have(c => c.AirDate = DateTime.Today.AddDays(-4)) .Have(c => c.EpisodeFileId = 0) + .WhereTheFirst(1) + .Have(c => c.Ignored = true) + .AndTheRemaining() + .Have(c => c.Ignored = false) .Build().ToList(); db.Insert(series); @@ -1213,8 +1227,8 @@ namespace NzbDrone.Core.Test var missingFiles = mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(true); //Assert - missingFiles.Should().HaveCount(4); - missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(4); + missingFiles.Should().HaveCount(2); + missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(2); mocker.VerifyAllMocks(); } diff --git a/NzbDrone.Core/Providers/EpisodeProvider.cs b/NzbDrone.Core/Providers/EpisodeProvider.cs index 40c0475c2..d0cf2858b 100644 --- a/NzbDrone.Core/Providers/EpisodeProvider.cs +++ b/NzbDrone.Core/Providers/EpisodeProvider.cs @@ -162,7 +162,7 @@ namespace NzbDrone.Core.Providers var episodes = _database.Query<Episode, Series>(@"SELECT Episodes.*, Series.Title FROM Episodes INNER JOIN Series ON Episodes.SeriesId = Series.SeriesId - WHERE (EpisodeFileId=0 OR EpisodeFileId=NULL) AND AirDate<=@0", + WHERE (EpisodeFileId=0 OR EpisodeFileId=NULL) AND Ignored = 0 AND AirDate<=@0", DateTime.Now.Date); if (!includeSpecials) return episodes.Where(e => e.SeasonNumber > 0).ToList(); From 9cf77955d0811d2ada810399a8076e93e45c0e89 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Wed, 3 Aug 2011 23:59:08 -0700 Subject: [PATCH 10/15] Removed broken season editor from Series/Index editor. Cleaned up series editor UI, with descriptions. --- NzbDrone.Web/Models/SeriesModel.cs | 4 ++ NzbDrone.Web/NzbDrone.Web.csproj | 3 -- .../Series/EditorTemplates/SeriesModel.cshtml | 46 +++++++++---------- NzbDrone.Web/Views/Series/Index.cshtml | 29 +----------- NzbDrone.Web/Views/Series/SeasonEditor.cshtml | 36 --------------- 5 files changed, 28 insertions(+), 90 deletions(-) delete mode 100644 NzbDrone.Web/Views/Series/SeasonEditor.cshtml diff --git a/NzbDrone.Web/Models/SeriesModel.cs b/NzbDrone.Web/Models/SeriesModel.cs index 7a2429acb..be1111df2 100644 --- a/NzbDrone.Web/Models/SeriesModel.cs +++ b/NzbDrone.Web/Models/SeriesModel.cs @@ -26,16 +26,20 @@ namespace NzbDrone.Web.Models //View & Edit [DisplayName("Path")] + [Description("Where should NzbDrone store episodes for this series?")] public string Path { get; set; } [DisplayName("Quality Profile")] + [Description("Which Quality Profile should NzbDrone use to download episodes?")] public virtual int QualityProfileId { get; set; } //Editing Only [DisplayName("Use Season Folder")] + [Description("Should downloaded episodes be stored in season folders?")] public bool SeasonFolder { get; set; } [DisplayName("Monitored")] + [Description("Should NzbDrone download episodes for this series?")] public bool Monitored { get; set; } [DisplayName("Season Editor")] diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index e204fe659..f7ad632b0 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -320,9 +320,6 @@ <ItemGroup> <Content Include="Views\Series\EditorTemplates\SeriesModel.cshtml" /> </ItemGroup> - <ItemGroup> - <Content Include="Views\Series\SeasonEditor.cshtml" /> - </ItemGroup> <ItemGroup> <Content Include="Views\Series\SingleSeason.cshtml" /> </ItemGroup> diff --git a/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml b/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml index 66426ce84..8beff06af 100644 --- a/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml +++ b/NzbDrone.Web/Views/Series/EditorTemplates/SeriesModel.cshtml @@ -5,31 +5,29 @@ Layout = null; } -<div class=".settingsForm"> - @Html.HiddenFor(m => m.SeriesId) - <label class="labelClass">@Html.LabelFor(m => m.Monitored) - <span class="small">@Html.DescriptionFor(m => m.Monitored)</span> - </label> - @Html.CheckBoxFor(m => m.Monitored, new { @class = "inputClass checkClass" }) - <label class="labelClass">@Html.LabelFor(m => m.SeasonFolder) - <span class="small">@Html.DescriptionFor(m => m.SeasonFolder)</span> - </label> - @Html.CheckBoxFor(m => m.SeasonFolder, new { @class = "inputClass checkClass" }) - <label class="labelClass">@Html.LabelFor(m => m.QualityProfileId) - <span class="small">@Html.DescriptionFor(m => m.QualityProfileId)</span> - </label> - @Html.DropDownListFor(m => m.QualityProfileId, (SelectList)ViewData["SelectList"], new { @class = "inputClass" }) - - <div id="seasonEditorSection"> - <div style="font-weight: bold; padding-right: 15px; padding-bottom: 5px;"> - @Html.LabelFor(m => m.SeasonEditor) - <span id="seasonEditorLoader"> - <img src="../../../Content/Images/ajax-loader.gif" width="14px" height="14px" style="margin-bottom: -2px;" /></span> - </div> - <div id="season-editor"> - </div> +<link rel="stylesheet" type="text/css" href="../../../Content/Settings.css" /> + +<div id="stylized" style="border-color: transparent;"> + <div class="settingsForm clearfix"> + @Html.HiddenFor(m => m.SeriesId) + <label class="labelClass">@Html.LabelFor(m => m.Monitored) + <span class="small">@Html.DescriptionFor(m => m.Monitored)</span> + </label> + @Html.CheckBoxFor(m => m.Monitored, new { @class = "inputClass checkClass" }) + <label class="labelClass">@Html.LabelFor(m => m.SeasonFolder) + <span class="small">@Html.DescriptionFor(m => m.SeasonFolder)</span> + </label> + @Html.CheckBoxFor(m => m.SeasonFolder, new { @class = "inputClass checkClass" }) + <label class="labelClass">@Html.LabelFor(m => m.QualityProfileId) + <span class="small">@Html.DescriptionFor(m => m.QualityProfileId)</span> + </label> + @Html.DropDownListFor(m => m.QualityProfileId, (SelectList)ViewData["SelectList"], new { @class = "inputClass" }) + <label class="labelClass">@Html.LabelFor(m => m.Path) + <span class="small">@Html.DescriptionFor(m => m.Path)</span> + </label> + @Html.TextBoxFor(m => m.Path, new { @class = "inputClass" }) </div> </div> -<span id="ajaxSaveWheel" style="display: none; float: right; padding-right: 368px; +<span id="ajaxSaveWheel" style="display: none; float: right; padding-right: 550px; padding-top: 1.5px;"> <img src="../../../Content/Images/ajax-loader.gif" width="20px" height="20px" /></span> \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/Index.cshtml b/NzbDrone.Web/Views/Series/Index.cshtml index 380e19584..82a08a942 100644 --- a/NzbDrone.Web/Views/Series/Index.cshtml +++ b/NzbDrone.Web/Views/Series/Index.cshtml @@ -4,6 +4,7 @@ @section TitleContent{ NZBDrone } + <style> /* progress bar container */ .progressbar @@ -46,6 +47,7 @@ NZBDrone background: #E5ECF9; } </style> + @section ActionMenu{ @{Html.RenderPartial("SubMenu");} } @@ -103,37 +105,10 @@ NZBDrone .closest(".t-window") .data("tWindow") .center(); - - var seriesId = args.dataItem.SeriesId; - var url = '@Url.Action("SeasonEditor", "Series")'; - $('#season-editor').load(url, { seriesId: seriesId }, function (response, status, xhr) { - $('#seasonEditorLoader').hide(); - }); } function grid_save(e) { $('#ajaxSaveWheel').show(); - - var seasonEditor = e.form.SeasonEditor_collection; - var saveSeasonEditUrl = '@Url.Action("SaveSeason", "Series")'; - - jQuery.each(seasonEditor, function () { - var guid = $(this).val(); - var prefix = '#SeasonEditor_' + guid + '__'; - var seriesId = $(prefix + 'SeriesId').val(); - var seasonNumber = $(prefix + 'SeasonNumber').val(); - var monitored = $(prefix + 'Monitored').attr('checked'); - - $.ajax({ - type: "POST", - url: saveSeasonEditUrl, - data: jQuery.param({ seriesId: seriesId, seasonNumber: seasonNumber, monitored: monitored }), - error: function (req, status, error) { - alert("Sorry! We could save season changes at this time. " + error); - }, - success: function (data, textStatus, jqXHR) { } - }); - }); } function grid_rowBound(e) { diff --git a/NzbDrone.Web/Views/Series/SeasonEditor.cshtml b/NzbDrone.Web/Views/Series/SeasonEditor.cshtml deleted file mode 100644 index 3aad85958..000000000 --- a/NzbDrone.Web/Views/Series/SeasonEditor.cshtml +++ /dev/null @@ -1,36 +0,0 @@ -@using NzbDrone.Web.Models; -@model List<SeasonEditModel> -@{ - Layout = null; -} -<div style="vertical-align: middle"> - @foreach (var season in Model) - { - Html.RenderAction("GetSingleSeasonView", "Series", season); - } -</div> -@section Scripts{ - <script type="text/javascript"> - var lastChecked = null; - - $(document).ready(function () { - $('.chkbox').click(function (event) { - if (!lastChecked) { - lastChecked = this; - return; - } - - if (event.shiftKey) { - var start = $('.chkbox').index(this); - var end = $('.chkbox').index(lastChecked); - - for (i = Math.min(start, end); i <= Math.max(start, end); i++) { - $('.chkbox')[i].checked = lastChecked.checked; - } - } - - lastChecked = this; - }); - }); - </script> -} From d85a8bc15d116585f5a07774e401b661ce6087f2 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Thu, 4 Aug 2011 21:22:42 -0700 Subject: [PATCH 11/15] If an unsorted episode that had a series that was not being watched it would prevent additional episodes from being processed. --- NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs index 509c9bea5..3af0c7557 100644 --- a/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs +++ b/NzbDrone.Core/Providers/Jobs/PostDownloadScanJob.cs @@ -79,8 +79,8 @@ namespace NzbDrone.Core.Providers.Jobs if (series == null) { - Logger.Warn("Unable to Import new download, series doesn't exist in database."); - return; + Logger.Warn("Unable to Import new download [{0}], series doesn't exist in database.", subfolder); + continue; } var importedFiles = _diskScanProvider.Scan(series, subfolder); From 3507b97887fbd0d16378ef8ded610e8ed6fe214f Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Thu, 4 Aug 2011 21:38:18 -0700 Subject: [PATCH 12/15] Settings save buttons are disabled until jQuery loads and attribute is removed (prevents saving without ajax if saving too quickly). Notifications are set when saving now (progress) until basic is redone. --- .../Model/Notification/BasicNotification.cs | 2 +- NzbDrone.Web/Content/Settings.css | 8 +++ .../Controllers/NotificationController.cs | 2 + .../Controllers/SettingsController.cs | 64 +++++++++---------- NzbDrone.Web/Scripts/settingsForm.js | 1 + .../Views/Settings/EpisodeSorting.cshtml | 2 +- NzbDrone.Web/Views/Settings/Indexers.cshtml | 9 ++- .../Views/Settings/Notifications.cshtml | 2 +- NzbDrone.Web/Views/Settings/Quality.cshtml | 2 +- NzbDrone.Web/Views/Settings/Sabnzbd.cshtml | 2 +- NzbDrone.Web/Views/Settings/Test.cshtml | 31 ++++----- 11 files changed, 65 insertions(+), 60 deletions(-) diff --git a/NzbDrone.Core/Model/Notification/BasicNotification.cs b/NzbDrone.Core/Model/Notification/BasicNotification.cs index f06172336..225dc97a7 100644 --- a/NzbDrone.Core/Model/Notification/BasicNotification.cs +++ b/NzbDrone.Core/Model/Notification/BasicNotification.cs @@ -6,7 +6,7 @@ namespace NzbDrone.Core.Model.Notification { public BasicNotification() { - Id = Guid.Empty; + Id = Guid.NewGuid(); } /// <summary> diff --git a/NzbDrone.Web/Content/Settings.css b/NzbDrone.Web/Content/Settings.css index 2169714bf..fc8d817e7 100644 --- a/NzbDrone.Web/Content/Settings.css +++ b/NzbDrone.Web/Content/Settings.css @@ -107,4 +107,12 @@ p, h1, form, button{border:0; margin:0; padding:0;} width: 20px; height: 20px; display: none; +} + +#save_button[disabled="disabled"] +{ + padding: 0px 6px 0px 6px; + border: 2px outset ButtonFace; + color: lightgrey; + cursor: progress; } \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/NotificationController.cs b/NzbDrone.Web/Controllers/NotificationController.cs index c1796cac0..43a02b5c0 100644 --- a/NzbDrone.Web/Controllers/NotificationController.cs +++ b/NzbDrone.Web/Controllers/NotificationController.cs @@ -67,6 +67,8 @@ namespace NzbDrone.Web.Controllers private string GetCurrentMessage() { + var notes = _notifications.ProgressNotifications; + if (_notifications.ProgressNotifications.Count != 0) return _notifications.ProgressNotifications[0].CurrentMessage; diff --git a/NzbDrone.Web/Controllers/SettingsController.cs b/NzbDrone.Web/Controllers/SettingsController.cs index 5c2a36436..f263828bf 100644 --- a/NzbDrone.Web/Controllers/SettingsController.cs +++ b/NzbDrone.Web/Controllers/SettingsController.cs @@ -32,6 +32,7 @@ namespace NzbDrone.Web.Controllers private readonly DiskProvider _diskProvider; private readonly SeriesProvider _seriesProvider; private readonly ExternalNotificationProvider _externalNotificationProvider; + private readonly ProgressNotification _progressNotification; public SettingsController(ConfigProvider configProvider, IndexerProvider indexerProvider, QualityProvider qualityProvider, RootDirProvider rootDirProvider, @@ -48,6 +49,8 @@ namespace NzbDrone.Web.Controllers _notificationProvider = notificationProvider; _diskProvider = diskProvider; _seriesProvider = seriesProvider; + + _progressNotification = new ProgressNotification("Settings"); } public ActionResult Test() @@ -270,9 +273,7 @@ namespace NzbDrone.Web.Controllers [HttpPost] public ActionResult SaveIndexers(IndexerSettingsModel data) { - var basicNotification = new BasicNotification(); - basicNotification.Type = BasicNotificationType.Info; - basicNotification.AutoDismiss = true; + _notificationProvider.Register(_progressNotification); if (ModelState.IsValid) { @@ -304,22 +305,20 @@ namespace NzbDrone.Web.Controllers _configProvider.NewzbinUsername = data.NewzbinUsername; _configProvider.NewzbinPassword = data.NewzbinPassword; - basicNotification.Title = SETTINGS_SAVED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_SAVED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_SAVED); } - basicNotification.Title = SETTINGS_FAILED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_FAILED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_FAILED); } [HttpPost] public ActionResult SaveSabnzbd(SabnzbdSettingsModel data) { - var basicNotification = new BasicNotification(); - basicNotification.Type = BasicNotificationType.Info; - basicNotification.AutoDismiss = true; + _notificationProvider.Register(_progressNotification); if (ModelState.IsValid) { @@ -332,22 +331,20 @@ namespace NzbDrone.Web.Controllers _configProvider.SabTvPriority = data.SabTvPriority; _configProvider.SabDropDirectory = data.SabDropDirectory; - basicNotification.Title = SETTINGS_SAVED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_SAVED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_SAVED); } - basicNotification.Title = SETTINGS_FAILED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_FAILED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_FAILED); } [HttpPost] public ActionResult SaveQuality(QualityModel data) { - var basicNotification = new BasicNotification(); - basicNotification.Type = BasicNotificationType.Info; - basicNotification.AutoDismiss = true; + _notificationProvider.Register(_progressNotification); if (ModelState.IsValid) { @@ -379,22 +376,21 @@ namespace NzbDrone.Web.Controllers _qualityProvider.Update(profile); } - basicNotification.Title = SETTINGS_SAVED; - _notificationProvider.Register(basicNotification); + + _progressNotification.CurrentMessage = SETTINGS_SAVED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_SAVED); } - basicNotification.Title = SETTINGS_FAILED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_FAILED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_FAILED); } [HttpPost] public ActionResult SaveNotifications(NotificationSettingsModel data) { - var basicNotification = new BasicNotification(); - basicNotification.Type = BasicNotificationType.Info; - basicNotification.AutoDismiss = true; + _notificationProvider.Register(_progressNotification); if (ModelState.IsValid) { @@ -411,22 +407,20 @@ namespace NzbDrone.Web.Controllers _configProvider.XbmcUsername = data.XbmcUsername; _configProvider.XbmcPassword = data.XbmcPassword; - basicNotification.Title = SETTINGS_SAVED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_SAVED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_SAVED); } - basicNotification.Title = SETTINGS_FAILED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_FAILED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_FAILED); } [HttpPost] public ActionResult SaveEpisodeSorting(EpisodeSortingModel data) { - var basicNotification = new BasicNotification(); - basicNotification.Type = BasicNotificationType.Info; - basicNotification.AutoDismiss = true; + _notificationProvider.Register(_progressNotification); if (ModelState.IsValid) { @@ -440,13 +434,13 @@ namespace NzbDrone.Web.Controllers _configProvider.SortingNumberStyle = data.NumberStyle; _configProvider.SortingMultiEpisodeStyle = data.MultiEpisodeStyle; - basicNotification.Title = SETTINGS_SAVED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_SAVED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_SAVED); } - basicNotification.Title = SETTINGS_FAILED; - _notificationProvider.Register(basicNotification); + _progressNotification.CurrentMessage = SETTINGS_FAILED; + _progressNotification.Status = ProgressNotificationStatus.Completed; return Content(SETTINGS_FAILED); } } diff --git a/NzbDrone.Web/Scripts/settingsForm.js b/NzbDrone.Web/Scripts/settingsForm.js index d4b051599..dfb7b8609 100644 --- a/NzbDrone.Web/Scripts/settingsForm.js +++ b/NzbDrone.Web/Scripts/settingsForm.js @@ -7,6 +7,7 @@ resetForm: false }; $('#form').ajaxForm(options); + $('#save_button').removeAttr('disabled'); }); function showRequest(formData, jqForm, options) { diff --git a/NzbDrone.Web/Views/Settings/EpisodeSorting.cshtml b/NzbDrone.Web/Views/Settings/EpisodeSorting.cshtml index dd96cf082..1bc05aa36 100644 --- a/NzbDrone.Web/Views/Settings/EpisodeSorting.cshtml +++ b/NzbDrone.Web/Views/Settings/EpisodeSorting.cshtml @@ -81,7 +81,7 @@ </div> </div> - <button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> + <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> } </div> <div id="result" class="hiddenResult"></div> diff --git a/NzbDrone.Web/Views/Settings/Indexers.cshtml b/NzbDrone.Web/Views/Settings/Indexers.cshtml index 1f0e104f7..44094b9fb 100644 --- a/NzbDrone.Web/Views/Settings/Indexers.cshtml +++ b/NzbDrone.Web/Views/Settings/Indexers.cshtml @@ -9,8 +9,7 @@ { padding-top: 20px; } - -</style> + </style> } @section TitleContent{ @@ -126,10 +125,14 @@ } </div> <br/> - <button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> + <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> } </div> <div id="result" class="hiddenResult"></div> +} + +@section Scripts{ + <script src="/Scripts/settingsForm.js" type="text/javascript"></script> } \ No newline at end of file diff --git a/NzbDrone.Web/Views/Settings/Notifications.cshtml b/NzbDrone.Web/Views/Settings/Notifications.cshtml index 0a040bdae..e5e859d63 100644 --- a/NzbDrone.Web/Views/Settings/Notifications.cshtml +++ b/NzbDrone.Web/Views/Settings/Notifications.cshtml @@ -92,7 +92,7 @@ @Html.TextBoxFor(m => m.XbmcPassword, new { @class = "inputClass" }) </div> - <button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> + <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> } </div> diff --git a/NzbDrone.Web/Views/Settings/Quality.cshtml b/NzbDrone.Web/Views/Settings/Quality.cshtml index b27bed24c..7be8f82ed 100644 --- a/NzbDrone.Web/Views/Settings/Quality.cshtml +++ b/NzbDrone.Web/Views/Settings/Quality.cshtml @@ -40,7 +40,7 @@ Settings </div> </div> <br /> - <button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> + <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> </div> } </div> diff --git a/NzbDrone.Web/Views/Settings/Sabnzbd.cshtml b/NzbDrone.Web/Views/Settings/Sabnzbd.cshtml index a731bc2d7..03696c16c 100644 --- a/NzbDrone.Web/Views/Settings/Sabnzbd.cshtml +++ b/NzbDrone.Web/Views/Settings/Sabnzbd.cshtml @@ -66,7 +66,7 @@ </label> @Html.TextBoxFor(m => m.SabDropDirectory, new { @class = "inputClass folderLookup" }) - <button type="submit" id="save_button" >Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> + <button type="submit" id="save_button" disabled="disabled">Save</button><img src="../../Content/Images/ajax-loader.gif" alt="Loader" id="saveAjax"/> } </div> diff --git a/NzbDrone.Web/Views/Settings/Test.cshtml b/NzbDrone.Web/Views/Settings/Test.cshtml index 03660916e..18d52b0b0 100644 --- a/NzbDrone.Web/Views/Settings/Test.cshtml +++ b/NzbDrone.Web/Views/Settings/Test.cshtml @@ -1,20 +1,17 @@ -<div id="image" value="5"> - <img src='../../Content/Images/watched.png' class='ignoreEpisode watched' value='5' /> -</div> - -<script type="text/javascript"> - $(".ignoreEpisode").live("click", function () { - var toggle = $(this); - - if (toggle.hasClass('watched')) { - toggle.removeClass('watched'); - toggle.attr('src', '../../Content/Images/unwatched.png'); - } - - else { - toggle.addClass('watched'); - toggle.attr('src', '../../Content/Images/watched.png'); - } +<script type="text/javascript"> + $(document).ready(function () { + //$('#save_button').attr('disabled', ''); }); </script> +<style> + #save_button[disabled="disabled"] + { + padding: 0px 6px 0px 6px; + border: 2px outset ButtonFace; + color: GrayText; + cursor: inherit; + } +</style> + +<button type="submit" id="save_button" disabled="disabled">Save</button> \ No newline at end of file From 3d784b828d3902dbc59fb7b4d887b3c962a83bf0 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Thu, 4 Aug 2011 22:10:36 -0700 Subject: [PATCH 13/15] Ignored image is loaded half and half instead of all grey. --- NzbDrone.Web/Content/Images/ignoredNeutral.png | Bin 0 -> 3719 bytes NzbDrone.Web/NzbDrone.Web.csproj | 1 + NzbDrone.Web/Views/Series/Details.cshtml | 8 ++++---- 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 NzbDrone.Web/Content/Images/ignoredNeutral.png diff --git a/NzbDrone.Web/Content/Images/ignoredNeutral.png b/NzbDrone.Web/Content/Images/ignoredNeutral.png new file mode 100644 index 0000000000000000000000000000000000000000..a7181b004e995bd53d50e8846873a5181c979c45 GIT binary patch literal 3719 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 literal 0 HcmV?d00001 diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index f7ad632b0..d72690add 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -184,6 +184,7 @@ <Content Include="Content\Blueprint\screen.css" /> <Content Include="Content\Blueprint\liquid.css" /> <Content Include="Content\Images\ignored.png" /> + <Content Include="Content\Images\ignoredNeutral.png" /> <Content Include="Content\Images\notIgnored.png" /> <Content Include="Content\Images\x_16.png" /> <Content Include="Content\jQueryUI\images\ui-bg_diagonals-thick_30_a32d00_40x40.png" /> diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index 2a4a44ddc..0d1d22c72 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -71,9 +71,9 @@ .Columns(columns => { columns.Bound(o => o.Ignored) - .Title("<img src='../../Content/Images/ignored.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_" + season + "' />") + .Title("<img src='../../Content/Images/ignoredNeutral.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_" + season + "' />") .ClientTemplate( - "<img src='../../Content/Images/ignored.png' class='ignoreEpisode ignoreEpisode_" + season + " ignored' id='<#= EpisodeId #>' />") + "<img src='../../Content/Images/ignoredNeutral.png' class='ignoreEpisode ignoreEpisode_" + season + " ignored' id='<#= EpisodeId #>' />") .Width(20) .HtmlAttributes(new { style = "text-align:center" }); @@ -117,9 +117,9 @@ .Columns(columns => { columns.Bound(o => o.Ignored) - .Title("<img src='../../Content/Images/ignored.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_0' />") + .Title("<img src='../../Content/Images/ignoredNeutral.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_0' />") .ClientTemplate( - "<img src='../../Content/Images/ignored.png' class='ignoreEpisode ignoreEpisode_0 ignored' id='<#= EpisodeId #>' />") + "<img src='../../Content/Images/ignoredNeutral.png' class='ignoreEpisode ignoreEpisode_0 ignored' id='<#= EpisodeId #>' />") .Width(20) .HtmlAttributes(new { style = "text-align:center" }); From aa82264774af6cd18538a6347335c05f185fe31c Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Fri, 5 Aug 2011 19:04:35 -0700 Subject: [PATCH 14/15] Menus are now custom built, using AJAX loading of links where acceptable. --- NzbDrone.Web/Content/Menu.css | 20 ++++++++ NzbDrone.Web/Controllers/CommandController.cs | 49 +++++++++++++++++++ NzbDrone.Web/Controllers/HistoryController.cs | 8 +-- NzbDrone.Web/Controllers/LogController.cs | 6 +-- NzbDrone.Web/Controllers/SeriesController.cs | 21 -------- NzbDrone.Web/NzbDrone.Web.csproj | 6 ++- NzbDrone.Web/Views/History/Index.cshtml | 18 +++++-- NzbDrone.Web/Views/Log/Index.cshtml | 14 +++++- NzbDrone.Web/Views/Series/Details.cshtml | 39 +++------------ NzbDrone.Web/Views/Series/Index.cshtml | 9 ++-- NzbDrone.Web/Views/Series/SubMenu.cshtml | 7 --- NzbDrone.Web/Views/Settings/Indexers.cshtml | 2 +- NzbDrone.Web/Views/Settings/SubMenu.cshtml | 18 +++---- NzbDrone.Web/Views/Settings/Test.cshtml | 18 +------ NzbDrone.Web/Views/Shared/_Layout.cshtml | 1 + NzbDrone.Web/Views/Upcoming/Index.cshtml | 5 +- 16 files changed, 130 insertions(+), 111 deletions(-) create mode 100644 NzbDrone.Web/Content/Menu.css create mode 100644 NzbDrone.Web/Controllers/CommandController.cs delete mode 100644 NzbDrone.Web/Views/Series/SubMenu.cshtml diff --git a/NzbDrone.Web/Content/Menu.css b/NzbDrone.Web/Content/Menu.css new file mode 100644 index 000000000..2dc07197c --- /dev/null +++ b/NzbDrone.Web/Content/Menu.css @@ -0,0 +1,20 @@ +#sub-menu +{ + padding-left: 5px; +} + +#sub-menu li +{ + display: inline; + list-style-type: none; + padding-left: 8px; + padding-right: 12px; + padding-top: 6px; + border-right: 1px solid #F0F0F0; +} + +#sub-menu a +{ + text-decoration: none; + color: #105CD6; +} \ No newline at end of file diff --git a/NzbDrone.Web/Controllers/CommandController.cs b/NzbDrone.Web/Controllers/CommandController.cs new file mode 100644 index 000000000..00c8e7638 --- /dev/null +++ b/NzbDrone.Web/Controllers/CommandController.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Mvc; +using NzbDrone.Core.Providers.Jobs; + +namespace NzbDrone.Web.Controllers +{ + public class CommandController : Controller + { + private readonly JobProvider _jobProvider; + + public CommandController(JobProvider jobProvider) + { + _jobProvider = jobProvider; + } + + public JsonResult RssSync() + { + _jobProvider.QueueJob(typeof(RssSyncJob)); + return new JsonResult { Data = "ok" }; + } + + public JsonResult SyncEpisodesOnDisk(int seriesId) + { + //Syncs the episodes on disk for the specified series + _jobProvider.QueueJob(typeof(DiskScanJob), seriesId); + + return new JsonResult { Data = "ok" }; + } + + public JsonResult UpdateInfo(int seriesId) + { + //Syncs the episodes on disk for the specified series + _jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId); + + return new JsonResult { Data = "ok" }; + } + + public JsonResult RenameSeries(int seriesId) + { + //Syncs the episodes on disk for the specified series + //_jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId); + + return new JsonResult { Data = "ok" }; + } + } +} diff --git a/NzbDrone.Web/Controllers/HistoryController.cs b/NzbDrone.Web/Controllers/HistoryController.cs index e0a1e2e8c..8145614bf 100644 --- a/NzbDrone.Web/Controllers/HistoryController.cs +++ b/NzbDrone.Web/Controllers/HistoryController.cs @@ -26,16 +26,16 @@ namespace NzbDrone.Web.Controllers return View(); } - public ActionResult Trim() + public JsonResult Trim() { _historyProvider.Trim(); - return RedirectToAction("Index"); + return new JsonResult { Data = "ok" }; } - public ActionResult Purge() + public JsonResult Purge() { _historyProvider.Purge(); - return RedirectToAction("Index"); + return new JsonResult { Data = "ok" }; } [GridAction] diff --git a/NzbDrone.Web/Controllers/LogController.cs b/NzbDrone.Web/Controllers/LogController.cs index e17915924..8df032ecc 100644 --- a/NzbDrone.Web/Controllers/LogController.cs +++ b/NzbDrone.Web/Controllers/LogController.cs @@ -18,11 +18,11 @@ namespace NzbDrone.Web.Controllers return View(); } - - public ActionResult Clear() + public JsonResult Clear() { _logProvider.DeleteAll(); - return RedirectToAction("Index"); + + return new JsonResult { Data = "ok" }; } [GridAction] diff --git a/NzbDrone.Web/Controllers/SeriesController.cs b/NzbDrone.Web/Controllers/SeriesController.cs index 59566e3bb..aaec2aadf 100644 --- a/NzbDrone.Web/Controllers/SeriesController.cs +++ b/NzbDrone.Web/Controllers/SeriesController.cs @@ -47,12 +47,6 @@ namespace NzbDrone.Web.Controllers return View(); } - public ActionResult RssSync() - { - _jobProvider.QueueJob(typeof(RssSyncJob)); - return RedirectToAction("Index"); - } - public ActionResult SeasonEditor(int seriesId) { var model = new List<SeasonEditModel>(); @@ -185,21 +179,6 @@ namespace NzbDrone.Web.Controllers return View(model); } - public ActionResult SyncEpisodesOnDisk(int seriesId) - { - //Syncs the episodes on disk for the specified series - _jobProvider.QueueJob(typeof(DiskScanJob), seriesId); - - return RedirectToAction("Details", new { seriesId }); - } - - public ActionResult UpdateInfo(int seriesId) - { - //Syncs the episodes on disk for the specified series - _jobProvider.QueueJob(typeof(UpdateInfoJob), seriesId); - return RedirectToAction("Details", new { seriesId }); - } - private List<SeriesModel> GetSeriesModels(IList<Series> seriesInDb) { var series = seriesInDb.Select(s => new SeriesModel diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index d72690add..f3400e515 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -135,6 +135,8 @@ </Reference> </ItemGroup> <ItemGroup> + <Content Include="Content\Menu.css" /> + <Compile Include="Controllers\CommandController.cs" /> <Compile Include="Controllers\DirectoryController.cs" /> <Compile Include="Controllers\EpisodeController.cs" /> <Compile Include="Controllers\HealthController.cs" /> @@ -152,6 +154,7 @@ <Compile Include="Global.asax.cs"> <DependentUpon>Global.asax</DependentUpon> </Compile> + <Compile Include="Helpers\MenuExtension.cs" /> <Compile Include="Helpers\ValueExtension.cs" /> <Compile Include="Helpers\DescriptionExtension.cs" /> <Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" /> @@ -287,11 +290,10 @@ <Content Include="Views\Shared\Footer.cshtml" /> <Content Include="Views\_ViewStart.cshtml" /> <Content Include="Views\History\Index.cshtml" /> - <Content Include="Views\Log\index.cshtml" /> + <Content Include="Views\Log\Index.cshtml" /> <Content Include="Views\Upcoming\Index.cshtml" /> <Content Include="Views\Series\Details.cshtml" /> <Content Include="Views\Series\Index.cshtml" /> - <Content Include="Views\Series\SubMenu.cshtml" /> <Content Include="Views\Series\SeriesSearchResults.cshtml" /> <Content Include="Views\Shared\Error.cshtml" /> <Content Include="Views\Settings\QualityProfileItem.cshtml" /> diff --git a/NzbDrone.Web/Views/History/Index.cshtml b/NzbDrone.Web/Views/History/Index.cshtml index 360180511..a5f67248f 100644 --- a/NzbDrone.Web/Views/History/Index.cshtml +++ b/NzbDrone.Web/Views/History/Index.cshtml @@ -4,11 +4,10 @@ History } @section ActionMenu{ - @{Html.Telerik().Menu().Name("historyMenu").Items(items => - { - items.Add().Text("Trim History").Action("Trim", "History"); - items.Add().Text("Purge History").Action("Purge", "History"); - }).Render();} + <ul id="sub-menu"> + <li>@Ajax.ActionLink("Trim History", "Trim", "History", new AjaxOptions{ OnSuccess = "reloadGrid" })</li> + <li>@Ajax.ActionLink("Purge History", "Purge", "History", new AjaxOptions{ OnSuccess = "reloadGrid" })</li> + </ul> } @section MainContent{ <div class="grid-container"> @@ -42,3 +41,12 @@ History .Render();} </div> } + +<script type="text/javascript"> + +function reloadGrid() { + var grid = $('#history').data('tGrid'); + grid.rebind(); +} + +</script> \ No newline at end of file diff --git a/NzbDrone.Web/Views/Log/Index.cshtml b/NzbDrone.Web/Views/Log/Index.cshtml index 656d6cd87..6d6c0a5c4 100644 --- a/NzbDrone.Web/Views/Log/Index.cshtml +++ b/NzbDrone.Web/Views/Log/Index.cshtml @@ -22,10 +22,13 @@ @section TitleContent{ Logs } + @section ActionMenu{ - @{Html.Telerik().Menu().Name("logMenu").Items(items => items.Add().Text("Clear Logs").Action("Clear", "Log")) - .Render();} + <ul id="sub-menu"> + <li>@Ajax.ActionLink("Clear Logs", "Clear", "Log", new AjaxOptions{ OnSuccess = "reloadGrid" })</li> + </ul> } + @section MainContent{ @{Html.Telerik().Grid(Model).Name("logsGrid") .TableHtmlAttributes(new { @class = "Grid" }) @@ -47,3 +50,10 @@ Logs .ClientEvents(c => c.OnRowDataBound("onRowDataBound")) .Render();} } + +<script type="text/javascript"> + function reloadGrid() { + var grid = $('#logsGrid').data('tGrid'); + grid.rebind(); + } +</script> \ No newline at end of file diff --git a/NzbDrone.Web/Views/Series/Details.cshtml b/NzbDrone.Web/Views/Series/Details.cshtml index 0d1d22c72..4405cfd21 100644 --- a/NzbDrone.Web/Views/Series/Details.cshtml +++ b/NzbDrone.Web/Views/Series/Details.cshtml @@ -25,43 +25,16 @@ </style> @section ActionMenu{ - @{Html.Telerik().Menu().Name("SeriesMenu").Items(items => - { - items.Add().Text("Back to Series List").Action("Index", "Series"); - items.Add().Text("Scan For Episodes on Disk") - .Action("SyncEpisodesOnDisk", "Series", new { seriesId = Model.SeriesId }); - items.Add().Text("Update Info").Action("UpdateInfo", "Series", new { seriesId = Model.SeriesId }); - items.Add().Text("Rename Series").Action("RenameSeries", "Series", new { seriesId = Model.SeriesId }); - }).Render();} + <ul id="sub-menu"> + <li>@Html.ActionLink("Back to Series List", "Index", "Series")</li> + <li>@Ajax.ActionLink("Scan For Episodes on Disk", "SyncEpisodesOnDisk", "Command", new { seriesId = Model.SeriesId }, null)</li> + <li>@Ajax.ActionLink("Update Info", "UpdateInfo", "Command", new { seriesId = Model.SeriesId }, null)</li> + <li>@Ajax.ActionLink("Rename Series", "RenameSeries", "Command", new { seriesId = Model.SeriesId }, null)</li> + </ul> } @section MainContent{ - <fieldset> - <div class="display-label"> - ID</div> - <div class="display-field"> - @Model.SeriesId</div> - <div class="display-label"> - Overview</div> - <div class="display-field"> - @Model.Overview</div> - <div class="display-label"> - Status</div> - <div class="display-field"> - @Model.Status</div> - <div class="display-label"> - AirTimes</div> - <div class="display-field"> - @Model.AirsDayOfWeek</div> - <div class="display-label"> - Language</div> - <div class="display-label"> - Location</div> - <div class="display-field"> - @Model.Path</div> - </fieldset> @foreach (var season in Model.Seasons.Where(s => s > 0).Reverse()) { - <br /> <h3> Season @season</h3> <div class="grid-container"> diff --git a/NzbDrone.Web/Views/Series/Index.cshtml b/NzbDrone.Web/Views/Series/Index.cshtml index 82a08a942..fee117b0d 100644 --- a/NzbDrone.Web/Views/Series/Index.cshtml +++ b/NzbDrone.Web/Views/Series/Index.cshtml @@ -1,4 +1,5 @@ @using NzbDrone.Core.Repository; +@using NzbDrone.Web.Controllers @using NzbDrone.Web.Models; @model IEnumerable<NzbDrone.Core.Repository.Series> @section TitleContent{ @@ -49,7 +50,10 @@ NZBDrone </style> @section ActionMenu{ - @{Html.RenderPartial("SubMenu");} + <ul id="sub-menu"> + <li>@Html.ActionLink("Add Series", "Index", "AddSeries")</li> + <li>@Ajax.ActionLink("Start RSS Sync", "RssSync", "Command", null)</li> + </ul> } @section MainContent{ <div class="grid-container"> @@ -119,8 +123,7 @@ NZBDrone $("#progressbar_" + seriesId).episodeProgress(episodeFileCount, episodeCount); } - </script> - <script type="text/javascript"> + (function ($) { $.fn.episodeProgress = function (episodes, totalEpisodes) { return this.each( diff --git a/NzbDrone.Web/Views/Series/SubMenu.cshtml b/NzbDrone.Web/Views/Series/SubMenu.cshtml deleted file mode 100644 index 7f599b762..000000000 --- a/NzbDrone.Web/Views/Series/SubMenu.cshtml +++ /dev/null @@ -1,7 +0,0 @@ -@using NzbDrone.Web.Controllers - -@{Html.Telerik().Menu().Name("telerikGrid").Items(items => - { - items.Add().Text("Add Series").Action<AddSeriesController>(c => c.Index()); - items.Add().Text("Start RSS Sync").Action<SeriesController>(c => c.RssSync()); - }).Render();} \ No newline at end of file diff --git a/NzbDrone.Web/Views/Settings/Indexers.cshtml b/NzbDrone.Web/Views/Settings/Indexers.cshtml index 44094b9fb..0d8927859 100644 --- a/NzbDrone.Web/Views/Settings/Indexers.cshtml +++ b/NzbDrone.Web/Views/Settings/Indexers.cshtml @@ -25,7 +25,7 @@ @using (Html.BeginForm("SaveIndexers", "Settings", FormMethod.Post, new { id = "form", name = "form", @class = "settingsForm" })) { - <h1>Indexer</h1> + <h1>Indexers</h1> <p></p> @Html.ValidationSummary(true, "Unable to save your settings. Please correct the errors and try again.") diff --git a/NzbDrone.Web/Views/Settings/SubMenu.cshtml b/NzbDrone.Web/Views/Settings/SubMenu.cshtml index ba71af2df..7e728e81d 100644 --- a/NzbDrone.Web/Views/Settings/SubMenu.cshtml +++ b/NzbDrone.Web/Views/Settings/SubMenu.cshtml @@ -1,13 +1,9 @@ -@{Html.Telerik().Menu().Name("SubMenu").Items(items => - { - items.Add().Text("Indexers").Action("Indexers", "Settings"); - items.Add().Text("SABnzbd").Action("Sabnzbd", "Settings"); - items.Add().Text("Quality").Action("Quality", "Settings"); - items.Add().Text("Episode Sorting").Action("EpisodeSorting", - "Settings"); - items.Add().Text("Notifications").Action("Notifications", - "Settings"); - }).Render(); -} +<ul id="sub-menu"> + <li>@Html.ActionLink("Indexers", "Indexers", "Settings")</li> + <li>@Html.ActionLink("SABnzbd", "Sabnzbd", "Settings")</li> + <li>@Html.ActionLink("Quality", "Quality", "Settings")</li> + <li>@Html.ActionLink("Episode Sorting", "EpisodeSorting", "Settings")</li> + <li>@Html.ActionLink("Notifications", "Notifications", "Settings")</li> + </ul> <div style="margin-bottom: 10px"></div> \ No newline at end of file diff --git a/NzbDrone.Web/Views/Settings/Test.cshtml b/NzbDrone.Web/Views/Settings/Test.cshtml index 18d52b0b0..5f282702b 100644 --- a/NzbDrone.Web/Views/Settings/Test.cshtml +++ b/NzbDrone.Web/Views/Settings/Test.cshtml @@ -1,17 +1 @@ -<script type="text/javascript"> - $(document).ready(function () { - //$('#save_button').attr('disabled', ''); - }); -</script> - -<style> - #save_button[disabled="disabled"] - { - padding: 0px 6px 0px 6px; - border: 2px outset ButtonFace; - color: GrayText; - cursor: inherit; - } -</style> - -<button type="submit" id="save_button" disabled="disabled">Save</button> \ No newline at end of file + \ No newline at end of file diff --git a/NzbDrone.Web/Views/Shared/_Layout.cshtml b/NzbDrone.Web/Views/Shared/_Layout.cshtml index 510c9733f..e0f040b4b 100644 --- a/NzbDrone.Web/Views/Shared/_Layout.cshtml +++ b/NzbDrone.Web/Views/Shared/_Layout.cshtml @@ -13,6 +13,7 @@ <link type="text/css" rel="stylesheet" href="/Content/Notibar.css" /> <link type="text/css" rel="stylesheet" href="/Content/ActionButton.css" /> <link type="text/css" rel="stylesheet" href="/Content/overrides.css" /> + <link type="text/css" rel="stylesheet" href="/Content/Menu.css" /> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js"></script> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/yui/3.3.0/build/yui/yui-min.js"></script> diff --git a/NzbDrone.Web/Views/Upcoming/Index.cshtml b/NzbDrone.Web/Views/Upcoming/Index.cshtml index 492d18a5d..0522ea0d2 100644 --- a/NzbDrone.Web/Views/Upcoming/Index.cshtml +++ b/NzbDrone.Web/Views/Upcoming/Index.cshtml @@ -4,8 +4,9 @@ Upcoming } @section ActionMenu{ - @{Html.Telerik().Menu().Name("historyMenu").Items( - items => { items.Add().Text("Start RSS Sync").Action("RssSync", "Series"); }).Render();} + <ul id="sub-menu"> + <li>@Ajax.ActionLink("Start RSS Sync", "RssSync", "Command", null, null)</li> + </ul> } @section MainContent{ <div id="yesterday"> From 963691b48649541c20265d8b20b5cdcb9948e656 Mon Sep 17 00:00:00 2001 From: Mark McDowall <markus.mcd5@gmail.com> Date: Fri, 5 Aug 2011 19:10:00 -0700 Subject: [PATCH 15/15] Updated project file to reflect deleted file. --- NzbDrone.Web/NzbDrone.Web.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index f3400e515..01cedf526 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -154,7 +154,6 @@ <Compile Include="Global.asax.cs"> <DependentUpon>Global.asax</DependentUpon> </Compile> - <Compile Include="Helpers\MenuExtension.cs" /> <Compile Include="Helpers\ValueExtension.cs" /> <Compile Include="Helpers\DescriptionExtension.cs" /> <Compile Include="Helpers\HtmlPrefixScopeExtensions.cs" />