1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -04:00

New: Drone now uses the Download Client API to determine if a download is ready for import. (User configuration is required to replace the drone factory with this feature)

This commit is contained in:
Taloth Saldono
2014-04-19 17:09:22 +02:00
parent dcb586b937
commit 2035fe8578
196 changed files with 3961 additions and 2223 deletions

View File

@@ -26,6 +26,11 @@ define(
title = 'Queued';
}
if (status === 'completed') {
icon = 'icon-inbox';
title = 'Downloaded';
}
this.$el.html('<i class="{0}" title="{1}"></i>'.format(icon, title));
}

View File

@@ -1,23 +1,14 @@
'use strict';
define([
'marionette',
'Settings/ThingyAddCollectionView',
'Settings/ThingyHeaderGroupView',
'Settings/DownloadClient/Add/DownloadClientAddItemView'
], function (Marionette, AddItemView) {
], function (ThingyAddCollectionView, ThingyHeaderGroupView, AddItemView) {
return Marionette.CompositeView.extend({
itemView : AddItemView,
return ThingyAddCollectionView.extend({
itemView : ThingyHeaderGroupView.extend({ itemView: AddItemView }),
itemViewContainer: '.add-download-client .items',
template : 'Settings/DownloadClient/Add/DownloadClientAddCollectionViewTemplate',
itemViewOptions: function () {
return {
downloadClientCollection: this.downloadClientCollection
};
},
initialize: function (options) {
this.downloadClientCollection = options.downloadClientCollection;
}
template : 'Settings/DownloadClient/Add/DownloadClientAddCollectionViewTemplate'
});
});

View File

@@ -1,10 +1,11 @@
'use strict';
define([
'jquery',
'AppLayout',
'marionette',
'Settings/DownloadClient/Edit/DownloadClientEditView'
], function (AppLayout, Marionette, EditView) {
], function ($, AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Add/DownloadClientAddItemViewTemplate',
@@ -15,7 +16,7 @@ define([
},
initialize: function (options) {
this.downloadClientCollection = options.downloadClientCollection;
this.targetCollection = options.targetCollection;
},
_add: function (e) {
@@ -25,11 +26,11 @@ define([
this.model.set({
id : undefined,
name : this.model.get('implementationName'),
name : this.model.get('implementation'),
enable : true
});
var editView = new EditView({ model: this.model, downloadClientCollection: this.downloadClientCollection });
var editView = new EditView({ model: this.model, targetCollection: this.targetCollection });
AppLayout.modalRegion.show(editView);
}
});

View File

@@ -0,0 +1,36 @@
'use strict';
define([
'underscore',
'AppLayout',
'backbone',
'Settings/DownloadClient/DownloadClientCollection',
'Settings/DownloadClient/Add/DownloadClientAddCollectionView'
], function (_, AppLayout, Backbone, SchemaCollection, AddCollectionView) {
return ({
open: function (collection) {
var schemaCollection = new SchemaCollection();
var originalUrl = schemaCollection.url;
schemaCollection.url = schemaCollection.url + '/schema';
schemaCollection.fetch();
schemaCollection.url = originalUrl;
var groupedSchemaCollection = new Backbone.Collection();
schemaCollection.on('sync', function() {
var groups = schemaCollection.groupBy(function(model, iterator) { return model.get('protocol'); });
var modelCollection = _.map(groups, function(values, key, list) {
return { 'header': key, collection: values };
});
groupedSchemaCollection.reset(modelCollection);
});
var view = new AddCollectionView({ collection: groupedSchemaCollection, targetCollection: collection });
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -1,20 +0,0 @@
'use strict';
define([
'AppLayout',
'Settings/DownloadClient/DownloadClientCollection',
'Settings/DownloadClient/Add/DownloadClientAddCollectionView'
], function (AppLayout, DownloadClientCollection, DownloadClientAddCollectionView) {
return ({
open: function (collection) {
var schemaCollection = new DownloadClientCollection();
var originalUrl = schemaCollection.url;
schemaCollection.url = schemaCollection.url + '/schema';
schemaCollection.fetch();
schemaCollection.url = originalUrl;
var view = new DownloadClientAddCollectionView({ collection: schemaCollection, downloadClientCollection: collection});
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -1,23 +1,23 @@
'use strict';
define(
[
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Delete/DownloadClientDeleteViewTemplate',
events: {
'click .x-confirm-delete': '_delete'
},
define([
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Delete/DownloadClientDeleteViewTemplate',
_delete: function () {
this.model.destroy({
wait : true,
success: function () {
vent.trigger(vent.Commands.CloseModalCommand);
}
});
}
});
events: {
'click .x-confirm-delete': '_delete'
},
_delete: function () {
this.model.destroy({
wait : true,
success: function () {
vent.trigger(vent.Commands.CloseModalCommand);
}
});
}
});
});

View File

@@ -1,12 +1,31 @@
'use strict';
define(
[
'backbone',
'Settings/DownloadClient/DownloadClientModel'
], function (Backbone, DownloadClientModel) {
return Backbone.Collection.extend({
model: DownloadClientModel,
url : window.NzbDrone.ApiRoot + '/downloadclient'
});
define([
'backbone',
'Settings/DownloadClient/DownloadClientModel'
], function (Backbone, DownloadClientModel) {
return Backbone.Collection.extend({
model: DownloadClientModel,
url : window.NzbDrone.ApiRoot + '/downloadclient',
comparator : function(left, right, collection) {
var result = 0;
if (left.get('protocol')) {
result = -left.get('protocol').localeCompare(right.get('protocol'));
}
if (result === 0 && left.get('name')) {
result = left.get('name').localeCompare(right.get('name'));
}
if (result === 0) {
result = left.get('implementation').localeCompare(right.get('implementation'));
}
return result;
}
});
});

View File

@@ -1,31 +1,29 @@
'use strict';
define(
[
'underscore',
'AppLayout',
'marionette',
'Settings/DownloadClient/DownloadClientItemView',
'Settings/DownloadClient/Add/SchemaModal'
], function (_, AppLayout, Marionette, DownloadClientItemView, SchemaModal) {
return Marionette.CompositeView.extend({
itemView : DownloadClientItemView,
itemViewContainer: '#x-download-clients',
template : 'Settings/DownloadClient/DownloadClientCollectionViewTemplate',
ui: {
'addCard': '.x-add-card'
},
define([
'marionette',
'Settings/DownloadClient/DownloadClientItemView',
'Settings/DownloadClient/Add/DownloadClientSchemaModal'
], function (Marionette, ItemView, SchemaModal) {
return Marionette.CompositeView.extend({
itemView : ItemView,
itemViewContainer: '.download-client-list',
template : 'Settings/DownloadClient/DownloadClientCollectionViewTemplate',
events: {
'click .x-add-card': '_openSchemaModal'
},
ui: {
'addCard': '.x-add-card'
},
appendHtml: function (collectionView, itemView, index) {
collectionView.ui.addCard.parent('li').before(itemView.el);
},
events: {
'click .x-add-card': '_openSchemaModal'
},
_openSchemaModal: function () {
SchemaModal.open(this.collection);
}
});
appendHtml: function (collectionView, itemView, index) {
collectionView.ui.addCard.parent('li').before(itemView.el);
},
_openSchemaModal: function () {
SchemaModal.open(this.collection);
}
});
});

View File

@@ -2,7 +2,7 @@
<legend>Download Clients</legend>
<div class="row">
<div class="col-md-12">
<ul id="x-download-clients" class="download-client-list thingies">
<ul class="download-client-list thingies">
<li>
<div class="download-client-item thingy add-card x-add-card">
<span class="center well">

View File

@@ -1,27 +1,26 @@
'use strict';
define(
[
'AppLayout',
'marionette',
'Settings/DownloadClient/Edit/DownloadClientEditView'
], function (AppLayout, Marionette, EditView) {
define([
'AppLayout',
'marionette',
'Settings/DownloadClient/Edit/DownloadClientEditView'
], function (AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/DownloadClient/DownloadClientItemViewTemplate',
tagName : 'li',
return Marionette.ItemView.extend({
template: 'Settings/DownloadClient/DownloadClientItemViewTemplate',
tagName : 'li',
events: {
'click' : '_edit'
},
events: {
'click' : '_edit'
},
initialize: function () {
this.listenTo(this.model, 'sync', this.render);
},
initialize: function () {
this.listenTo(this.model, 'sync', this.render);
},
_edit: function () {
var view = new EditView({ model: this.model, downloadClientCollection: this.model.collection });
AppLayout.modalRegion.show(view);
}
});
_edit: function () {
var view = new EditView({ model: this.model, targetCollection: this.model.collection});
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -1,32 +1,31 @@
'use strict';
define(
[
'marionette',
'Settings/DownloadClient/DownloadClientCollection',
'Settings/DownloadClient/DownloadClientCollectionView',
'Settings/DownloadClient/Options/DownloadClientOptionsView',
'Settings/DownloadClient/FailedDownloadHandling/FailedDownloadHandlingView'
], function (Marionette, DownloadClientCollection, DownloadClientCollectionView, DownloadClientOptionsView, FailedDownloadHandlingView) {
define([
'marionette',
'Settings/DownloadClient/DownloadClientCollection',
'Settings/DownloadClient/DownloadClientCollectionView',
'Settings/DownloadClient/DroneFactory/DroneFactoryView',
'Settings/DownloadClient/DownloadHandling/DownloadHandlingView'
], function (Marionette, DownloadClientCollection, CollectionView, DroneFactoryView, DownloadHandlingView) {
return Marionette.Layout.extend({
template : 'Settings/DownloadClient/DownloadClientLayoutTemplate',
return Marionette.Layout.extend({
template : 'Settings/DownloadClient/DownloadClientLayoutTemplate',
regions: {
downloadClients : '#x-download-clients-region',
downloadClientOptions : '#x-download-client-options-region',
failedDownloadHandling : '#x-failed-download-handling-region'
},
regions: {
downloadClients : '#x-download-clients-region',
downloadHandling : '#x-download-handling-region',
droneFactory : '#x-dronefactory-region'
},
initialize: function () {
this.downloadClientCollection = new DownloadClientCollection();
this.downloadClientCollection.fetch();
},
initialize: function () {
this.downloadClientsCollection = new DownloadClientCollection();
this.downloadClientsCollection.fetch();
},
onShow: function () {
this.downloadClients.show(new DownloadClientCollectionView({ collection: this.downloadClientCollection }));
this.downloadClientOptions.show(new DownloadClientOptionsView({ model: this.model }));
this.failedDownloadHandling.show(new FailedDownloadHandlingView({ model: this.model }));
}
});
});
onShow: function () {
this.downloadClients.show(new CollectionView({ collection: this.downloadClientsCollection }));
this.downloadHandling.show(new DownloadHandlingView({ model: this.model }));
this.droneFactory.show(new DroneFactoryView({ model: this.model }));
}
});
});

View File

@@ -1,6 +1,5 @@
<div id="x-download-clients-region"></div>
<div class="form-horizontal">
<div id="x-download-client-options-region"></div>
<div id="x-failed-download-handling-region"></div>
<div id="x-download-handling-region"></div>
<div id="x-dronefactory-region"></div>
</div>

View File

@@ -1,10 +1,9 @@
'use strict';
define(
[
'backbone.deepmodel'
], function (DeepModel) {
return DeepModel.DeepModel.extend({
});
define([
'backbone.deepmodel'
], function (DeepModel) {
return DeepModel.DeepModel.extend({
});
});

View File

@@ -1,11 +1,11 @@
'use strict';
define(
[
'Settings/SettingsModelBase'
], function (SettingsModelBase) {
return SettingsModelBase.extend({
url : window.NzbDrone.ApiRoot + '/config/downloadclient',
successMessage: 'Download client settings saved',
errorMessage : 'Failed to save download client settings'
});
define([
'Settings/SettingsModelBase'
], function (SettingsModelBase) {
return SettingsModelBase.extend({
url : window.NzbDrone.ApiRoot + '/config/downloadclient',
successMessage: 'Download client settings saved',
errorMessage : 'Failed to save download client settings'
});
});

View File

@@ -0,0 +1,60 @@
'use strict';
define(
[
'marionette',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView'
], function (Marionette, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Settings/DownloadClient/DownloadHandling/DownloadHandlingViewTemplate',
ui: {
completedDownloadHandlingCheckbox : '.x-completed-download-handling',
completedDownloadOptions : '.x-completed-download-options',
failedDownloadHandlingCheckbox : '.x-failed-download-handling',
failedDownloadOptions : '.x-failed-download-options'
},
events: {
'change .x-completed-download-handling' : '_setCompletedDownloadOptionsVisibility',
'change .x-failed-download-handling' : '_setFailedDownloadOptionsVisibility'
},
onRender: function () {
if (!this.ui.completedDownloadHandlingCheckbox.prop('checked')) {
this.ui.completedDownloadOptions.hide();
}
if (!this.ui.failedDownloadHandlingCheckbox.prop('checked')) {
this.ui.failedDownloadOptions.hide();
}
},
_setCompletedDownloadOptionsVisibility: function () {
var checked = this.ui.completedDownloadHandlingCheckbox.prop('checked');
if (checked) {
this.ui.completedDownloadOptions.slideDown();
}
else {
this.ui.completedDownloadOptions.slideUp();
}
},
_setFailedDownloadOptionsVisibility: function () {
var checked = this.ui.failedDownloadHandlingCheckbox.prop('checked');
if (checked) {
this.ui.failedDownloadOptions.slideDown();
}
else {
this.ui.failedDownloadOptions.slideUp();
}
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
return view;
});

View File

@@ -1,6 +1,56 @@
<fieldset class="advanced-setting">
<legend>Failed Download Handling</legend>
<fieldset>
<legend>Download Client Import Handling</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Enable</label>
<div class="col-sm-8">
<div class="input-group">
<label class="checkbox toggle well">
<input type="checkbox" name="enableCompletedDownloadHandling" class="x-completed-download-handling"/>
<p>
<span>Yes</span>
<span>No</span>
</p>
<div class="btn btn-primary slide-button"/>
</label>
<span class="help-inline-checkbox">
<i class="icon-nd-form-info" title="Import completed downloads in download client history"/>
<i class="icon-nd-form-warning" title="Download client history items that are stored in the drone factory will be ignored. Configure the Drone Factory for a different path"/>
</span>
</div>
</div>
</div>
<div class="x-completed-download-options advanced-setting"">
<div class="form-group">
<label class="col-sm-3 control-label">Remove</label>
<div class="col-sm-8">
<div class="input-group">
<label class="checkbox toggle well">
<input type="checkbox" name="removeCompletedDownloads"/>
<p>
<span>Yes</span>
<span>No</span>
</p>
<div class="btn btn-primary slide-button"/>
</label>
<span class="help-inline-checkbox">
<i class="icon-nd-form-info" title="Remove imported downloads from download client history"/>
</span>
</div>
</div>
</div>
</div>
</fieldset>
<fieldset class="advanced-setting">
<legend>Download Client Failed Handling</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Enable</label>
@@ -40,7 +90,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-nd-form-info" title="Automatically search for and attempt to download another release when a download fails?"/>
<i class="icon-nd-form-info" title="Automatically search for and attempt to download another release"/>
</span>
</div>
</div>
@@ -62,7 +112,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-nd-form-info" title="Automatically remove failed downloads from history and encrypted downloads from queue?"/>
<i class="icon-nd-form-info" title="Remove failed downloads from download client history"/>
</span>
</div>
</div>

View File

@@ -8,7 +8,7 @@ define(
], function (Marionette, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Options/DownloadClientOptionsViewTemplate',
template: 'Settings/DownloadClient/DroneFactory/DroneFactoryViewTemplate',
ui: {
droneFactory : '.x-path'

View File

@@ -1,10 +1,10 @@
<fieldset>
<legend>Options</legend>
<legend>Drone Factory Options</legend>
<div class="form-group">
<label class="col-sm-3 control-label">Drone Factory</label>
<div class="col-sm-1 col-sm-push-8 help-inline">
<i class="icon-nd-form-info" title="The folder where your download client downloads TV shows to (Completed Download Directory)"/>
<i class="icon-nd-form-info" title="Optional folder to periodically scan for available imports"/>
<i class="icon-nd-form-warning" title="Do not use the folder that contains some or all of your sorted and named TV shows - doing so could cause data loss"></i>
</div>

View File

@@ -1,97 +1,96 @@
'use strict';
define(
[
'vent',
'AppLayout',
'marionette',
'Settings/DownloadClient/Delete/DownloadClientDeleteView',
'Commands/CommandController',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView',
'underscore',
'Form/FormBuilder',
'Mixins/AutoComplete',
'bootstrap'
], function (vent, AppLayout, Marionette, DeleteView, CommandController, AsModelBoundView, AsValidatedView, _) {
define([
'vent',
'AppLayout',
'marionette',
'Settings/DownloadClient/Delete/DownloadClientDeleteView',
'Commands/CommandController',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView',
'underscore',
'Form/FormBuilder',
'Mixins/AutoComplete',
'bootstrap'
], function (vent, AppLayout, Marionette, DeleteView, CommandController, AsModelBoundView, AsValidatedView, _) {
var view = Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Edit/DownloadClientEditViewTemplate',
var view = Marionette.ItemView.extend({
template: 'Settings/DownloadClient/Edit/DownloadClientEditViewTemplate',
ui: {
path : '.x-path',
modalBody : '.modal-body'
},
ui: {
path : '.x-path',
modalBody : '.modal-body'
},
events: {
'click .x-save' : '_save',
'click .x-save-and-add': '_saveAndAdd',
'click .x-delete' : '_delete',
'click .x-back' : '_back',
'click .x-test' : '_test'
},
events: {
'click .x-save' : '_save',
'click .x-save-and-add': '_saveAndAdd',
'click .x-delete' : '_delete',
'click .x-back' : '_back',
'click .x-test' : '_test'
},
initialize: function (options) {
this.downloadClientCollection = options.downloadClientCollection;
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
onShow: function () {
//Hack to deal with modals not overflowing
if (this.ui.path.length > 0) {
this.ui.modalBody.addClass('modal-overflow');
}
this.ui.path.autoComplete('/directories');
},
_save: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.downloadClientCollection.add(self.model, { merge: true });
vent.trigger(vent.Commands.CloseModalCommand);
});
}
},
_saveAndAdd: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.notificationCollection.add(self.model, { merge: true });
require('Settings/DownloadClient/Add/SchemaModal').open(self.downloadClientCollection);
});
}
},
_delete: function () {
var view = new DeleteView({ model: this.model });
AppLayout.modalRegion.show(view);
},
_back: function () {
require('Settings/DownloadClient/Add/SchemaModal').open(this.downloadClientCollection);
},
_test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation'));
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
onShow: function () {
//Hack to deal with modals not overflowing
if (this.ui.path.length > 0) {
this.ui.modalBody.addClass('modal-overflow');
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
this.ui.path.autoComplete('/directories');
},
return view;
_save: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.targetCollection.add(self.model, { merge: true });
vent.trigger(vent.Commands.CloseModalCommand);
});
}
},
_saveAndAdd: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.targetCollection.add(self.model, { merge: true });
require('Settings/DownloadClient/Add/DownloadClientSchemaModal').open(self.targetCollection);
});
}
},
_delete: function () {
var view = new DeleteView({ model: this.model });
AppLayout.modalRegion.show(view);
},
_back: function () {
require('Settings/DownloadClient/Add/DownloadClientSchemaModal').open(this.targetCollection);
},
_test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation'));
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
return view;
});

View File

@@ -1,37 +0,0 @@
'use strict';
define(
[
'marionette',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView'
], function (Marionette, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Settings/DownloadClient/FailedDownloadHandling/FailedDownloadHandlingViewTemplate',
ui: {
failedDownloadHandlingCheckbox: '.x-failed-download-handling',
failedDownloadOptions : '.x-failed-download-options'
},
events: {
'change .x-failed-download-handling': '_setFailedDownloadOptionsVisibility'
},
_setFailedDownloadOptionsVisibility: function () {
var checked = this.ui.failedDownloadHandlingCheckbox.prop('checked');
if (checked) {
this.ui.failedDownloadOptions.slideDown();
}
else {
this.ui.failedDownloadOptions.slideUp();
}
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
return view;
});

View File

@@ -0,0 +1,14 @@
'use strict';
define([
'Settings/ThingyAddCollectionView',
'Settings/ThingyHeaderGroupView',
'Settings/Indexers/Add/IndexerAddItemView'
], function (ThingyAddCollectionView, ThingyHeaderGroupView, AddItemView) {
return ThingyAddCollectionView.extend({
itemView : ThingyHeaderGroupView.extend({ itemView: AddItemView }),
itemViewContainer: '.add-indexer .items',
template : 'Settings/Indexers/Add/IndexerAddCollectionViewTemplate'
});
});

View File

@@ -0,0 +1,16 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Add Indexer</h3>
</div>
<div class="modal-body">
<div class="add-indexer add-thingies">
<ul class="items"></ul>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">close</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,37 @@
'use strict';
define([
'jquery',
'AppLayout',
'marionette',
'Settings/Indexers/Edit/IndexerEditView'
], function ($, AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/Indexers/Add/IndexerAddItemViewTemplate',
tagName : 'li',
events: {
'click': '_add'
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
_add: function (e) {
if (this.$(e.target).hasClass('icon-info-sign')) {
return;
}
this.model.set({
id : undefined,
name : this.model.get('implementation'),
enable : true
});
var editView = new EditView({ model: this.model, targetCollection: this.targetCollection });
AppLayout.modalRegion.show(editView);
}
});
});

View File

@@ -0,0 +1,36 @@
'use strict';
define([
'underscore',
'AppLayout',
'backbone',
'Settings/Indexers/IndexerCollection',
'Settings/Indexers/Add/IndexerAddCollectionView'
], function (_, AppLayout, Backbone, SchemaCollection, AddCollectionView) {
return ({
open: function (collection) {
var schemaCollection = new SchemaCollection();
var originalUrl = schemaCollection.url;
schemaCollection.url = schemaCollection.url + '/schema';
schemaCollection.fetch();
schemaCollection.url = originalUrl;
var groupedSchemaCollection = new Backbone.Collection();
schemaCollection.on('sync', function() {
var groups = schemaCollection.groupBy(function(model, iterator) { return model.get('protocol'); });
var modelCollection = _.map(groups, function(values, key, list) {
return { 'header': key, collection: values };
});
groupedSchemaCollection.reset(modelCollection);
});
var view = new AddCollectionView({ collection: groupedSchemaCollection, targetCollection: collection });
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -1,11 +0,0 @@
'use strict';
define(
[
'backbone',
'Settings/Indexers/Model',
], function (Backbone, IndexerModel) {
return Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/indexer',
model: IndexerModel
});
});

View File

@@ -1,52 +0,0 @@
'use strict';
define(
[
'AppLayout',
'marionette',
'Settings/Indexers/ItemView',
'Settings/Indexers/EditView',
'Settings/Indexers/Collection',
'underscore'
], function (AppLayout, Marionette, IndexerItemView, IndexerEditView, IndexerCollection, _) {
return Marionette.CompositeView.extend({
itemView : IndexerItemView,
itemViewContainer: '#x-indexers',
template : 'Settings/Indexers/CollectionTemplate',
ui: {
'addCard': '.x-add-card'
},
events: {
'click .x-add-card': '_openSchemaModal'
},
appendHtml: function (collectionView, itemView, index) {
collectionView.ui.addCard.parent('li').before(itemView.el);
},
_openSchemaModal: function () {
var self = this;
var schemaCollection = new IndexerCollection();
var originalUrl = schemaCollection.url;
schemaCollection.url = schemaCollection.url + '/schema';
schemaCollection.fetch({
success: function (collection) {
collection.url = originalUrl;
var model = _.first(collection.models);
model.set({
id : undefined,
name : '',
enable: true
});
var view = new IndexerEditView({ model: model, indexerCollection: self.collection});
AppLayout.modalRegion.show(view);
}
});
}
});
});

View File

@@ -0,0 +1,23 @@
'use strict';
define([
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/Indexers/Delete/IndexerDeleteViewTemplate',
events: {
'click .x-confirm-delete': '_delete'
},
_delete: function () {
this.model.destroy({
wait : true,
success: function () {
vent.trigger(vent.Commands.CloseModalCommand);
}
});
}
});
});

View File

@@ -1,23 +0,0 @@
'use strict';
define(
[
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/Indexers/DeleteViewTemplate',
events: {
'click .x-confirm-delete': '_removeNotification'
},
_removeNotification: function () {
this.model.destroy({
wait : true,
success: function () {
vent.trigger(vent.Commands.CloseModalCommand);
}
});
}
});
});

View File

@@ -0,0 +1,86 @@
'use strict';
define([
'vent',
'AppLayout',
'marionette',
'Settings/Indexers/Delete/IndexerDeleteView',
'Commands/CommandController',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView',
'underscore',
'Form/FormBuilder',
'Mixins/AutoComplete',
'bootstrap'
], function (vent, AppLayout, Marionette, DeleteView, CommandController, AsModelBoundView, AsValidatedView, _) {
var view = Marionette.ItemView.extend({
template: 'Settings/Indexers/Edit/IndexerEditViewTemplate',
events: {
'click .x-save' : '_save',
'click .x-save-and-add': '_saveAndAdd',
'click .x-delete' : '_delete',
'click .x-back' : '_back',
'click .x-test' : '_test'
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
_save: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.targetCollection.add(self.model, { merge: true });
vent.trigger(vent.Commands.CloseModalCommand);
});
}
},
_saveAndAdd: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.targetCollection.add(self.model, { merge: true });
require('Settings/Indexers/Add/IndexerSchemaModal').open(self.targetCollection);
});
}
},
_delete: function () {
var view = new DeleteView({ model: this.model });
AppLayout.modalRegion.show(view);
},
_back: function () {
if (this.model.isNew()) {
this.model.destroy();
}
require('Settings/Indexers/Add/IndexerSchemaModal').open(this.targetCollection);
},
_test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation'));
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
return view;
});

View File

@@ -1,14 +1,14 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close x-cancel"aria-hidden="true">&times;</button>
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
{{#if id}}
<h3>Edit</h3>
<h3>Edit - {{implementation}}</h3>
{{else}}
<h3>Add Newznab</h3>
<h3>Add - {{implementation}}</h3>
{{/if}}
</div>
<div class="modal-body">
<div class="modal-body indexer-modal">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
@@ -41,12 +41,14 @@
</div>
<div class="modal-footer">
{{#if id}}
<button class="btn btn-danger pull-left x-remove">delete</button>
<button class="btn btn-danger pull-left x-delete">delete</button>
{{else}}
<button class="btn pull-left x-back">back</button>
{{/if}}
<span class="x-activity"></span>
<button class="btn x-cancel">cancel</button>
<!-- Testing is currently not yet supported for indexers, but leaving the infrastructure for later -->
<!-- <button class="btn x-test">test <i class="x-test-icon icon-nd-test"/></button> -->
<button class="btn" data-dismiss="modal">cancel</button>
<div class="btn-group">
<button class="btn btn-primary x-save">save</button>

View File

@@ -1,86 +0,0 @@
'use strict';
define(
[
'vent',
'marionette',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView',
'underscore'
], function (vent, Marionette, AsModelBoundView, AsValidatedView, _) {
var view = Marionette.ItemView.extend({
template: 'Settings/Indexers/EditTemplate',
ui: {
activity: '.x-activity'
},
events: {
'click .x-save' : '_save',
'click .x-save-and-add': '_saveAndAdd',
'click .x-cancel' : '_cancel'
},
initialize: function (options) {
this.indexerCollection = options.indexerCollection;
},
_save: function () {
this.ui.activity.html('<i class="icon-nd-spinner"></i>');
var self = this;
var promise = this.model.saveSettings();
if (promise) {
promise.done(function () {
self.indexerCollection.add(self.model, { merge: true });
vent.trigger(vent.Commands.CloseModalCommand);
});
promise.fail(function () {
self.ui.activity.empty();
});
}
},
_saveAndAdd: function () {
this.ui.activity.html('<i class="icon-nd-spinner"></i>');
var self = this;
var promise = this.model.saveSettings();
if (promise) {
promise.done(function () {
self.indexerCollection.add(self.model, { merge: true });
self.model.set({
id : undefined,
name : '',
enable: false
});
_.each(self.model.get('fields'), function (value, key, list) {
self.model.set('fields.' + key + '.value', '');
});
});
promise.fail(function () {
self.ui.activity.empty();
});
}
},
_cancel: function () {
if (this.model.isNew()) {
this.model.destroy();
vent.trigger(vent.Commands.CloseModalCommand);
}
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
return view;
});

View File

@@ -0,0 +1,31 @@
'use strict';
define([
'backbone',
'Settings/Indexers/IndexerModel'
], function (Backbone, IndexerModel) {
return Backbone.Collection.extend({
model: IndexerModel,
url : window.NzbDrone.ApiRoot + '/indexer',
comparator : function(left, right, collection) {
var result = 0;
if (left.get('protocol')) {
result = -left.get('protocol').localeCompare(right.get('protocol'));
}
if (result === 0 && left.get('name')) {
result = left.get('name').localeCompare(right.get('name'));
}
if (result === 0) {
result = left.get('implementation').localeCompare(right.get('implementation'));
}
return result;
}
});
});

View File

@@ -0,0 +1,29 @@
'use strict';
define([
'marionette',
'Settings/Indexers/IndexerItemView',
'Settings/Indexers/Add/IndexerSchemaModal'
], function (Marionette, ItemView, SchemaModal) {
return Marionette.CompositeView.extend({
itemView : ItemView,
itemViewContainer: '.indexer-list',
template : 'Settings/Indexers/IndexerCollectionViewTemplate',
ui: {
'addCard': '.x-add-card'
},
events: {
'click .x-add-card': '_openSchemaModal'
},
appendHtml: function (collectionView, itemView, index) {
collectionView.ui.addCard.parent('li').before(itemView.el);
},
_openSchemaModal: function () {
SchemaModal.open(this.collection);
}
});
});

View File

@@ -2,11 +2,11 @@
<legend>Indexers</legend>
<div class="row">
<div class="col-md-12">
<ul id="x-indexers" class="indexer-list thingies">
<ul class="indexer-list thingies">
<li>
<div class="indexer-settings-item add-card x-add-card">
<div class="indexer-item thingy add-card x-add-card">
<span class="center well">
<i class="icon-plus" title="Add Newznab"/>
<i class="icon-plus" title="Add Indexer"/>
</span>
</div>
</li>

View File

@@ -0,0 +1,26 @@
'use strict';
define([
'AppLayout',
'marionette',
'Settings/Indexers/Edit/IndexerEditView'
], function (AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/Indexers/IndexerItemViewTemplate',
tagName : 'li',
events: {
'click' : '_edit'
},
initialize: function () {
this.listenTo(this.model, 'sync', this.render);
},
_edit: function () {
var view = new EditView({ model: this.model, targetCollection: this.model.collection });
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -0,0 +1,13 @@
<div class="indexer-item thingy" title="Click to edit">
<div>
<h3>{{name}}</h3>
</div>
<div class="settings">
{{#if enable}}
<span class="label label-success">Enabled</span>
{{else}}
<span class="label label-default">Not Enabled</span>
{{/if}}
</div>
</div>

View File

@@ -1,28 +1,28 @@
'use strict';
define(
[
'marionette',
'Settings/Indexers/CollectionView',
'Settings/Indexers/Options/IndexerOptionsView'
], function (Marionette, CollectionView, OptionsView) {
return Marionette.Layout.extend({
template: 'Settings/Indexers/IndexerLayoutTemplate',
define([
'marionette',
'Settings/Indexers/IndexerCollection',
'Settings/Indexers/IndexerCollectionView',
'Settings/Indexers/Options/IndexerOptionsView'
], function (Marionette, IndexerCollection, CollectionView, OptionsView) {
regions: {
indexersRegion : '#indexers-collection',
indexerOptions : '#indexer-options'
},
return Marionette.Layout.extend({
template: 'Settings/Indexers/IndexerLayoutTemplate',
initialize: function (options) {
this.settings = options.settings;
this.indexersCollection = options.indexersCollection;
},
regions: {
indexers : '#x-indexers-region',
indexerOptions : '#x-indexer-options-region'
},
onShow: function () {
this.indexersRegion.show(new CollectionView({ collection: this.indexersCollection }));
this.indexerOptions.show(new OptionsView({ model: this.settings }));
}
});
initialize: function (options) {
this.indexersCollection = new IndexerCollection();
this.indexersCollection.fetch();
},
onShow: function () {
this.indexers.show(new CollectionView({ collection: this.indexersCollection }));
this.indexerOptions.show(new OptionsView({ model: this.model }));
}
});
});

View File

@@ -1,5 +1,4 @@
<div id="indexers-collection"></div>
<div id="x-indexers-region"></div>
<div class="form-horizontal">
<div id="indexer-options"></div>
<div id="x-indexer-options-region"></div>
</div>

View File

@@ -0,0 +1,9 @@
'use strict';
define([
'backbone.deepmodel'
], function (DeepModel) {
return DeepModel.DeepModel.extend({
});
});

View File

@@ -1,11 +1,11 @@
'use strict';
define(
[
'Settings/SettingsModelBase'
], function (SettingsModelBase) {
return SettingsModelBase.extend({
url : window.NzbDrone.ApiRoot + '/config/indexer',
successMessage: 'Indexer settings saved',
errorMessage : 'Failed to save indexer settings'
});
define([
'Settings/SettingsModelBase'
], function (SettingsModelBase) {
return SettingsModelBase.extend({
url : window.NzbDrone.ApiRoot + '/config/indexer',
successMessage: 'Indexer settings saved',
errorMessage : 'Failed to save indexer settings'
});
});

View File

@@ -1,37 +0,0 @@
<div class="indexer-settings-item thingy">
<div>
<h3>{{name}}</h3>
{{#if_eq implementation compare="Newznab"}}
<span class="btn-group pull-right">
<button class="btn btn-xs btn-icon-only x-delete">
<i class="icon-nd-delete"/>
</button>
</span>
{{/if_eq}}
</div>
<div class="form-group">
<label class="control-label">Enable</label>
<div class="input-group">
<label class="checkbox toggle well">
<input type="checkbox" name="enable"/>
<p>
<span>Yes</span>
<span>No</span>
</p>
<div class="btn btn-primary slide-button"/>
</label>
</div>
</div>
{{formBuilder}}
{{#if_eq name compare="WomblesIndex"}}
<div class="alert">
<i class="icon-nd-warning"></i>
Does not support searching
</div>
{{/if_eq}}
</div>

View File

@@ -1,29 +0,0 @@
'use strict';
define(
[
'AppLayout',
'marionette',
'Settings/Indexers/DeleteView',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView'
], function (AppLayout, Marionette, DeleteView, AsModelBoundView, AsValidatedView) {
var view = Marionette.ItemView.extend({
template: 'Settings/Indexers/ItemTemplate',
tagName : 'li',
events: {
'click .x-delete': '_deleteIndexer'
},
_deleteIndexer: function () {
var view = new DeleteView({ model: this.model});
AppLayout.modalRegion.show(view);
}
});
AsModelBoundView.call(view);
return AsValidatedView.call(view);
});

View File

@@ -1,24 +0,0 @@
'use strict';
define([
'Settings/SettingsModelBase'], function (ModelBase) {
return ModelBase.extend({
baseInitialize: ModelBase.prototype.initialize,
initialize: function () {
var name = this.get('name');
if (name) {
this.successMessage = 'Saved indexer: ' + name;
this.errorMessage = 'Couldn\'t save indexer: ' + name;
}
else {
this.successMessage = 'Saved indexer';
this.errorMessage = 'Couldn\'t save indexer';
}
this.baseInitialize.call(this);
}
});
});

View File

@@ -1,29 +1,33 @@
.indexer-settings-item {
@import "../../Shared/Styles/clickable.less";
width: 220px;
height: 295px;
.indexer-list {
li {
display: inline-block;
vertical-align: top;
}
}
.indexer-item {
.clickable;
width: 290px;
height: 90px;
padding: 10px 15px;
h3 {
width: 175px;
overflow: visible;
}
&.add-card {
margin-top: 10px;
margin-left: 10px;
.center {
margin-top: 90px;
margin-top: -3px;
}
}
}
/* Super hack to keep using form builder, this should be dead when we do proper modals for editing */
.col-sm-1, .col-sm-3, .col-sm-5 {
display : block;
width : 100%;
padding: 0px;
float: none;
position: inherit;
.modal-overflow {
overflow-y: visible;
}
.add-indexer {
li {
width: 33%;
}
}

View File

@@ -0,0 +1,13 @@
'use strict';
define([
'Settings/ThingyAddCollectionView',
'Settings/Notifications/Add/NotificationAddItemView'
], function (ThingyAddCollectionView, AddItemView) {
return ThingyAddCollectionView.extend({
itemView : AddItemView,
itemViewContainer: '.add-notifications .items',
template : 'Settings/Notifications/Add/NotificationAddCollectionViewTemplate'
});
});

View File

@@ -1,37 +1,38 @@
'use strict';
define([
'jquery',
'AppLayout',
'marionette',
'Settings/Notifications/NotificationEditView'
], function (AppLayout, Marionette, EditView) {
'Settings/Notifications/Edit/NotificationEditView'
], function ($, AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
template: 'Settings/Notifications/AddItemTemplate',
template: 'Settings/Notifications/Add/NotificationAddItemViewTemplate',
tagName : 'li',
events: {
'click': 'addNotification'
'click': '_add'
},
initialize: function (options) {
this.notificationCollection = options.notificationCollection;
this.targetCollection = options.targetCollection;
},
addNotification: function (e) {
_add: function (e) {
if (this.$(e.target).hasClass('icon-info-sign')) {
return;
}
this.model.set({
id : undefined,
name : this.model.get('implementationName'),
name : this.model.get('implementation'),
onGrab : true,
onDownload : true,
onUpgrade : true
});
var editView = new EditView({ model: this.model, notificationCollection: this.notificationCollection });
var editView = new EditView({ model: this.model, targetCollection: this.targetCollection });
AppLayout.modalRegion.show(editView);
}
});

View File

@@ -0,0 +1,6 @@
<div class="add-thingy">
{{implementation}}
{{#if link}}
<a href="{{link}}"><i class="icon-info-sign"/></a>
{{/if}}
</div>

View File

@@ -0,0 +1,21 @@
'use strict';
define([
'AppLayout',
'Settings/Notifications/NotificationCollection',
'Settings/Notifications/Add/NotificationAddCollectionView'
], function (AppLayout, SchemaCollection, AddCollectionView) {
return ({
open: function (collection) {
var schemaCollection = new SchemaCollection();
var originalUrl = schemaCollection.url;
schemaCollection.url = schemaCollection.url + '/schema';
schemaCollection.fetch();
schemaCollection.url = originalUrl;
var view = new AddCollectionView({ collection: schemaCollection, targetCollection: collection});
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -1,23 +0,0 @@
'use strict';
define([
'marionette',
'Settings/Notifications/AddItemView'
], function (Marionette, AddItemView) {
return Marionette.CompositeView.extend({
itemView : AddItemView,
itemViewContainer: '.add-notifications .items',
template : 'Settings/Notifications/AddTemplate',
itemViewOptions: function () {
return {
notificationCollection: this.notificationCollection
};
},
initialize: function (options) {
this.notificationCollection = options.notificationCollection;
}
});
});

View File

@@ -1,11 +0,0 @@
'use strict';
define(
[
'backbone',
'Settings/Notifications/Model'
], function (Backbone, NotificationModel) {
return Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/notification',
model: NotificationModel
});
});

View File

@@ -1,13 +0,0 @@
<div class="row">
<div class="col-md-12">
<ul class="notifications thingies">
<li>
<div class="notification-item thingy add-card x-add-card">
<span class="center well">
<i class="icon-plus" title="Add Connection"/>
</span>
</div>
</li>
</ul>
</div>
</div>

View File

@@ -1,28 +0,0 @@
'use strict';
define([
'marionette',
'Settings/Notifications/NotificationsItemView',
'Settings/Notifications/SchemaModal'
], function (Marionette, NotificationItemView, SchemaModal) {
return Marionette.CompositeView.extend({
itemView : NotificationItemView,
itemViewContainer: '.notifications',
template : 'Settings/Notifications/CollectionTemplate',
ui: {
'addCard': '.x-add-card'
},
events: {
'click .x-add-card': '_openSchemaModal'
},
appendHtml: function(collectionView, itemView, index){
collectionView.ui.addCard.parent('li').before(itemView.el);
},
_openSchemaModal: function () {
SchemaModal.open(this.collection);
}
});
});

View File

@@ -0,0 +1,23 @@
'use strict';
define([
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/Notifications/Delete/NotificationDeleteViewTemplate',
events: {
'click .x-confirm-delete': '_delete'
},
_delete: function () {
this.model.destroy({
wait : true,
success: function () {
vent.trigger(vent.Commands.CloseModalCommand);
}
});
}
});
});

View File

@@ -1,23 +0,0 @@
'use strict';
define(
[
'vent',
'marionette'
], function (vent, Marionette) {
return Marionette.ItemView.extend({
template: 'Settings/Notifications/DeleteTemplate',
events: {
'click .x-confirm-delete': '_removeNotification'
},
_removeNotification: function () {
this.model.destroy({
wait : true,
success: function () {
vent.trigger(vent.Commands.CloseModalCommand);
}
});
}
});
});

View File

@@ -0,0 +1,113 @@
'use strict';
define([
'vent',
'AppLayout',
'marionette',
'Settings/Notifications/Delete/NotificationDeleteView',
'Commands/CommandController',
'Mixins/AsModelBoundView',
'Mixins/AsValidatedView',
'underscore',
'Form/FormBuilder'
], function (vent, AppLayout, Marionette, DeleteView, CommandController, AsModelBoundView, AsValidatedView, _) {
var view = Marionette.ItemView.extend({
template: 'Settings/Notifications/Edit/NotificationEditViewTemplate',
ui: {
onDownloadToggle: '.x-on-download',
onUpgradeSection: '.x-on-upgrade'
},
events: {
'click .x-save' : '_save',
'click .x-save-and-add': '_saveAndAdd',
'click .x-delete' : '_delete',
'click .x-back' : '_back',
'click .x-cancel' : '_cancel',
'click .x-test' : '_test',
'change .x-on-download': '_onDownloadChanged'
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
},
onRender: function () {
this._onDownloadChanged();
},
_save: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.targetCollection.add(self.model, { merge: true });
vent.trigger(vent.Commands.CloseModalCommand);
});
}
},
_saveAndAdd: function () {
var self = this;
var promise = this.model.save();
if (promise) {
promise.done(function () {
self.targetCollection.add(self.model, { merge: true });
require('Settings/Notifications/Add/NotificationSchemaModal').open(self.targetCollection);
});
}
},
_delete: function () {
var view = new DeleteView({ model: this.model });
AppLayout.modalRegion.show(view);
},
_back: function () {
if (this.model.isNew()) {
this.model.destroy();
}
require('Settings/Notifications/Add/NotificationSchemaModal').open(this.targetCollection);
},
_cancel: function () {
if (this.model.isNew()) {
this.model.destroy();
}
},
_test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation'));
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
},
_onDownloadChanged: function () {
var checked = this.ui.onDownloadToggle.prop('checked');
if (checked) {
this.ui.onUpgradeSection.show();
}
else {
this.ui.onUpgradeSection.hide();
}
}
});
AsModelBoundView.call(view);
AsValidatedView.call(view);
return view;
});

View File

@@ -1,14 +1,14 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close x-cancel" aria-hidden="true">&times;</button>
<button type="button" class="close x-cancel" data-dismiss="modal" aria-hidden="true">&times;</button>
{{#if id}}
<h3>Edit - {{implementation}}</h3>
{{else}}
<h3>Add - {{implementation}}</h3>
{{/if}}
</div>
<div class="modal-body">
<div class="modal-body notification-modal">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label">Name</label>
@@ -95,7 +95,7 @@
{{/if}}
<button class="btn x-test">test <i class="x-test-icon icon-nd-test"/></button>
<button class="btn x-cancel">cancel</button>
<button class="btn x-cancel" data-dismiss="modal">cancel</button>
<div class="btn-group">
<button class="btn btn-primary x-save">save</button>

View File

@@ -0,0 +1,13 @@
'use strict';
define([
'backbone',
'Settings/Notifications/NotificationModel'
], function (Backbone, NotificationModel) {
return Backbone.Collection.extend({
model: NotificationModel,
url : window.NzbDrone.ApiRoot + '/notification'
});
});

View File

@@ -0,0 +1,29 @@
'use strict';
define([
'marionette',
'Settings/Notifications/NotificationItemView',
'Settings/Notifications/Add/NotificationSchemaModal'
], function (Marionette, ItemView, SchemaModal) {
return Marionette.CompositeView.extend({
itemView : ItemView,
itemViewContainer: '.notification-list',
template : 'Settings/Notifications/NotificationCollectionViewTemplate',
ui: {
'addCard': '.x-add-card'
},
events: {
'click .x-add-card': '_openSchemaModal'
},
appendHtml: function (collectionView, itemView, index) {
collectionView.ui.addCard.parent('li').before(itemView.el);
},
_openSchemaModal: function () {
SchemaModal.open(this.collection);
}
});
});

View File

@@ -0,0 +1,16 @@
<fieldset>
<legend>Connections</legend>
<div class="row">
<div class="col-md-12">
<ul class="notification-list thingies">
<li>
<div class="notification-item thingy add-card x-add-card">
<span class="center well">
<i class="icon-plus" title="Add Connection"/>
</span>
</div>
</li>
</ul>
</div>
</div>
</fieldset>

View File

@@ -1,113 +0,0 @@
'use strict';
define(
[
'vent',
'AppLayout',
'marionette',
'Settings/Notifications/DeleteView',
'Commands/CommandController',
'Mixins/AsModelBoundView',
'underscore',
'Form/FormBuilder'
], function (vent, AppLayout, Marionette, DeleteView, CommandController, AsModelBoundView, _) {
var model = Marionette.ItemView.extend({
template: 'Settings/Notifications/NotificationEditViewTemplate',
ui: {
onDownloadToggle: '.x-on-download',
onUpgradeSection: '.x-on-upgrade'
},
events: {
'click .x-save' : '_saveClient',
'click .x-save-and-add': '_saveAndAddNotification',
'click .x-delete' : '_deleteNotification',
'click .x-back' : '_back',
'click .x-test' : '_test',
'click .x-cancel' : '_cancel',
'change .x-on-download': '_onDownloadChanged'
},
initialize: function (options) {
this.notificationCollection = options.notificationCollection;
},
onRender: function () {
this._onDownloadChanged();
},
_saveClient: function () {
var self = this;
var promise = this.model.saveSettings();
if (promise) {
promise.done(function () {
self.notificationCollection.add(self.model, { merge: true });
vent.trigger(vent.Commands.CloseModalCommand);
});
}
},
_saveAndAddNotification: function () {
var self = this;
var promise = this.model.saveSettings();
if (promise) {
promise.done(function () {
self.notificationCollection.add(self.model, { merge: true });
require('Settings/Notifications/SchemaModal').open(self.notificationCollection);
});
}
},
_cancel: function () {
if (this.model.isNew()) {
this.model.destroy();
}
vent.trigger(vent.Commands.CloseModalCommand);
},
_deleteNotification: function () {
var view = new DeleteView({ model: this.model });
AppLayout.modalRegion.show(view);
},
_back: function () {
if (this.model.isNew()) {
this.model.destroy();
}
require('Settings/Notifications/SchemaModal').open(this.notificationCollection);
},
_test: function () {
var testCommand = 'test{0}'.format(this.model.get('implementation'));
var properties = {};
_.each(this.model.get('fields'), function (field) {
properties[field.name] = field.value;
});
CommandController.Execute(testCommand, properties);
},
_onDownloadChanged: function () {
var checked = this.ui.onDownloadToggle.prop('checked');
if (checked) {
this.ui.onUpgradeSection.show();
}
else {
this.ui.onUpgradeSection.hide();
}
}
});
return AsModelBoundView.call(model);
});

View File

@@ -3,8 +3,7 @@
define([
'AppLayout',
'marionette',
'Settings/Notifications/NotificationEditView'
'Settings/Notifications/Edit/NotificationEditView'
], function (AppLayout, Marionette, EditView) {
return Marionette.ItemView.extend({
@@ -12,15 +11,15 @@ define([
tagName : 'li',
events: {
'click' : '_editNotification'
'click' : '_edit'
},
initialize: function () {
this.listenTo(this.model, 'sync', this.render);
},
_editNotification: function () {
var view = new EditView({ model: this.model, notificationCollection: this.model.collection});
_edit: function () {
var view = new EditView({ model: this.model, targetCollection: this.model.collection});
AppLayout.modalRegion.show(view);
}
});

View File

@@ -1,6 +1,7 @@
'use strict';
define([
'Settings/SettingsModelBase'], function (ModelBase) {
'Settings/SettingsModelBase'
], function (ModelBase) {
return ModelBase.extend({
successMessage: 'Notification Saved',

View File

@@ -1,20 +0,0 @@
'use strict';
define([
'AppLayout',
'Settings/Notifications/Collection',
'Settings/Notifications/AddView'
], function (AppLayout, NotificationCollection, AddSelectionNotificationView) {
return ({
open: function (collection) {
var schemaCollection = new NotificationCollection();
var orginalUrl = schemaCollection.url;
schemaCollection.url = schemaCollection.url + '/schema';
schemaCollection.fetch();
schemaCollection.url = orginalUrl;
var view = new AddSelectionNotificationView({ collection: schemaCollection, notificationCollection: collection});
AppLayout.modalRegion.show(view);
}
});
});

View File

@@ -12,12 +12,12 @@ define(
'Settings/MediaManagement/MediaManagementSettingsModel',
'Settings/Quality/QualityLayout',
'Settings/Indexers/IndexerLayout',
'Settings/Indexers/Collection',
'Settings/Indexers/IndexerCollection',
'Settings/Indexers/IndexerSettingsModel',
'Settings/DownloadClient/DownloadClientLayout',
'Settings/DownloadClient/DownloadClientSettingsModel',
'Settings/Notifications/CollectionView',
'Settings/Notifications/Collection',
'Settings/Notifications/NotificationCollectionView',
'Settings/Notifications/NotificationCollection',
'Settings/Metadata/MetadataLayout',
'Settings/General/GeneralView',
'Shared/LoadingView',
@@ -93,7 +93,6 @@ define(
this.mediaManagementSettings = new MediaManagementSettingsModel();
this.namingSettings = new NamingModel();
this.indexerSettings = new IndexerSettingsModel();
this.indexerCollection = new IndexerCollection();
this.downloadClientSettings = new DownloadClientSettingsModel();
this.notificationCollection = new NotificationCollection();
this.generalSettings = new GeneralSettingsModel();
@@ -102,7 +101,6 @@ define(
this.mediaManagementSettings.fetch(),
this.namingSettings.fetch(),
this.indexerSettings.fetch(),
this.indexerCollection.fetch(),
this.downloadClientSettings.fetch(),
this.notificationCollection.fetch(),
this.generalSettings.fetch()
@@ -112,7 +110,7 @@ define(
self.loading.$el.hide();
self.mediaManagement.show(new MediaManagementLayout({ settings: self.mediaManagementSettings, namingSettings: self.namingSettings }));
self.quality.show(new QualityLayout());
self.indexers.show(new IndexerLayout({ settings: self.indexerSettings, indexersCollection: self.indexerCollection }));
self.indexers.show(new IndexerLayout({ model: self.indexerSettings }));
self.downloadClient.show(new DownloadClientLayout({ model: self.downloadClientSettings }));
self.notifications.show(new NotificationCollectionView({ collection: self.notificationCollection }));
self.metadata.show(new MetadataLayout());

View File

@@ -0,0 +1,18 @@
'use strict';
define([
'marionette'
], function (Marionette) {
return Marionette.CompositeView.extend({
itemViewOptions : function () {
return {
targetCollection: this.targetCollection || this.options.targetCollection
};
},
initialize: function (options) {
this.targetCollection = options.targetCollection;
}
});
});

View File

@@ -0,0 +1,23 @@
'use strict';
define([
'backbone',
'marionette'
], function (Backbone, Marionette) {
return Marionette.CompositeView.extend({
itemViewContainer: '.item-list',
template: 'Settings/ThingyHeaderGroupViewTemplate',
tagName : 'div',
itemViewOptions: function () {
return {
targetCollection: this.targetCollection || this.options.targetCollection
};
},
initialize: function () {
this.collection = new Backbone.Collection(this.model.get('collection'));
}
});
});

View File

@@ -0,0 +1,2 @@
<legend>{{header}}</legend>
<ul class="item-list" />

View File

@@ -25,6 +25,11 @@
.add-thingies {
text-align: center;
legend {
text-align: left;
text-transform: capitalize;
}
.items {
list-style-type: none;
margin: 0px;