New: Added Presets to Indexers to add indexers with default properties. In an older version of NzbDrone these default indexers were added automatically and could not be removed.

This commit is contained in:
Taloth Saldono 2014-06-20 00:56:49 +02:00
parent 6184105d3c
commit 9633afc612
21 changed files with 241 additions and 190 deletions

View File

@ -5,6 +5,7 @@ using NzbDrone.Common;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.Annotations;
using Omu.ValueInjecter;
namespace NzbDrone.Api.ClientSchema
{
@ -55,7 +56,7 @@ namespace NzbDrone.Api.ClientSchema
}
public static object ReadFormSchema(List<Field> fields, Type targetType)
public static object ReadFormSchema(List<Field> fields, Type targetType, object defaults = null)
{
Ensure.That(targetType, () => targetType).IsNotNull();
@ -63,6 +64,11 @@ namespace NzbDrone.Api.ClientSchema
var target = Activator.CreateInstance(targetType);
if (defaults != null)
{
target.InjectFrom(defaults);
}
foreach (var propertyInfo in properties)
{
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.Download;
using Omu.ValueInjecter;
namespace NzbDrone.Api.DownloadClient
{
public class DownloadClientSchemaModule : NzbDroneRestModule<DownloadClientResource>
{
private readonly IDownloadClientFactory _downloadClientFactory;
public DownloadClientSchemaModule(IDownloadClientFactory downloadClientFactory)
: base("downloadclient/schema")
{
_downloadClientFactory = downloadClientFactory;
GetResourceAll = GetSchema;
}
private List<DownloadClientResource> GetSchema()
{
var downloadClients = _downloadClientFactory.Templates();
var result = new List<DownloadClientResource>(downloadClients.Count);
foreach (var downloadClient in downloadClients)
{
var downloadClientResource = new DownloadClientResource();
downloadClientResource.InjectFrom(downloadClient);
downloadClientResource.Fields = SchemaBuilder.ToSchema(downloadClient.Settings);
result.Add(downloadClientResource);
}
return result;
}
}
}

View File

@ -1,38 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.Indexers;
using Omu.ValueInjecter;
namespace NzbDrone.Api.Indexers
{
public class IndexerSchemaModule : NzbDroneRestModule<IndexerResource>
{
private readonly IIndexerFactory _indexerFactory;
public IndexerSchemaModule(IIndexerFactory indexerFactory)
: base("indexer/schema")
{
_indexerFactory = indexerFactory;
GetResourceAll = GetSchema;
}
private List<IndexerResource> GetSchema()
{
var indexers = _indexerFactory.Templates();
var result = new List<IndexerResource>(indexers.Count());
foreach (var indexer in indexers)
{
var indexerResource = new IndexerResource();
indexerResource.InjectFrom(indexer);
indexerResource.Fields = SchemaBuilder.ToSchema(indexer.Settings);
result.Add(indexerResource);
}
return result;
}
}
}

View File

@ -1,37 +0,0 @@
using System.Collections.Generic;
using NzbDrone.Api.ClientSchema;
using NzbDrone.Core.Notifications;
using Omu.ValueInjecter;
namespace NzbDrone.Api.Notifications
{
public class NotificationSchemaModule : NzbDroneRestModule<NotificationResource>
{
private readonly INotificationFactory _notificationFactory;
public NotificationSchemaModule(INotificationFactory notificationFactory)
: base("notification/schema")
{
_notificationFactory = notificationFactory;
GetResourceAll = GetSchema;
}
private List<NotificationResource> GetSchema()
{
var notifications = _notificationFactory.Templates();
var result = new List<NotificationResource>(notifications.Count);
foreach (var notification in notifications)
{
var notificationResource = new NotificationResource();
notificationResource.InjectFrom(notification);
notificationResource.Fields = SchemaBuilder.ToSchema(notification.Settings);
result.Add(notificationResource);
}
return result;
}
}
}

View File

@ -144,11 +144,9 @@
<Compile Include="MediaCovers\MediaCoverModule.cs" />
<Compile Include="Metadata\MetadataResource.cs" />
<Compile Include="Metadata\MetadataModule.cs" />
<Compile Include="Notifications\NotificationSchemaModule.cs" />
<Compile Include="NzbDroneFeedModule.cs" />
<Compile Include="ProviderResource.cs" />
<Compile Include="ProviderModuleBase.cs" />
<Compile Include="Indexers\IndexerSchemaModule.cs" />
<Compile Include="Indexers\IndexerModule.cs" />
<Compile Include="Indexers\IndexerResource.cs" />
<Compile Include="Indexers\ReleaseModule.cs" />
@ -172,7 +170,6 @@
<Compile Include="Queue\QueueModule.cs" />
<Compile Include="Queue\QueueResource.cs" />
<Compile Include="ResourceChangeMessage.cs" />
<Compile Include="DownloadClient\DownloadClientSchemaModule.cs" />
<Compile Include="Notifications\NotificationModule.cs" />
<Compile Include="Notifications\NotificationResource.cs" />
<Compile Include="NzbDroneRestModule.cs" />

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using Nancy;
@ -22,7 +23,7 @@ namespace NzbDrone.Api
: base(resource)
{
_providerFactory = providerFactory;
Get["templates"] = x => GetTemplates();
Get["schema"] = x => GetTemplates();
GetResourceAll = GetAll;
GetResourceById = GetProviderById;
CreateResource = CreateProvider;
@ -82,8 +83,13 @@ namespace NzbDrone.Api
definition.InjectFrom(providerResource);
var preset = _providerFactory.GetPresetDefinitions(definition)
.Where(v => v.Name == definition.Name)
.Select(v => v.Settings)
.FirstOrDefault();
var configContract = ReflectionExtensions.CoreAssembly.FindTypeByName(definition.ConfigContract);
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract);
definition.Settings = (IProviderConfig)SchemaBuilder.ReadFormSchema(providerResource.Fields, configContract, preset);
Validate(definition);
@ -97,15 +103,29 @@ namespace NzbDrone.Api
private Response GetTemplates()
{
var templates = _providerFactory.Templates();
var defaultDefinitions = _providerFactory.GetDefaultDefinitions();
var result = new List<TProviderResource>(templates.Count());
var result = new List<TProviderResource>(defaultDefinitions.Count());
foreach (var providerDefinition in templates)
foreach (var providerDefinition in defaultDefinitions)
{
var providerResource = new TProviderResource();
providerResource.InjectFrom(providerDefinition);
providerResource.Fields = SchemaBuilder.ToSchema(providerDefinition.Settings);
providerResource.InfoLink = String.Format("https://github.com/NzbDrone/NzbDrone/wiki/Supported-{0}#{1}",
typeof(TProviderResource).Name.Replace("Resource", "s"),
providerDefinition.Implementation.ToLower());
var presetDefinitions = _providerFactory.GetPresetDefinitions(providerDefinition);
providerResource.Presets = presetDefinitions.Select(v =>
{
var presetResource = new TProviderResource();
presetResource.InjectFrom(v);
presetResource.Fields = SchemaBuilder.ToSchema(v.Settings);
return presetResource as ProviderResource;
}).ToList();
result.Add(providerResource);
}

View File

@ -11,5 +11,8 @@ namespace NzbDrone.Api
public List<Field> Fields { get; set; }
public String Implementation { get; set; }
public String ConfigContract { get; set; }
public String InfoLink { get; set; }
public List<ProviderResource> Presets { get; set; }
}
}

View File

@ -27,9 +27,9 @@ namespace NzbDrone.Core.Download
return base.Active().Where(c => c.Enable).ToList();
}
protected override DownloadClientDefinition GetTemplate(IDownloadClient provider)
protected override DownloadClientDefinition GetProviderCharacteristics(IDownloadClient provider, DownloadClientDefinition definition)
{
var definition = base.GetTemplate(provider);
definition = base.GetProviderCharacteristics(provider, definition);
definition.Protocol = provider.Protocol;

View File

@ -50,9 +50,9 @@ namespace NzbDrone.Core.Indexers
return base.Create(definition);
}
protected override IndexerDefinition GetTemplate(IIndexer provider)
protected override IndexerDefinition GetProviderCharacteristics(IIndexer provider, IndexerDefinition definition)
{
var definition = base.GetTemplate(provider);
definition = base.GetProviderCharacteristics(provider, definition);
definition.Protocol = provider.Protocol;

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace NzbDrone.Core.ThingiProvider
{
@ -12,6 +13,7 @@ namespace NzbDrone.Core.ThingiProvider
TProviderDefinition Create(TProviderDefinition indexer);
void Update(TProviderDefinition indexer);
void Delete(int id);
List<TProviderDefinition> Templates();
IEnumerable<TProviderDefinition> GetDefaultDefinitions();
IEnumerable<TProviderDefinition> GetPresetDefinitions(TProviderDefinition providerDefinition);
}
}

View File

@ -38,9 +38,41 @@ namespace NzbDrone.Core.ThingiProvider
return _providerRepository.All().ToList();
}
public List<TProviderDefinition> Templates()
public IEnumerable<TProviderDefinition> GetDefaultDefinitions()
{
return _providers.Select(GetTemplate).ToList();
foreach (var provider in _providers)
{
var definition = provider.DefaultDefinitions
.OfType<TProviderDefinition>()
.FirstOrDefault(v => v.Name == null || v.Name == provider.GetType().Name);
if (definition == null)
{
definition = new TProviderDefinition()
{
Name = string.Empty,
ConfigContract = provider.ConfigContract.Name,
Implementation = provider.GetType().Name,
Settings = (IProviderConfig)Activator.CreateInstance(provider.ConfigContract)
};
}
definition = GetProviderCharacteristics(provider, definition);
yield return definition;
}
}
public IEnumerable<TProviderDefinition> GetPresetDefinitions(TProviderDefinition providerDefinition)
{
var provider = _providers.First(v => v.GetType().Name == providerDefinition.Implementation);
var definitions = provider.DefaultDefinitions
.OfType<TProviderDefinition>()
.Where(v => v.Name != null && v.Name != provider.GetType().Name)
.ToList();
return definitions;
}
public List<TProvider> GetAvailableProviders()
@ -82,18 +114,6 @@ namespace NzbDrone.Core.ThingiProvider
return _providers.Select(c => c.GetType()).SingleOrDefault(c => c.Name.Equals(definition.Implementation, StringComparison.InvariantCultureIgnoreCase));
}
protected virtual TProviderDefinition GetTemplate(TProvider provider)
{
var definition = new TProviderDefinition()
{
ConfigContract = provider.ConfigContract.Name,
Implementation = provider.GetType().Name,
Settings = (IProviderConfig)Activator.CreateInstance(provider.ConfigContract)
};
return definition;
}
public void Handle(ApplicationStartedEvent message)
{
_logger.Debug("Initializing Providers. Count {0}", _providers.Count);
@ -112,6 +132,11 @@ namespace NzbDrone.Core.ThingiProvider
return All().Where(c => c.Settings.Validate().IsValid).ToList();
}
protected virtual TProviderDefinition GetProviderCharacteristics(TProvider provider, TProviderDefinition definition)
{
return definition;
}
private void RemoveMissingImplementations()
{
var storedProvider = _providerRepository.All();

View File

@ -1,32 +1,51 @@
'use strict';
define([
'underscore',
'jquery',
'AppLayout',
'marionette',
'Settings/DownloadClient/Edit/DownloadClientEditView'
], function ($, AppLayout, Marionette, EditView) {
], function (_, $, AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate',
tagName : 'li',
template : 'Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate',
tagName : 'li',
className : 'add-thingy-item',
events: {
'click': '_add'
'click .x-preset': '_addPreset',
'click' : '_add'
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
_addPreset: function (e) {
var presetName = $(e.target).closest('.x-preset').attr('data-id');
var presetData = _.where(this.model.get('presets'), {name: presetName})[0];
this.model.set(presetData);
this.model.set({
id : undefined,
enable : true
});
var editView = new EditView({ model: this.model, targetCollection: this.targetCollection });
AppLayout.modalRegion.show(editView);
},
_add: function (e) {
if (this.$(e.target).hasClass('icon-info-sign')) {
if ($(e.target).closest('.btn,.btn-group').length !== 0) {
return;
}
this.model.set({
id : undefined,
name : this.model.get('implementation'),
enable : true
});

View File

@ -1,6 +1,27 @@
<div class="add-thingy">
{{implementation}}
{{#if link}}
<a href="{{link}}"><i class="icon-info-sign"/></a>
{{/if}}
<div>
{{implementation}}
</div>
<div class="pull-right">
{{#if_gt presets.length compare=0}}
<div class="btn-group">
<button class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Presets
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{{#each presets}}
<li class="x-preset" data-id="{{name}}">
<a>{{name}}</a>
</li>
{{/each}}
</ul>
</div>
{{/if_gt}}
{{#if infoLink}}
<a class="btn btn-xs btn-default x-info" href="{{infoLink}}">
<i class="icon-info-sign"/>
</a>
{{/if}}
</div>
</div>

View File

@ -27,7 +27,7 @@
}
.add-download-client {
li {
li.add-thingy-item {
width: 33%;
}
}

View File

@ -1,32 +1,51 @@
'use strict';
define([
'underscore',
'jquery',
'AppLayout',
'marionette',
'Settings/Indexers/Edit/IndexerEditView'
], function ($, AppLayout, Marionette, EditView) {
], function (_, $, AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/Indexers/Add/IndexerAddItemViewTemplate',
tagName : 'li',
template : 'Settings/Indexers/Add/IndexerAddItemViewTemplate',
tagName : 'li',
className : 'add-thingy-item',
events: {
'click': '_add'
'click .x-preset': '_addPreset',
'click' : '_add'
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
_addPreset: function (e) {
var presetName = $(e.target).closest('.x-preset').attr('data-id');
var presetData = _.where(this.model.get('presets'), {name: presetName})[0];
this.model.set(presetData);
this.model.set({
id : undefined,
enable : true
});
var editView = new EditView({ model: this.model, targetCollection: this.targetCollection });
AppLayout.modalRegion.show(editView);
},
_add: function (e) {
if (this.$(e.target).hasClass('icon-info-sign')) {
if ($(e.target).closest('.btn,.btn-group').length !== 0) {
return;
}
this.model.set({
id : undefined,
name : undefined,
enable : true
});

View File

@ -1,6 +1,27 @@
<div class="add-thingy">
{{implementation}}
{{#if link}}
<a href="{{link}}"><i class="icon-info-sign"/></a>
{{/if}}
<div>
{{implementation}}
</div>
<div class="pull-right">
{{#if_gt presets.length compare=0}}
<div class="btn-group">
<button class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Presets
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{{#each presets}}
<li class="x-preset" data-id="{{name}}">
<a>{{name}}</a>
</li>
{{/each}}
</ul>
</div>
{{/if_gt}}
{{#if infoLink}}
<a class="btn btn-xs btn-default x-info" href="{{infoLink}}">
<i class="icon-info-sign"/>
</a>
{{/if}}
</div>
</div>

View File

@ -27,7 +27,7 @@
}
.add-indexer {
li {
li.add-thingy-item {
width: 33%;
}
}

View File

@ -1,32 +1,53 @@
'use strict';
define([
'underscore',
'jquery',
'AppLayout',
'marionette',
'Settings/Notifications/Edit/NotificationEditView'
], function ($, AppLayout, Marionette, EditView) {
], function (_, $, AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/Notifications/Add/NotificationAddItemViewTemplate',
tagName : 'li',
template : 'Settings/Notifications/Add/NotificationAddItemViewTemplate',
tagName : 'li',
className : 'add-thingy-item',
events: {
'click': '_add'
'click .x-preset': '_addPreset',
'click' : '_add'
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
_addPreset: function (e) {
var presetName = $(e.target).closest('.x-preset').attr('data-id');
var presetData = _.where(this.model.get('presets'), {name: presetName})[0];
this.model.set(presetData);
this.model.set({
id : undefined,
onGrab : true,
onDownload : true,
onUpgrade : true
});
var editView = new EditView({ model: this.model, targetCollection: this.targetCollection });
AppLayout.modalRegion.show(editView);
},
_add: function (e) {
if (this.$(e.target).hasClass('icon-info-sign')) {
if ($(e.target).closest('.btn,.btn-group').length !== 0) {
return;
}
this.model.set({
id : undefined,
name : this.model.get('implementation'),
onGrab : true,
onDownload : true,
onUpgrade : true

View File

@ -1,6 +1,27 @@
<div class="add-thingy">
{{implementation}}
{{#if link}}
<a href="{{link}}"><i class="icon-info-sign"/></a>
{{/if}}
<div>
{{implementation}}
</div>
<div class="pull-right">
{{#if_gt presets.length compare=0}}
<div class="btn-group">
<button class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown">
Presets
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{{#each presets}}
<li class="x-preset" data-id="{{name}}">
<a>{{name}}</a>
</li>
{{/each}}
</ul>
</div>
{{/if_gt}}
{{#if infoLink}}
<a class="btn btn-xs btn-default x-info" href="{{infoLink}}">
<i class="icon-info-sign"/>
</a>
{{/if}}
</div>
</div>

View File

@ -25,7 +25,7 @@
}
.add-notifications {
li {
li.add-thingy-item {
width: 40%;
}
}

View File

@ -7,19 +7,7 @@
font-size: 24px;
font-weight: lighter;
text-align: center;
a {
font-size: 16px;
color: #595959;
i {
.clickable;
}
}
a:hover {
text-decoration: none;
}
height: 85px;
}
.add-thingies {
@ -30,12 +18,12 @@
text-transform: capitalize;
}
.items {
ul.items {
list-style-type: none;
margin: 0px;
padding: 0px;
li {
li.add-thingy-item {
display: inline-block;
vertical-align: top;
}