New: Blacklist added to UI (under history)

This commit is contained in:
markus101 2014-02-01 14:07:30 -08:00
parent 953e59d7e3
commit 2cd347b829
15 changed files with 355 additions and 10 deletions

View File

@ -0,0 +1,42 @@
using System;
using NzbDrone.Core.Blacklisting;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Api.Blacklist
{
public class BlacklistModule : NzbDroneRestModule<BlacklistResource>
{
private readonly IBlacklistService _blacklistService;
public BlacklistModule(IBlacklistService blacklistService)
{
_blacklistService = blacklistService;
GetResourcePaged = GetBlacklist;
DeleteResource = Delete;
}
private PagingResource<BlacklistResource> GetBlacklist(PagingResource<BlacklistResource> pagingResource)
{
var pagingSpec = new PagingSpec<Core.Blacklisting.Blacklist>
{
Page = pagingResource.Page,
PageSize = pagingResource.PageSize,
SortKey = pagingResource.SortKey,
SortDirection = pagingResource.SortDirection
};
//This is a hack to deal with backgrid setting the sortKey to the column name instead of sortValue
if (pagingSpec.SortKey.Equals("series", StringComparison.InvariantCultureIgnoreCase))
{
pagingSpec.SortKey = "series.title";
}
return ApplyToPage(_blacklistService.Paged, pagingSpec);
}
private void Delete(int id)
{
_blacklistService.Delete(id);
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using NzbDrone.Api.REST;
using NzbDrone.Core.Tv;
namespace NzbDrone.Api.Blacklist
{
public class BlacklistResource : RestResource
{
public int SeriesId { get; set; }
public List<int> EpisodeIds { get; set; }
public string SourceTitle { get; set; }
public QualityModel Quality { get; set; }
public DateTime Date { get; set; }
}
}

View File

@ -82,6 +82,8 @@
<Compile Include="Authentication\EnableStatelessAuthInNancy.cs" /> <Compile Include="Authentication\EnableStatelessAuthInNancy.cs" />
<Compile Include="Authentication\EnableBasicAuthInNancy.cs" /> <Compile Include="Authentication\EnableBasicAuthInNancy.cs" />
<Compile Include="Authentication\NzbDroneUser.cs" /> <Compile Include="Authentication\NzbDroneUser.cs" />
<Compile Include="Blacklist\BlacklistModule.cs" />
<Compile Include="Blacklist\BlacklistResource.cs" />
<Compile Include="Calendar\CalendarModule.cs" /> <Compile Include="Calendar\CalendarModule.cs" />
<Compile Include="ClientSchema\SchemaDeserializer.cs" /> <Compile Include="ClientSchema\SchemaDeserializer.cs" />
<Compile Include="ClientSchema\FieldDefinitionAttribute.cs" /> <Compile Include="ClientSchema\FieldDefinitionAttribute.cs" />

View File

@ -1,5 +1,7 @@
using System; using System;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Blacklisting namespace NzbDrone.Core.Blacklisting
@ -7,9 +9,11 @@ namespace NzbDrone.Core.Blacklisting
public interface IBlacklistService public interface IBlacklistService
{ {
bool Blacklisted(string sourceTitle); bool Blacklisted(string sourceTitle);
PagingSpec<Blacklist> Paged(PagingSpec<Blacklist> pagingSpec);
void Delete(int id);
} }
public class BlacklistService : IBlacklistService, IHandle<DownloadFailedEvent> public class BlacklistService : IBlacklistService, IHandle<DownloadFailedEvent>, IExecute<ClearBlacklistCommand>
{ {
private readonly IBlacklistRepository _blacklistRepository; private readonly IBlacklistRepository _blacklistRepository;
private readonly IRedownloadFailedDownloads _redownloadFailedDownloadService; private readonly IRedownloadFailedDownloads _redownloadFailedDownloadService;
@ -25,6 +29,16 @@ namespace NzbDrone.Core.Blacklisting
return _blacklistRepository.Blacklisted(sourceTitle); return _blacklistRepository.Blacklisted(sourceTitle);
} }
public PagingSpec<Blacklist> Paged(PagingSpec<Blacklist> pagingSpec)
{
return _blacklistRepository.GetPaged(pagingSpec);
}
public void Delete(int id)
{
_blacklistRepository.Delete(id);
}
public void Handle(DownloadFailedEvent message) public void Handle(DownloadFailedEvent message)
{ {
var blacklist = new Blacklist var blacklist = new Blacklist
@ -40,5 +54,10 @@ namespace NzbDrone.Core.Blacklisting
_redownloadFailedDownloadService.Redownload(message.SeriesId, message.EpisodeIds); _redownloadFailedDownloadService.Redownload(message.SeriesId, message.EpisodeIds);
} }
public void Execute(ClearBlacklistCommand message)
{
_blacklistRepository.Purge();
}
} }
} }

View File

@ -0,0 +1,15 @@
using NzbDrone.Core.Messaging.Commands;
namespace NzbDrone.Core.Blacklisting
{
public class ClearBlacklistCommand : Command
{
public override bool SendUpdatesToClient
{
get
{
return true;
}
}
}
}

View File

@ -122,6 +122,7 @@
<Compile Include="Blacklisting\Blacklist.cs" /> <Compile Include="Blacklisting\Blacklist.cs" />
<Compile Include="Blacklisting\BlacklistRepository.cs" /> <Compile Include="Blacklisting\BlacklistRepository.cs" />
<Compile Include="Blacklisting\BlacklistService.cs" /> <Compile Include="Blacklisting\BlacklistService.cs" />
<Compile Include="Blacklisting\ClearBlacklistCommand.cs" />
<Compile Include="Configuration\Config.cs" /> <Compile Include="Configuration\Config.cs" />
<Compile Include="Configuration\ConfigFileProvider.cs" /> <Compile Include="Configuration\ConfigFileProvider.cs" />
<Compile Include="Configuration\ConfigRepository.cs" /> <Compile Include="Configuration\ConfigRepository.cs" />

View File

@ -0,0 +1,26 @@
'use strict';
define(
[
'Cells/NzbDroneCell'
], function (NzbDroneCell) {
return NzbDroneCell.extend({
className: 'blacklist-controls-cell',
events: {
'click': '_delete'
},
render: function () {
this.$el.empty();
this.$el.html('<i class="icon-nd-delete"></i>');
return this;
},
_delete: function () {
this.model.destroy();
}
});
});

View File

@ -0,0 +1,44 @@
'use strict';
define(
[
'History/Blacklist/BlacklistModel',
'backbone.pageable',
'Mixins/AsPersistedStateCollection'
], function (BlacklistModel, PageableCollection, AsPersistedStateCollection) {
var collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/blacklist',
model: BlacklistModel,
state: {
pageSize: 15,
sortKey : 'date',
order : 1
},
queryParams: {
totalPages : null,
totalRecords: null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1': 'asc',
'1' : 'desc'
}
},
parseState: function (resp) {
return { totalRecords: resp.totalRecords };
},
parseRecords: function (resp) {
if (resp) {
return resp.records;
}
return resp;
}
});
return AsPersistedStateCollection.apply(collection);
});

View File

@ -0,0 +1,132 @@
'use strict';
define(
[
'vent',
'marionette',
'backgrid',
'History/Blacklist/BlacklistCollection',
'Cells/SeriesTitleCell',
'Cells/QualityCell',
'Cells/RelativeDateCell',
'History/Blacklist/BlacklistActionsCell',
'Shared/Grid/Pager',
'Shared/LoadingView',
'Shared/Toolbar/ToolbarLayout'
], function (vent,
Marionette,
Backgrid,
BlacklistCollection,
SeriesTitleCell,
QualityCell,
RelativeDateCell,
BlacklistActionsCell,
GridPager,
LoadingView,
ToolbarLayout) {
return Marionette.Layout.extend({
template: 'History/Blacklist/BlacklistLayoutTemplate',
regions: {
blacklist : '#x-blacklist',
toolbar : '#x-toolbar',
pager : '#x-pager'
},
columns:
[
{
name : 'series',
label: 'Series',
cell : SeriesTitleCell,
sortValue: 'series.title'
},
{
name : 'sourceTitle',
label: 'Source Title',
cell : 'string',
sortValue: 'sourceTitle'
},
{
name : 'quality',
label : 'Quality',
cell : QualityCell,
sortable: false
},
{
name : 'date',
label: 'Date',
cell : RelativeDateCell
},
{
name : 'this',
label : '',
cell : BlacklistActionsCell,
sortable: false
}
],
initialize: function () {
this.collection = new BlacklistCollection({ tableName: 'blacklist' });
this.listenTo(this.collection, 'sync', this._showTable);
vent.on(vent.Events.CommandComplete, this._commandComplete, this);
},
onShow: function () {
this.blacklist.show(new LoadingView());
this._showToolbar();
this.collection.fetch();
},
_showTable: function (collection) {
this.blacklist.show(new Backgrid.Grid({
columns : this.columns,
collection: collection,
className : 'table table-hover'
}));
this.pager.show(new GridPager({
columns : this.columns,
collection: collection
}));
},
_showToolbar: function () {
var leftSideButtons = {
type : 'default',
storeState: false,
items :
[
{
title : 'Clear Blacklist',
icon : 'icon-trash',
command : 'clearBlacklist'
}
]
};
this.toolbar.show(new ToolbarLayout({
left :
[
leftSideButtons
],
context: this
}));
},
_refreshTable: function (buttonContext) {
this.collection.state.currentPage = 1;
var promise = this.collection.fetch({ reset: true });
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise);
}
},
_commandComplete: function (options) {
if (options.command.get('name') === 'clearblacklist') {
this._refreshTable();
}
}
});
});

View File

@ -0,0 +1,11 @@
<div id="x-toolbar"/>
<div class="row">
<div class="span12">
<div id="x-blacklist"/>
</div>
</div>
<div class="row">
<div class="span12">
<div id="x-pager"/>
</div>
</div>

View File

@ -0,0 +1,21 @@
'use strict';
define(
[
'backbone',
'Series/SeriesCollection'
], function (Backbone, SeriesCollection) {
return Backbone.Model.extend({
//Hack to deal with Backbone 1.0's bug
initialize: function () {
this.url = function () {
return this.collection.url + '/' + this.get('id');
};
},
parse: function (model) {
model.series = SeriesCollection.get(model.seriesId);
return model;
}
});
});

View File

@ -5,24 +5,28 @@ define(
'backbone', 'backbone',
'backgrid', 'backgrid',
'History/Table/HistoryTableLayout', 'History/Table/HistoryTableLayout',
'History/Blacklist/BlacklistLayout',
'History/Queue/QueueLayout' 'History/Queue/QueueLayout'
], function (Marionette, Backbone, Backgrid, HistoryTableLayout, QueueLayout) { ], function (Marionette, Backbone, Backgrid, HistoryTableLayout, BlacklistLayout, QueueLayout) {
return Marionette.Layout.extend({ return Marionette.Layout.extend({
template: 'History/HistoryLayoutTemplate', template: 'History/HistoryLayoutTemplate',
regions: { regions: {
history : '#history', history : '#history',
blacklist : '#blacklist',
queueRegion: '#queue' queueRegion: '#queue'
}, },
ui: { ui: {
historyTab: '.x-history-tab', historyTab: '.x-history-tab',
blacklistTab: '.x-blacklist-tab',
queueTab : '.x-queue-tab' queueTab : '.x-queue-tab'
}, },
events: { events: {
'click .x-history-tab': '_showHistory', 'click .x-history-tab' : '_showHistory',
'click .x-queue-tab' : '_showQueue' 'click .x-blacklist-tab' : '_showBlacklist',
'click .x-queue-tab' : '_showQueue'
}, },
initialize: function (options) { initialize: function (options) {
@ -55,6 +59,16 @@ define(
this._navigate('/history'); this._navigate('/history');
}, },
_showBlacklist: function (e) {
if (e) {
e.preventDefault();
}
this.blacklist.show(new BlacklistLayout());
this.ui.blacklistTab.tab('show');
this._navigate('/history/blacklist');
},
_showQueue: function (e) { _showQueue: function (e) {
if (e) { if (e) {
e.preventDefault(); e.preventDefault();

View File

@ -1,9 +1,11 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li><a href="#history" class="x-history-tab no-router">History</a></li> <li><a href="#history" class="x-history-tab no-router">History</a></li>
<li><a href="#blacklist" class="x-blacklist-tab no-router">Blacklist</a></li>
<li><a href="#queue" class="x-queue-tab no-router">Queue</a></li> <li><a href="#queue" class="x-queue-tab no-router">Queue</a></li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane" id="history"></div> <div class="tab-pane" id="history"></div>
<div class="tab-pane" id="blacklist"></div>
<div class="tab-pane" id="queue"></div> <div class="tab-pane" id="queue"></div>
</div> </div>

View File

@ -81,7 +81,7 @@ define(
title : 'Refresh', title : 'Refresh',
icon : 'icon-refresh', icon : 'icon-refresh',
ownerContext : this, ownerContext : this,
callback : this._refreshLogs callback : this._refreshTable
}, },
{ {
@ -140,7 +140,7 @@ define(
this.contents.show(new ContentsView({ model: model })); this.contents.show(new ContentsView({ model: model }));
}, },
_refreshLogs: function (buttonContext) { _refreshTable: function (buttonContext) {
this.contents.close(); this.contents.close();
var promise = this.collection.fetch(); var promise = this.collection.fetch();
@ -152,7 +152,7 @@ define(
_commandComplete: function (options) { _commandComplete: function (options) {
if (options.command.get('name') === 'deletelogfiles') { if (options.command.get('name') === 'deletelogfiles') {
this._refreshLogs(); this._refreshTable();
} }
} }
}); });

View File

@ -97,7 +97,7 @@ define(
title : 'Refresh', title : 'Refresh',
icon : 'icon-refresh', icon : 'icon-refresh',
ownerContext : this, ownerContext : this,
callback : this._refreshLogs callback : this._refreshTable
}, },
{ {
@ -117,7 +117,7 @@ define(
})); }));
}, },
_refreshLogs: function (buttonContext) { _refreshTable: function (buttonContext) {
this.collection.state.currentPage = 1; this.collection.state.currentPage = 1;
var promise = this.collection.fetch({ reset: true }); var promise = this.collection.fetch({ reset: true });
@ -128,7 +128,7 @@ define(
_commandComplete: function (options) { _commandComplete: function (options) {
if (options.command.get('name') === 'clearlog') { if (options.command.get('name') === 'clearlog') {
this._refreshLogs(); this._refreshTable();
} }
} }
}); });