From 488da5914330c92e0e83d264805c7c91ae9d80e8 Mon Sep 17 00:00:00 2001 From: Keivan Beigi Date: Wed, 21 Aug 2013 17:36:35 -0700 Subject: [PATCH] more validation --- NzbDrone.Api/NzbDrone.Api.csproj | 2 +- NzbDrone.Api/REST/ResourceValidator.cs | 5 +- NzbDrone.Api/Series/SeriesModule.cs | 6 ++- ...dationRule.cs => RuleBuilderExtensions.cs} | 1 - .../inspectionProfiles/Project_Default.xml | 3 +- UI/Form/PasswordTemplate.html | 2 +- UI/Form/TextboxTemplate.html | 2 +- UI/Instrumentation/ErrorHandler.js | 7 ++- UI/Mixins/AsValidatedView.js | 13 +++-- UI/Series/Edit/EditSeriesView.js | 6 ++- UI/Settings/Indexers/ItemView.js | 43 ++++++++------- .../MediaManagement/Sorting/ViewTemplate.html | 2 +- UI/jQuery/Validation.js | 30 ----------- UI/jQuery/jquery.validation.js | 52 +++++++++++++++++++ 14 files changed, 105 insertions(+), 69 deletions(-) rename NzbDrone.Api/Validation/{IdValidationRule.cs => RuleBuilderExtensions.cs} (99%) delete mode 100644 UI/jQuery/Validation.js create mode 100644 UI/jQuery/jquery.validation.js diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index 06f3cb936..23fdba9b4 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -165,7 +165,7 @@ - + diff --git a/NzbDrone.Api/REST/ResourceValidator.cs b/NzbDrone.Api/REST/ResourceValidator.cs index a5c0e4268..8062e6fd0 100644 --- a/NzbDrone.Api/REST/ResourceValidator.cs +++ b/NzbDrone.Api/REST/ResourceValidator.cs @@ -14,7 +14,7 @@ public class ResourceValidator : AbstractValidator public IRuleBuilderInitial RuleForField(Expression>> fieldListAccessor, string fieldName) { var rule = new PropertyRule(fieldListAccessor.GetMember(), c => GetValue(c, fieldListAccessor.Compile(), fieldName), null, () => CascadeMode.Continue, typeof(TProperty), typeof(TResource)); - rule.PropertyName += "." + fieldName; + rule.PropertyName = fieldName; rule.DisplayName = new StaticStringSource(fieldName); AddRule(rule); @@ -34,7 +34,4 @@ private static object GetValue(object container, Func s.QualityProfileId).ValidId(); - SharedValidator.RuleFor(s => s.Path).NotEmpty().When(s => String.IsNullOrEmpty(s.RootFolderPath)); - SharedValidator.RuleFor(s => s.RootFolderPath).NotEmpty().When(s => String.IsNullOrEmpty(s.Path)); + PutValidator.RuleFor(s => s.Path).NotEmpty(); + + PostValidator.RuleFor(s => s.Path).NotEmpty().When(s => String.IsNullOrEmpty(s.RootFolderPath)); + PostValidator.RuleFor(s => s.RootFolderPath).NotEmpty().When(s => String.IsNullOrEmpty(s.Path)); PostValidator.RuleFor(s => s.Title).NotEmpty(); } diff --git a/NzbDrone.Api/Validation/IdValidationRule.cs b/NzbDrone.Api/Validation/RuleBuilderExtensions.cs similarity index 99% rename from NzbDrone.Api/Validation/IdValidationRule.cs rename to NzbDrone.Api/Validation/RuleBuilderExtensions.cs index f2ef55a82..f14c833bd 100644 --- a/NzbDrone.Api/Validation/IdValidationRule.cs +++ b/NzbDrone.Api/Validation/RuleBuilderExtensions.cs @@ -15,5 +15,4 @@ public static IRuleBuilderOptions IsZero(this IRuleBuilder ru return ruleBuilder.SetValidator(new EqualValidator(0)); } } - } \ No newline at end of file diff --git a/UI/.idea/inspectionProfiles/Project_Default.xml b/UI/.idea/inspectionProfiles/Project_Default.xml index 9d0540909..1e9bbe387 100644 --- a/UI/.idea/inspectionProfiles/Project_Default.xml +++ b/UI/.idea/inspectionProfiles/Project_Default.xml @@ -38,8 +38,9 @@ diff --git a/UI/Form/PasswordTemplate.html b/UI/Form/PasswordTemplate.html index 18b91a7dc..9f8c4a9a0 100644 --- a/UI/Form/PasswordTemplate.html +++ b/UI/Form/PasswordTemplate.html @@ -2,7 +2,7 @@
- + {{#if helpText}} diff --git a/UI/Form/TextboxTemplate.html b/UI/Form/TextboxTemplate.html index f0e0e5734..b257c7f03 100644 --- a/UI/Form/TextboxTemplate.html +++ b/UI/Form/TextboxTemplate.html @@ -2,7 +2,7 @@
- + {{> FormHelpPartial}}
diff --git a/UI/Instrumentation/ErrorHandler.js b/UI/Instrumentation/ErrorHandler.js index 5b2b5d07f..589723771 100644 --- a/UI/Instrumentation/ErrorHandler.js +++ b/UI/Instrumentation/ErrorHandler.js @@ -69,10 +69,13 @@ return false; //message.message = 'NzbDrone Server Not Reachable. make sure NzbDrone is running.'; } - else { - message.message = '[{0}] {1} : {2}'.format(ajaxOptions.type, xmlHttpRequest.statusText, ajaxOptions.url); + else if (xmlHttpRequest.status === 400 && ajaxOptions.isValidatedCall) { + return false; } + + message.message = '[{0}] {1} : {2}'.format(ajaxOptions.type, xmlHttpRequest.statusText, ajaxOptions.url); + window.Messenger().post(message); return false; }); diff --git a/UI/Mixins/AsValidatedView.js b/UI/Mixins/AsValidatedView.js index fe16adb13..a8c6cf06e 100644 --- a/UI/Mixins/AsValidatedView.js +++ b/UI/Mixins/AsValidatedView.js @@ -2,7 +2,7 @@ define( [ 'backbone.validation', 'underscore', - 'jQuery/Validation' + 'jQuery/jquery.validation' ], function (Validation, _) { 'use strict'; @@ -27,7 +27,10 @@ define( var boundHandler = errorHandler.bind(this); this.model.sync = function () { - self.$el.removeBootstrapError(); + self.$el.removeAllErrors(); + + arguments[2].isValidatedCall = true; + return self.originalSync.apply(this, arguments).fail(boundHandler); }; } @@ -71,9 +74,13 @@ define( var validationErrors = JSON.parse(response.responseText); _.each(validationErrors, function (error) { - view.$el.addBootstrapError(error); + view.$el.processServerError(error); }); } }; + + + + return this; }; }); diff --git a/UI/Series/Edit/EditSeriesView.js b/UI/Series/Edit/EditSeriesView.js index b00ec61c0..43bc46fde 100644 --- a/UI/Series/Edit/EditSeriesView.js +++ b/UI/Series/Edit/EditSeriesView.js @@ -5,8 +5,9 @@ define( 'marionette', 'Quality/QualityProfileCollection', 'Mixins/AsModelBoundView', + 'Mixins/AsValidatedView', 'Mixins/AutoComplete' - ], function (App, Marionette, QualityProfiles, AsModelBoundView) { + ], function (App, Marionette, QualityProfiles, AsModelBoundView, AsValidatedView) { var view = Marionette.ItemView.extend({ template: 'Series/Edit/EditSeriesTemplate', @@ -49,5 +50,6 @@ define( }); - return AsModelBoundView.apply(view); + AsModelBoundView.apply(view); + return AsValidatedView.apply(view); }); diff --git a/UI/Settings/Indexers/ItemView.js b/UI/Settings/Indexers/ItemView.js index 38135e746..2b9d2fed2 100644 --- a/UI/Settings/Indexers/ItemView.js +++ b/UI/Settings/Indexers/ItemView.js @@ -1,26 +1,29 @@ 'use strict'; -define([ - 'app', - 'marionette', - 'Settings/Notifications/DeleteView', - 'Mixins/AsModelBoundView'], - function (App, Marionette, DeleteView, AsModelBoundView) { +define( + [ + 'app', + 'marionette', + 'Settings/Notifications/DeleteView', + 'Mixins/AsModelBoundView', + 'Mixins/AsValidatedView' + ], function (App, Marionette, DeleteView, AsModelBoundView, AsValidatedView) { - var view = Marionette.ItemView.extend({ - template: 'Settings/Indexers/ItemTemplate', - tagName : 'li', + var view = Marionette.ItemView.extend({ + template: 'Settings/Indexers/ItemTemplate', + tagName : 'li', - events: { - 'click .x-delete': '_deleteIndexer' - }, + events: { + 'click .x-delete': '_deleteIndexer' + }, + + _deleteIndexer: function () { + var view = new DeleteView({ model: this.model}); + App.modalRegion.show(view); + } + }); + + AsModelBoundView.call(view); + return AsValidatedView.call(view); - _deleteIndexer: function () { - var view = new DeleteView({ model: this.model}); - App.modalRegion.show(view); - } }); - - return AsModelBoundView.call(view); - -}); diff --git a/UI/Settings/MediaManagement/Sorting/ViewTemplate.html b/UI/Settings/MediaManagement/Sorting/ViewTemplate.html index e44141004..a09c0f37d 100644 --- a/UI/Settings/MediaManagement/Sorting/ViewTemplate.html +++ b/UI/Settings/MediaManagement/Sorting/ViewTemplate.html @@ -1,5 +1,5 @@ 
- Sorting + Season Folder
diff --git a/UI/jQuery/Validation.js b/UI/jQuery/Validation.js deleted file mode 100644 index 04ad8727e..000000000 --- a/UI/jQuery/Validation.js +++ /dev/null @@ -1,30 +0,0 @@ -define( - [ - 'jquery' - ], function ($) { - 'use strict'; - - $.fn.addBootstrapError = function (error) { - var input = this.find('[name]').filter(function () { - return this.name.toLowerCase() === error.propertyName.toLowerCase(); - }); - - var controlGroup = input.parents('.control-group'); - if (controlGroup.find('.help-inline').length === 0) { - controlGroup.find('.controls').append('' + error.errorMessage + ''); - } - - controlGroup.addClass('error'); - - return controlGroup.find('.help-inline').text(); - }; - - - $.fn.removeBootstrapError = function () { - - this.removeClass('error'); - - return this.parents('.control-group').find('.help-inline.error-message').remove(); - }; - - }); diff --git a/UI/jQuery/jquery.validation.js b/UI/jQuery/jquery.validation.js new file mode 100644 index 000000000..fda5087c5 --- /dev/null +++ b/UI/jQuery/jquery.validation.js @@ -0,0 +1,52 @@ +define( + [ + 'jquery' + ], function ($) { + 'use strict'; + + $.fn.processServerError = function (error) { + + var validationName = error.propertyName.toLowerCase(); + + var input = this.find('[name]').filter(function () { + return this.name.toLowerCase() === validationName; + }); + + + if (input.length === 0) { + input = this.find('[validation-name]').filter(function () { + return $(this).attr('validation-name').toLowerCase() === validationName; + }); + + //still not found? + if (input.length === 0) { + this.addFormError(error); + console.error('couldn\'t find input for ' + error.propertyName); + return this; + } + } + + var controlGroup = input.parents('.control-group'); + controlGroup.find('.controls').append('' + error.errorMessage + ''); + + controlGroup.addClass('error'); + + return controlGroup.find('.help-inline').text(); + }; + + + $.fn.processClientError = function (error) { + + }; + + $.fn.addFormError = function (error) { + this.find('.control-group').parent().prepend('
'+ error.errorMessage +'
') + }; + + $.fn.removeAllErrors = function () { + this.find('.error').removeClass('error'); + this.find('.validation-error').remove(); + return this.find('.help-inline.error-message').remove(); + }; + + });