UI Cleanup - Updated System, Tags and Wanted subtrees.

This commit is contained in:
Taloth Saldono 2015-02-14 11:18:21 +01:00
parent d6079a701c
commit 32fc68b9df
46 changed files with 1008 additions and 643 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,43 +3,62 @@ var LogsModel = require('./LogsModel');
var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
module.exports = (function(){
var collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/log',
model : LogsModel,
tableName : 'logs',
state : {
pageSize : 50,
sortKey : 'time',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
"-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;
var collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/log',
model : LogsModel,
tableName : 'logs',
state : {
pageSize : 50,
sortKey : 'time',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1' : 'asc',
'1' : 'desc'
}
});
collection = AsFilteredCollection.apply(collection);
return AsPersistedStateCollection.apply(collection);
}).call(this);
},
// Filter Modes
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);

View File

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

View File

@ -1,4 +1,6 @@
var vent = require('vent');
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'
});

View File

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

View File

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

View File

@ -4,9 +4,11 @@ var UiSettings = require('../../../Shared/UiSettingsModel');
module.exports = NzbDroneCell.extend({
className : 'log-time-cell',
render : function(){
render : function() {
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))));
return this;
}
});

View File

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

View File

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

View File

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

View File

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

View File

@ -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="#updates" class="x-updates-tab no-router">Updates</a></li>
<li><a href="#tasks" class="x-tasks-tab no-router">Tasks</a></li>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,15 +2,13 @@
var TagModel = require('./TagModel');
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',
model : TagModel,
comparator : function(model){
comparator : function(model) {
return model.get('label');
}
});
module.exports = new collection(ApiData.get('tag')).bindSignalR();
module.exports = new Collection(ApiData.get('tag'));

View File

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

View File

@ -5,43 +5,59 @@ var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
module.exports = (function(){
var Collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/wanted/cutoff',
model : EpisodeModel,
tableName : 'wanted.cutoff',
state : {
pageSize : 15,
sortKey : 'airDateUtc',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
"-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;
var Collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/wanted/cutoff',
model : EpisodeModel,
tableName : 'wanted.cutoff',
state : {
pageSize : 15,
sortKey : 'airDateUtc',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1' : 'asc',
'1' : 'desc'
}
});
Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection);
return AsPersistedStateCollection.call(Collection);
}).call(this);
},
// Filter Modes
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);
module.exports = AsPersistedStateCollection.call(Collection);

View File

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

View File

@ -5,43 +5,57 @@ var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
module.exports = (function(){
var Collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/wanted/missing',
model : EpisodeModel,
tableName : 'wanted.missing',
state : {
pageSize : 15,
sortKey : 'airDateUtc',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
"-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;
var Collection = PagableCollection.extend({
url : window.NzbDrone.ApiRoot + '/wanted/missing',
model : EpisodeModel,
tableName : 'wanted.missing',
state : {
pageSize : 15,
sortKey : 'airDateUtc',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1' : 'asc',
'1' : 'desc'
}
});
Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection);
return AsPersistedStateCollection.call(Collection);
}).call(this);
},
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);
module.exports = AsPersistedStateCollection.call(Collection);

View File

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

View File

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