New: Blacklist added to UI (under history)
This commit is contained in:
parent
953e59d7e3
commit
2cd347b829
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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" />
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Blacklisting
|
||||||
|
{
|
||||||
|
public class ClearBlacklistCommand : Command
|
||||||
|
{
|
||||||
|
public override bool SendUpdatesToClient
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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" />
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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>
|
|
@ -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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -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();
|
||||||
|
|
|
@ -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>
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue