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.

pull/2/head
Taloth Saldono 11 years ago
parent 6184105d3c
commit 9633afc612

@ -5,6 +5,7 @@ using NzbDrone.Common;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Reflection; using NzbDrone.Common.Reflection;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
using Omu.ValueInjecter;
namespace NzbDrone.Api.ClientSchema 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(); Ensure.That(targetType, () => targetType).IsNotNull();
@ -63,6 +64,11 @@ namespace NzbDrone.Api.ClientSchema
var target = Activator.CreateInstance(targetType); var target = Activator.CreateInstance(targetType);
if (defaults != null)
{
target.InjectFrom(defaults);
}
foreach (var propertyInfo in properties) foreach (var propertyInfo in properties)
{ {
var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false); var fieldAttribute = propertyInfo.GetAttribute<FieldDefinitionAttribute>(false);

@ -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;
}
}
}

@ -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;
}
}
}

@ -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;
}
}
}

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

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentValidation; using FluentValidation;
using Nancy; using Nancy;
@ -22,7 +23,7 @@ namespace NzbDrone.Api
: base(resource) : base(resource)
{ {
_providerFactory = providerFactory; _providerFactory = providerFactory;
Get["templates"] = x => GetTemplates(); Get["schema"] = x => GetTemplates();
GetResourceAll = GetAll; GetResourceAll = GetAll;
GetResourceById = GetProviderById; GetResourceById = GetProviderById;
CreateResource = CreateProvider; CreateResource = CreateProvider;
@ -82,8 +83,13 @@ namespace NzbDrone.Api
definition.InjectFrom(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); 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); Validate(definition);
@ -97,15 +103,29 @@ namespace NzbDrone.Api
private Response GetTemplates() 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(); var providerResource = new TProviderResource();
providerResource.InjectFrom(providerDefinition); providerResource.InjectFrom(providerDefinition);
providerResource.Fields = SchemaBuilder.ToSchema(providerDefinition.Settings); 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); result.Add(providerResource);
} }

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

@ -27,9 +27,9 @@ namespace NzbDrone.Core.Download
return base.Active().Where(c => c.Enable).ToList(); 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; definition.Protocol = provider.Protocol;

@ -50,9 +50,9 @@ namespace NzbDrone.Core.Indexers
return base.Create(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; definition.Protocol = provider.Protocol;

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

@ -38,9 +38,41 @@ namespace NzbDrone.Core.ThingiProvider
return _providerRepository.All().ToList(); 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() 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)); 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) public void Handle(ApplicationStartedEvent message)
{ {
_logger.Debug("Initializing Providers. Count {0}", _providers.Count); _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(); return All().Where(c => c.Settings.Validate().IsValid).ToList();
} }
protected virtual TProviderDefinition GetProviderCharacteristics(TProvider provider, TProviderDefinition definition)
{
return definition;
}
private void RemoveMissingImplementations() private void RemoveMissingImplementations()
{ {
var storedProvider = _providerRepository.All(); var storedProvider = _providerRepository.All();

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

@ -1,6 +1,27 @@
<div class="add-thingy"> <div class="add-thingy">
{{implementation}} <div>
{{#if link}} {{implementation}}
<a href="{{link}}"><i class="icon-info-sign"/></a> </div>
{{/if}} <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> </div>

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

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

@ -1,6 +1,27 @@
<div class="add-thingy"> <div class="add-thingy">
{{implementation}} <div>
{{#if link}} {{implementation}}
<a href="{{link}}"><i class="icon-info-sign"/></a> </div>
{{/if}} <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> </div>

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

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

@ -1,6 +1,27 @@
<div class="add-thingy"> <div class="add-thingy">
{{implementation}} <div>
{{#if link}} {{implementation}}
<a href="{{link}}"><i class="icon-info-sign"/></a> </div>
{{/if}} <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> </div>

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

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

Loading…
Cancel
Save