diff --git a/NzbDrone.Api/ErrorManagment/ErrorPipeline.cs b/NzbDrone.Api/ErrorManagment/ErrorPipeline.cs index 7e8d234df..feb15e929 100644 --- a/NzbDrone.Api/ErrorManagment/ErrorPipeline.cs +++ b/NzbDrone.Api/ErrorManagment/ErrorPipeline.cs @@ -2,6 +2,7 @@ using System.Linq; using NLog; using Nancy; +using NzbDrone.Api.Extentions; namespace NzbDrone.Api.ErrorManagment { @@ -17,15 +18,21 @@ namespace NzbDrone.Api.ErrorManagment public Response HandleException(NancyContext context, Exception exception) { var apiException = exception as ApiException; - + if (apiException != null) { _logger.WarnException("API Error", apiException); return apiException.ToErrorResponse(); } - + _logger.ErrorException("Unexpected error", exception); - return null; + + + return new ErrorModel() + { + Message = exception.Message, + Description = exception.ToString() + }.AsResponse(HttpStatusCode.InternalServerError); } } } \ No newline at end of file diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 564817013..64e7e85e9 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -59,6 +59,9 @@ ..\packages\AutoMapper.2.2.0\lib\net40\AutoMapper.dll + + ..\packages\FluentValidation.3.4.6.0\lib\Net40\FluentValidation.dll + ..\packages\Nancy.0.15.3\lib\net40\Nancy.dll @@ -93,6 +96,7 @@ + diff --git a/NzbDrone.Api/Series/SeriesLookupModule.cs b/NzbDrone.Api/Series/SeriesLookupModule.cs new file mode 100644 index 000000000..27460c95b --- /dev/null +++ b/NzbDrone.Api/Series/SeriesLookupModule.cs @@ -0,0 +1,27 @@ +using System.Linq; +using Nancy; +using NzbDrone.Api.Extentions; +using NzbDrone.Api.QualityType; +using NzbDrone.Core.Providers; + +namespace NzbDrone.Api.Series +{ + public class SeriesLookupModule : NzbDroneApiModule + { + private readonly TvDbProvider _tvDbProvider; + + public SeriesLookupModule(TvDbProvider tvDbProvider) + : base("/Series/lookup") + { + _tvDbProvider = tvDbProvider; + Get["/"] = x => GetQualityType(); + } + + + private Response GetQualityType() + { + var tvDbResults = _tvDbProvider.SearchSeries((string)Request.Query.term); + return tvDbResults.AsResponse(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Series/SeriesModule.cs b/NzbDrone.Api/Series/SeriesModule.cs index 86df821f5..b19381ea7 100644 --- a/NzbDrone.Api/Series/SeriesModule.cs +++ b/NzbDrone.Api/Series/SeriesModule.cs @@ -1,27 +1,58 @@ using System.Linq; +using FluentValidation; using Nancy; using NzbDrone.Api.Extentions; -using NzbDrone.Api.QualityType; +using NzbDrone.Common; +using NzbDrone.Core.Jobs; using NzbDrone.Core.Providers; namespace NzbDrone.Api.Series { public class SeriesModule : NzbDroneApiModule { - private readonly TvDbProvider _tvDbProvider; + private readonly SeriesProvider _seriesProvider; + private readonly JobProvider _jobProvider; - public SeriesModule(TvDbProvider tvDbProvider) + public SeriesModule(SeriesProvider seriesProvider, JobProvider jobProvider) : base("/Series") { - _tvDbProvider = tvDbProvider; - Get["/lookup"] = x => GetQualityType(); + _seriesProvider = seriesProvider; + _jobProvider = jobProvider; + Post["/"] = x => AddSeries(); } - private Response GetQualityType() + private Response AddSeries() { - var tvDbResults = _tvDbProvider.SearchSeries((string)Request.Query.term); - return tvDbResults.AsResponse(); + var request = Request.Body.FromJson(); + + _seriesProvider.AddSeries("", request.Path, request.SeriesId, request.QualityProfileId, null); + _jobProvider.QueueJob(typeof(ImportNewSeriesJob)); + + return new Response { StatusCode = HttpStatusCode.Created }; + } + } + + + public class SeriesValidator : AbstractValidator + { + private readonly DiskProvider _diskProvider; + + public SeriesValidator(DiskProvider diskProvider) + { + _diskProvider = diskProvider; } + + public SeriesValidator() + { + RuleSet("POST", () => + { + RuleFor(s => s.SeriesId).GreaterThan(0); + RuleFor(s => s.Path).NotEmpty().Must(_diskProvider.FolderExists); + RuleFor(s => s.QualityProfileId).GreaterThan(0); + }); + } + + } } \ No newline at end of file diff --git a/NzbDrone.Api/packages.config b/NzbDrone.Api/packages.config index b3fd32088..98eaade7f 100644 --- a/NzbDrone.Api/packages.config +++ b/NzbDrone.Api/packages.config @@ -2,6 +2,7 @@ + diff --git a/NzbDrone.Web/NzbDrone.Web.csproj b/NzbDrone.Web/NzbDrone.Web.csproj index 6c965456c..7ce94346e 100644 --- a/NzbDrone.Web/NzbDrone.Web.csproj +++ b/NzbDrone.Web/NzbDrone.Web.csproj @@ -390,6 +390,7 @@ + diff --git a/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/AddNewSeriesView.js b/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/AddNewSeriesView.js index 9344a699f..f114c2b8d 100644 --- a/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/AddNewSeriesView.js +++ b/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/AddNewSeriesView.js @@ -66,8 +66,8 @@ NzbDrone.AddSeries.AddNewSeriesView = Backbone.Marionette.Layout.extend({ resultUpdated: function (options, context) { _.each(options.models, function (model) { - model.set('rootFolders', context.rootFoldersCollection.models); - model.set('qualityProfiles', context.qualityProfilesCollection.models); + model.set('rootFolders', context.rootFoldersCollection); + model.set('qualityProfiles', context.qualityProfilesCollection); }); context.searchResult.show(context.resultView); diff --git a/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultTemplate.html b/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultTemplate.html index 05f0a3f7c..fe5368668 100644 --- a/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultTemplate.html +++ b/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultTemplate.html @@ -4,17 +4,17 @@
- + {{#each rootFolders.models}} {{/each}} - + {{#each qualityProfiles.models}} {{/each}} -
+
diff --git a/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultView.js b/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultView.js index 413ab5272..957c504d5 100644 --- a/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultView.js +++ b/NzbDrone.Web/_backboneApp/AddSeries/AddNewSeries/SearchResultView.js @@ -1,17 +1,47 @@ /// /// +/// /// NzbDrone.AddSeries.SearchItemView = Backbone.Marionette.ItemView.extend({ template: "AddSeries/AddNewSeries/SearchResultTemplate", className: 'search-item accordion-group', + + ui: { + qualityProfile: '.x-quality-profile', + rootFolder: '.x-root-folder' + }, + + events: { + 'click .x-add': 'add' + }, + onRender: function () { this.listenTo(this.model, 'change', this.render); - //this.listenTo(this.model.get('rootFolders'), 'reset', this.render); - //this.listenTo(this.model.get('qualityProfiles'), 'reset', this.render); + }, + + add: function () { + + var seriesId = this.model.get('id'); + var title = this.model.get('seriesName'); + var quality = this.ui.qualityProfile.val(); + var rootFolderId = this.ui.rootFolder.val(); + + var rootPath = this.model.get('rootFolders').get(rootFolderId).get('path'); + var path = rootPath + "\\" + title; + + var model = new NzbDrone.Series.SeriesModel({ + seriesId: seriesId, + title: title, + qualityProfileId: quality, + path: path + }); + + model.save(); } + }); NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend({ @@ -23,5 +53,7 @@ NzbDrone.AddSeries.SearchResultView = Backbone.Marionette.CollectionView.extend( initialize: function () { this.listenTo(this.collection, 'reset', this.render); }, + + }); diff --git a/NzbDrone.Web/_backboneApp/AddSeries/SearchResultCollection.js b/NzbDrone.Web/_backboneApp/AddSeries/SearchResultCollection.js index 44ff85732..d890f396e 100644 --- a/NzbDrone.Web/_backboneApp/AddSeries/SearchResultCollection.js +++ b/NzbDrone.Web/_backboneApp/AddSeries/SearchResultCollection.js @@ -1,8 +1,10 @@ /// /// +"use strict"; NzbDrone.AddSeries.SearchResultCollection = Backbone.Collection.extend({ url: NzbDrone.Constants.ApiRoot + '/series/lookup', - model: NzbDrone.AddSeries.SearchResultModel, + model: NzbDrone.AddSeries.SearchResultModel + }); diff --git a/NzbDrone.Web/_backboneApp/JsLibraries/backbone.marionette.extend.js b/NzbDrone.Web/_backboneApp/JsLibraries/backbone.marionette.extend.js index e3eb9fe30..5e3b8d3aa 100644 --- a/NzbDrone.Web/_backboneApp/JsLibraries/backbone.marionette.extend.js +++ b/NzbDrone.Web/_backboneApp/JsLibraries/backbone.marionette.extend.js @@ -9,7 +9,7 @@ _.extend(Marionette.TemplateCache.prototype, { console.log("Loading template '" + templateId + "'"); - jQuery.ajax({ + $.ajax({ url: '_backboneApp//' + templateId + '.html', cache:false, async: false diff --git a/NzbDrone.Web/_backboneApp/Series/SeriesModel.js b/NzbDrone.Web/_backboneApp/Series/SeriesModel.js new file mode 100644 index 000000000..4d34e7caf --- /dev/null +++ b/NzbDrone.Web/_backboneApp/Series/SeriesModel.js @@ -0,0 +1,10 @@ +NzbDrone.Series.SeriesModel = Backbone.Model.extend({ + url: NzbDrone.Constants.ApiRoot + '/series' + +}); + + +NzbDrone.Series.SeriesCollection = Backbone.Collection.extend({ + model: NzbDrone.Series.SeriesModel, + url: NzbDrone.Constants.ApiRoot + '/series', +}); diff --git a/NzbDrone.Web/_backboneApp/Shared/ErrorModel.js b/NzbDrone.Web/_backboneApp/Shared/ErrorModel.js index 96cbfa33e..352128146 100644 --- a/NzbDrone.Web/_backboneApp/Shared/ErrorModel.js +++ b/NzbDrone.Web/_backboneApp/Shared/ErrorModel.js @@ -9,6 +9,13 @@ NzbDrone.Shared.ErrorCollection = Backbone.Collection.extend({ NzbDrone.Shared.ErrorModel = Backbone.Model.extend({ + mutators: { + pre: function () { + return this.get('message').lines().lenght > 1; + } + }, + + defaults: { "title": "NO_TITLE", "message": "NO_MESSAGE", diff --git a/NzbDrone.Web/_backboneApp/Shared/ErrorTemplate.html b/NzbDrone.Web/_backboneApp/Shared/ErrorTemplate.html index c5a581e88..d9b5e2422 100644 --- a/NzbDrone.Web/_backboneApp/Shared/ErrorTemplate.html +++ b/NzbDrone.Web/_backboneApp/Shared/ErrorTemplate.html @@ -1,4 +1,9 @@ 
- {{title}} {{message}} + {{title}} + {{#if preFormatted}} +
 {{message}}
+ {{else}} + {{message}} + {{/if}}
diff --git a/NzbDrone.Web/_backboneApp/app.js b/NzbDrone.Web/_backboneApp/app.js index 8adb53e9c..6e92ee931 100644 --- a/NzbDrone.Web/_backboneApp/app.js +++ b/NzbDrone.Web/_backboneApp/app.js @@ -18,6 +18,7 @@ if (typeof console == "undefined") { } NzbDrone = new Backbone.Marionette.Application(); +NzbDrone.Series = NzbDrone.module("Series"); NzbDrone.AddSeries = NzbDrone.module("AddSeries"); NzbDrone.Quality = NzbDrone.module("Quality"); NzbDrone.Shared = NzbDrone.module("Shared"); @@ -36,14 +37,14 @@ NzbDrone.Constants = { }; NzbDrone.Events = { - DisplayInMainRegion: "DisplayInMainRegion", + DisplayInMainRegion: "DisplayInMainRegion" }; NzbDrone.Routes = { Series: { - Add: 'series/add', - }, + Add: 'series/add' + } }; NzbDrone.Controller = Backbone.Marionette.Controller.extend({ @@ -55,7 +56,7 @@ NzbDrone.Controller = Backbone.Marionette.Controller.extend({ notFound: function () { alert('route not found'); - }, + } }); @@ -78,7 +79,7 @@ NzbDrone.addInitializer(function (options) { NzbDrone.addRegions({ mainRegion: "#main-region", - errorRegion: "#error-region", + errorRegion: "#error-region" }); NzbDrone.Router = new NzbDrone.Router(); diff --git a/NzbDrone.sln.DotSettings b/NzbDrone.sln.DotSettings index 9f25055a6..676ad420d 100644 --- a/NzbDrone.sln.DotSettings +++ b/NzbDrone.sln.DotSettings @@ -1,3 +1,89 @@  BOTH_SIDES - OUTLINE \ No newline at end of file + OUTLINE + True + C:\Dropbox\Git\NzbDrone\NzbDrone.sln.DotSettings + True + 1 + Backbone model + 5 + True + True + js + Model + True + Backbone Model + True + getFileNameWithoutExtension() + 0 + True + getFileNameWithoutExtension() + -1 + 1 + True + True + InAnyWebProject + True + NzbDrone.$ModelName$Model = Backbone.Model.extend({ + +}); + + +$ModelName$Collection = Backbone.Collection.extend({ + + model: NzbDrone.$ModelName$Model, + url: NzbDrone.Constants.ApiRoot + '/$resource$', + +}); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Backbone Model + True + 0 + True + True + InJavaScriptFile + model + True + $ModelName$ = Backbone.M + + + + + + + + + + + \ No newline at end of file