mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
Fixed: Empty Sabnzbd category is now properly handled. But added UI validation to recommend adding a category.
This commit is contained in:
parent
a8e805fd5d
commit
6803e46782
@ -9,10 +9,10 @@ public DownloadClientModule(IDownloadClientFactory downloadClientFactory)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(DownloadClientDefinition definition)
|
||||
protected override void Validate(DownloadClientDefinition definition, bool includeWarnings)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
base.Validate(definition);
|
||||
base.Validate(definition, includeWarnings);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,10 @@ public IndexerModule(IndexerFactory indexerFactory)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(IndexerDefinition definition)
|
||||
protected override void Validate(IndexerDefinition definition, bool includeWarnings)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
base.Validate(definition);
|
||||
base.Validate(definition, includeWarnings);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,10 @@ public MetadataModule(IMetadataFactory metadataFactory)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(MetadataDefinition definition)
|
||||
protected override void Validate(MetadataDefinition definition, bool includeWarnings)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
base.Validate(definition);
|
||||
base.Validate(definition, includeWarnings);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,10 @@ public NotificationModule(NotificationFactory notificationFactory)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void Validate(NotificationDefinition definition)
|
||||
protected override void Validate(NotificationDefinition definition, bool includeWarnings)
|
||||
{
|
||||
if (!definition.OnGrab && !definition.OnDownload) return;
|
||||
base.Validate(definition);
|
||||
base.Validate(definition, includeWarnings);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,12 +2,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
using Nancy;
|
||||
using NzbDrone.Api.ClientSchema;
|
||||
using NzbDrone.Api.Extensions;
|
||||
using NzbDrone.Api.Mapping;
|
||||
using NzbDrone.Common.Reflection;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using Omu.ValueInjecter;
|
||||
|
||||
namespace NzbDrone.Api
|
||||
@ -72,12 +74,9 @@ private List<TProviderResource> GetAll()
|
||||
|
||||
private int CreateProvider(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource);
|
||||
var providerDefinition = GetDefinition(providerResource, false);
|
||||
|
||||
if (providerDefinition.Enable)
|
||||
{
|
||||
Test(providerDefinition);
|
||||
}
|
||||
Test(providerDefinition, false);
|
||||
|
||||
providerDefinition = _providerFactory.Create(providerDefinition);
|
||||
|
||||
@ -86,12 +85,14 @@ private int CreateProvider(TProviderResource providerResource)
|
||||
|
||||
private void UpdateProvider(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource);
|
||||
var providerDefinition = GetDefinition(providerResource, false);
|
||||
|
||||
Test(providerDefinition, false);
|
||||
|
||||
_providerFactory.Update(providerDefinition);
|
||||
}
|
||||
|
||||
private TProviderDefinition GetDefinition(TProviderResource providerResource)
|
||||
private TProviderDefinition GetDefinition(TProviderResource providerResource, bool includeWarnings = false)
|
||||
{
|
||||
var definition = new TProviderDefinition();
|
||||
|
||||
@ -105,7 +106,7 @@ private TProviderDefinition GetDefinition(TProviderResource providerResource)
|
||||
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
|
||||
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract, preset);
|
||||
|
||||
Validate(definition);
|
||||
Validate(definition, includeWarnings);
|
||||
|
||||
return definition;
|
||||
}
|
||||
@ -149,31 +150,42 @@ private Response GetTemplates()
|
||||
|
||||
private Response Test(TProviderResource providerResource)
|
||||
{
|
||||
var providerDefinition = GetDefinition(providerResource);
|
||||
var providerDefinition = GetDefinition(providerResource, true);
|
||||
|
||||
Test(providerDefinition);
|
||||
Test(providerDefinition, true);
|
||||
|
||||
return "{}";
|
||||
}
|
||||
|
||||
private void Test(TProviderDefinition providerDefinition)
|
||||
protected virtual void Validate(TProviderDefinition definition, bool includeWarnings)
|
||||
{
|
||||
var result = _providerFactory.Test(providerDefinition);
|
||||
var validationResult = definition.Settings.Validate();
|
||||
|
||||
VerifyValidationResult(validationResult, includeWarnings);
|
||||
}
|
||||
|
||||
protected virtual void Test(TProviderDefinition definition, bool includeWarnings)
|
||||
{
|
||||
if (!definition.Enable) return;
|
||||
|
||||
var validationResult = _providerFactory.Test(definition);
|
||||
|
||||
VerifyValidationResult(validationResult, includeWarnings);
|
||||
}
|
||||
|
||||
protected void VerifyValidationResult(ValidationResult validationResult, bool includeWarnings)
|
||||
{
|
||||
var result = new NzbDroneValidationResult(validationResult.Errors);
|
||||
|
||||
if (includeWarnings && (!result.IsValid || result.HasWarnings))
|
||||
{
|
||||
throw new ValidationException(result.Failures);
|
||||
}
|
||||
|
||||
if (!result.IsValid)
|
||||
{
|
||||
throw new ValidationException(result.Errors);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Validate(TProviderDefinition definition)
|
||||
{
|
||||
var validationResult = definition.Settings.Validate();
|
||||
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
throw new ValidationException(validationResult.Errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ public DelugeSettings()
|
||||
[FieldDefinition(2, Label = "Password", Type = FieldType.Password)]
|
||||
public String Password { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Category", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(3, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
public String TvCategory { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(DelugePriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||
|
@ -3,6 +3,7 @@
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Nzbget
|
||||
@ -18,6 +19,8 @@ public NzbgetSettingsValidator()
|
||||
|
||||
RuleFor(c => c.TvCategory).NotEmpty().When(c => !String.IsNullOrWhiteSpace(c.TvCategoryLocalPath));
|
||||
RuleFor(c => c.TvCategoryLocalPath).IsValidPath().When(c => !String.IsNullOrWhiteSpace(c.TvCategoryLocalPath));
|
||||
|
||||
RuleFor(c => c.TvCategory).NotEmpty().WithMessage("A category is recommended").AsWarning();
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +49,7 @@ public NzbgetSettings()
|
||||
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
||||
public String Password { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
public String TvCategory { get; set; }
|
||||
|
||||
// TODO: Remove around January 2015, this setting was superceded by the RemotePathMappingService, but has to remain for a while to properly migrate.
|
||||
|
@ -181,7 +181,7 @@ public override IEnumerable<DownloadClientItem> GetItems()
|
||||
|
||||
foreach (var downloadClientItem in GetQueue().Concat(GetHistory()))
|
||||
{
|
||||
if (downloadClientItem.Category == Settings.TvCategory)
|
||||
if (downloadClientItem.Category == Settings.TvCategory || downloadClientItem.Category == "*" && Settings.TvCategory.IsNullOrWhiteSpace())
|
||||
{
|
||||
yield return downloadClientItem;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Download.Clients.Sabnzbd
|
||||
{
|
||||
@ -24,6 +25,10 @@ public SabnzbdSettingsValidator()
|
||||
RuleFor(c => c.Password).NotEmpty()
|
||||
.WithMessage("Password is required when API key is not configured")
|
||||
.When(c => String.IsNullOrWhiteSpace(c.ApiKey));
|
||||
|
||||
RuleFor(c => c.TvCategory).NotEmpty()
|
||||
.WithMessage("A category is recommended")
|
||||
.AsWarning();
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +60,7 @@ public SabnzbdSettings()
|
||||
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
|
||||
public String Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
public String TvCategory { get; set; }
|
||||
|
||||
// TODO: Remove around January 2015, this setting was superceded by the RemotePathMappingService, but has to remain for a while to properly migrate.
|
||||
|
@ -42,7 +42,7 @@ public TransmissionSettings()
|
||||
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
|
||||
public String Password { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional. Creates a .[category] subdirectory in the output directory.")]
|
||||
public String TvCategory { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(TransmissionPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||
|
@ -39,7 +39,7 @@ public UTorrentSettings()
|
||||
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
||||
public String Password { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox)]
|
||||
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
|
||||
public String TvCategory { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
|
||||
|
@ -869,8 +869,10 @@
|
||||
<Compile Include="Update\UpdateVerificationFailedException.cs" />
|
||||
<Compile Include="Validation\FolderValidator.cs" />
|
||||
<Compile Include="Validation\IpValidation.cs" />
|
||||
<Compile Include="Validation\LangaugeValidator.cs" />
|
||||
<Compile Include="Validation\LanguageValidator.cs" />
|
||||
<Compile Include="Validation\NzbDroneValidationFailure.cs" />
|
||||
<Compile Include="Validation\NzbDroneValidationResult.cs" />
|
||||
<Compile Include="Validation\NzbDroneValidationState.cs" />
|
||||
<Compile Include="Validation\Paths\DroneFactoryValidator.cs" />
|
||||
<Compile Include="Validation\Paths\PathExistsValidator.cs" />
|
||||
<Compile Include="Validation\Paths\PathValidator.cs" />
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public class LangaugeValidator : PropertyValidator
|
||||
public class LanguageValidator : PropertyValidator
|
||||
{
|
||||
public LangaugeValidator()
|
||||
public LanguageValidator()
|
||||
: base("Unknown Language")
|
||||
{
|
||||
}
|
@ -5,13 +5,29 @@ namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public class NzbDroneValidationFailure : ValidationFailure
|
||||
{
|
||||
public String DetailedDescription { get; set; }
|
||||
public String InfoLink { get; set; }
|
||||
public bool IsWarning { get; set; }
|
||||
public string DetailedDescription { get; set; }
|
||||
public string InfoLink { get; set; }
|
||||
|
||||
public NzbDroneValidationFailure(String propertyName, String error)
|
||||
public NzbDroneValidationFailure(string propertyName, string error)
|
||||
: base(propertyName, error)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NzbDroneValidationFailure(string propertyName, string error, object attemptedValue)
|
||||
: base(propertyName, error, attemptedValue)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public NzbDroneValidationFailure(ValidationFailure validationFailure)
|
||||
: base(validationFailure.PropertyName, validationFailure.ErrorMessage, validationFailure.AttemptedValue)
|
||||
{
|
||||
CustomState = validationFailure.CustomState;
|
||||
var state = validationFailure.CustomState as NzbDroneValidationState;
|
||||
|
||||
IsWarning = state != null && state.IsWarning;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
55
src/NzbDrone.Core/Validation/NzbDroneValidationResult.cs
Normal file
55
src/NzbDrone.Core/Validation/NzbDroneValidationResult.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public class NzbDroneValidationResult : ValidationResult
|
||||
{
|
||||
public NzbDroneValidationResult()
|
||||
{
|
||||
}
|
||||
|
||||
public NzbDroneValidationResult(IEnumerable<ValidationFailure> failures)
|
||||
{
|
||||
var errors = new List<NzbDroneValidationFailure>();
|
||||
var warnings = new List<NzbDroneValidationFailure>();
|
||||
|
||||
foreach (var failureBase in failures)
|
||||
{
|
||||
var failure = failureBase as NzbDroneValidationFailure;
|
||||
if (failure == null)
|
||||
{
|
||||
failure = new NzbDroneValidationFailure(failureBase);
|
||||
}
|
||||
if (failure.IsWarning)
|
||||
{
|
||||
warnings.Add(failure);
|
||||
}
|
||||
else
|
||||
{
|
||||
errors.Add(failure);
|
||||
}
|
||||
}
|
||||
|
||||
Failures = errors.Concat(warnings).ToList();
|
||||
Errors = errors;
|
||||
errors.ForEach(base.Errors.Add);
|
||||
Warnings = warnings;
|
||||
}
|
||||
|
||||
public IList<NzbDroneValidationFailure> Failures { get; private set; }
|
||||
|
||||
public new IList<NzbDroneValidationFailure> Errors { get; private set; }
|
||||
|
||||
public IList<NzbDroneValidationFailure> Warnings { get; private set; }
|
||||
|
||||
public virtual bool HasWarnings
|
||||
{
|
||||
get { return Warnings.Any(); }
|
||||
}
|
||||
}
|
||||
}
|
16
src/NzbDrone.Core/Validation/NzbDroneValidationState.cs
Normal file
16
src/NzbDrone.Core/Validation/NzbDroneValidationState.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public class NzbDroneValidationState
|
||||
{
|
||||
public static NzbDroneValidationState Warning = new NzbDroneValidationState { IsWarning = true };
|
||||
|
||||
public bool IsWarning { get; set; }
|
||||
}
|
||||
}
|
17
src/NzbDrone.Core/Validation/NzbDroneValidator.cs
Normal file
17
src/NzbDrone.Core/Validation/NzbDroneValidator.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentValidation;
|
||||
using FluentValidation.Results;
|
||||
|
||||
namespace NzbDrone.Core.Validation
|
||||
{
|
||||
public abstract class NzbDroneValidator<T> : AbstractValidator<T>
|
||||
{
|
||||
public override ValidationResult Validate(T instance)
|
||||
{
|
||||
return new NzbDroneValidationResult(base.Validate(instance).Errors);
|
||||
}
|
||||
}
|
||||
}
|
@ -35,7 +35,12 @@ public static IRuleBuilderOptions<T, int> ValidPort<T>(this IRuleBuilder<T, int>
|
||||
|
||||
public static IRuleBuilderOptions<T, Language> ValidLanguage<T>(this IRuleBuilder<T, Language> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.SetValidator(new LangaugeValidator());
|
||||
return ruleBuilder.SetValidator(new LanguageValidator());
|
||||
}
|
||||
|
||||
public static IRuleBuilderOptions<T, TProp> AsWarning<T, TProp>(this IRuleBuilderOptions<T, TProp> ruleBuilder)
|
||||
{
|
||||
return ruleBuilder.WithState(v => NzbDroneValidationState.Warning);
|
||||
}
|
||||
}
|
||||
}
|
@ -102,6 +102,20 @@ h3 {
|
||||
}
|
||||
}
|
||||
|
||||
.has-warning {
|
||||
.help-inline {
|
||||
color: orange;
|
||||
margin-left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.validation-warning {
|
||||
i {
|
||||
text-decoration: none;
|
||||
color: orange;
|
||||
}
|
||||
}
|
||||
|
||||
// Tooltips
|
||||
|
||||
.help-inline-checkbox, .help-inline {
|
||||
|
@ -37,16 +37,21 @@ module.exports = function() {
|
||||
} else {
|
||||
var inputGroup = formGroup.find('.input-group');
|
||||
|
||||
if (inputGroup.length === 0) {
|
||||
formGroup.append('<span class="help-inline validation-error">' + errorMessage + '</span>');
|
||||
}
|
||||
var validationClass = error.isWarning ? 'validation-warning' : 'validation-error';
|
||||
|
||||
if (inputGroup.length === 0) {
|
||||
formGroup.append('<span class="help-inline {0}">{1}</span>'.format(validationClass, errorMessage));
|
||||
}
|
||||
else {
|
||||
inputGroup.parent().append('<span class="help-block validation-error">' + errorMessage + '</span>');
|
||||
inputGroup.parent().append('<span class="help-block {0}">{1}</span>'.format(validationClass, errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
formGroup.addClass('has-error');
|
||||
if (error.isWarning) {
|
||||
formGroup.addClass('has-warning');
|
||||
} else {
|
||||
formGroup.addClass('has-error');
|
||||
}
|
||||
|
||||
return formGroup.find('.help-inline').text();
|
||||
};
|
||||
@ -59,20 +64,23 @@ module.exports = function() {
|
||||
|
||||
var errorMessage = this.formatErrorMessage(error);
|
||||
|
||||
if (this.find('.modal-body')) {
|
||||
this.find('.modal-body').prepend('<div class="alert alert-danger validation-error">' + errorMessage + '</div>');
|
||||
var target = this.find('.modal-body');
|
||||
if (!target.length) {
|
||||
target = this;
|
||||
}
|
||||
|
||||
else {
|
||||
this.prepend('<div class="alert alert-danger validation-error">' + errorMessage + '</div>');
|
||||
}
|
||||
var validationClass = error.isWarning ? 'alert alert-warning validation-warning' : 'alert alert-danger validation-error';
|
||||
|
||||
target.prepend('<div class="{0}">{1}</div>'.format(validationClass, errorMessage));
|
||||
};
|
||||
|
||||
$.fn.removeAllErrors = function() {
|
||||
this.find('.has-error').removeClass('has-error');
|
||||
this.find('.has-warning').removeClass('has-warning');
|
||||
this.find('.error').removeClass('error');
|
||||
this.find('.validation-errors').removeClass('alert').removeClass('alert-danger').html('');
|
||||
this.find('.validation-errors').removeClass('alert').removeClass('alert-danger').removeClass('alert-warning').html('');
|
||||
this.find('.validation-error').remove();
|
||||
this.find('.validation-warning').remove();
|
||||
return this.find('.help-inline.error-message').remove();
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user