Merge branch 'markus101'

pull/4/head
kay.one 13 years ago
commit eaaf464d38

@ -55,10 +55,12 @@ namespace NzbDrone.Core.Test
mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(12));
mocker.GetMock<EpisodeProvider>()
.Setup(c => c.MarkEpisodeAsFetched(99));
mocker.GetMock<ExternalNotificationProvider>()
.Setup(c => c.OnGrab(It.IsAny<string>()));
mocker.Resolve<DownloadProvider>().DownloadReport(parseResult);
mocker.VerifyAllMocks();

@ -991,5 +991,246 @@ 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();
}
[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))
.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)
.WhereAll()
.Have(c => c.SeriesId = 10)
.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);
db.InsertMany(episodes);
db.InsertMany(specials);
//Act
var missingFiles= mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(false);
//Assert
missingFiles.Should().HaveCount(1);
missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(1);
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))
.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)
.WhereAll()
.Have(c => c.SeriesId = 10)
.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);
db.InsertMany(episodes);
db.InsertMany(specials);
//Act
var missingFiles = mocker.Resolve<EpisodeProvider>().EpisodesWithoutFiles(true);
//Assert
missingFiles.Should().HaveCount(2);
missingFiles.Where(e => e.EpisodeFileId == 0).Should().HaveCount(2);
mocker.VerifyAllMocks();
}
}
}

@ -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);

@ -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<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().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<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().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<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().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<Series>.CreateListOfSize(10)
.WhereAll()
.Have(c => c.QualityProfileId = 1)
.WhereTheFirst(1)
.Have(c => c.Path = path)
.Build();
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
mocker.Resolve<QualityProvider>();
//mocker.GetMock<IDatabase>().Setup(s => s.Fetch<Series, QualityProfile>(It.IsAny<string>())).Returns(
//fakeSeries.ToList());
var result = mocker.Resolve<SeriesProvider>().SeriesPathExists(@"C:\Test\TV\Not A match");
//Assert
result.Should().BeFalse();
}
}
}

@ -62,9 +62,9 @@ namespace NzbDrone.Core
_kernel.Get<QualityProvider>().SetupDefaultProfiles();
BindExternalNotifications();
BindIndexers();
BindJobs();
BindExternalNotifications();
}
private static void BindKernel()
@ -111,8 +111,9 @@ namespace NzbDrone.Core
private static void BindExternalNotifications()
{
_kernel.Bind<ExternalNotificationProviderBase>().To<XbmcNotificationProvider>().InSingletonScope();
var notifiers = _kernel.GetAll<ExternalNotificationProviderBase>();
_kernel.Bind<ExternalNotificationBase>().To<Xbmc>();
var notifiers = _kernel.GetAll<ExternalNotificationBase>();
_kernel.Get<ExternalNotificationProvider>().InitializeNotifiers(notifiers.ToList());
}

@ -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();
}
}
}

@ -6,7 +6,7 @@ namespace NzbDrone.Core.Model.Notification
{
public BasicNotification()
{
Id = Guid.Empty;
Id = Guid.NewGuid();
}
/// <summary>

@ -176,6 +176,7 @@
<Compile Include="Datastore\MigrationLogger.cs" />
<Compile Include="Datastore\MigrationsHelper.cs" />
<Compile Include="Datastore\CustomeMapper.cs" />
<Compile Include="Datastore\Migrations\Migration20110726.cs" />
<Compile Include="Datastore\Migrations\Migration20110707.cs" />
<Compile Include="Fluent.cs" />
<Compile Include="Helpers\EpisodeSortingHelper.cs" />
@ -203,8 +204,8 @@
<Compile Include="Model\Xbmc\VersionResult.cs" />
<Compile Include="Providers\DiskScanProvider.cs" />
<Compile Include="Providers\DownloadProvider.cs" />
<Compile Include="Providers\ExternalNotification\ExternalNotificationProviderBase.cs" />
<Compile Include="Providers\ExternalNotification\XbmcNotificationProvider.cs" />
<Compile Include="Providers\ExternalNotification\ExternalNotificationBase.cs" />
<Compile Include="Providers\ExternalNotification\Xbmc.cs" />
<Compile Include="Providers\InventoryProvider.cs" />
<Compile Include="Providers\Indexer\SyndicationFeedXmlReader.cs" />
<Compile Include="Providers\AutoConfigureProvider.cs" />
@ -224,7 +225,7 @@
<Compile Include="Providers\Jobs\UpdateInfoJob.cs" />
<Compile Include="Providers\SceneMappingProvider.cs" />
<Compile Include="Providers\Xbmc\EventClientProvider.cs" />
<Compile Include="Repository\ExternalNotificationSetting.cs" />
<Compile Include="Repository\ExternalNotificationDefinition.cs" />
<Compile Include="Repository\JobDefinition.cs" />
<Compile Include="Repository\IndexerDefinition.cs" />
<Compile Include="Model\EpisodeParseResult.cs" />

@ -19,11 +19,11 @@ namespace NzbDrone.Core
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Multi-Part episodes without a title (S01E05.S01E06)
new Regex(@"^(?:\W*S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s|\sto\s){1,2}(?<episode>\d{1,2}(?!\d+)))+){2,}\W?(?!\\)",
new Regex(@"^(?:\W*S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|\.|[ex]|\s){1,2}(?<episode>\d{1,2}(?!\d+)))+){2,}\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Single episodes or multi-episode (S01E05E06, S01E05-06, etc)
new Regex(@"^(?<title>.+?)(?:\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,9 @@ 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();
}

@ -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;
}

@ -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;
}
}

@ -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 Ignored = 0 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)
@ -296,26 +299,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 AND Ignored = @3",
isIgnored, seriesId, seasonNumber, !isIgnored);
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)

@ -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
/// </summary>
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);
}
}
/// <summary>
/// Performs the on grab action
/// </summary>

@ -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();
}
}
}
}

@ -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();
}
}
}
}

@ -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<ExternalNotificationBase> _notifiers;
[Inject]
public ExternalNotificationProvider(IDatabase database, IEnumerable<ExternalNotificationBase> notifiers)
{
_database = database;
_notifiers = notifiers;
}
public ExternalNotificationProvider()
@ -24,51 +29,54 @@ namespace NzbDrone.Core.Providers
}
public virtual List<ExternalNotificationSetting> All()
public virtual List<ExternalNotificationDefinition> All()
{
return _database.Fetch<ExternalNotificationSetting>();
return _database.Fetch<ExternalNotificationDefinition>();
}
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<ExternalNotificationSetting>("WHERE NotifierName = @0", type.ToString());
return _database.SingleOrDefault<ExternalNotificationDefinition>("WHERE ExternalNotificationProviderType = @0", type.ToString());
}
public virtual ExternalNotificationSetting GetSettings(int id)
public virtual IList<ExternalNotificationBase> GetEnabledExternalNotifiers()
{
return _database.SingleOrDefault<ExternalNotificationSetting>(id);
var all = All();
return _notifiers.Where(i => all.Exists(c => c.ExternalNotificationProviderType == i.GetType().ToString() && c.Enable)).ToList();
}
public virtual void InitializeNotifiers(IList<ExternalNotificationProviderBase> notifiers)
public virtual void InitializeNotifiers(IList<ExternalNotificationBase> 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);
}
}
}
}

@ -79,12 +79,12 @@ 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);
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())

@ -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;

@ -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; }
}
}

@ -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; }
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

@ -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;
}

@ -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;
}

@ -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" };
}
}
}

@ -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]

@ -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]

@ -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,

@ -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;

@ -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>();
@ -148,12 +142,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" };
}
@ -180,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
@ -227,8 +211,6 @@ namespace NzbDrone.Web.Controllers
var episodePath = String.Empty;
var episodeQuality = String.Empty;
if (e.EpisodeFile != null)
{
episodePath = e.EpisodeFile.Path;
@ -252,7 +234,8 @@ namespace NzbDrone.Web.Controllers
Path = episodePath,
EpisodeFileId = episodeFileId,
Status = e.Status.ToString(),
Quality = episodeQuality
Quality = episodeQuality,
Ignored = e.Ignored
});
}

@ -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,16 @@ namespace NzbDrone.Web.Controllers
private readonly NotificationProvider _notificationProvider;
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,
AutoConfigureProvider autoConfigureProvider, NotificationProvider notificationProvider,
DiskProvider diskProvider, SeriesProvider seriesProvider)
DiskProvider diskProvider, SeriesProvider seriesProvider,
ExternalNotificationProvider externalNotificationProvider)
{
_externalNotificationProvider = externalNotificationProvider;
_configProvider = configProvider;
_indexerProvider = indexerProvider;
_qualityProvider = qualityProvider;
@ -44,6 +49,8 @@ namespace NzbDrone.Web.Controllers
_notificationProvider = notificationProvider;
_diskProvider = diskProvider;
_seriesProvider = seriesProvider;
_progressNotification = new ProgressNotification("Settings");
}
public ActionResult Test()
@ -140,7 +147,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,14 +269,11 @@ namespace NzbDrone.Web.Controllers
return new JsonResult { Data = "failed" };
}
}
[HttpPost]
public ActionResult SaveIndexers(IndexerSettingsModel data)
{
var basicNotification = new BasicNotification();
basicNotification.Type = BasicNotificationType.Info;
basicNotification.AutoDismiss = true;
_notificationProvider.Register(_progressNotification);
if (ModelState.IsValid)
{
@ -301,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)
{
@ -329,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)
{
@ -376,26 +376,29 @@ 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)
{
_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;
@ -404,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)
{
@ -433,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);
}
}

@ -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; }
}
}

@ -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")]

@ -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" />
@ -183,6 +185,9 @@
<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\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" />
<Content Include="Content\jQueryUI\images\ui-bg_flat_0_065efe_40x100.png" />
@ -250,6 +255,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" />
@ -283,11 +289,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" />
@ -317,9 +322,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>

@ -0,0 +1,136 @@
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);
var ignored = toggle.hasClass('ignored');
if (ignored) {
toggle.removeClass('ignored');
toggle.attr('src', notIgnoredImage);
}
else {
toggle.addClass('ignored');
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')) {
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) {
ignoreEpisodes.each(function (index) {
$(this).addClass('ignored');
$(this).attr('src', ignoredImage);
});
}
else {
ignoreEpisodes.each(function (index) {
$(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;
var episodeId = dataItem.EpisodeId;
var ignoredIcon = $('#' + episodeId);
if (ignored) {
ignoredIcon.attr('src', ignoredImage);
}
else {
ignoredIcon.attr('src', notIgnoredImage);
ignoredIcon.removeClass('ignored');
}
if (seriesId == 0)
seriesId = dataItem.SeriesId
}
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 = ignoreEpisodes.filter('.ignored').length;
if (ignoredCount == count) {
master.attr('src', ignoredImage);
master.addClass('ignored');
}
else {
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);
}
});
}

@ -7,6 +7,7 @@
resetForm: false
};
$('#form').ajaxForm(options);
$('#save_button').removeAttr('disabled');
});
function showRequest(formData, jqForm, options) {

@ -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>

@ -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>

@ -4,44 +4,37 @@
@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 =>
{
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">
@ -50,11 +43,11 @@
.TableHtmlAttributes(new { @class = "Grid" })
.Columns(columns =>
{
columns.Bound(o => o.EpisodeId)
columns.Bound(o => o.Ignored)
.Title("<img src='../../Content/Images/ignoredNeutral.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_" + season + "' />")
.ClientTemplate(
"<input type='checkbox' name='checkedEpisodes' value='<#= EpisodeId #>' />")
.Title("")
.Width(1)
"<img src='../../Content/Images/ignoredNeutral.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 +61,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 +71,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 +89,11 @@
.TableHtmlAttributes(new { @class = "Grid" })
.Columns(columns =>
{
columns.Bound(o => o.EpisodeId)
columns.Bound(o => o.Ignored)
.Title("<img src='../../Content/Images/ignoredNeutral.png' class='ignoredEpisodesMaster ignoreEpisode' id='master_0' />")
.ClientTemplate(
"<input type='checkbox' name='checkedEpisodes' value='<#= EpisodeId #>' />")
.Title("")
.Width(1)
"<img src='../../Content/Images/ignoredNeutral.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 +103,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 +131,8 @@
}
});
}
seriesId = @Model.SeriesId;
</script>
}

@ -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>

@ -1,9 +1,11 @@
@using NzbDrone.Core.Repository;
@using NzbDrone.Web.Controllers
@using NzbDrone.Web.Models;
@model IEnumerable<NzbDrone.Core.Repository.Series>
@section TitleContent{
NZBDrone
}
<style>
/* progress bar container */
.progressbar
@ -46,8 +48,12 @@ NZBDrone
background: #E5ECF9;
}
</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">
@ -103,37 +109,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) {
@ -144,8 +123,7 @@ NZBDrone
$("#progressbar_" + seriesId).episodeProgress(episodeFileCount, episodeCount);
}
</script>
<script type="text/javascript">
(function ($) {
$.fn.episodeProgress = function (episodes, totalEpisodes) {
return this.each(

@ -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>
}

@ -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();}

@ -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>

@ -9,8 +9,7 @@
{
padding-top: 20px;
}
</style>
</style>
}
@section TitleContent{
@ -26,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.")
@ -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>
}

@ -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>

@ -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>

@ -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>

@ -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>

@ -1,94 +1 @@
<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;
}
/* ----------- 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>
<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>
<label>Name
<span class="small">Add your name</span>
</label>
<input type="text" name="name" id="name" />
<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>


@ -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>

@ -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">

Loading…
Cancel
Save