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.EnsureThat;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.Annotations;
using Omu.ValueInjecter;
namespace NzbDrone.Api.ClientSchema
{
@ -55,7 +56,7 @@ public static List<Field> ToSchema(object model)
}
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 @@ public static object ReadFormSchema(List<Field> fields, Type targetType)
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 @@ protected ProviderModuleBase(IProviderFactory<TProvider, TProviderDefinition> pr
: base(resource)
{
_providerFactory = providerFactory;
Get["templates"] = x => GetTemplates();
Get["schema"] = x => GetTemplates();
GetResourceAll = GetAll;
GetResourceById = GetProviderById;
CreateResource = CreateProvider;
@ -82,8 +83,13 @@ private TProviderDefinition GetDefinition(TProviderResource providerResource)
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 @@ private void DeleteProvider(int id)
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 @@ public class ProviderResource : RestResource
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 @@ protected override List<DownloadClientDefinition> Active()
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 @@ public override IndexerDefinition Create(IndexerDefinition definition)
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 @@ public interface IProviderFactory<TProvider, TProviderDefinition>
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 @@ public List<TProviderDefinition> All()
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 @@ private Type GetImplementation(TProviderDefinition definition)
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 @@ protected virtual List<TProviderDefinition> Active()
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;
}