From 0f27837246a295ef00c46c7464c21de0b55ed297 Mon Sep 17 00:00:00 2001 From: Qstick Date: Mon, 22 Feb 2021 20:56:36 -0500 Subject: [PATCH] New: Bulk Enable/Disable --- .../src/Indexer/Editor/IndexerEditorFooter.js | 25 +++++++++ frontend/src/Indexer/Index/IndexerIndex.js | 8 +-- .../src/Store/Actions/indexerIndexActions.js | 10 ++-- .../ThingiProvider/IProviderFactory.cs | 1 + .../ThingiProvider/ProviderFactory.cs | 5 ++ .../Indexers/IndexerEditorModule.cs | 56 +++++++++++++++++-- .../Indexers/IndexerEditorResource.cs | 10 ++++ .../Indexers/IndexerResource.cs | 5 ++ 8 files changed, 107 insertions(+), 13 deletions(-) diff --git a/frontend/src/Indexer/Editor/IndexerEditorFooter.js b/frontend/src/Indexer/Editor/IndexerEditorFooter.js index 7a9f2bff1..5053a5340 100644 --- a/frontend/src/Indexer/Editor/IndexerEditorFooter.js +++ b/frontend/src/Indexer/Editor/IndexerEditorFooter.js @@ -1,5 +1,6 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import SelectInput from 'Components/Form/SelectInput'; import SpinnerButton from 'Components/Link/SpinnerButton'; import PageContentFooter from 'Components/Page/PageContentFooter'; import { kinds } from 'Helpers/Props'; @@ -20,6 +21,7 @@ class IndexerEditorFooter extends Component { super(props, context); this.state = { + enable: NO_CHANGE, savingTags: false, isDeleteMovieModalOpen: false, isTagsModalOpen: false @@ -34,6 +36,7 @@ class IndexerEditorFooter extends Component { if (prevProps.isSaving && !isSaving && !saveError) { this.setState({ + enable: NO_CHANGE, savingTags: false }); } @@ -95,13 +98,35 @@ class IndexerEditorFooter extends Component { } = this.props; const { + enable, savingTags, isTagsModalOpen, isDeleteMovieModalOpen } = this.state; + const enableOptions = [ + { key: NO_CHANGE, value: translate('NoChange'), disabled: true }, + { key: 'true', value: translate('Enabled') }, + { key: 'false', value: translate('Disabled') } + ]; + return ( +
+ + + +
+
{ this.props.onSaveSelected({ - movieIds: this.getSelectedIds(), + indexerIds: this.getSelectedIds(), ...changes }); } @@ -290,7 +290,7 @@ class IndexerIndex extends Component { allUnselected } = this.state; - const selectedMovieIds = this.getSelectedIds(); + const selectedIndexerIds = this.getSelectedIds(); const ViewComponent = getViewComponent(); const isLoaded = !!(!error && isPopulated && items.length && scroller); @@ -448,8 +448,8 @@ class IndexerIndex extends Component { { isLoaded && isMovieEditorActive && { dispatch(batchActions([ - ...data.map((movie) => { + ...data.map((indexer) => { return updateItem({ - id: movie.id, - section: 'movies', - ...movie + id: indexer.id, + section: 'indexers', + ...indexer }); }), diff --git a/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs index bc52f9e16..12e249c4b 100644 --- a/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/IProviderFactory.cs @@ -12,6 +12,7 @@ namespace NzbDrone.Core.ThingiProvider TProviderDefinition Get(int id); TProviderDefinition Create(TProviderDefinition definition); void Update(TProviderDefinition definition); + void Update(IEnumerable definitions); void Delete(int id); IEnumerable GetDefaultDefinitions(); IEnumerable GetPresetDefinitions(TProviderDefinition providerDefinition); diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs index a59fc5d96..594df8535 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderFactory.cs @@ -109,6 +109,11 @@ namespace NzbDrone.Core.ThingiProvider _eventAggregator.PublishEvent(new ProviderUpdatedEvent(definition)); } + public virtual void Update(IEnumerable definitions) + { + _providerRepository.UpdateMany(definitions.ToList()); + } + public void Delete(int id) { _providerRepository.Delete(id); diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerEditorModule.cs b/src/Prowlarr.Api.V1/Indexers/IndexerEditorModule.cs index 4f0864de6..ad2d78579 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerEditorModule.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerEditorModule.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; +using System.Linq; using Nancy; using NzbDrone.Core.Indexers; using NzbDrone.Core.Messaging.Commands; @@ -7,22 +9,68 @@ namespace Prowlarr.Api.V1.Indexers { public class IndexerEditorModule : ProwlarrV1Module { - private readonly IIndexerFactory _movieService; + private readonly IIndexerFactory _indexerService; private readonly IManageCommandQueue _commandQueueManager; + public static readonly IndexerResourceMapper ResourceMapper = new IndexerResourceMapper(); - public IndexerEditorModule(IIndexerFactory movieService, IManageCommandQueue commandQueueManager) + public IndexerEditorModule(IIndexerFactory indexerService, IManageCommandQueue commandQueueManager) : base("/indexer/editor") { - _movieService = movieService; + _indexerService = indexerService; _commandQueueManager = commandQueueManager; + Put("/", movie => SaveAll()); Delete("/", movie => DeleteIndexers()); } + private object SaveAll() + { + var resource = Request.Body.FromJson(); + var indexersToUpdate = _indexerService.All().Where(x => resource.IndexerIds.Contains(x.Id)); + + foreach (var indexer in indexersToUpdate) + { + if (resource.Enable.HasValue) + { + indexer.Enable = resource.Enable.Value; + } + + if (resource.Tags != null) + { + var newTags = resource.Tags; + var applyTags = resource.ApplyTags; + + switch (applyTags) + { + case ApplyTags.Add: + newTags.ForEach(t => indexer.Tags.Add(t)); + break; + case ApplyTags.Remove: + newTags.ForEach(t => indexer.Tags.Remove(t)); + break; + case ApplyTags.Replace: + indexer.Tags = new HashSet(newTags); + break; + } + } + } + + _indexerService.Update(indexersToUpdate); + + var indexers = _indexerService.All(); + + foreach (var definition in indexers) + { + _indexerService.SetProviderCharacteristics(definition); + } + + return ResponseWithCode(ResourceMapper.ToResource(indexers), HttpStatusCode.Accepted); + } + private object DeleteIndexers() { var resource = Request.Body.FromJson(); - _movieService.DeleteIndexers(resource.IndexerIds); + _indexerService.DeleteIndexers(resource.IndexerIds); return new object(); } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerEditorResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerEditorResource.cs index 569cbf067..90918295a 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerEditorResource.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerEditorResource.cs @@ -5,5 +5,15 @@ namespace Prowlarr.Api.V1.Indexers public class IndexerEditorResource { public List IndexerIds { get; set; } + public bool? Enable { get; set; } + public List Tags { get; set; } + public ApplyTags ApplyTags { get; set; } + } + + public enum ApplyTags + { + Add, + Remove, + Replace } } diff --git a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs index c5dcc2c26..35e94a186 100644 --- a/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs +++ b/src/Prowlarr.Api.V1/Indexers/IndexerResource.cs @@ -134,5 +134,10 @@ namespace Prowlarr.Api.V1.Indexers return field; } + + public List ToResource(IEnumerable models) + { + return models.Select(ToResource).ToList(); + } } }