Fixed: Don't allow profile delete if in use by import list

Fixes #280
pull/284/head
Qstick 6 years ago
parent e5c5a3f91c
commit fde276f000

@ -8,7 +8,7 @@ import createDimensionsSelector from 'Store/Selectors/createDimensionsSelector';
import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions'; import { saveDimensions, setIsSidebarVisible } from 'Store/Actions/appActions';
import { fetchArtist } from 'Store/Actions/artistActions'; import { fetchArtist } from 'Store/Actions/artistActions';
import { fetchTags } from 'Store/Actions/tagActions'; import { fetchTags } from 'Store/Actions/tagActions';
import { fetchQualityProfiles, fetchLanguageProfiles, fetchMetadataProfiles, fetchUISettings } from 'Store/Actions/settingsActions'; import { fetchQualityProfiles, fetchLanguageProfiles, fetchMetadataProfiles, fetchUISettings, fetchImportLists } from 'Store/Actions/settingsActions';
import { fetchStatus } from 'Store/Actions/systemActions'; import { fetchStatus } from 'Store/Actions/systemActions';
import ErrorPage from './ErrorPage'; import ErrorPage from './ErrorPage';
import LoadingPage from './LoadingPage'; import LoadingPage from './LoadingPage';
@ -38,11 +38,17 @@ function createMapStateToProps() {
const isPopulated = artist.isPopulated && const isPopulated = artist.isPopulated &&
tags.isPopulated && tags.isPopulated &&
settings.qualityProfiles.isPopulated && settings.qualityProfiles.isPopulated &&
settings.languageProfiles.isPopulated &&
settings.metadataProfiles.isPopulated &&
settings.importLists.isPopulated &&
settings.ui.isPopulated; settings.ui.isPopulated;
const hasError = !!artist.error || const hasError = !!artist.error ||
!!tags.error || !!tags.error ||
!!settings.qualityProfiles.error || !!settings.qualityProfiles.error ||
!!settings.languageProfiles.error ||
!!settings.metadataProfiles.error ||
!!settings.importLists.error ||
!!settings.ui.error; !!settings.ui.error;
return { return {
@ -51,6 +57,9 @@ function createMapStateToProps() {
artistError: artist.error, artistError: artist.error,
tagsError: tags.error, tagsError: tags.error,
qualityProfilesError: settings.qualityProfiles.error, qualityProfilesError: settings.qualityProfiles.error,
languageProfilesError: settings.languageProfiles.error,
metadataProfilesError: settings.metadataProfiles.error,
importListsError: settings.importLists.error,
uiSettingsError: settings.ui.error, uiSettingsError: settings.ui.error,
isSmallScreen: dimensions.isSmallScreen, isSmallScreen: dimensions.isSmallScreen,
isSidebarVisible: app.isSidebarVisible, isSidebarVisible: app.isSidebarVisible,
@ -79,6 +88,9 @@ function createMapDispatchToProps(dispatch, props) {
dispatchFetchMetadataProfiles() { dispatchFetchMetadataProfiles() {
dispatch(fetchMetadataProfiles()); dispatch(fetchMetadataProfiles());
}, },
dispatchFetchImportLists() {
dispatch(fetchImportLists());
},
dispatchFetchUISettings() { dispatchFetchUISettings() {
dispatch(fetchUISettings()); dispatch(fetchUISettings());
}, },
@ -114,6 +126,7 @@ class PageConnector extends Component {
this.props.dispatchFetchQualityProfiles(); this.props.dispatchFetchQualityProfiles();
this.props.dispatchFetchLanguageProfiles(); this.props.dispatchFetchLanguageProfiles();
this.props.dispatchFetchMetadataProfiles(); this.props.dispatchFetchMetadataProfiles();
this.props.dispatchFetchImportLists();
this.props.dispatchFetchUISettings(); this.props.dispatchFetchUISettings();
this.props.dispatchFetchStatus(); this.props.dispatchFetchStatus();
} }
@ -138,6 +151,7 @@ class PageConnector extends Component {
dispatchFetchQualityProfiles, dispatchFetchQualityProfiles,
dispatchFetchLanguageProfiles, dispatchFetchLanguageProfiles,
dispatchFetchMetadataProfiles, dispatchFetchMetadataProfiles,
dispatchFetchImportLists,
dispatchFetchUISettings, dispatchFetchUISettings,
dispatchFetchStatus, dispatchFetchStatus,
...otherProps ...otherProps
@ -176,6 +190,7 @@ PageConnector.propTypes = {
dispatchFetchQualityProfiles: PropTypes.func.isRequired, dispatchFetchQualityProfiles: PropTypes.func.isRequired,
dispatchFetchLanguageProfiles: PropTypes.func.isRequired, dispatchFetchLanguageProfiles: PropTypes.func.isRequired,
dispatchFetchMetadataProfiles: PropTypes.func.isRequired, dispatchFetchMetadataProfiles: PropTypes.func.isRequired,
dispatchFetchImportLists: PropTypes.func.isRequired,
dispatchFetchUISettings: PropTypes.func.isRequired, dispatchFetchUISettings: PropTypes.func.isRequired,
dispatchFetchStatus: PropTypes.func.isRequired, dispatchFetchStatus: PropTypes.func.isRequired,
onSidebarVisibleChange: PropTypes.func.isRequired onSidebarVisibleChange: PropTypes.func.isRequired

@ -41,7 +41,7 @@ function EditImportListModalContent(props) {
enableAutomaticAdd, enableAutomaticAdd,
shouldMonitor, shouldMonitor,
rootFolderPath, rootFolderPath,
profileId, qualityProfileId,
languageProfileId, languageProfileId,
metadataProfileId, metadataProfileId,
fields fields
@ -121,9 +121,9 @@ function EditImportListModalContent(props) {
<FormInputGroup <FormInputGroup
type={inputTypes.QUALITY_PROFILE_SELECT} type={inputTypes.QUALITY_PROFILE_SELECT}
name="profileId" name="qualityProfileId"
helpText={'Quality Profile list items should be added with'} helpText={'Quality Profile list items should be added with'}
{...profileId} {...qualityProfileId}
onChange={onInputChange} onChange={onInputChange}
/> />
</FormGroup> </FormGroup>

@ -101,7 +101,7 @@ function EditLanguageProfileModalContent(props) {
id && id &&
<div <div
className={styles.deleteButtonContainer} className={styles.deleteButtonContainer}
title={isInUse ? 'Can\'t delete a language profile that is attached to a artist' : undefined} title={isInUse ? 'Can\'t delete a language profile that is attached to an artist or import list' : undefined}
> >
<Button <Button
kind={kinds.DANGER} kind={kinds.DANGER}

@ -107,7 +107,7 @@ function EditMetadataProfileModalContent(props) {
id && id &&
<div <div
className={styles.deleteButtonContainer} className={styles.deleteButtonContainer}
title={isInUse ? 'Can\'t delete a metadata profile that is attached to a artist' : undefined} title={isInUse ? 'Can\'t delete a metadata profile that is attached to an artist or import list' : undefined}
> >
<Button <Button
kind={kinds.DANGER} kind={kinds.DANGER}

@ -200,7 +200,7 @@ class EditQualityProfileModalContent extends Component {
id && id &&
<div <div
className={styles.deleteButtonContainer} className={styles.deleteButtonContainer}
title={isInUse ? 'Can\'t delete a quality profile that is attached to a artist' : undefined} title={isInUse ? 'Can\'t delete a quality profile that is attached to an artist or import list' : undefined}
> >
<Button <Button
kind={kinds.DANGER} kind={kinds.DANGER}

@ -6,12 +6,17 @@ function createProfileInUseSelector(profileProp) {
return createSelector( return createSelector(
(state, { id }) => id, (state, { id }) => id,
createAllArtistSelector(), createAllArtistSelector(),
(id, artist) => { (state) => state.settings.importLists.items,
(id, artist, lists) => {
if (!id) { if (!id) {
return false; return false;
} }
return _.some(artist, { [profileProp]: id }); if (_.some(artist, { [profileProp]: id }) || _.some(lists, { [profileProp]: id })) {
return true;
}
return false;
} }
); );
} }

@ -7,7 +7,7 @@ namespace Lidarr.Api.V1.ImportLists
public bool EnableAutomaticAdd { get; set; } public bool EnableAutomaticAdd { get; set; }
public bool ShouldMonitor { get; set; } public bool ShouldMonitor { get; set; }
public string RootFolderPath { get; set; } public string RootFolderPath { get; set; }
public int ProfileId { get; set; } public int QualityProfileId { get; set; }
public int LanguageProfileId { get; set; } public int LanguageProfileId { get; set; }
public int MetadataProfileId { get; set; } public int MetadataProfileId { get; set; }
} }
@ -26,7 +26,7 @@ namespace Lidarr.Api.V1.ImportLists
resource.EnableAutomaticAdd = definition.EnableAutomaticAdd; resource.EnableAutomaticAdd = definition.EnableAutomaticAdd;
resource.ShouldMonitor = definition.ShouldMonitor; resource.ShouldMonitor = definition.ShouldMonitor;
resource.RootFolderPath = definition.RootFolderPath; resource.RootFolderPath = definition.RootFolderPath;
resource.ProfileId = definition.ProfileId; resource.QualityProfileId = definition.ProfileId;
resource.LanguageProfileId = definition.LanguageProfileId; resource.LanguageProfileId = definition.LanguageProfileId;
resource.MetadataProfileId = definition.MetadataProfileId; resource.MetadataProfileId = definition.MetadataProfileId;
@ -45,7 +45,7 @@ namespace Lidarr.Api.V1.ImportLists
definition.EnableAutomaticAdd = resource.EnableAutomaticAdd; definition.EnableAutomaticAdd = resource.EnableAutomaticAdd;
definition.ShouldMonitor = resource.ShouldMonitor; definition.ShouldMonitor = resource.ShouldMonitor;
definition.RootFolderPath = resource.RootFolderPath; definition.RootFolderPath = resource.RootFolderPath;
definition.ProfileId = resource.ProfileId; definition.ProfileId = resource.QualityProfileId;
definition.LanguageProfileId = resource.LanguageProfileId; definition.LanguageProfileId = resource.LanguageProfileId;
definition.MetadataProfileId = resource.MetadataProfileId; definition.MetadataProfileId = resource.MetadataProfileId;

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
@ -22,12 +23,14 @@ namespace NzbDrone.Core.Profiles.Languages
{ {
private readonly ILanguageProfileRepository _profileRepository; private readonly ILanguageProfileRepository _profileRepository;
private readonly IArtistService _artistService; private readonly IArtistService _artistService;
private readonly IImportListFactory _importListFactory;
private readonly Logger _logger; private readonly Logger _logger;
public LanguageProfileService(ILanguageProfileRepository profileRepository, IArtistService artistService, Logger logger) public LanguageProfileService(ILanguageProfileRepository profileRepository, IArtistService artistService, IImportListFactory importListFactory, Logger logger)
{ {
_profileRepository = profileRepository; _profileRepository = profileRepository;
_artistService = artistService; _artistService = artistService;
_importListFactory = importListFactory;
_logger = logger; _logger = logger;
} }
@ -43,7 +46,7 @@ namespace NzbDrone.Core.Profiles.Languages
public void Delete(int id) public void Delete(int id)
{ {
if (_artistService.GetAllArtists().Any(c => c.LanguageProfileId == id)) if (_artistService.GetAllArtists().Any(c => c.LanguageProfileId == id) || _importListFactory.All().Any(c => c.LanguageProfileId == id))
{ {
var profile = _profileRepository.Get(id); var profile = _profileRepository.Get(id);
throw new LanguageProfileInUseException(profile.Name); throw new LanguageProfileInUseException(profile.Name);

@ -4,6 +4,7 @@ using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Music; using NzbDrone.Core.Music;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Core.ImportLists;
namespace NzbDrone.Core.Profiles.Metadata namespace NzbDrone.Core.Profiles.Metadata
{ {
@ -21,12 +22,17 @@ namespace NzbDrone.Core.Profiles.Metadata
{ {
private readonly IMetadataProfileRepository _profileRepository; private readonly IMetadataProfileRepository _profileRepository;
private readonly IArtistService _artistService; private readonly IArtistService _artistService;
private readonly IImportListFactory _importListFactory;
private readonly Logger _logger; private readonly Logger _logger;
public MetadataProfileService(IMetadataProfileRepository profileRepository, IArtistService artistService, Logger logger) public MetadataProfileService(IMetadataProfileRepository profileRepository,
IArtistService artistService,
IImportListFactory importListFactory,
Logger logger)
{ {
_profileRepository = profileRepository; _profileRepository = profileRepository;
_artistService = artistService; _artistService = artistService;
_importListFactory = importListFactory;
_logger = logger; _logger = logger;
} }
@ -42,7 +48,7 @@ namespace NzbDrone.Core.Profiles.Metadata
public void Delete(int id) public void Delete(int id)
{ {
if (_artistService.GetAllArtists().Any(c => c.MetadataProfileId == id)) if (_artistService.GetAllArtists().Any(c => c.MetadataProfileId == id) || _importListFactory.All().Any(c => c.MetadataProfileId == id))
{ {
var profile = _profileRepository.Get(id); var profile = _profileRepository.Get(id);
throw new MetadataProfileInUseException(profile.Name); throw new MetadataProfileInUseException(profile.Name);

@ -3,6 +3,7 @@ using System.Linq;
using NLog; using NLog;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http.Dispatchers; using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Core.ImportLists;
using NzbDrone.Core.Lifecycle; using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -26,12 +27,14 @@ namespace NzbDrone.Core.Profiles.Qualities
{ {
private readonly IProfileRepository _profileRepository; private readonly IProfileRepository _profileRepository;
private readonly IArtistService _artistService; private readonly IArtistService _artistService;
private readonly IImportListFactory _importListFactory;
private readonly Logger _logger; private readonly Logger _logger;
public ProfileService(IProfileRepository profileRepository, IArtistService artistService, Logger logger) public ProfileService(IProfileRepository profileRepository, IArtistService artistService, IImportListFactory importListFactory, Logger logger)
{ {
_profileRepository = profileRepository; _profileRepository = profileRepository;
_artistService = artistService; _artistService = artistService;
_importListFactory = importListFactory;
_logger = logger; _logger = logger;
} }
@ -47,7 +50,7 @@ namespace NzbDrone.Core.Profiles.Qualities
public void Delete(int id) public void Delete(int id)
{ {
if (_artistService.GetAllArtists().Any(c => c.ProfileId == id)) if (_artistService.GetAllArtists().Any(c => c.ProfileId == id) || _importListFactory.All().Any(c => c.ProfileId == id))
{ {
var profile = _profileRepository.Get(id); var profile = _profileRepository.Get(id);
throw new ProfileInUseException(profile.Name); throw new ProfileInUseException(profile.Name);

Loading…
Cancel
Save