diff --git a/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs b/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs index 8a8a20cef..29abd1468 100644 --- a/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs +++ b/src/NzbDrone.Api/ClientSchema/SchemaBuilder.cs @@ -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 fields, Type targetType) + public static object ReadFormSchema(List 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(false); diff --git a/src/NzbDrone.Api/DownloadClient/DownloadClientSchemaModule.cs b/src/NzbDrone.Api/DownloadClient/DownloadClientSchemaModule.cs deleted file mode 100644 index 0ea47266b..000000000 --- a/src/NzbDrone.Api/DownloadClient/DownloadClientSchemaModule.cs +++ /dev/null @@ -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 - { - private readonly IDownloadClientFactory _downloadClientFactory; - - public DownloadClientSchemaModule(IDownloadClientFactory downloadClientFactory) - : base("downloadclient/schema") - { - _downloadClientFactory = downloadClientFactory; - GetResourceAll = GetSchema; - } - - private List GetSchema() - { - var downloadClients = _downloadClientFactory.Templates(); - - var result = new List(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; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api/Indexers/IndexerSchemaModule.cs b/src/NzbDrone.Api/Indexers/IndexerSchemaModule.cs deleted file mode 100644 index 3de973599..000000000 --- a/src/NzbDrone.Api/Indexers/IndexerSchemaModule.cs +++ /dev/null @@ -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 - { - private readonly IIndexerFactory _indexerFactory; - - public IndexerSchemaModule(IIndexerFactory indexerFactory) - : base("indexer/schema") - { - _indexerFactory = indexerFactory; - GetResourceAll = GetSchema; - } - - private List GetSchema() - { - var indexers = _indexerFactory.Templates(); - - var result = new List(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; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api/Notifications/NotificationSchemaModule.cs b/src/NzbDrone.Api/Notifications/NotificationSchemaModule.cs deleted file mode 100644 index 920b9bb7b..000000000 --- a/src/NzbDrone.Api/Notifications/NotificationSchemaModule.cs +++ /dev/null @@ -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 - { - private readonly INotificationFactory _notificationFactory; - - public NotificationSchemaModule(INotificationFactory notificationFactory) - : base("notification/schema") - { - _notificationFactory = notificationFactory; - GetResourceAll = GetSchema; - } - - private List GetSchema() - { - var notifications = _notificationFactory.Templates(); - - var result = new List(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; - } - } -} \ No newline at end of file diff --git a/src/NzbDrone.Api/NzbDrone.Api.csproj b/src/NzbDrone.Api/NzbDrone.Api.csproj index da8ffac05..0c23ee731 100644 --- a/src/NzbDrone.Api/NzbDrone.Api.csproj +++ b/src/NzbDrone.Api/NzbDrone.Api.csproj @@ -144,11 +144,9 @@ - - @@ -172,7 +170,6 @@ - diff --git a/src/NzbDrone.Api/ProviderModuleBase.cs b/src/NzbDrone.Api/ProviderModuleBase.cs index fb0ca9389..943f27077 100644 --- a/src/NzbDrone.Api/ProviderModuleBase.cs +++ b/src/NzbDrone.Api/ProviderModuleBase.cs @@ -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(templates.Count()); + var result = new List(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); } diff --git a/src/NzbDrone.Api/ProviderResource.cs b/src/NzbDrone.Api/ProviderResource.cs index f866341e0..60c5ad78d 100644 --- a/src/NzbDrone.Api/ProviderResource.cs +++ b/src/NzbDrone.Api/ProviderResource.cs @@ -11,5 +11,8 @@ namespace NzbDrone.Api public List Fields { get; set; } public String Implementation { get; set; } public String ConfigContract { get; set; } + public String InfoLink { get; set; } + + public List Presets { get; set; } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/Download/DownloadClientFactory.cs b/src/NzbDrone.Core/Download/DownloadClientFactory.cs index c038b5177..d48d6d456 100644 --- a/src/NzbDrone.Core/Download/DownloadClientFactory.cs +++ b/src/NzbDrone.Core/Download/DownloadClientFactory.cs @@ -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; diff --git a/src/NzbDrone.Core/Indexers/IndexerFactory.cs b/src/NzbDrone.Core/Indexers/IndexerFactory.cs index bf3627dfb..2b214ed7c 100644 --- a/src/NzbDrone.Core/Indexers/IndexerFactory.cs +++ b/src/NzbDrone.Core/Indexers/IndexerFactory.cs @@ -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; diff --git a/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs index 54e8315a6..b70b1e06a 100644 --- a/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs @@ -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 Templates(); + IEnumerable GetDefaultDefinitions(); + IEnumerable GetPresetDefinitions(TProviderDefinition providerDefinition); } } \ No newline at end of file diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs index 0c7530ac5..fa9cd32c2 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs @@ -38,9 +38,41 @@ namespace NzbDrone.Core.ThingiProvider return _providerRepository.All().ToList(); } - public List Templates() + public IEnumerable GetDefaultDefinitions() { - return _providers.Select(GetTemplate).ToList(); + foreach (var provider in _providers) + { + var definition = provider.DefaultDefinitions + .OfType() + .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 GetPresetDefinitions(TProviderDefinition providerDefinition) + { + var provider = _providers.First(v => v.GetType().Name == providerDefinition.Implementation); + + var definitions = provider.DefaultDefinitions + .OfType() + .Where(v => v.Name != null && v.Name != provider.GetType().Name) + .ToList(); + + return definitions; } public List 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(); diff --git a/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemView.js b/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemView.js index 4f8b07c7d..aafbc33f3 100644 --- a/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemView.js +++ b/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemView.js @@ -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 }); diff --git a/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate.html b/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate.html index f892a4d01..a2c0295be 100644 --- a/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate.html +++ b/src/UI/Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate.html @@ -1,6 +1,27 @@ 
- {{implementation}} - {{#if link}} - - {{/if}} +
+ {{implementation}} +
+
+ {{#if_gt presets.length compare=0}} +
+ + +
+ {{/if_gt}} + {{#if infoLink}} + + + + {{/if}} +
\ No newline at end of file diff --git a/src/UI/Settings/DownloadClient/downloadclient.less b/src/UI/Settings/DownloadClient/downloadclient.less index bd8da872e..9b9f30f10 100644 --- a/src/UI/Settings/DownloadClient/downloadclient.less +++ b/src/UI/Settings/DownloadClient/downloadclient.less @@ -27,7 +27,7 @@ } .add-download-client { - li { + li.add-thingy-item { width: 33%; } } \ No newline at end of file diff --git a/src/UI/Settings/Indexers/Add/IndexerAddItemView.js b/src/UI/Settings/Indexers/Add/IndexerAddItemView.js index 217a1c999..c28f40bfb 100644 --- a/src/UI/Settings/Indexers/Add/IndexerAddItemView.js +++ b/src/UI/Settings/Indexers/Add/IndexerAddItemView.js @@ -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 }); diff --git a/src/UI/Settings/Indexers/Add/IndexerAddItemViewTemplate.html b/src/UI/Settings/Indexers/Add/IndexerAddItemViewTemplate.html index f892a4d01..a2c0295be 100644 --- a/src/UI/Settings/Indexers/Add/IndexerAddItemViewTemplate.html +++ b/src/UI/Settings/Indexers/Add/IndexerAddItemViewTemplate.html @@ -1,6 +1,27 @@ 
- {{implementation}} - {{#if link}} - - {{/if}} +
+ {{implementation}} +
+
+ {{#if_gt presets.length compare=0}} +
+ + +
+ {{/if_gt}} + {{#if infoLink}} + + + + {{/if}} +
\ No newline at end of file diff --git a/src/UI/Settings/Indexers/indexers.less b/src/UI/Settings/Indexers/indexers.less index d478f0e64..3fed3ef5f 100644 --- a/src/UI/Settings/Indexers/indexers.less +++ b/src/UI/Settings/Indexers/indexers.less @@ -27,7 +27,7 @@ } .add-indexer { - li { + li.add-thingy-item { width: 33%; } } \ No newline at end of file diff --git a/src/UI/Settings/Notifications/Add/NotificationAddItemView.js b/src/UI/Settings/Notifications/Add/NotificationAddItemView.js index 5c8c0ec9f..500f7bca8 100644 --- a/src/UI/Settings/Notifications/Add/NotificationAddItemView.js +++ b/src/UI/Settings/Notifications/Add/NotificationAddItemView.js @@ -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 diff --git a/src/UI/Settings/Notifications/Add/NotificationAddItemViewTemplate.html b/src/UI/Settings/Notifications/Add/NotificationAddItemViewTemplate.html index f892a4d01..a2c0295be 100644 --- a/src/UI/Settings/Notifications/Add/NotificationAddItemViewTemplate.html +++ b/src/UI/Settings/Notifications/Add/NotificationAddItemViewTemplate.html @@ -1,6 +1,27 @@ 
- {{implementation}} - {{#if link}} - - {{/if}} +
+ {{implementation}} +
+
+ {{#if_gt presets.length compare=0}} +
+ + +
+ {{/if_gt}} + {{#if infoLink}} + + + + {{/if}} +
\ No newline at end of file diff --git a/src/UI/Settings/Notifications/notifications.less b/src/UI/Settings/Notifications/notifications.less index 3a20ab1bf..01fd71567 100644 --- a/src/UI/Settings/Notifications/notifications.less +++ b/src/UI/Settings/Notifications/notifications.less @@ -25,7 +25,7 @@ } .add-notifications { - li { + li.add-thingy-item { width: 40%; } } \ No newline at end of file diff --git a/src/UI/Settings/thingy.less b/src/UI/Settings/thingy.less index 69b8def1d..e2348b8c0 100644 --- a/src/UI/Settings/thingy.less +++ b/src/UI/Settings/thingy.less @@ -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; }