mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-10-31 16:02:29 +01:00
added input validation to quality profiles
This commit is contained in:
parent
6367d3d204
commit
147bb5476b
@ -1,20 +1,11 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Core.Datastore;
|
|
||||||
using NzbDrone.Core.Qualities;
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Api.Mapping;
|
using NzbDrone.Api.Mapping;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using FluentValidation;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Qualities
|
namespace NzbDrone.Api.Qualities
|
||||||
{
|
{
|
||||||
|
|
||||||
public static class LazyLoadedExtensions
|
|
||||||
{
|
|
||||||
public static IEnumerable<int> GetForeignKeys(this IEnumerable<ModelBase> models)
|
|
||||||
{
|
|
||||||
return models.Select(c => c.Id).Distinct();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class QualityProfileModule : NzbDroneRestModule<QualityProfileResource>
|
public class QualityProfileModule : NzbDroneRestModule<QualityProfileResource>
|
||||||
{
|
{
|
||||||
private readonly QualityProfileService _qualityProfileService;
|
private readonly QualityProfileService _qualityProfileService;
|
||||||
@ -24,6 +15,10 @@ namespace NzbDrone.Api.Qualities
|
|||||||
{
|
{
|
||||||
_qualityProfileService = qualityProfileService;
|
_qualityProfileService = qualityProfileService;
|
||||||
|
|
||||||
|
SharedValidator.RuleFor(c => c.Name).NotEmpty();
|
||||||
|
SharedValidator.RuleFor(c => c.Cutoff).NotNull();
|
||||||
|
SharedValidator.RuleFor(c => c.Allowed).NotEmpty();
|
||||||
|
|
||||||
GetResourceAll = GetAll;
|
GetResourceAll = GetAll;
|
||||||
|
|
||||||
GetResourceById = GetById;
|
GetResourceById = GetById;
|
||||||
|
@ -20,9 +20,9 @@
|
|||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Cutoff</label>
|
<label class="control-label">Cutoff</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<select class="x-cutoff" name="cutoff.id">
|
<select class="x-cutoff" name="cutoff.id" validation-name="cutoff">
|
||||||
{{#each allowed}}
|
{{#each allowed}}
|
||||||
<option value="{{id}}">{{name}}</option>
|
<option value="{{id}}">{{name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
<span class="help-inline">
|
<span class="help-inline">
|
||||||
@ -41,12 +41,16 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="span3">
|
<div class="span3">
|
||||||
<h3>Allowed</h3>
|
<div class="control-group">
|
||||||
<select multiple="multiple" class="x-allowed-list">
|
<div class="controls">
|
||||||
{{#each allowed}}
|
<h3>Allowed</h3>
|
||||||
<option value="{{id}}">{{name}}</option>
|
<select multiple="multiple" class="x-allowed-list" validation-name="allowed">
|
||||||
{{/each}}
|
{{#each allowed}}
|
||||||
</select>
|
<option value="{{id}}">{{name}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,74 +1,81 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
define(['app', 'marionette', 'Mixins/AsModelBoundView'], function (App, Marionette, AsModelBoundView) {
|
define(
|
||||||
|
[
|
||||||
|
'app',
|
||||||
|
'marionette',
|
||||||
|
'Mixins/AsModelBoundView',
|
||||||
|
'Mixins/AsValidatedView'
|
||||||
|
], function (App, Marionette, AsModelBoundView, AsValidatedView) {
|
||||||
|
|
||||||
var view = Marionette.ItemView.extend({
|
var view = Marionette.ItemView.extend({
|
||||||
template: 'Settings/Quality/Profile/EditQualityProfileTemplate',
|
template: 'Settings/Quality/Profile/EditQualityProfileTemplate',
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
cutoff: '.x-cutoff'
|
cutoff: '.x-cutoff'
|
||||||
},
|
},
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
'click .x-save' : '_saveQualityProfile',
|
'click .x-save' : '_saveQualityProfile',
|
||||||
'dblclick .x-available-list': '_moveQuality',
|
'dblclick .x-available-list': '_moveQuality',
|
||||||
'dblclick .x-allowed-list' : '_moveQuality'
|
'dblclick .x-allowed-list' : '_moveQuality'
|
||||||
},
|
},
|
||||||
|
|
||||||
initialize: function (options) {
|
initialize: function (options) {
|
||||||
this.profileCollection = options.profileCollection;
|
this.profileCollection = options.profileCollection;
|
||||||
},
|
},
|
||||||
|
|
||||||
_moveQuality: function (event) {
|
_moveQuality: function (event) {
|
||||||
|
|
||||||
var quality;
|
var quality;
|
||||||
var qualityId = event.target.value;
|
var qualityId = event.target.value;
|
||||||
var availableCollection = new Backbone.Collection(this.model.get('available'));
|
var availableCollection = new Backbone.Collection(this.model.get('available'));
|
||||||
availableCollection.comparator = function (model) {
|
availableCollection.comparator = function (model) {
|
||||||
return model.get('weight');
|
return model.get('weight');
|
||||||
};
|
};
|
||||||
|
|
||||||
var allowedCollection = new Backbone.Collection(this.model.get('allowed'));
|
var allowedCollection = new Backbone.Collection(this.model.get('allowed'));
|
||||||
allowedCollection.comparator = function (model) {
|
allowedCollection.comparator = function (model) {
|
||||||
return model.get('weight');
|
return model.get('weight');
|
||||||
};
|
};
|
||||||
|
|
||||||
if (availableCollection.get(qualityId)) {
|
if (availableCollection.get(qualityId)) {
|
||||||
quality = availableCollection.get(qualityId);
|
quality = availableCollection.get(qualityId);
|
||||||
availableCollection.remove(quality);
|
availableCollection.remove(quality);
|
||||||
allowedCollection.add(quality);
|
allowedCollection.add(quality);
|
||||||
|
}
|
||||||
|
else if (allowedCollection.get(qualityId)) {
|
||||||
|
quality = allowedCollection.get(qualityId);
|
||||||
|
|
||||||
|
allowedCollection.remove(quality);
|
||||||
|
availableCollection.add(quality);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw 'couldnt find quality id ' + qualityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.model.set('available', availableCollection.toJSON());
|
||||||
|
this.model.set('allowed', allowedCollection.toJSON());
|
||||||
|
|
||||||
|
this.render();
|
||||||
|
},
|
||||||
|
|
||||||
|
_saveQualityProfile: function () {
|
||||||
|
var self = this;
|
||||||
|
var cutoff = _.findWhere(this.model.get('allowed'), { id: parseInt(this.ui.cutoff.val())});
|
||||||
|
this.model.set('cutoff', cutoff);
|
||||||
|
|
||||||
|
var promise = this.model.save();
|
||||||
|
|
||||||
|
if (promise) {
|
||||||
|
promise.done(function () {
|
||||||
|
self.profileCollection.add(self.model, { merge: true });
|
||||||
|
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (allowedCollection.get(qualityId)) {
|
});
|
||||||
quality = allowedCollection.get(qualityId);
|
|
||||||
|
|
||||||
allowedCollection.remove(quality);
|
AsValidatedView.call(view);
|
||||||
availableCollection.add(quality);
|
return AsModelBoundView.call(view);
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw 'couldnt find quality id ' + qualityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.model.set('available', availableCollection.toJSON());
|
|
||||||
this.model.set('allowed', allowedCollection.toJSON());
|
|
||||||
|
|
||||||
this.render();
|
|
||||||
},
|
|
||||||
|
|
||||||
_saveQualityProfile: function () {
|
|
||||||
var self = this;
|
|
||||||
var cutoff = _.findWhere(this.model.get('allowed'), { id: parseInt(this.ui.cutoff.val())});
|
|
||||||
this.model.set('cutoff', cutoff);
|
|
||||||
|
|
||||||
var promise = this.model.save();
|
|
||||||
|
|
||||||
if (promise) {
|
|
||||||
promise.done(function () {
|
|
||||||
self.profileCollection.add(self.model, { merge: true });
|
|
||||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return AsModelBoundView.call(view);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
Loading…
Reference in New Issue
Block a user