From ce6a299c98803c0ac58b076e23e0ae9980c43937 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Thu, 28 Nov 2013 22:31:46 -0800 Subject: [PATCH] Deleting episode files from episode details is a go New: Delete episode file from episode details --- .../EpisodeFiles/EpisodeFileModule.cs | 20 +++++- src/UI/Cells/DeleteEpisodeFileCell.js | 33 ++++++++++ src/UI/Cells/IndexerCell.js | 2 +- src/UI/Cells/cells.less | 12 ++++ .../Episode/Summary/EpisodeSummaryLayout.js | 61 ++++++++++++++++--- src/UI/vent.js | 9 +-- 6 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 src/UI/Cells/DeleteEpisodeFileCell.js diff --git a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs index be8e92952..99b913671 100644 --- a/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs +++ b/src/NzbDrone.Api/EpisodeFiles/EpisodeFileModule.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using NLog; using NzbDrone.Api.REST; using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.MediaFiles; @@ -13,14 +14,22 @@ namespace NzbDrone.Api.EpisodeFiles IHandle { private readonly IMediaFileService _mediaFileService; + private readonly IRecycleBinProvider _recycleBinProvider; + private readonly Logger _logger; - public EpisodeModule(ICommandExecutor commandExecutor, IMediaFileService mediaFileService) + public EpisodeModule(ICommandExecutor commandExecutor, + IMediaFileService mediaFileService, + IRecycleBinProvider recycleBinProvider, + Logger logger) : base(commandExecutor) { _mediaFileService = mediaFileService; + _recycleBinProvider = recycleBinProvider; + _logger = logger; GetResourceById = GetEpisodeFile; GetResourceAll = GetEpisodeFiles; UpdateResource = SetQuality; + DeleteResource = DeleteEpisodeFile; } private EpisodeFileResource GetEpisodeFile(int id) @@ -47,6 +56,15 @@ namespace NzbDrone.Api.EpisodeFiles _mediaFileService.Update(episodeFile); } + private void DeleteEpisodeFile(int id) + { + var episodeFile = _mediaFileService.Get(id); + + _logger.Info("Deleting episode file: {0}", episodeFile.Path); + _recycleBinProvider.DeleteFile(episodeFile.Path); + _mediaFileService.Delete(episodeFile); + } + public void Handle(EpisodeFileAddedEvent message) { BroadcastResourceChange(ModelAction.Updated, message.EpisodeFile.Id); diff --git a/src/UI/Cells/DeleteEpisodeFileCell.js b/src/UI/Cells/DeleteEpisodeFileCell.js new file mode 100644 index 000000000..ac754c002 --- /dev/null +++ b/src/UI/Cells/DeleteEpisodeFileCell.js @@ -0,0 +1,33 @@ +'use strict'; +define( + [ + 'vent', + 'backgrid' + ], function (vent, Backgrid) { + return Backgrid.Cell.extend({ + + className : 'delete-episode-file-cell', + + events: { + 'click': '_onClick' + }, + + render: function () { + this.$el.empty(); + this.$el.html(''); + + return this; + }, + + _onClick: function () { + var self = this; + + if (window.confirm('Are you sure you want to delete \'{0}\' form disk?'.format(this.model.get('path')))) { + this.model.destroy() + .done(function () { + vent.trigger(vent.Events.EpisodeFileDeleted, { episodeFile: self.model }); + }); + } + } + }); + }); diff --git a/src/UI/Cells/IndexerCell.js b/src/UI/Cells/IndexerCell.js index 82fc80f48..9c51f4bec 100644 --- a/src/UI/Cells/IndexerCell.js +++ b/src/UI/Cells/IndexerCell.js @@ -5,7 +5,7 @@ define( ], function (Backgrid) { return Backgrid.Cell.extend({ - class : 'indexer-cell', + className : 'indexer-cell', render: function () { var indexer = this.model.get(this.column.get('name')); diff --git a/src/UI/Cells/cells.less b/src/UI/Cells/cells.less index c85561a67..6efd806ad 100644 --- a/src/UI/Cells/cells.less +++ b/src/UI/Cells/cells.less @@ -105,4 +105,16 @@ td.episode-status-cell, td.quality-cell { .download-log-cell { width: 80px; +} + +td.delete-episode-file-cell { + .clickable(); + + text-align: center; + width: 20px; + font-size: 20px; + + i { + .clickable(); + } } \ No newline at end of file diff --git a/src/UI/Episode/Summary/EpisodeSummaryLayout.js b/src/UI/Episode/Summary/EpisodeSummaryLayout.js index 32011b169..cca3914fc 100644 --- a/src/UI/Episode/Summary/EpisodeSummaryLayout.js +++ b/src/UI/Episode/Summary/EpisodeSummaryLayout.js @@ -8,9 +8,19 @@ define( 'Series/EpisodeFileCollection', 'Cells/FileSizeCell', 'Cells/QualityCell', + 'Cells/DeleteEpisodeFileCell', 'Episode/Summary/NoFileView', 'Shared/LoadingView' - ], function (reqres, Marionette, Backgrid, EpisodeFileModel, EpisodeFileCollection, FileSizeCell, QualityCell, NoFileView, LoadingView) { + ], function (reqres, + Marionette, + Backgrid, + EpisodeFileModel, + EpisodeFileCollection, + FileSizeCell, + QualityCell, + DeleteEpisodeFileCell, + NoFileView, + LoadingView) { return Marionette.Layout.extend({ template: 'Episode/Summary/EpisodeSummaryLayoutTemplate', @@ -40,6 +50,12 @@ define( cell : QualityCell, sortable: false, editable: true + }, + { + name : 'this', + label : '', + cell : DeleteEpisodeFileCell, + sortable: false } ], @@ -57,8 +73,10 @@ define( if (reqres.hasHandler(reqres.Requests.GetEpisodeFileById)) { var episodeFile = reqres.request(reqres.Requests.GetEpisodeFileById, episodeFileId); - var episodeFileCollection = new EpisodeFileCollection(episodeFile, { seriesId: this.model.get('seriesId') }); - this._showTable(episodeFileCollection); + this.episodeFileCollection = new EpisodeFileCollection(episodeFile, { seriesId: this.model.get('seriesId') }); + this.listenTo(episodeFile, 'destroy', this._episodeFileDeleted); + + this._showTable(); } else { @@ -66,26 +84,51 @@ define( var self = this; var newEpisodeFile = new EpisodeFileModel({ id: episodeFileId }); - var newEpisodeFileCollection = new EpisodeFileCollection(newEpisodeFile, { seriesId: this.model.get('seriesId') }); + this.episodeFileCollection = new EpisodeFileCollection(newEpisodeFile, { seriesId: this.model.get('seriesId') }); var promise = newEpisodeFile.fetch(); + this.listenTo(newEpisodeFile, 'destroy', this._episodeFileDeleted); promise.done(function () { - self._showTable(newEpisodeFileCollection); + self._showTable(); }); } + + this.listenTo(this.episodeFileCollection, 'all', this._collectionChanged); } else { - this.activity.show(new NoFileView()); + this._showNoFileView(); } }, - _showTable: function (episodeFileCollection) { + _showTable: function () { this.activity.show(new Backgrid.Grid({ - collection: episodeFileCollection, + collection: this.episodeFileCollection, columns : this.columns, - className : 'table table-bordered' + className : 'table table-bordered', + emptyText : 'Nothing to see here!' })); + }, + + _showNoFileView: function () { + this.activity.show(new NoFileView()); + }, + + _collectionChanged: function () { + if (!this.episodeFileCollection.any()) { + this._showNoFileView(); + } + + else { + this._showTable(); + } + }, + + _episodeFileDeleted: function () { + this.model.set({ + episodeFileId: 0, + hasFile : false + }); } }); }); diff --git a/src/UI/vent.js b/src/UI/vent.js index fdd869153..9de304e2f 100644 --- a/src/UI/vent.js +++ b/src/UI/vent.js @@ -8,10 +8,11 @@ define( var vent = new Backbone.Wreqr.EventAggregator(); vent.Events = { - SeriesAdded : 'series:added', - SeriesDeleted : 'series:deleted', - CommandComplete: 'command:complete', - ServerUpdated : 'server:updated' + SeriesAdded : 'series:added', + SeriesDeleted : 'series:deleted', + CommandComplete : 'command:complete', + ServerUpdated : 'server:updated', + EpisodeFileDeleted: 'episodefile:deleted' }; vent.Commands = {