UI Cleanup - Updated System, Tags and Wanted subtrees.

pull/4/head
Taloth Saldono 9 years ago
parent d6079a701c
commit 32fc68b9df

@ -4,10 +4,12 @@ var BackupModel = require('./BackupModel');
module.exports = PageableCollection.extend({ module.exports = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/system/backup', url : window.NzbDrone.ApiRoot + '/system/backup',
model : BackupModel, model : BackupModel,
state : { state : {
sortKey : 'time', sortKey : 'time',
order : 1, order : 1,
pageSize : 100000 pageSize : 100000
}, },
mode : 'client'
mode : 'client'
}); });

@ -1,3 +1,5 @@
var Marionette = require('marionette'); var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({template : 'System/Backup/BackupEmptyViewTemplate'}); module.exports = Marionette.ItemView.extend({
template : 'System/Backup/BackupEmptyViewTemplate'
});

@ -10,55 +10,68 @@ var LoadingView = require('../../Shared/LoadingView');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout'); var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Backup/BackupLayoutTemplate', template : 'System/Backup/BackupLayoutTemplate',
regions : {
regions : {
backups : '#x-backups', backups : '#x-backups',
toolbar : '#x-backup-toolbar' toolbar : '#x-backup-toolbar'
}, },
columns : [{
name : 'type', columns : [
label : '', {
sortable : false, name : 'type',
cell : BackupTypeCell label : '',
}, { sortable : false,
name : 'this', cell : BackupTypeCell
label : 'Name', },
sortable : false, {
cell : BackupFilenameCell name : 'this',
}, { label : 'Name',
name : 'time', sortable : false,
label : 'Time', cell : BackupFilenameCell
sortable : false, },
cell : RelativeDateCell {
}], name : 'time',
leftSideButtons : { label : 'Time',
sortable : false,
cell : RelativeDateCell
}
],
leftSideButtons : {
type : 'default', type : 'default',
storeState : false, storeState : false,
collapse : false, collapse : false,
items : [{ items : [
title : 'Backup', {
icon : 'icon-file-text', title : 'Backup',
command : 'backup', icon : 'icon-file-text',
properties : {type : 'manual'}, command : 'backup',
successMessage : 'Database and settings were backed up successfully', properties : { type : 'manual' },
errorMessage : 'Backup Failed!' successMessage : 'Database and settings were backed up successfully',
}] errorMessage : 'Backup Failed!'
}
]
}, },
initialize : function(){
initialize : function() {
this.backupCollection = new BackupCollection(); this.backupCollection = new BackupCollection();
this.listenTo(this.backupCollection, 'sync', this._showBackups); this.listenTo(this.backupCollection, 'sync', this._showBackups);
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
}, },
onRender : function(){
onRender : function() {
this._showToolbar(); this._showToolbar();
this.backups.show(new LoadingView()); this.backups.show(new LoadingView());
this.backupCollection.fetch(); this.backupCollection.fetch();
}, },
_showBackups : function(){
if(this.backupCollection.length === 0) { _showBackups : function() {
if (this.backupCollection.length === 0) {
this.backups.show(new EmptyView()); this.backups.show(new EmptyView());
} } else {
else {
this.backups.show(new Backgrid.Grid({ this.backups.show(new Backgrid.Grid({
columns : this.columns, columns : this.columns,
collection : this.backupCollection, collection : this.backupCollection,
@ -66,14 +79,15 @@ module.exports = Marionette.Layout.extend({
})); }));
} }
}, },
_showToolbar : function(){
_showToolbar : function() {
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
left : [this.leftSideButtons], left : [this.leftSideButtons],
context : this context : this
})); }));
}, },
_commandComplete : function(options){ _commandComplete : function(options) {
if(options.command.get('name') === 'backup') { if (options.command.get('name') === 'backup') {
this.backupCollection.fetch(); this.backupCollection.fetch();
} }
} }

@ -2,20 +2,25 @@ var NzbDroneCell = require('../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'backup-type-cell', className : 'backup-type-cell',
render : function(){
render : function() {
this.$el.empty(); this.$el.empty();
var icon = 'icon-time'; var icon = 'icon-time';
var title = 'Scheduled'; var title = 'Scheduled';
var type = this.model.get(this.column.get('name')); var type = this.model.get(this.column.get('name'));
if(type === 'manual') {
if (type === 'manual') {
icon = 'icon-book'; icon = 'icon-book';
title = 'Manual'; title = 'Manual';
} } else if (type === 'update') {
else if(type === 'update') {
icon = 'icon-retweet'; icon = 'icon-retweet';
title = 'Before update'; title = 'Before update';
} }
this.$el.html('<i class="{0}" title="{1}"></i>'.format(icon, title)); this.$el.html('<i class="{0}" title="{1}"></i>'.format(icon, title));
return this; return this;
} }
}); });

@ -2,8 +2,9 @@ var Marionette = require('marionette');
var StatusModel = require('../../StatusModel'); var StatusModel = require('../../StatusModel');
module.exports = Marionette.ItemView.extend({ module.exports = Marionette.ItemView.extend({
template : 'System/Info/About/AboutViewTemplate', template : 'System/Info/About/AboutViewTemplate',
initialize : function(){
initialize : function() {
this.model = StatusModel; this.model = StatusModel;
} }
}); });

@ -1,4 +1,4 @@
var vent = require('vent'); var vent = require('vent');
var Marionette = require('marionette'); var Marionette = require('marionette');
var Backgrid = require('backgrid'); var Backgrid = require('backgrid');
var DiskSpaceCollection = require('./DiskSpaceCollection'); var DiskSpaceCollection = require('./DiskSpaceCollection');
@ -7,35 +7,47 @@ var DiskSpacePathCell = require('./DiskSpacePathCell');
var FileSizeCell = require('../../../Cells/FileSizeCell'); var FileSizeCell = require('../../../Cells/FileSizeCell');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Info/DiskSpace/DiskSpaceLayoutTemplate', template : 'System/Info/DiskSpace/DiskSpaceLayoutTemplate',
regions : {grid : '#x-grid'},
columns : [{ regions : {
name : 'path', grid : '#x-grid'
label : 'Location', },
cell : DiskSpacePathCell,
sortable : false columns : [
}, { {
name : 'freeSpace', name : 'path',
label : 'Free Space', label : 'Location',
cell : FileSizeCell, cell : DiskSpacePathCell,
sortable : false sortable : false
}, { },
name : 'totalSpace', {
label : 'Total Space', name : 'freeSpace',
cell : FileSizeCell, label : 'Free Space',
sortable : false cell : FileSizeCell,
}], sortable : false
initialize : function(){ },
{
name : 'totalSpace',
label : 'Total Space',
cell : FileSizeCell,
sortable : false
}
],
initialize : function() {
this.collection = new DiskSpaceCollection(); this.collection = new DiskSpaceCollection();
this.listenTo(this.collection, 'sync', this._showTable); this.listenTo(this.collection, 'sync', this._showTable);
}, },
onRender : function(){
onRender : function() {
this.grid.show(new LoadingView()); this.grid.show(new LoadingView());
}, },
onShow : function(){
onShow : function() {
this.collection.fetch(); this.collection.fetch();
}, },
_showTable : function(){
_showTable : function() {
this.grid.show(new Backgrid.Grid({ this.grid.show(new Backgrid.Grid({
row : Backgrid.Row, row : Backgrid.Row,
columns : this.columns, columns : this.columns,

@ -2,15 +2,21 @@ var Backgrid = require('backgrid');
module.exports = Backgrid.Cell.extend({ module.exports = Backgrid.Cell.extend({
className : 'disk-space-path-cell', className : 'disk-space-path-cell',
render : function(){
render : function() {
this.$el.empty(); this.$el.empty();
var path = this.model.get('path'); var path = this.model.get('path');
var label = this.model.get('label'); var label = this.model.get('label');
var contents = path; var contents = path;
if(label) {
if (label) {
contents += ' ({0})'.format(label); contents += ' ({0})'.format(label);
} }
this.$el.html(contents); this.$el.html(contents);
return this; return this;
} }
}); });

@ -2,9 +2,11 @@ var NzbDroneCell = require('../../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'log-level-cell', className : 'log-level-cell',
render : function(){
render : function() {
var level = this._getValue(); var level = this._getValue();
this.$el.html('<i class="icon-nd-health-{0}" title="{1}"/>'.format(this._getValue().toLowerCase(), level)); this.$el.html('<i class="icon-nd-health-{0}" title="{1}"/>'.format(this._getValue().toLowerCase(), level));
return this; return this;
} }
}); });

@ -6,37 +6,47 @@ var HealthWikiCell = require('./HealthWikiCell');
var HealthOkView = require('./HealthOkView'); var HealthOkView = require('./HealthOkView');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Info/Health/HealthLayoutTemplate', template : 'System/Info/Health/HealthLayoutTemplate',
regions : {grid : '#x-health-grid'},
columns : [{ regions : {
name : 'type', grid : '#x-health-grid'
label : '', },
cell : HealthCell,
sortable : false columns : [
}, { {
name : 'message', name : 'type',
label : 'Message', label : '',
cell : 'string', cell : HealthCell,
sortable : false sortable : false
}, { },
name : 'wikiUrl', {
label : '', name : 'message',
cell : HealthWikiCell, label : 'Message',
sortable : false cell : 'string',
}], sortable : false
initialize : function(){ },
{
name : 'wikiUrl',
label : '',
cell : HealthWikiCell,
sortable : false
}
],
initialize : function() {
this.listenTo(HealthCollection, 'sync', this.render); this.listenTo(HealthCollection, 'sync', this.render);
HealthCollection.fetch(); HealthCollection.fetch();
}, },
onRender : function(){
if(HealthCollection.length === 0) { onRender : function() {
if (HealthCollection.length === 0) {
this.grid.show(new HealthOkView()); this.grid.show(new HealthOkView());
} } else {
else {
this._showTable(); this._showTable();
} }
}, },
_showTable : function(){
_showTable : function() {
this.grid.show(new Backgrid.Grid({ this.grid.show(new Backgrid.Grid({
row : Backgrid.Row, row : Backgrid.Row,
columns : this.columns, columns : this.columns,

@ -1,3 +1,5 @@
var Marionette = require('marionette'); var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({template : 'System/Info/Health/HealthOkViewTemplate'}); module.exports = Marionette.ItemView.extend({
template : 'System/Info/Health/HealthOkViewTemplate'
});

@ -3,9 +3,12 @@ var Backgrid = require('backgrid');
module.exports = Backgrid.UriCell.extend({ module.exports = Backgrid.UriCell.extend({
className : 'wiki-link-cell', className : 'wiki-link-cell',
title : 'Read the Wiki for more information',
text : 'Wiki', title : 'Read the Wiki for more information',
render : function(){
text : 'Wiki',
render : function() {
this.$el.empty(); this.$el.empty();
var rawValue = this.model.get(this.column.get('name')); var rawValue = this.model.get(this.column.get('name'));
var formattedValue = this.formatter.fromRaw(rawValue, this.model); var formattedValue = this.formatter.fromRaw(rawValue, this.model);

@ -1,3 +1,5 @@
var Marionette = require('marionette'); var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({template : 'System/Info/MoreInfo/MoreInfoViewTemplate'}); module.exports = Marionette.ItemView.extend({
template : 'System/Info/MoreInfo/MoreInfoViewTemplate'
});

@ -7,13 +7,15 @@ var MoreInfoView = require('./MoreInfo/MoreInfoView');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Info/SystemInfoLayoutTemplate', template : 'System/Info/SystemInfoLayoutTemplate',
regions : {
regions : {
about : '#about', about : '#about',
diskSpace : '#diskspace', diskSpace : '#diskspace',
health : '#health', health : '#health',
moreInfo : '#more-info' moreInfo : '#more-info'
}, },
onRender : function(){
onRender : function() {
this.health.show(new HealthLayout()); this.health.show(new HealthLayout());
this.diskSpace.show(new DiskSpaceLayout()); this.diskSpace.show(new DiskSpaceLayout());
this.about.show(new AboutView()); this.about.show(new AboutView());

@ -1,10 +1,11 @@
var Backbone = require('backbone'); var Backbone = require('backbone');
module.exports = Backbone.Model.extend({ module.exports = Backbone.Model.extend({
url : function(){ url : function() {
return this.get('contentsUrl'); return this.get('contentsUrl');
}, },
parse : function(contents){
parse : function(contents) {
var response = {}; var response = {};
response.contents = contents; response.contents = contents;
return response; return response;

@ -1,3 +1,5 @@
var Marionette = require('marionette'); var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({template : 'System/Logs/Files/ContentsViewTemplate'}); module.exports = Marionette.ItemView.extend({
template : 'System/Logs/Files/ContentsViewTemplate'
});

@ -2,9 +2,11 @@ var NzbDroneCell = require('../../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'download-log-cell', className : 'download-log-cell',
render : function(){
render : function() {
this.$el.empty(); this.$el.empty();
this.$el.html('<a href="{0}" class="no-router" target="_blank">Download</a>'.format(this.cellValue)); this.$el.html('<a href="{0}" class="no-router" target="_blank">Download</a>'.format(this.cellValue));
return this; return this;
} }
}); });

@ -2,9 +2,11 @@ var NzbDroneCell = require('../../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'log-filename-cell', className : 'log-filename-cell',
render : function(){
render : function() {
var filename = this._getValue(); var filename = this._getValue();
this.$el.html(filename); this.$el.html(filename);
return this; return this;
} }
}); });

@ -4,6 +4,7 @@ var LogFileModel = require('./LogFileModel');
module.exports = Backbone.Collection.extend({ module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/log/file', url : window.NzbDrone.ApiRoot + '/log/file',
model : LogFileModel, model : LogFileModel,
state : { state : {
sortKey : 'lastWriteTime', sortKey : 'lastWriteTime',
order : 1 order : 1

@ -12,63 +12,79 @@ var LoadingView = require('../../../Shared/LoadingView');
require('../../../jQuery/jquery.spin'); require('../../../jQuery/jquery.spin');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Logs/Files/LogFileLayoutTemplate', template : 'System/Logs/Files/LogFileLayoutTemplate',
regions : {
regions : {
toolbar : '#x-toolbar', toolbar : '#x-toolbar',
grid : '#x-grid', grid : '#x-grid',
contents : '#x-contents' contents : '#x-contents'
}, },
columns : [{
name : 'filename', columns : [
label : 'Filename', {
cell : FilenameCell, name : 'filename',
sortable : false label : 'Filename',
}, { cell : FilenameCell,
name : 'lastWriteTime', sortable : false
label : 'Last Write Time', },
cell : RelativeDateCell, {
sortable : false name : 'lastWriteTime',
}, { label : 'Last Write Time',
name : 'downloadUrl', cell : RelativeDateCell,
label : '', sortable : false
cell : DownloadLogCell, },
sortable : false {
}], name : 'downloadUrl',
initialize : function(options){ label : '',
cell : DownloadLogCell,
sortable : false
}
],
initialize : function(options) {
this.collection = options.collection; this.collection = options.collection;
this.deleteFilesCommand = options.deleteFilesCommand; this.deleteFilesCommand = options.deleteFilesCommand;
this.listenTo(vent, vent.Commands.ShowLogFile, this._fetchLogFileContents); this.listenTo(vent, vent.Commands.ShowLogFile, this._fetchLogFileContents);
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
this.listenTo(this.collection, 'sync', this._collectionSynced); this.listenTo(this.collection, 'sync', this._collectionSynced);
this.collection.fetch(); this.collection.fetch();
}, },
onShow : function(){
onShow : function() {
this._showToolbar(); this._showToolbar();
this._showTable(); this._showTable();
}, },
_showToolbar : function(){
_showToolbar : function() {
var leftSideButtons = { var leftSideButtons = {
type : 'default', type : 'default',
storeState : false, storeState : false,
items : [{ items : [
title : 'Refresh', {
icon : 'icon-refresh', title : 'Refresh',
ownerContext : this, icon : 'icon-refresh',
callback : this._refreshTable ownerContext : this,
}, { callback : this._refreshTable
title : 'Delete Log Files', },
icon : 'icon-trash', {
command : this.deleteFilesCommand, title : 'Delete Log Files',
successMessage : 'Log files have been deleted', icon : 'icon-trash',
errorMessage : 'Failed to delete log files' command : this.deleteFilesCommand,
}] successMessage : 'Log files have been deleted',
errorMessage : 'Failed to delete log files'
}
]
}; };
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
left : [leftSideButtons], left : [leftSideButtons],
context : this context : this
})); }));
}, },
_showTable : function(){
_showTable : function() {
this.grid.show(new Backgrid.Grid({ this.grid.show(new Backgrid.Grid({
row : LogFileRow, row : LogFileRow,
columns : this.columns, columns : this.columns,
@ -76,32 +92,43 @@ module.exports = Marionette.Layout.extend({
className : 'table table-hover' className : 'table table-hover'
})); }));
}, },
_collectionSynced : function(){
if(!this.collection.any()) { _collectionSynced : function() {
if (!this.collection.any()) {
return; return;
} }
var model = this.collection.first(); var model = this.collection.first();
this._fetchLogFileContents({model : model}); this._fetchLogFileContents({ model : model });
}, },
_fetchLogFileContents : function(options){
_fetchLogFileContents : function(options) {
this.contents.show(new LoadingView()); this.contents.show(new LoadingView());
var model = options.model; var model = options.model;
var contentsModel = new ContentsModel(model.toJSON()); var contentsModel = new ContentsModel(model.toJSON());
this.listenToOnce(contentsModel, 'sync', this._showDetails); this.listenToOnce(contentsModel, 'sync', this._showDetails);
contentsModel.fetch({dataType : 'text'});
contentsModel.fetch({ dataType : 'text' });
}, },
_showDetails : function(model){
this.contents.show(new ContentsView({model : model})); _showDetails : function(model) {
this.contents.show(new ContentsView({ model : model }));
}, },
_refreshTable : function(buttonContext){
_refreshTable : function(buttonContext) {
this.contents.close(); this.contents.close();
var promise = this.collection.fetch(); var promise = this.collection.fetch();
if(buttonContext) {
//Would be nice to spin the icon on the refresh button
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise); buttonContext.ui.icon.spinForPromise(promise);
} }
}, },
_commandComplete : function(options){
if(options.command.get('name') === this.deleteFilesCommand.toLowerCase()) { _commandComplete : function(options) {
if (options.command.get('name') === this.deleteFilesCommand.toLowerCase()) {
this._refreshTable(); this._refreshTable();
} }
} }

@ -2,9 +2,13 @@ var vent = require('vent');
var Backgrid = require('backgrid'); var Backgrid = require('backgrid');
module.exports = Backgrid.Row.extend({ module.exports = Backgrid.Row.extend({
className : 'log-file-row', className : 'log-file-row',
events : {"click" : '_showDetails'},
_showDetails : function(){ events : {
vent.trigger(vent.Commands.ShowLogFile, {model : this.model}); 'click' : '_showDetails'
},
_showDetails : function() {
vent.trigger(vent.Commands.ShowLogFile, { model : this.model });
} }
}); });

@ -3,43 +3,62 @@ var LogsModel = require('./LogsModel');
var AsFilteredCollection = require('../../Mixins/AsFilteredCollection'); var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection'); var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
module.exports = (function(){ var collection = PagableCollection.extend({
var collection = PagableCollection.extend({ url : window.NzbDrone.ApiRoot + '/log',
url : window.NzbDrone.ApiRoot + '/log', model : LogsModel,
model : LogsModel, tableName : 'logs',
tableName : 'logs',
state : { state : {
pageSize : 50, pageSize : 50,
sortKey : 'time', sortKey : 'time',
order : 1 order : 1
}, },
queryParams : {
totalPages : null, queryParams : {
totalRecords : null, totalPages : null,
pageSize : 'pageSize', totalRecords : null,
sortKey : 'sortKey', pageSize : 'pageSize',
order : 'sortDir', sortKey : 'sortKey',
directions : { order : 'sortDir',
"-1" : 'asc', directions : {
"1" : 'desc' '-1' : 'asc',
} '1' : 'desc'
},
filterModes : {
"all" : [null, null],
"info" : ['level', 'Info'],
"warn" : ['level', 'Warn'],
"error" : ['level', 'Error']
},
parseState : function(resp, queryParams, state){
return {totalRecords : resp.totalRecords};
},
parseRecords : function(resp){
if(resp) {
return resp.records;
}
return resp;
} }
}); },
collection = AsFilteredCollection.apply(collection);
return AsPersistedStateCollection.apply(collection); // Filter Modes
}).call(this); filterModes : {
"all" : [
null,
null
],
"info" : [
'level',
'Info'
],
"warn" : [
'level',
'Warn'
],
"error" : [
'level',
'Error'
]
},
parseState : function(resp, queryParams, state) {
return { totalRecords : resp.totalRecords };
},
parseRecords : function(resp) {
if (resp) {
return resp.records;
}
return resp;
}
});
collection = AsFilteredCollection.apply(collection);
module.exports = AsPersistedStateCollection.apply(collection);

@ -5,46 +5,56 @@ var LogFileCollection = require('./Files/LogFileCollection');
var UpdateLogFileCollection = require('./Updates/LogFileCollection'); var UpdateLogFileCollection = require('./Updates/LogFileCollection');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Logs/LogsLayoutTemplate', template : 'System/Logs/LogsLayoutTemplate',
ui : {
ui : {
tableTab : '.x-table-tab', tableTab : '.x-table-tab',
filesTab : '.x-files-tab', filesTab : '.x-files-tab',
updateFilesTab : '.x-update-files-tab' updateFilesTab : '.x-update-files-tab'
}, },
regions : {
regions : {
table : '#table', table : '#table',
files : '#files', files : '#files',
updateFiles : '#update-files' updateFiles : '#update-files'
}, },
events : {
"click .x-table-tab" : '_showTable', events : {
"click .x-files-tab" : '_showFiles', 'click .x-table-tab' : '_showTable',
"click .x-update-files-tab" : '_showUpdateFiles' 'click .x-files-tab' : '_showFiles',
'click .x-update-files-tab' : '_showUpdateFiles'
}, },
onShow : function(){
onShow : function() {
this._showTable(); this._showTable();
}, },
_showTable : function(e){
if(e) { _showTable : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.ui.tableTab.tab('show'); this.ui.tableTab.tab('show');
this.table.show(new LogsTableLayout()); this.table.show(new LogsTableLayout());
}, },
_showFiles : function(e){
if(e) { _showFiles : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.ui.filesTab.tab('show'); this.ui.filesTab.tab('show');
this.files.show(new LogsFileLayout({ this.files.show(new LogsFileLayout({
collection : new LogFileCollection(), collection : new LogFileCollection(),
deleteFilesCommand : 'deleteLogFiles' deleteFilesCommand : 'deleteLogFiles'
})); }));
}, },
_showUpdateFiles : function(e){
if(e) { _showUpdateFiles : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.ui.updateFilesTab.tab('show'); this.ui.updateFilesTab.tab('show');
this.updateFiles.show(new LogsFileLayout({ this.updateFiles.show(new LogsFileLayout({
collection : new UpdateLogFileCollection(), collection : new UpdateLogFileCollection(),

@ -1,4 +1,6 @@
var vent = require('vent'); var vent = require('vent');
var Marionette = require('marionette'); var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({template : 'System/Logs/Table/Details/LogDetailsViewTemplate'}); module.exports = Marionette.ItemView.extend({
template : 'System/Logs/Table/Details/LogDetailsViewTemplate'
});

@ -2,9 +2,11 @@ var NzbDroneCell = require('../../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'log-level-cell', className : 'log-level-cell',
render : function(){
render : function() {
var level = this._getValue(); var level = this._getValue();
this.$el.html('<i class="icon-{0}" title="{1}"/>'.format(this._getValue().toLowerCase(), level)); this.$el.html('<i class="icon-{0}" title="{1}"/>'.format(this._getValue().toLowerCase(), level));
return this; return this;
} }
}); });

@ -2,9 +2,13 @@ var vent = require('vent');
var Backgrid = require('backgrid'); var Backgrid = require('backgrid');
module.exports = Backgrid.Row.extend({ module.exports = Backgrid.Row.extend({
className : 'log-row', className : 'log-row',
events : {"click" : '_showDetails'},
_showDetails : function(){ events : {
vent.trigger(vent.Commands.ShowLogDetails, {model : this.model}); 'click' : '_showDetails'
},
_showDetails : function() {
vent.trigger(vent.Commands.ShowLogDetails, { model : this.model });
} }
}); });

@ -4,9 +4,11 @@ var UiSettings = require('../../../Shared/UiSettingsModel');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'log-time-cell', className : 'log-time-cell',
render : function(){
render : function() {
var date = moment(this._getValue()); var date = moment(this._getValue());
this.$el.html('<span title="{1}">{0}</span>'.format(date.format(UiSettings.time(true, false)), date.format(UiSettings.longDateTime(true)))); this.$el.html('<span title="{1}">{0}</span>'.format(date.format(UiSettings.time(true, false)), date.format(UiSettings.longDateTime(true))));
return this; return this;
} }
}); });

@ -11,126 +11,164 @@ var LoadingView = require('../../../Shared/LoadingView');
require('../../../jQuery/jquery.spin'); require('../../../jQuery/jquery.spin');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Logs/Table/LogsTableLayoutTemplate', template : 'System/Logs/Table/LogsTableLayoutTemplate',
regions : {
regions : {
grid : '#x-grid', grid : '#x-grid',
toolbar : '#x-toolbar', toolbar : '#x-toolbar',
pager : '#x-pager' pager : '#x-pager'
}, },
attributes : {id : 'logs-screen'},
columns : [{ attributes : {
name : 'level', id : 'logs-screen'
label : '', },
sortable : true,
cell : LogLevelCell columns : [
}, { {
name : 'logger', name : 'level',
label : 'Component', label : '',
sortable : true, sortable : true,
cell : Backgrid.StringCell.extend({className : 'log-logger-cell'}) cell : LogLevelCell
}, { },
name : 'message', {
label : 'Message', name : 'logger',
sortable : false, label : 'Component',
cell : Backgrid.StringCell.extend({className : 'log-message-cell'}) sortable : true,
}, { cell : Backgrid.StringCell.extend({
name : 'time', className : 'log-logger-cell'
label : 'Time', })
cell : LogTimeCell },
}], {
initialize : function(){ name : 'message',
label : 'Message',
sortable : false,
cell : Backgrid.StringCell.extend({
className : 'log-message-cell'
})
},
{
name : 'time',
label : 'Time',
cell : LogTimeCell
}
],
initialize : function() {
this.collection = new LogCollection(); this.collection = new LogCollection();
this.listenTo(this.collection, 'sync', this._showTable); this.listenTo(this.collection, 'sync', this._showTable);
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete); this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
}, },
onRender : function(){
onRender : function() {
this.grid.show(new LoadingView()); this.grid.show(new LoadingView());
}, },
onShow : function(){
onShow : function() {
this._showToolbar(); this._showToolbar();
}, },
_showTable : function(){
_showTable : function() {
this.grid.show(new Backgrid.Grid({ this.grid.show(new Backgrid.Grid({
row : LogRow, row : LogRow,
columns : this.columns, columns : this.columns,
collection : this.collection, collection : this.collection,
className : 'table table-hover' className : 'table table-hover'
})); }));
this.pager.show(new GridPager({ this.pager.show(new GridPager({
columns : this.columns, columns : this.columns,
collection : this.collection collection : this.collection
})); }));
}, },
_showToolbar : function(){
_showToolbar : function() {
var filterButtons = { var filterButtons = {
type : 'radio', type : 'radio',
storeState : true, storeState : true,
menuKey : 'logs.filterMode', menuKey : 'logs.filterMode',
defaultAction : 'all', defaultAction : 'all',
items : [{ items : [
key : 'all', {
title : '', key : 'all',
tooltip : 'All', title : '',
icon : 'icon-circle-blank', tooltip : 'All',
callback : this._setFilter icon : 'icon-circle-blank',
}, { callback : this._setFilter
key : 'info', },
title : '', {
tooltip : 'Info', key : 'info',
icon : 'icon-info', title : '',
callback : this._setFilter tooltip : 'Info',
}, { icon : 'icon-info',
key : 'warn', callback : this._setFilter
title : '', },
tooltip : 'Warn', {
icon : 'icon-warn', key : 'warn',
callback : this._setFilter title : '',
}, { tooltip : 'Warn',
key : 'error', icon : 'icon-warn',
title : '', callback : this._setFilter
tooltip : 'Error', },
icon : 'icon-error', {
callback : this._setFilter key : 'error',
}] title : '',
tooltip : 'Error',
icon : 'icon-error',
callback : this._setFilter
}
]
}; };
var leftSideButtons = { var leftSideButtons = {
type : 'default', type : 'default',
storeState : false, storeState : false,
items : [{ items : [
title : 'Refresh', {
icon : 'icon-refresh', title : 'Refresh',
ownerContext : this, icon : 'icon-refresh',
callback : this._refreshTable ownerContext : this,
}, { callback : this._refreshTable
title : 'Clear Logs', },
icon : 'icon-trash', {
command : 'clearLog' title : 'Clear Logs',
}] icon : 'icon-trash',
command : 'clearLog'
}
]
}; };
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
left : [leftSideButtons], left : [leftSideButtons],
right : [filterButtons], right : [filterButtons],
context : this context : this
})); }));
}, },
_refreshTable : 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 });
if(buttonContext) {
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise); buttonContext.ui.icon.spinForPromise(promise);
} }
}, },
_setFilter : function(buttonContext){
_setFilter : function(buttonContext) {
var mode = buttonContext.model.get('key'); var mode = buttonContext.model.get('key');
this.collection.setFilterMode(mode, {reset : false});
this.collection.setFilterMode(mode, { reset : false });
this.collection.state.currentPage = 1; this.collection.state.currentPage = 1;
var promise = this.collection.fetch({reset : true}); var promise = this.collection.fetch({ reset : true });
if(buttonContext) {
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise); buttonContext.ui.icon.spinForPromise(promise);
} }
}, },
_commandComplete : function(options){
if(options.command.get('name') === 'clearlog') { _commandComplete : function(options) {
if (options.command.get('name') === 'clearlog') {
this._refreshTable(); this._refreshTable();
} }
} }

@ -4,6 +4,7 @@ var LogFileModel = require('./LogFileModel');
module.exports = Backbone.Collection.extend({ module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/log/file/update', url : window.NzbDrone.ApiRoot + '/log/file/update',
model : LogFileModel, model : LogFileModel,
state : { state : {
sortKey : 'lastWriteTime', sortKey : 'lastWriteTime',
order : 1 order : 1

@ -1,8 +1,9 @@
var Backbone = require('backbone'); var Backbone = require('backbone');
var ApiData = require('../Shared/ApiData'); var ApiData = require('../Shared/ApiData');
module.exports = (function(){ var StatusModel = Backbone.Model.extend({
var StatusModel = Backbone.Model.extend({url : window.NzbDrone.ApiRoot + '/system/status'}); url : window.NzbDrone.ApiRoot + '/system/status'
var instance = new StatusModel(ApiData.get('system/status')); });
return instance; var instance = new StatusModel(ApiData.get('system/status'));
}).call(this);
module.exports = instance;

@ -1,4 +1,4 @@
var $ = require('jquery'); var $ = require('jquery');
var Backbone = require('backbone'); var Backbone = require('backbone');
var Marionette = require('marionette'); var Marionette = require('marionette');
var SystemInfoLayout = require('./Info/SystemInfoLayout'); var SystemInfoLayout = require('./Info/SystemInfoLayout');
@ -10,22 +10,25 @@ var Messenger = require('../Shared/Messenger');
var StatusModel = require('./StatusModel'); var StatusModel = require('./StatusModel');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/SystemLayoutTemplate', template : 'System/SystemLayoutTemplate',
regions : {
regions : {
status : '#status', status : '#status',
logs : '#logs', logs : '#logs',
updates : '#updates', updates : '#updates',
backup : '#backup', backup : '#backup',
tasks : '#tasks' tasks : '#tasks'
}, },
ui : {
ui : {
statusTab : '.x-status-tab', statusTab : '.x-status-tab',
logsTab : '.x-logs-tab', logsTab : '.x-logs-tab',
updatesTab : '.x-updates-tab', updatesTab : '.x-updates-tab',
backupTab : '.x-backup-tab', backupTab : '.x-backup-tab',
tasksTab : '.x-tasks-tab' tasksTab : '.x-tasks-tab'
}, },
events : {
events : {
'click .x-status-tab' : '_showStatus', 'click .x-status-tab' : '_showStatus',
'click .x-logs-tab' : '_showLogs', 'click .x-logs-tab' : '_showLogs',
'click .x-updates-tab' : '_showUpdates', 'click .x-updates-tab' : '_showUpdates',
@ -34,8 +37,9 @@ module.exports = Marionette.Layout.extend({
'click .x-shutdown' : '_shutdown', 'click .x-shutdown' : '_shutdown',
'click .x-restart' : '_restart' 'click .x-restart' : '_restart'
}, },
initialize : function(options){
if(options.action) { initialize : function(options) {
if (options.action) {
this.action = options.action.toLowerCase(); this.action = options.action.toLowerCase();
} }
@ -43,7 +47,8 @@ module.exports = Marionette.Layout.extend({
authentication : StatusModel.get('authentication') authentication : StatusModel.get('authentication')
}; };
}, },
onShow : function(){
onShow : function() {
switch (this.action) { switch (this.action) {
case 'logs': case 'logs':
this._showLogs(); this._showLogs();
@ -61,13 +66,15 @@ module.exports = Marionette.Layout.extend({
this._showStatus(); this._showStatus();
} }
}, },
_navigate : function(route){
_navigate : function(route) {
Backbone.history.navigate(route, { Backbone.history.navigate(route, {
trigger : true, trigger : true,
replace : true replace : true
}); });
}, },
_showStatus : function (e) {
_showStatus : function(e) {
if (e) { if (e) {
e.preventDefault(); e.preventDefault();
} }
@ -76,53 +83,65 @@ module.exports = Marionette.Layout.extend({
this.ui.statusTab.tab('show'); this.ui.statusTab.tab('show');
this._navigate('system/status'); this._navigate('system/status');
}, },
_showLogs : function(e){
if(e) { _showLogs : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.logs.show(new LogsLayout()); this.logs.show(new LogsLayout());
this.ui.logsTab.tab('show'); this.ui.logsTab.tab('show');
this._navigate('system/logs'); this._navigate('system/logs');
}, },
_showUpdates : function(e){
if(e) { _showUpdates : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.updates.show(new UpdateLayout()); this.updates.show(new UpdateLayout());
this.ui.updatesTab.tab('show'); this.ui.updatesTab.tab('show');
this._navigate('system/updates'); this._navigate('system/updates');
}, },
_showBackup : function(e){
if(e) { _showBackup : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.backup.show(new BackupLayout()); this.backup.show(new BackupLayout());
this.ui.backupTab.tab('show'); this.ui.backupTab.tab('show');
this._navigate('system/backup'); this._navigate('system/backup');
}, },
_showTasks : function(e){
if(e) { _showTasks : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.tasks.show(new TaskLayout()); this.tasks.show(new TaskLayout());
this.ui.tasksTab.tab('show'); this.ui.tasksTab.tab('show');
this._navigate('system/tasks'); this._navigate('system/tasks');
}, },
_shutdown : function(){
_shutdown : function() {
$.ajax({ $.ajax({
url : window.NzbDrone.ApiRoot + '/system/shutdown', url : window.NzbDrone.ApiRoot + '/system/shutdown',
type : 'POST' type : 'POST'
}); });
Messenger.show({ Messenger.show({
message : 'Sonarr will shutdown shortly', message : 'Sonarr will shutdown shortly',
type : 'info' type : 'info'
}); });
}, },
_restart : function(){
_restart : function() {
$.ajax({ $.ajax({
url : window.NzbDrone.ApiRoot + '/system/restart', url : window.NzbDrone.ApiRoot + '/system/restart',
type : 'POST' type : 'POST'
}); });
Messenger.show({ Messenger.show({
message : 'Sonarr will restart shortly', message : 'Sonarr will restart shortly',
type : 'info' type : 'info'

@ -1,4 +1,4 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li><a href="#status" class="x-status-tab no-router">Status</a></li> <li><a href="#status" class="x-status-tab no-router">Status</a></li>
<li><a href="#updates" class="x-updates-tab no-router">Updates</a></li> <li><a href="#updates" class="x-updates-tab no-router">Updates</a></li>
<li><a href="#tasks" class="x-tasks-tab no-router">Tasks</a></li> <li><a href="#tasks" class="x-tasks-tab no-router">Tasks</a></li>

@ -2,20 +2,29 @@ var NzbDroneCell = require('../../Cells/NzbDroneCell');
var CommandController = require('../../Commands/CommandController'); var CommandController = require('../../Commands/CommandController');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'execute-task-cell', className : 'execute-task-cell',
events : {"click .x-execute" : '_executeTask'},
render : function(){ events : {
'click .x-execute' : '_executeTask'
},
render : function() {
this.$el.empty(); this.$el.empty();
var name = this.model.get('name'); var name = this.model.get('name');
var task = this.model.get('taskName'); var task = this.model.get('taskName');
this.$el.html('<i class="icon-refresh icon-can-spin x-execute" title="Execute {0}"></i>'.format(name)); this.$el.html('<i class="icon-refresh icon-can-spin x-execute" title="Execute {0}"></i>'.format(name));
CommandController.bindToCommand({ CommandController.bindToCommand({
element : this.$el.find('.x-execute'), element : this.$el.find('.x-execute'),
command : {name : task} command : { name : task }
}); });
return this; return this;
}, },
_executeTask : function(){
CommandController.Execute(this.model.get('taskName'), {name : this.model.get('taskName')}); _executeTask : function() {
CommandController.Execute(this.model.get('taskName'), { name : this.model.get('taskName') });
} }
}); });

@ -4,28 +4,31 @@ var UiSettings = require('../../Shared/UiSettingsModel');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'next-execution-cell', className : 'next-execution-cell',
render : function(){
render : function() {
this.$el.empty(); this.$el.empty();
var interval = this.model.get('interval'); var interval = this.model.get('interval');
var nextExecution = moment(this.model.get('nextExecution')); var nextExecution = moment(this.model.get('nextExecution'));
if(interval === 0) {
if (interval === 0) {
this.$el.html('-'); this.$el.html('-');
} } else if (moment().isAfter(nextExecution)) {
else if(moment().isAfter(nextExecution)) {
this.$el.html('now'); this.$el.html('now');
} } else {
else {
var result = '<span title="{0}">{1}</span>'; var result = '<span title="{0}">{1}</span>';
var tooltip = nextExecution.format(UiSettings.longDateTime()); var tooltip = nextExecution.format(UiSettings.longDateTime());
var text; var text;
if(UiSettings.get('showRelativeDates')) {
if (UiSettings.get('showRelativeDates')) {
text = nextExecution.fromNow(); text = nextExecution.fromNow();
} } else {
else {
text = nextExecution.format(UiSettings.shortDateTime()); text = nextExecution.format(UiSettings.shortDateTime());
} }
this.$el.html(result.format(tooltip, text)); this.$el.html(result.format(tooltip, text));
} }
return this; return this;
} }
}); });

@ -4,10 +4,12 @@ var TaskModel = require('./TaskModel');
module.exports = PageableCollection.extend({ module.exports = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/system/task', url : window.NzbDrone.ApiRoot + '/system/task',
model : TaskModel, model : TaskModel,
state : { state : {
sortKey : 'name', sortKey : 'name',
order : -1, order : -1,
pageSize : 100000 pageSize : 100000
}, },
mode : 'client'
mode : 'client'
}); });

@ -3,16 +3,19 @@ var moment = require('moment');
module.exports = NzbDroneCell.extend({ module.exports = NzbDroneCell.extend({
className : 'task-interval-cell', className : 'task-interval-cell',
render : function(){
render : function() {
this.$el.empty(); this.$el.empty();
var interval = this.model.get('interval'); var interval = this.model.get('interval');
var duration = moment.duration(interval, 'minutes').humanize().replace(/an?(?=\s)/, '1'); var duration = moment.duration(interval, 'minutes').humanize().replace(/an?(?=\s)/, '1');
if(interval === 0) {
if (interval === 0) {
this.$el.html('disabled'); this.$el.html('disabled');
} } else {
else {
this.$el.html(duration); this.$el.html(duration);
} }
return this; return this;
} }
}); });

@ -9,44 +9,59 @@ var LoadingView = require('../../Shared/LoadingView');
require('../../Mixins/backbone.signalr.mixin'); require('../../Mixins/backbone.signalr.mixin');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Task/TaskLayoutTemplate', template : 'System/Task/TaskLayoutTemplate',
regions : {tasks : '#x-tasks'},
columns : [{ regions : {
name : 'name', tasks : '#x-tasks'
label : 'Name', },
sortable : true,
cell : 'string' columns : [
}, { {
name : 'interval', name : 'name',
label : 'Interval', label : 'Name',
sortable : true, sortable : true,
cell : TaskIntervalCell cell : 'string'
}, { },
name : 'lastExecution', {
label : 'Last Execution', name : 'interval',
sortable : true, label : 'Interval',
cell : RelativeTimeCell sortable : true,
}, { cell : TaskIntervalCell
name : 'nextExecution', },
label : 'Next Execution', {
sortable : true, name : 'lastExecution',
cell : NextExecutionCell label : 'Last Execution',
}, { sortable : true,
name : 'this', cell : RelativeTimeCell
label : '', },
sortable : false, {
cell : ExecuteTaskCell name : 'nextExecution',
}], label : 'Next Execution',
initialize : function(){ sortable : true,
cell : NextExecutionCell
},
{
name : 'this',
label : '',
sortable : false,
cell : ExecuteTaskCell
}
],
initialize : function() {
this.taskCollection = new BackupCollection(); this.taskCollection = new BackupCollection();
this.listenTo(this.taskCollection, 'sync', this._showTasks); this.listenTo(this.taskCollection, 'sync', this._showTasks);
this.taskCollection.bindSignalR(); this.taskCollection.bindSignalR();
}, },
onRender : function(){
onRender : function() {
this.tasks.show(new LoadingView()); this.tasks.show(new LoadingView());
this.taskCollection.fetch(); this.taskCollection.fetch();
}, },
_showTasks : function(){
_showTasks : function() {
this.tasks.show(new Backgrid.Grid({ this.tasks.show(new Backgrid.Grid({
columns : this.columns, columns : this.columns,
collection : this.taskCollection, collection : this.taskCollection,

@ -1,3 +1,5 @@
var Marionette = require('marionette'); var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({template : 'System/Update/EmptyViewTemplate'}); module.exports = Marionette.ItemView.extend({
template : 'System/Update/EmptyViewTemplate'
});

@ -2,20 +2,28 @@ var Marionette = require('marionette');
var CommandController = require('../../Commands/CommandController'); var CommandController = require('../../Commands/CommandController');
module.exports = Marionette.ItemView.extend({ module.exports = Marionette.ItemView.extend({
template : 'System/Update/UpdateItemViewTemplate', template : 'System/Update/UpdateItemViewTemplate',
events : {"click .x-install-update" : '_installUpdate'},
initialize : function(){ events : {
'click .x-install-update' : '_installUpdate'
},
initialize : function() {
this.updating = false; this.updating = false;
}, },
_installUpdate : function(){
if(this.updating) { _installUpdate : function() {
if (this.updating) {
return; return;
} }
this.updating = true; this.updating = true;
var self = this; var self = this;
var promise = CommandController.Execute('applicationUpdate'); var promise = CommandController.Execute('applicationUpdate');
promise.done(function(){
window.setTimeout(function(){ promise.done(function() {
window.setTimeout(function() {
self.updating = false; self.updating = false;
}, 5000); }, 5000);
}); });

@ -5,17 +5,25 @@ var UpdateCollectionView = require('./UpdateCollectionView');
var LoadingView = require('../../Shared/LoadingView'); var LoadingView = require('../../Shared/LoadingView');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'System/Update/UpdateLayoutTemplate', template : 'System/Update/UpdateLayoutTemplate',
regions : {updates : '#x-updates'},
initialize : function(){ regions : {
updates : '#x-updates'
},
initialize : function() {
this.updateCollection = new UpdateCollection(); this.updateCollection = new UpdateCollection();
this.listenTo(this.updateCollection, 'sync', this._showUpdates); this.listenTo(this.updateCollection, 'sync', this._showUpdates);
}, },
onRender : function(){
onRender : function() {
this.updates.show(new LoadingView()); this.updates.show(new LoadingView());
this.updateCollection.fetch(); this.updateCollection.fetch();
}, },
_showUpdates : function(){
this.updates.show(new UpdateCollectionView({collection : this.updateCollection})); _showUpdates : function() {
this.updates.show(new UpdateCollectionView({ collection : this.updateCollection }));
} }
}); });

@ -2,15 +2,13 @@
var TagModel = require('./TagModel'); var TagModel = require('./TagModel');
var ApiData = require('../Shared/ApiData'); var ApiData = require('../Shared/ApiData');
require('../Mixins/backbone.signalr.mixin'); var Collection = Backbone.Collection.extend({
var collection = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/tag', url : window.NzbDrone.ApiRoot + '/tag',
model : TagModel, model : TagModel,
comparator : function(model){ comparator : function(model) {
return model.get('label'); return model.get('label');
} }
}); });
module.exports = new collection(ApiData.get('tag')).bindSignalR(); module.exports = new Collection(ApiData.get('tag'));

@ -2,22 +2,24 @@ var _ = require('underscore');
var Handlebars = require('handlebars'); var Handlebars = require('handlebars');
var TagCollection = require('./TagCollection'); var TagCollection = require('./TagCollection');
module.exports = (function(){ Handlebars.registerHelper('tagDisplay', function(tags) {
Handlebars.registerHelper('tagDisplay', function(tags){ var tagLabels = _.map(TagCollection.filter(function(tag) {
var tagLabels = _.map(TagCollection.filter(function(tag){ return _.contains(tags, tag.get('id'));
return _.contains(tags, tag.get('id')); }), function(tag) {
}), function(tag){ return '<span class="label label-info">{0}</span>'.format(tag.get('label'));
return '<span class="label label-info">{0}</span>'.format(tag.get('label'));
});
return new Handlebars.SafeString(tagLabels.join(' '));
}); });
Handlebars.registerHelper('genericTagDisplay', function(tags, classes){
if(!tags) { return new Handlebars.SafeString(tagLabels.join(' '));
return new Handlebars.SafeString(''); });
}
var tagLabels = _.map(tags.split(','), function(tag){ Handlebars.registerHelper('genericTagDisplay', function(tags, classes) {
return '<span class="{0}">{1}</span>'.format(classes, tag); if (!tags) {
}); return new Handlebars.SafeString('');
return new Handlebars.SafeString(tagLabels.join(' ')); }
var tagLabels = _.map(tags.split(','), function(tag) {
return '<span class="{0}">{1}</span>'.format(classes, tag);
}); });
}).call(this);
return new Handlebars.SafeString(tagLabels.join(' '));
});

@ -5,43 +5,59 @@ var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../../Mixins/AsSortedCollection'); var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection'); var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
module.exports = (function(){ var Collection = PagableCollection.extend({
var Collection = PagableCollection.extend({ url : window.NzbDrone.ApiRoot + '/wanted/cutoff',
url : window.NzbDrone.ApiRoot + '/wanted/cutoff', model : EpisodeModel,
model : EpisodeModel, tableName : 'wanted.cutoff',
tableName : 'wanted.cutoff',
state : { state : {
pageSize : 15, pageSize : 15,
sortKey : 'airDateUtc', sortKey : 'airDateUtc',
order : 1 order : 1
}, },
queryParams : {
totalPages : null, queryParams : {
totalRecords : null, totalPages : null,
pageSize : 'pageSize', totalRecords : null,
sortKey : 'sortKey', pageSize : 'pageSize',
order : 'sortDir', sortKey : 'sortKey',
directions : { order : 'sortDir',
"-1" : 'asc', directions : {
"1" : 'desc' '-1' : 'asc',
} '1' : 'desc'
}, }
filterModes : { },
"monitored" : ['monitored', 'true'],
"unmonitored" : ['monitored', 'false'] // Filter Modes
}, filterModes : {
sortMappings : {"series" : {sortKey : 'series.sortTitle'}}, 'monitored' : [
parseState : function(resp){ 'monitored',
return {totalRecords : resp.totalRecords}; 'true'
}, ],
parseRecords : function(resp){ 'unmonitored' : [
if(resp) { 'monitored',
return resp.records; 'false'
} ],
return resp; },
sortMappings : {
'series' : { sortKey : 'series.sortTitle' }
},
parseState : function(resp) {
return { totalRecords : resp.totalRecords };
},
parseRecords : function(resp) {
if (resp) {
return resp.records;
} }
});
Collection = AsFilteredCollection.call(Collection); return resp;
Collection = AsSortedCollection.call(Collection); }
return AsPersistedStateCollection.call(Collection); });
}).call(this);
Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection);
module.exports = AsPersistedStateCollection.call(Collection);

@ -16,127 +16,169 @@ require('backgrid.selectall');
require('../../Mixins/backbone.signalr.mixin'); require('../../Mixins/backbone.signalr.mixin');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'Wanted/Cutoff/CutoffUnmetLayoutTemplate', template : 'Wanted/Cutoff/CutoffUnmetLayoutTemplate',
regions : {
regions : {
cutoff : '#x-cutoff-unmet', cutoff : '#x-cutoff-unmet',
toolbar : '#x-toolbar', toolbar : '#x-toolbar',
pager : '#x-pager' pager : '#x-pager'
}, },
ui : {searchSelectedButton : '.btn i.icon-search'},
columns : [{ ui : {
name : '', searchSelectedButton : '.btn i.icon-search'
cell : 'select-row', },
headerCell : 'select-all',
sortable : false columns : [
}, { {
name : 'series', name : '',
label : 'Series Title', cell : 'select-row',
cell : SeriesTitleCell, headerCell : 'select-all',
sortValue : 'series.sortTitle' sortable : false
}, { },
name : 'this', {
label : 'Episode', name : 'series',
cell : EpisodeNumberCell, label : 'Series Title',
sortable : false cell : SeriesTitleCell,
}, { sortValue : 'series.sortTitle'
name : 'this', },
label : 'Episode Title', {
cell : EpisodeTitleCell, name : 'this',
sortable : false label : 'Episode',
}, { cell : EpisodeNumberCell,
name : 'airDateUtc', sortable : false
label : 'Air Date', },
cell : RelativeDateCell {
}, { name : 'this',
name : 'status', label : 'Episode Title',
label : 'Status', cell : EpisodeTitleCell,
cell : EpisodeStatusCell, sortable : false
sortable : false },
}], {
initialize : function(){ name : 'airDateUtc',
this.collection = new CutoffUnmetCollection().bindSignalR({updateOnly : true}); label : 'Air Date',
cell : RelativeDateCell
},
{
name : 'status',
label : 'Status',
cell : EpisodeStatusCell,
sortable : false
}
],
initialize : function() {
this.collection = new CutoffUnmetCollection().bindSignalR({ updateOnly : true });
this.listenTo(this.collection, 'sync', this._showTable); this.listenTo(this.collection, 'sync', this._showTable);
}, },
onShow : function(){
onShow : function() {
this.cutoff.show(new LoadingView()); this.cutoff.show(new LoadingView());
this._showToolbar(); this._showToolbar();
this.collection.fetch(); this.collection.fetch();
}, },
_showTable : function(){
_showTable : function() {
this.cutoffGrid = new Backgrid.Grid({ this.cutoffGrid = new Backgrid.Grid({
columns : this.columns, columns : this.columns,
collection : this.collection, collection : this.collection,
className : 'table table-hover' className : 'table table-hover'
}); });
this.cutoff.show(this.cutoffGrid); this.cutoff.show(this.cutoffGrid);
this.pager.show(new GridPager({ this.pager.show(new GridPager({
columns : this.columns, columns : this.columns,
collection : this.collection collection : this.collection
})); }));
}, },
_showToolbar : function(){
_showToolbar : function() {
var leftSideButtons = { var leftSideButtons = {
type : 'default', type : 'default',
storeState : false, storeState : false,
items : [{ items : [
title : 'Search Selected', {
icon : 'icon-search', title : 'Search Selected',
callback : this._searchSelected, icon : 'icon-search',
ownerContext : this, callback : this._searchSelected,
className : 'x-search-selected' ownerContext : this,
}, { className : 'x-search-selected'
title : 'Season Pass', },
icon : 'icon-bookmark', {
route : 'seasonpass' title : 'Season Pass',
}] icon : 'icon-bookmark',
route : 'seasonpass'
}
]
}; };
var filterOptions = { var filterOptions = {
type : 'radio', type : 'radio',
storeState : false, storeState : false,
menuKey : 'wanted.filterMode', menuKey : 'wanted.filterMode',
defaultAction : 'monitored', defaultAction : 'monitored',
items : [{ items : [
key : 'monitored', {
title : '', key : 'monitored',
tooltip : 'Monitored Only', title : '',
icon : 'icon-nd-monitored', tooltip : 'Monitored Only',
callback : this._setFilter icon : 'icon-nd-monitored',
}, { callback : this._setFilter
key : 'unmonitored', },
title : '', {
tooltip : 'Unmonitored Only', key : 'unmonitored',
icon : 'icon-nd-unmonitored', title : '',
callback : this._setFilter tooltip : 'Unmonitored Only',
}] icon : 'icon-nd-unmonitored',
callback : this._setFilter
}
]
}; };
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
left : [leftSideButtons], left : [
right : [filterOptions], leftSideButtons
],
right : [
filterOptions
],
context : this context : this
})); }));
CommandController.bindToCommand({ CommandController.bindToCommand({
element : this.$('.x-search-selected'), element : this.$('.x-search-selected'),
command : {name : 'episodeSearch'} command : {
name : 'episodeSearch'
}
}); });
}, },
_setFilter : function(buttonContext){
_setFilter : function(buttonContext) {
var mode = buttonContext.model.get('key'); var mode = buttonContext.model.get('key');
this.collection.state.currentPage = 1; this.collection.state.currentPage = 1;
var promise = this.collection.setFilterMode(mode); var promise = this.collection.setFilterMode(mode);
if(buttonContext) {
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise); buttonContext.ui.icon.spinForPromise(promise);
} }
}, },
_searchSelected : function(){
_searchSelected : function() {
var selected = this.cutoffGrid.getSelectedModels(); var selected = this.cutoffGrid.getSelectedModels();
if(selected.length === 0) {
if (selected.length === 0) {
Messenger.show({ Messenger.show({
type : 'error', type : 'error',
message : 'No episodes selected' message : 'No episodes selected'
}); });
return; return;
} }
var ids = _.pluck(selected, 'id'); var ids = _.pluck(selected, 'id');
CommandController.Execute('episodeSearch', { CommandController.Execute('episodeSearch', {
name : 'episodeSearch', name : 'episodeSearch',
episodeIds : ids episodeIds : ids

@ -5,43 +5,57 @@ var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../../Mixins/AsSortedCollection'); var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection'); var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
module.exports = (function(){ var Collection = PagableCollection.extend({
var Collection = PagableCollection.extend({ url : window.NzbDrone.ApiRoot + '/wanted/missing',
url : window.NzbDrone.ApiRoot + '/wanted/missing', model : EpisodeModel,
model : EpisodeModel, tableName : 'wanted.missing',
tableName : 'wanted.missing',
state : { state : {
pageSize : 15, pageSize : 15,
sortKey : 'airDateUtc', sortKey : 'airDateUtc',
order : 1 order : 1
}, },
queryParams : {
totalPages : null, queryParams : {
totalRecords : null, totalPages : null,
pageSize : 'pageSize', totalRecords : null,
sortKey : 'sortKey', pageSize : 'pageSize',
order : 'sortDir', sortKey : 'sortKey',
directions : { order : 'sortDir',
"-1" : 'asc', directions : {
"1" : 'desc' '-1' : 'asc',
} '1' : 'desc'
},
filterModes : {
"monitored" : ['monitored', 'true'],
"unmonitored" : ['monitored', 'false']
},
sortMappings : {"series" : {sortKey : 'series.sortTitle'}},
parseState : function(resp){
return {totalRecords : resp.totalRecords};
},
parseRecords : function(resp){
if(resp) {
return resp.records;
}
return resp;
} }
}); },
Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection); filterModes : {
return AsPersistedStateCollection.call(Collection); 'monitored' : [
}).call(this); 'monitored',
'true'
],
'unmonitored' : [
'monitored',
'false'
]
},
sortMappings : {
'series' : { sortKey : 'series.sortTitle' }
},
parseState : function(resp) {
return { totalRecords : resp.totalRecords };
},
parseRecords : function(resp) {
if (resp) {
return resp.records;
}
return resp;
}
});
Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection);
module.exports = AsPersistedStateCollection.call(Collection);

@ -16,110 +16,138 @@ require('backgrid.selectall');
require('../../Mixins/backbone.signalr.mixin'); require('../../Mixins/backbone.signalr.mixin');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'Wanted/Missing/MissingLayoutTemplate', template : 'Wanted/Missing/MissingLayoutTemplate',
regions : {
regions : {
missing : '#x-missing', missing : '#x-missing',
toolbar : '#x-toolbar', toolbar : '#x-toolbar',
pager : '#x-pager' pager : '#x-pager'
}, },
ui : {searchSelectedButton : '.btn i.icon-search'},
columns : [{ ui : {
name : '', searchSelectedButton : '.btn i.icon-search'
cell : 'select-row', },
headerCell : 'select-all',
sortable : false columns : [
}, { {
name : 'series', name : '',
label : 'Series Title', cell : 'select-row',
cell : SeriesTitleCell, headerCell : 'select-all',
sortValue : 'series.sortTitle' sortable : false
}, { },
name : 'this', {
label : 'Episode', name : 'series',
cell : EpisodeNumberCell, label : 'Series Title',
sortable : false cell : SeriesTitleCell,
}, { sortValue : 'series.sortTitle'
name : 'this', },
label : 'Episode Title', {
cell : EpisodeTitleCell, name : 'this',
sortable : false label : 'Episode',
}, { cell : EpisodeNumberCell,
name : 'airDateUtc', sortable : false
label : 'Air Date', },
cell : RelativeDateCell {
}, { name : 'this',
name : 'status', label : 'Episode Title',
label : 'Status', cell : EpisodeTitleCell,
cell : EpisodeStatusCell, sortable : false
sortable : false },
}], {
initialize : function(){ name : 'airDateUtc',
this.collection = new MissingCollection().bindSignalR({updateOnly : true}); label : 'Air Date',
cell : RelativeDateCell
},
{
name : 'status',
label : 'Status',
cell : EpisodeStatusCell,
sortable : false
}
],
initialize : function() {
this.collection = new MissingCollection().bindSignalR({ updateOnly : true });
this.listenTo(this.collection, 'sync', this._showTable); this.listenTo(this.collection, 'sync', this._showTable);
}, },
onShow : function(){
onShow : function() {
this.missing.show(new LoadingView()); this.missing.show(new LoadingView());
this._showToolbar(); this._showToolbar();
this.collection.fetch(); this.collection.fetch();
}, },
_showTable : function(){
_showTable : function() {
this.missingGrid = new Backgrid.Grid({ this.missingGrid = new Backgrid.Grid({
columns : this.columns, columns : this.columns,
collection : this.collection, collection : this.collection,
className : 'table table-hover' className : 'table table-hover'
}); });
this.missing.show(this.missingGrid); this.missing.show(this.missingGrid);
this.pager.show(new GridPager({ this.pager.show(new GridPager({
columns : this.columns, columns : this.columns,
collection : this.collection collection : this.collection
})); }));
}, },
_showToolbar : function(){
_showToolbar : function() {
var leftSideButtons = { var leftSideButtons = {
type : 'default', type : 'default',
storeState : false, storeState : false,
collapse : true, collapse : true,
items : [{ items : [
title : 'Search Selected', {
icon : 'icon-search', title : 'Search Selected',
callback : this._searchSelected, icon : 'icon-search',
ownerContext : this, callback : this._searchSelected,
className : 'x-search-selected' ownerContext : this,
}, { className : 'x-search-selected'
title : 'Search All Missing', },
icon : 'icon-search', {
callback : this._searchMissing, title : 'Search All Missing',
ownerContext : this, icon : 'icon-search',
className : 'x-search-missing' callback : this._searchMissing,
}, { ownerContext : this,
title : 'Season Pass', className : 'x-search-missing'
icon : 'icon-bookmark', },
route : 'seasonpass' {
}, { title : 'Season Pass',
title : 'Rescan Drone Factory Folder', icon : 'icon-bookmark',
icon : 'icon-refresh', route : 'seasonpass'
command : 'downloadedepisodesscan', },
properties : {sendUpdates : true} {
}] title : 'Rescan Drone Factory Folder',
icon : 'icon-refresh',
command : 'downloadedepisodesscan',
properties : { sendUpdates : true }
}
]
}; };
var filterOptions = { var filterOptions = {
type : 'radio', type : 'radio',
storeState : false, storeState : false,
menuKey : 'wanted.filterMode', menuKey : 'wanted.filterMode',
defaultAction : 'monitored', defaultAction : 'monitored',
items : [{ items : [
key : 'monitored', {
title : '', key : 'monitored',
tooltip : 'Monitored Only', title : '',
icon : 'icon-nd-monitored', tooltip : 'Monitored Only',
callback : this._setFilter icon : 'icon-nd-monitored',
}, { callback : this._setFilter
key : 'unmonitored', },
title : '', {
tooltip : 'Unmonitored Only', key : 'unmonitored',
icon : 'icon-nd-unmonitored', title : '',
callback : this._setFilter tooltip : 'Unmonitored Only',
}] icon : 'icon-nd-unmonitored',
callback : this._setFilter
}
]
}; };
this.toolbar.show(new ToolbarLayout({ this.toolbar.show(new ToolbarLayout({
left : [leftSideButtons], left : [leftSideButtons],
@ -128,24 +156,24 @@ module.exports = Marionette.Layout.extend({
})); }));
CommandController.bindToCommand({ CommandController.bindToCommand({
element : this.$('.x-search-selected'), element : this.$('.x-search-selected'),
command : {name : 'episodeSearch'} command : { name : 'episodeSearch' }
}); });
CommandController.bindToCommand({ CommandController.bindToCommand({
element : this.$('.x-search-missing'), element : this.$('.x-search-missing'),
command : {name : 'missingEpisodeSearch'} command : { name : 'missingEpisodeSearch' }
}); });
}, },
_setFilter : function(buttonContext){ _setFilter : function(buttonContext) {
var mode = buttonContext.model.get('key'); var mode = buttonContext.model.get('key');
this.collection.state.currentPage = 1; this.collection.state.currentPage = 1;
var promise = this.collection.setFilterMode(mode); var promise = this.collection.setFilterMode(mode);
if(buttonContext) { if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise); buttonContext.ui.icon.spinForPromise(promise);
} }
}, },
_searchSelected : function(){ _searchSelected : function() {
var selected = this.missingGrid.getSelectedModels(); var selected = this.missingGrid.getSelectedModels();
if(selected.length === 0) { if (selected.length === 0) {
Messenger.show({ Messenger.show({
type : 'error', type : 'error',
message : 'No episodes selected' message : 'No episodes selected'
@ -158,9 +186,10 @@ module.exports = Marionette.Layout.extend({
episodeIds : ids episodeIds : ids
}); });
}, },
_searchMissing : function(){ _searchMissing : function() {
if(window.confirm('Are you sure you want to search for {0} missing episodes? '.format(this.collection.state.totalRecords) + 'One API request to each indexer will be used for each episode. ' + 'This cannot be stopped once started.')) { if (window.confirm('Are you sure you want to search for {0} missing episodes? '.format(this.collection.state.totalRecords) +
CommandController.Execute('missingEpisodeSearch', {name : 'missingEpisodeSearch'}); 'One API request to each indexer will be used for each episode. ' + 'This cannot be stopped once started.')) {
CommandController.Execute('missingEpisodeSearch', { name : 'missingEpisodeSearch' });
} }
} }
}); });

@ -5,22 +5,31 @@ var MissingLayout = require('./Missing/MissingLayout');
var CutoffUnmetLayout = require('./Cutoff/CutoffUnmetLayout'); var CutoffUnmetLayout = require('./Cutoff/CutoffUnmetLayout');
module.exports = Marionette.Layout.extend({ module.exports = Marionette.Layout.extend({
template : 'Wanted/WantedLayoutTemplate', template : 'Wanted/WantedLayoutTemplate',
regions : {content : '#content'},
ui : { regions : {
content : '#content'
//missing : '#missing',
//cutoff : '#cutoff'
},
ui : {
missingTab : '.x-missing-tab', missingTab : '.x-missing-tab',
cutoffTab : '.x-cutoff-tab' cutoffTab : '.x-cutoff-tab'
}, },
events : {
"click .x-missing-tab" : '_showMissing', events : {
"click .x-cutoff-tab" : '_showCutoffUnmet' 'click .x-missing-tab' : '_showMissing',
'click .x-cutoff-tab' : '_showCutoffUnmet'
}, },
initialize : function(options){
if(options.action) { initialize : function(options) {
if (options.action) {
this.action = options.action.toLowerCase(); this.action = options.action.toLowerCase();
} }
}, },
onShow : function(){
onShow : function() {
switch (this.action) { switch (this.action) {
case 'cutoff': case 'cutoff':
this._showCutoffUnmet(); this._showCutoffUnmet();
@ -29,24 +38,29 @@ module.exports = Marionette.Layout.extend({
this._showMissing(); this._showMissing();
} }
}, },
_navigate : function(route){
_navigate : function(route) {
Backbone.history.navigate(route, { Backbone.history.navigate(route, {
trigger : false, trigger : false,
replace : true replace : true
}); });
}, },
_showMissing : function(e){
if(e) { _showMissing : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.content.show(new MissingLayout()); this.content.show(new MissingLayout());
this.ui.missingTab.tab('show'); this.ui.missingTab.tab('show');
this._navigate('/wanted/missing'); this._navigate('/wanted/missing');
}, },
_showCutoffUnmet : function(e){
if(e) { _showCutoffUnmet : function(e) {
if (e) {
e.preventDefault(); e.preventDefault();
} }
this.content.show(new CutoffUnmetLayout()); this.content.show(new CutoffUnmetLayout());
this.ui.cutoffTab.tab('show'); this.ui.cutoffTab.tab('show');
this._navigate('/wanted/cutoff'); this._navigate('/wanted/cutoff');

Loading…
Cancel
Save