mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-26 22:46:37 -04:00
Moved source code under src folder - massive change
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AutoComplete',
|
||||
'bootstrap'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/DownloadClient/BlackholeViewTemplate',
|
||||
|
||||
ui: {
|
||||
'blackholeFolder': '.x-path'
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.ui.blackholeFolder.autoComplete('/directories');
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
<fieldset>
|
||||
<legend>Blackhole</legend>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Blackhole Folder</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="blackholeFolder" class="x-path"/>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="The folder where your download client will pickup .nzb files"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,78 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Settings/DownloadClient/SabView',
|
||||
'Settings/DownloadClient/BlackholeView',
|
||||
'Settings/DownloadClient/PneumaticView',
|
||||
'Settings/DownloadClient/NzbgetView',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AutoComplete',
|
||||
'bootstrap'
|
||||
], function (Marionette, SabView, BlackholeView, PneumaticView, NzbgetView, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.Layout.extend({
|
||||
template : 'Settings/DownloadClient/LayoutTemplate',
|
||||
|
||||
regions: {
|
||||
downloadClient: '#download-client-settings-region'
|
||||
},
|
||||
|
||||
ui: {
|
||||
downloadClientSelect: '.x-download-client',
|
||||
downloadedEpisodesFolder: '.x-path'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change .x-download-client': 'downloadClientChanged'
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.sabView = new SabView({ model: this.model});
|
||||
this.blackholeView = new BlackholeView({ model: this.model});
|
||||
this.pneumaticView = new PneumaticView({ model: this.model});
|
||||
this.nzbgetView = new NzbgetView({ model: this.model});
|
||||
|
||||
this.ui.downloadedEpisodesFolder.autoComplete('/directories');
|
||||
|
||||
var client = this.model.get('downloadClient');
|
||||
this.refreshUIVisibility(client);
|
||||
},
|
||||
|
||||
downloadClientChanged: function () {
|
||||
var clientId = this.ui.downloadClientSelect.val();
|
||||
this.refreshUIVisibility(clientId);
|
||||
},
|
||||
|
||||
refreshUIVisibility: function (clientId) {
|
||||
|
||||
if (!clientId) {
|
||||
clientId = 'sabnzbd';
|
||||
}
|
||||
|
||||
switch (clientId.toString()) {
|
||||
case 'sabnzbd':
|
||||
this.downloadClient.show(this.sabView);
|
||||
break;
|
||||
|
||||
case 'blackhole':
|
||||
this.downloadClient.show(this.blackholeView);
|
||||
break;
|
||||
|
||||
case 'pneumatic':
|
||||
this.downloadClient.show(this.pneumaticView);
|
||||
break;
|
||||
|
||||
case 'nzbget':
|
||||
this.downloadClient.show(this.nzbgetView);
|
||||
break;
|
||||
|
||||
default :
|
||||
throw 'unknown download client id' + clientId;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
<fieldset class="form-horizontal">
|
||||
<legend>General</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Download Client</label>
|
||||
|
||||
<div class="controls">
|
||||
<select class="inputClass x-download-client" name="downloadClient">
|
||||
<option value="sabnzbd">SABnzbd</option>
|
||||
<option value="blackhole">Blackhole</option>
|
||||
<option value="pneumatic">Pneumatic</option>
|
||||
<option value="nzbget">NZBGet</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Drone Factory</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="downloadedEpisodesFolder" class="x-path"/>
|
||||
<span class="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-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>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div id="download-client-settings-region" class="form-horizontal"></div>
|
||||
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'bootstrap'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/DownloadClient/NzbgetViewTemplate'
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
<fieldset>
|
||||
<legend>NZBGet</legend>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Host</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="nzbgetHost"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Port</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="nzbgetPort"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">API Key</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="nzbgetApiKey"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Username</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="nzbgetUsername"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Password</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="password" name="nzbgetPassword"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">TV Category</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="nzbgetTvCategory"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Download Priority</label>
|
||||
|
||||
<div class="controls">
|
||||
<select name="nzbgetRecentTvPriority">
|
||||
<option value="default">Default</option>
|
||||
<option value="pasued">Paused</option>
|
||||
<option value="low">Low</option>
|
||||
<option value="normal">Normal</option>
|
||||
<option value="high">High</option>
|
||||
<option value="force">Force</option>
|
||||
</select>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="Priority to use when sending episodes that aired within the last 14 days"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Older Download Priority</label>
|
||||
|
||||
<div class="controls">
|
||||
<select name="nzbgetOlderTvPriority">
|
||||
<option value="default">Default</option>
|
||||
<option value="pasued">Paused</option>
|
||||
<option value="low">Low</option>
|
||||
<option value="normal">Normal</option>
|
||||
<option value="high">High</option>
|
||||
<option value="force">Force</option>
|
||||
</select>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="Priority to use when sending episodes that aired over 14 days ago"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AutoComplete',
|
||||
'bootstrap'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/DownloadClient/PneumaticViewTemplate',
|
||||
|
||||
ui: {
|
||||
'pneumaticFolder': '.x-path'
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.ui.pneumaticFolder.autoComplete('/directories');
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
<fieldset>
|
||||
<legend>Pneumatic</legend>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Nzb Folder</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="pneumaticFolder" class="x-path"/>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="Folder to save NZBs for Pneumatic<br/>must be accessible from XBMC"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'bootstrap'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/DownloadClient/SabViewTemplate'
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
<fieldset>
|
||||
<legend>SABnzbd</legend>
|
||||
|
||||
{{!<div class="control-group">
|
||||
<label class="control-label">Auto-Configure</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="button" value="Auto-Configure" class="btn btn-inverse"/>
|
||||
<span class="help-inline">
|
||||
<i class="icon-question-sign"
|
||||
title="(Windows only) If access to SABnzbd doesn't require a username & password and it is on the same system as NzbDrone, you can auto-configure it"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>}}
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Host</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="sabHost"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Port</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="sabPort"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">API Key</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="sabApiKey"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Username</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="sabUsername"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Password</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="password" name="sabPassword"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">TV Category</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="sabTvCategory" placeholder="This is not the dropdownlist you're looking for"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Download Priority</label>
|
||||
|
||||
<div class="controls">
|
||||
<select name="sabRecentTvPriority">
|
||||
<option value="default">Default</option>
|
||||
<option value="paused">Paused</option>
|
||||
<option value="low">Low</option>
|
||||
<option value="normal">Normal</option>
|
||||
</option> <option value="high">High</option>
|
||||
<option value="force">Force</option>
|
||||
</select>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="Priority to use when sending episodes that aired within the last 14 days"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Older Download Priority</label>
|
||||
|
||||
<div class="controls">
|
||||
<select name="sabOlderTvPriority">
|
||||
<option value="default">Default</option>
|
||||
<option value="paused">Paused</option>
|
||||
<option value="low">Low</option>
|
||||
<option value="normal">Normal</option>
|
||||
<option value="high">High</option>
|
||||
<option value="force">Force</option>
|
||||
</select>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="Priority to use when sending episodes that aired over 14 days ago"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Settings/SettingsModelBase'
|
||||
], function (SettingsModelBase) {
|
||||
return SettingsModelBase.extend({
|
||||
|
||||
url : window.NzbDrone.ApiRoot + '/settings/host',
|
||||
successMessage: 'General settings saved',
|
||||
errorMessage : 'Failed to save general settings'
|
||||
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
<div class="form-horizontal">
|
||||
<fieldset>
|
||||
<legend>Start-Up</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Port Number</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="number" placeholder="8989" name="port"/>
|
||||
<span>
|
||||
<i class="icon-nd-form-warning" title="Requires restart to take effect"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group advanced-setting">
|
||||
<label class="control-label">Enable SSL</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="enableSsl" class="x-ssl"/>
|
||||
|
||||
<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-warning" title="Requires restart running as administrator to take effect"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="x-ssl-options">
|
||||
<div class="control-group advanced-setting">
|
||||
<label class="control-label">SSL Port Number</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="number" placeholder="8989" name="sslPort"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group advanced-setting">
|
||||
<label class="control-label">SSL Cert Hash</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="sslCertHash"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Open browser on start</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="launchBrowser"/>
|
||||
|
||||
<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="Open a web browser and navigate to NzbDrone homepage on app start. Has no effect if installed as a windows service"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Security</legend>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Authentication</label>
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" class='x-auth' name="authenticationEnabled"/>
|
||||
<p>
|
||||
<span>On</span>
|
||||
<span>Off</span>
|
||||
</p>
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-nd-form-info" title="Require Username and Password to access Nzbdrone"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class='x-auth-options'>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Username</label>
|
||||
<div class="controls">
|
||||
<input type="text" placeholder="Username" name="username"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" name="password"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset>
|
||||
<legend>Logging</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Log Level</label>
|
||||
|
||||
<div class="controls">
|
||||
<select name="logLevel">
|
||||
<option value="Trace">Trace</option>
|
||||
<option value="Debug">Debug</option>
|
||||
<option value="Info">Info</option>
|
||||
</select>
|
||||
|
||||
<span>
|
||||
<i class="icon-nd-form-warning" title="Trace and Debug logging should only be enabled temporarily"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="advanced-setting">
|
||||
<legend>Development</legend>
|
||||
<div class="alert">
|
||||
<i class="icon-nd-warning"></i>
|
||||
Don't change anything here unless you know what you are doing.
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Branch</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" placeholder="master" name="branch"/>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/General/GeneralTemplate',
|
||||
|
||||
events: {
|
||||
'change .x-auth': '_setAuthOptionsVisibility',
|
||||
'change .x-ssl': '_setSslOptionsVisibility'
|
||||
},
|
||||
|
||||
ui: {
|
||||
authToggle : '.x-auth',
|
||||
authOptions: '.x-auth-options',
|
||||
sslToggle : '.x-ssl',
|
||||
sslOptions: '.x-ssl-options'
|
||||
},
|
||||
|
||||
onRender: function(){
|
||||
if(!this.ui.authToggle.prop('checked')){
|
||||
this.ui.authOptions.hide();
|
||||
}
|
||||
|
||||
if(!this.ui.sslToggle.prop('checked')){
|
||||
this.ui.sslOptions.hide();
|
||||
}
|
||||
},
|
||||
|
||||
_setAuthOptionsVisibility: function () {
|
||||
|
||||
var showAuthOptions = this.ui.authToggle.prop('checked');
|
||||
|
||||
if (showAuthOptions) {
|
||||
this.ui.authOptions.slideDown();
|
||||
}
|
||||
|
||||
else {
|
||||
this.ui.authOptions.slideUp();
|
||||
}
|
||||
},
|
||||
|
||||
_setSslOptionsVisibility: function () {
|
||||
|
||||
var showSslOptions = this.ui.sslToggle.prop('checked');
|
||||
|
||||
if (showSslOptions) {
|
||||
this.ui.sslOptions.slideDown();
|
||||
}
|
||||
|
||||
else {
|
||||
this.ui.sslOptions.slideUp();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Settings/Indexers/Model',
|
||||
'Form/FormBuilder'
|
||||
], function (IndexerModel) {
|
||||
return Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/indexer',
|
||||
model: IndexerModel
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
<fieldset>
|
||||
<legend>Indexers</legend>
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<ul id="x-indexers" class="indexer-list">
|
||||
<li>
|
||||
<div class="indexer-settings-item add-card x-add-card">
|
||||
<span class="center well">
|
||||
<i class="icon-plus" title="Add Newznab"/>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
define(['app',
|
||||
'marionette',
|
||||
'Settings/Indexers/ItemView',
|
||||
'Settings/Indexers/EditView',
|
||||
'Settings/Indexers/Collection'],
|
||||
function (App, 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;
|
||||
//TODO: Is there a better way to deal with changing URLs?
|
||||
var schemaCollection = new IndexerCollection();
|
||||
schemaCollection.url = '/api/indexer/schema';
|
||||
schemaCollection.fetch({
|
||||
success: function (collection) {
|
||||
collection.url = '/api/indexer';
|
||||
var model = _.first(collection.models);
|
||||
|
||||
model.set({
|
||||
id: undefined,
|
||||
name: '',
|
||||
enable: true
|
||||
});
|
||||
|
||||
var view = new IndexerEditView({ model: model, indexerCollection: self.collection});
|
||||
App.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Delete Indexer</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete '{{name}}'?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-danger x-confirm-delete">delete</button>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
define(['app', 'marionette'], function (App, Marionette) {
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Settings/Notifications/DeleteTemplate',
|
||||
|
||||
events: {
|
||||
'click .x-confirm-delete': '_removeIndexer'
|
||||
},
|
||||
|
||||
_removeIndexer: function () {
|
||||
this.model.destroy({
|
||||
wait : true,
|
||||
success: function () {
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
{{#if id}}
|
||||
<h3>Edit</h3>
|
||||
{{else}}
|
||||
<h3>Add Newznab</h3>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label">Name</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="name"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Enable</label>
|
||||
|
||||
<div class="controls">
|
||||
<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}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{#if id}}
|
||||
<button class="btn btn-danger pull-left x-remove">delete</button>
|
||||
{{/if}}
|
||||
|
||||
<span class="x-activity"></span>
|
||||
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary x-save">save</button>
|
||||
<button class="btn btn-icon-only btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="save-and-add x-save-and-add">
|
||||
save and add
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AsValidatedView'
|
||||
], function (App, 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'
|
||||
},
|
||||
|
||||
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 });
|
||||
App.vent.trigger(App.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();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AsModelBoundView.call(view);
|
||||
AsValidatedView.call(view);
|
||||
|
||||
return view;
|
||||
});
|
||||
@@ -0,0 +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',
|
||||
|
||||
regions: {
|
||||
indexersRegion : '#indexers-collection',
|
||||
indexerOptions : '#indexer-options'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.settings = options.settings;
|
||||
this.indexersCollection = options.indexersCollection;
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.indexersRegion.show(new CollectionView({ collection: this.indexersCollection }));
|
||||
this.indexerOptions.show(new OptionsView({ model: this.settings }));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div id="indexers-collection"></div>
|
||||
|
||||
<div class="form-horizontal">
|
||||
<div id="indexer-options"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
<div class="indexer-settings-item">
|
||||
<div>
|
||||
<h3>{{name}}</h3>
|
||||
{{#if_eq implementation compare="Newznab"}}
|
||||
<span class="btn-group pull-right">
|
||||
<button class="btn btn-mini btn-icon-only x-delete">
|
||||
<i class="icon-nd-delete"/>
|
||||
</button>
|
||||
</span>
|
||||
{{/if_eq}}
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Enable</label>
|
||||
|
||||
<div class="controls">
|
||||
<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>
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/Notifications/DeleteView',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AsValidatedView'
|
||||
], function (App, 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});
|
||||
App.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
|
||||
AsModelBoundView.call(view);
|
||||
return AsValidatedView.call(view);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
'use strict';
|
||||
define([
|
||||
'Settings/SettingsModelBase'], function (ModelBase) {
|
||||
return ModelBase.extend({
|
||||
|
||||
baseInitialize: ModelBase.prototype.initialize,
|
||||
|
||||
initialize: function () {
|
||||
var name = this.get('name');
|
||||
|
||||
this.successMessage = 'Saved indexer: ' + name;
|
||||
this.errorMessage = 'Couldn\'t save indexer: ' + name;
|
||||
|
||||
this.baseInitialize.call(this);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/Indexers/Options/IndexerOptionsViewTemplate'
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
<fieldset>
|
||||
<legend>Options</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Retention</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="number" min="0" name="retention"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group advanced-setting">
|
||||
<label class="control-label">RSS Sync Interval</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="number" min="10" max="120" name="rssSyncInterval"/>
|
||||
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-warning" title="This will apply to all indexers, please follow the rules set forth by them"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group advanced-setting">
|
||||
<label class="control-label">Release Restrictions</label>
|
||||
|
||||
<div class="controls">
|
||||
<textarea rows="3" name="releaseRestrictions" class="release-restrictions"></textarea>
|
||||
|
||||
<span class="help-inline">
|
||||
<i class="icon-question-sign" title="Blacklist NZBs based on these words (case-insensitive)"/>
|
||||
</span>
|
||||
|
||||
<span class="text-area-help">Newline-delimited set of rules</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,36 @@
|
||||
@import "../../Shared/Styles/card";
|
||||
|
||||
.indexer-list {
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.indexer-settings-item {
|
||||
|
||||
.card;
|
||||
|
||||
width: 220px;
|
||||
height: 260px;
|
||||
padding: 10px 15px;
|
||||
|
||||
h3 {
|
||||
margin-top: 0px;
|
||||
display: inline-block;
|
||||
width: 190px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&.add-card {
|
||||
.center {
|
||||
margin-top: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AutoComplete'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/MediaManagement/FileManagement/FileManagementViewTemplate',
|
||||
|
||||
ui: {
|
||||
recyclingBin: '.x-path'
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.ui.recyclingBin.autoComplete('/directories');
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
<fieldset class="advanced-setting">
|
||||
<legend>File Management</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Ignore Deleted Episodes</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="autoUnmonitorPreviouslyDownloadedEpisodes"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-question-sign" title="Episodes deleted from disk are automatically unmonitored in NzbDrone"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Download Propers</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="autoDownloadPropers"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-question-sign" title="Should NzbDrone automatically upgrade to propers when available?"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Recycling Bin</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="recycleBin" class="x-path"/>
|
||||
<span class="help-inline">
|
||||
<i class="icon-nd-form-info" title="Episode files will go here when deleted instead of being permanently deleted"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Settings/MediaManagement/Naming/View',
|
||||
'Settings/MediaManagement/Sorting/View',
|
||||
'Settings/MediaManagement/FileManagement/FileManagementView'
|
||||
], function (Marionette, NamingView, SortingView, FileManagementView) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Settings/MediaManagement/MediaManagementLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
episodeNaming : '#episode-naming',
|
||||
sorting : '#sorting',
|
||||
fileManagement : '#file-management'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.settings = options.settings;
|
||||
this.namingSettings = options.namingSettings;
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.episodeNaming.show(new NamingView({ model: this.namingSettings }));
|
||||
this.sorting.show(new SortingView({ model: this.settings }));
|
||||
this.fileManagement.show(new FileManagementView({ model: this.settings }));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div class="form-horizontal">
|
||||
<div id="episode-naming"></div>
|
||||
<div id="sorting"></div>
|
||||
<div id="file-management"></div>
|
||||
</div>
|
||||
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Settings/SettingsModelBase'
|
||||
], function (ModelBase) {
|
||||
return ModelBase.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/config/naming',
|
||||
successMessage: 'MediaManagement settings saved',
|
||||
errorMessage : 'Couldn\'t save naming settings'
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/MediaManagement/Naming/ViewTemplate',
|
||||
|
||||
ui: {
|
||||
namingOptions : '.x-naming-options',
|
||||
renameEpisodesCheckbox: '.x-rename-episodes',
|
||||
singleEpisodeExample : '.x-single-episode-example',
|
||||
multiEpisodeExample : '.x-multi-episode-example'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change .x-rename-episodes': '_setNamingOptionsVisibility'
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
if (!this.model.get('renameEpisodes')) {
|
||||
this.ui.namingOptions.hide();
|
||||
}
|
||||
|
||||
this.listenTo(this.model, 'change', this._updateExamples);
|
||||
this._updateExamples();
|
||||
},
|
||||
|
||||
_setNamingOptionsVisibility: function () {
|
||||
var checked = this.ui.renameEpisodesCheckbox.prop('checked');
|
||||
if (checked) {
|
||||
this.ui.namingOptions.slideDown();
|
||||
}
|
||||
|
||||
else {
|
||||
this.ui.namingOptions.slideUp();
|
||||
}
|
||||
},
|
||||
|
||||
_updateExamples: function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
var promise = $.ajax({
|
||||
type: 'GET',
|
||||
url : window.NzbDrone.ApiRoot + '/config/naming/samples',
|
||||
data: this.model.toJSON()
|
||||
});
|
||||
|
||||
promise.done(function (result) {
|
||||
self.ui.singleEpisodeExample.html(result.singleEpisodeExample);
|
||||
self.ui.multiEpisodeExample.html(result.multiEpisodeExample);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,148 @@
|
||||
<fieldset>
|
||||
<legend>Episode Naming</legend>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Rename Episodes</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="renameEpisodes" class="x-rename-episodes"/>
|
||||
|
||||
<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-warning" title="NzbDrone will use the existing file name if set to no"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="x-naming-options">
|
||||
<div class="control-group">
|
||||
<label class="control-label">Include Series Title</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="includeSeriesTitle"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Include Episode Title</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="includeEpisodeTitle"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Include Quality</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="includeQuality"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Replace Spaces</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="replaceSpaces"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Separator</label>
|
||||
|
||||
<div class="controls">
|
||||
<select class="inputClass" name="separator">
|
||||
<option value=" - ">Dash</option>
|
||||
<option value=" ">Space</option>
|
||||
<option value=".">Period</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Numbering Style</label>
|
||||
|
||||
<div class="controls">
|
||||
<select class="inputClass" name="numberStyle">
|
||||
<option value="0">1x05</option>
|
||||
<option value="1">01x05</option>
|
||||
<option value="2">S01E05</option>
|
||||
<option value="3">s01e05</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Multi-Episode Style</label>
|
||||
|
||||
<div class="controls">
|
||||
<select class="inputClass" name="multiEpisodeStyle">
|
||||
<option value="0">Extend</option>
|
||||
<option value="1">Duplicate</option>
|
||||
<option value="2">Repeat</option>
|
||||
<option value="3">Scene</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Single Episode Example</label>
|
||||
|
||||
<div class="controls">
|
||||
<span class="x-single-episode-example naming-example"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Multi-Episode Example</label>
|
||||
|
||||
<div class="controls">
|
||||
<span class="x-multi-episode-example naming-example"></span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView'
|
||||
], function (Marionette, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/MediaManagement/Sorting/ViewTemplate'
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
<fieldset>
|
||||
<legend>Season Folder</legend>
|
||||
|
||||
<!--TODO: Remove this and move it to Add Series-->
|
||||
<div class="control-group">
|
||||
<label class="control-label">Use Season Folder</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="useSeasonFolder"/>
|
||||
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">Season Folder Format</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" placeholder="Season %s" name="seasonFolderFormat"/>
|
||||
<span class="help-inline">
|
||||
<i class="icon-question-sign" title="How should season folders be named? (Use %0s to pad to two digits, %sn for Series Name)"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class="add-notification-item span3">
|
||||
<div class="row">
|
||||
<div class="span3">
|
||||
{{implementationName}}
|
||||
{{#if link}}
|
||||
<a href="{{link}}"><i class="icon-info-sign"/></a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/Notifications/EditView'
|
||||
], function (App, Marionette, EditView) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Settings/Notifications/AddItemTemplate',
|
||||
tagName : 'li',
|
||||
|
||||
events: {
|
||||
'click': 'addNotification'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.notificationCollection = options.notificationCollection;
|
||||
},
|
||||
|
||||
addNotification: function (e) {
|
||||
if ($(e.target).hasClass('icon-info-sign')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.set({
|
||||
id: undefined,
|
||||
name: this.model.get('implementationName'),
|
||||
onGrab: true,
|
||||
onDownload: true
|
||||
});
|
||||
|
||||
var editView = new EditView({ model: this.model, notificationCollection: this.notificationCollection });
|
||||
App.modalRegion.show(editView);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Add Notification</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="add-notifications">
|
||||
<ul class="items"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">close</button>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
'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;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'Settings/Notifications/Model'
|
||||
], function (NotificationModel) {
|
||||
return Backbone.Collection.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/notification',
|
||||
model: NotificationModel
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<ul class="notifications">
|
||||
<li>
|
||||
<div class="notification-item add-card x-add-card">
|
||||
<span class="center well">
|
||||
<i class="icon-plus" title="Add Connection"/>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
define([
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/Notifications/ItemView',
|
||||
'Settings/Notifications/SchemaModal'
|
||||
], function (App, 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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Delete Notification</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete '{{name}}'?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-danger x-confirm-delete">delete</button>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
define(['app', 'marionette'], function (App, Marionette) {
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Settings/Notifications/DeleteTemplate',
|
||||
|
||||
events: {
|
||||
'click .x-confirm-delete': '_removeNotification'
|
||||
},
|
||||
|
||||
_removeNotification: function () {
|
||||
this.model.destroy({
|
||||
wait : true,
|
||||
success: function () {
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,83 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
{{#if id}}
|
||||
<h3>Edit - {{implementationName}}</h3>
|
||||
{{else}}
|
||||
<h3>Add - {{implementationName}}</h3>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label">Name</label>
|
||||
|
||||
<div class="controls">
|
||||
<input type="text" name="name"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">On Grab</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="onGrab"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-question-sign" title="Do you want to get notifications when episodes are grabbed?"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label">On Download</label>
|
||||
|
||||
<div class="controls">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="onDownload"/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-question-sign" title="Do you want to get notifications when episodes are downloaded?"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{formBuilder}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{#if id}}
|
||||
<button class="btn btn-danger pull-left x-delete">delete</button>
|
||||
{{else}}
|
||||
<button class="btn pull-left x-back">back</button>
|
||||
{{/if}}
|
||||
|
||||
<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>
|
||||
<button class="btn btn-icon-only btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="save-and-add x-save-and-add">
|
||||
save and add
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/Notifications/Model',
|
||||
'Settings/Notifications/DeleteView',
|
||||
'Shared/Messenger',
|
||||
'Commands/CommandController',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Form/FormBuilder'
|
||||
|
||||
], function (App, Marionette, NotificationModel, DeleteView, Messenger, CommandController, AsModelBoundView) {
|
||||
|
||||
var model = Marionette.ItemView.extend({
|
||||
template: 'Settings/Notifications/EditTemplate',
|
||||
|
||||
events: {
|
||||
'click .x-save' : '_saveNotification',
|
||||
'click .x-save-and-add' : '_saveAndAddNotification',
|
||||
'click .x-delete' : '_deleteNotification',
|
||||
'click .x-back' : '_back',
|
||||
'click .x-test' : '_test'
|
||||
},
|
||||
|
||||
ui: {
|
||||
testButton: '.x-test',
|
||||
testIcon : '.x-test-icon'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.notificationCollection = options.notificationCollection;
|
||||
},
|
||||
|
||||
_saveNotification: function () {
|
||||
var self = this;
|
||||
var promise = this.model.saveSettings();
|
||||
|
||||
if (promise) {
|
||||
promise.done(function () {
|
||||
self.notificationCollection.add(self.model, { merge: true });
|
||||
App.vent.trigger(App.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);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_deleteNotification: function () {
|
||||
var view = new DeleteView({ model: this.model });
|
||||
App.modalRegion.show(view);
|
||||
},
|
||||
|
||||
_back: function () {
|
||||
require('Settings/Notifications/SchemaModal').open(this.notificationCollection);
|
||||
},
|
||||
|
||||
_test: function () {
|
||||
var testCommand = this.model.get('testCommand');
|
||||
if (testCommand) {
|
||||
this.idle = false;
|
||||
var properties = {};
|
||||
|
||||
_.each(this.model.get('fields'), function (field) {
|
||||
properties[field.name] = field.value;
|
||||
});
|
||||
|
||||
|
||||
CommandController.Execute(testCommand, properties);
|
||||
}
|
||||
},
|
||||
|
||||
_testOnAlways: function () {
|
||||
if (!this.isClosed) {
|
||||
this.idle = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(model);
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="notification-item">
|
||||
<div>
|
||||
<h3>{{name}}</h3>
|
||||
<span class="btn-group pull-right">
|
||||
<button class="btn btn-mini btn-icon-only x-edit"><i class="icon-nd-edit"/></button>
|
||||
<button class="btn btn-mini btn-icon-only x-delete"><i class="icon-nd-delete"/></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="settings">
|
||||
{{#if onGrab}}
|
||||
<span class="label label-success">On Grab</span>
|
||||
{{else}}
|
||||
<span class="label">On Grab</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if onDownload}}
|
||||
<span class="label label-success">On Download</span>
|
||||
{{else}}
|
||||
<span class="label">On Download</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/Notifications/EditView',
|
||||
'Settings/Notifications/DeleteView'
|
||||
|
||||
], function (App, Marionette, EditView, DeleteView) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Settings/Notifications/ItemTemplate',
|
||||
tagName : 'li',
|
||||
|
||||
events: {
|
||||
'click .x-edit' : '_editNotification',
|
||||
'click .x-delete': '_deleteNotification'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'sync', this.render);
|
||||
},
|
||||
|
||||
_editNotification: function () {
|
||||
var view = new EditView({ model: this.model, notificationCollection: this.model.collection});
|
||||
App.modalRegion.show(view);
|
||||
},
|
||||
|
||||
_deleteNotification: function () {
|
||||
var view = new DeleteView({ model: this.model});
|
||||
App.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
define([
|
||||
'Settings/SettingsModelBase'], function (ModelBase) {
|
||||
return ModelBase.extend({
|
||||
|
||||
successMessage: 'Notification Saved',
|
||||
errorMessage : 'Couldn\'t save notification'
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
define([
|
||||
'app',
|
||||
'Settings/Notifications/Collection',
|
||||
'Settings/Notifications/AddView'
|
||||
], function (App, NotificationCollection, AddSelectionNotificationView) {
|
||||
return ({
|
||||
|
||||
open: function (collection) {
|
||||
var schemaCollection = new NotificationCollection();
|
||||
schemaCollection.url = '/api/notification/schema';
|
||||
schemaCollection.fetch();
|
||||
schemaCollection.url = '/api/notification';
|
||||
|
||||
var view = new AddSelectionNotificationView({ collection: schemaCollection, notificationCollection: collection});
|
||||
App.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
@import "../../Shared/Styles/card.less";
|
||||
@import "../../Shared/Styles/clickable.less";
|
||||
|
||||
.add-notification-item {
|
||||
.card;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
font-weight: lighter;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
font-size: 16px;
|
||||
color: #595959;
|
||||
|
||||
i {
|
||||
.clickable;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.add-notifications {
|
||||
text-align: center;
|
||||
|
||||
.items {
|
||||
list-style-type: none;
|
||||
margin: 0px;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notifications {
|
||||
width: -webkit-fit-content;
|
||||
width: -moz-fit-content;
|
||||
width: fit-content;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
.notification-item {
|
||||
|
||||
.card;
|
||||
|
||||
width: 290px;
|
||||
height: 90px;
|
||||
padding: 20px 20px;
|
||||
|
||||
h3 {
|
||||
margin-top: 0px;
|
||||
display: inline-block;
|
||||
width: 230px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
&.add-card {
|
||||
.center {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
define(['app', 'handlebars'], function (App,Handlebars) {
|
||||
Handlebars.registerHelper('allowedLabeler', function () {
|
||||
var ret = '';
|
||||
var cutoff = this.cutoff;
|
||||
_.each(this.allowed, function (allowed) {
|
||||
if (allowed.id === cutoff.id) {
|
||||
ret += '<span class="label label-info" title="Cutoff">' + allowed.name + '</span> ';
|
||||
}
|
||||
|
||||
else {
|
||||
ret += '<span class="label">' + allowed.name + '</span> ';
|
||||
}
|
||||
});
|
||||
|
||||
return new Handlebars.SafeString(ret);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Delete: {{name}}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete '{{name}}'?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-danger x-confirm-delete">delete</button>
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette'
|
||||
], function (App, Marionette) {
|
||||
|
||||
return Marionette.ItemView.extend({
|
||||
template: 'Settings/Quality/Profile/DeleteTemplate',
|
||||
|
||||
events: {
|
||||
'click .x-confirm-delete': '_removeProfile'
|
||||
},
|
||||
|
||||
_removeProfile: function () {
|
||||
|
||||
this.model.destroy({
|
||||
wait: true
|
||||
}).done(function () {
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
{{#if id}}
|
||||
<h3>Edit</h3>
|
||||
{{else}}
|
||||
<h3>Add</h3>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label">Name</label>
|
||||
<div class="controls">
|
||||
<input type="text" name="name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label">Cutoff</label>
|
||||
<div class="controls">
|
||||
<select class="x-cutoff" name="cutoff.id" validation-name="cutoff">
|
||||
{{#each allowed}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<span class="help-inline">
|
||||
<i class="icon-question-sign" title="Once this quality is reached NzbDrone will no longer download episodes"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="offset1 span3">
|
||||
<h3>Available</h3>
|
||||
<select multiple="multiple" class="x-available-list">
|
||||
{{#each available}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<h3>Allowed</h3>
|
||||
<select multiple="multiple" class="x-allowed-list" validation-name="allowed">
|
||||
{{#each allowed}}
|
||||
<option value="{{id}}">{{name}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{{#if id}}
|
||||
<button class="btn btn-danger pull-left x-delete">delete</button>
|
||||
{{/if}}
|
||||
<button class="btn" data-dismiss="modal">cancel</button>
|
||||
<button class="btn btn-primary x-save">save</button>
|
||||
</div>
|
||||
@@ -0,0 +1,81 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Mixins/AsValidatedView'
|
||||
], function (App, Marionette, AsModelBoundView, AsValidatedView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/Quality/Profile/EditQualityProfileTemplate',
|
||||
|
||||
ui: {
|
||||
cutoff: '.x-cutoff'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-save' : '_saveQualityProfile',
|
||||
'dblclick .x-available-list': '_moveQuality',
|
||||
'dblclick .x-allowed-list' : '_moveQuality'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.profileCollection = options.profileCollection;
|
||||
},
|
||||
|
||||
_moveQuality: function (event) {
|
||||
|
||||
var quality;
|
||||
var qualityId = event.target.value;
|
||||
var availableCollection = new Backbone.Collection(this.model.get('available'));
|
||||
availableCollection.comparator = function (model) {
|
||||
return model.get('weight');
|
||||
};
|
||||
|
||||
var allowedCollection = new Backbone.Collection(this.model.get('allowed'));
|
||||
allowedCollection.comparator = function (model) {
|
||||
return model.get('weight');
|
||||
};
|
||||
|
||||
if (availableCollection.get(qualityId)) {
|
||||
quality = availableCollection.get(qualityId);
|
||||
availableCollection.remove(quality);
|
||||
allowedCollection.add(quality);
|
||||
}
|
||||
else if (allowedCollection.get(qualityId)) {
|
||||
quality = allowedCollection.get(qualityId);
|
||||
|
||||
allowedCollection.remove(quality);
|
||||
availableCollection.add(quality);
|
||||
}
|
||||
else {
|
||||
throw 'couldnt find quality id ' + qualityId;
|
||||
}
|
||||
|
||||
this.model.set('available', availableCollection.toJSON());
|
||||
this.model.set('allowed', allowedCollection.toJSON());
|
||||
|
||||
this.render();
|
||||
},
|
||||
|
||||
_saveQualityProfile: function () {
|
||||
var self = this;
|
||||
var cutoff = _.findWhere(this.model.get('allowed'), { id: parseInt(this.ui.cutoff.val())});
|
||||
this.model.set('cutoff', cutoff);
|
||||
|
||||
var promise = this.model.save();
|
||||
|
||||
if (promise) {
|
||||
promise.done(function () {
|
||||
self.profileCollection.add(self.model, { merge: true });
|
||||
App.vent.trigger(App.Commands.CloseModalCommand);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AsValidatedView.call(view);
|
||||
return AsModelBoundView.call(view);
|
||||
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
<fieldset>
|
||||
<legend>Quality Profiles</legend>
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<ul class="quality-profiles">
|
||||
<li>
|
||||
<div class="quality-profile-item add-card x-add-card">
|
||||
<span class="center well">
|
||||
<i class="icon-plus" title="Add Profile"/>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
define(['app',
|
||||
'marionette',
|
||||
'Settings/Quality/Profile/QualityProfileView',
|
||||
'Settings/Quality/Profile/EditQualityProfileView',
|
||||
'Settings/Quality/Profile/QualityProfileSchemaCollection'
|
||||
], function (App, Marionette, QualityProfileView, EditProfileView, ProfileCollection) {
|
||||
|
||||
return Marionette.CompositeView.extend({
|
||||
itemView : QualityProfileView,
|
||||
itemViewContainer: '.quality-profiles',
|
||||
template : 'Settings/Quality/Profile/QualityProfileCollectionTemplate',
|
||||
|
||||
ui: {
|
||||
'addCard': '.x-add-card'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-add-card': '_addProfile'
|
||||
},
|
||||
|
||||
appendHtml: function(collectionView, itemView, index){
|
||||
collectionView.ui.addCard.parent('li').before(itemView.el);
|
||||
},
|
||||
|
||||
_addProfile: function () {
|
||||
var self = this;
|
||||
var schemaCollection = new ProfileCollection();
|
||||
schemaCollection.fetch({
|
||||
success: function (collection) {
|
||||
var model = _.first(collection.models);
|
||||
model.set('id', undefined);
|
||||
model.set('name', '');
|
||||
model.collection = self.collection;
|
||||
|
||||
var view = new EditProfileView({ model: model, profileCollection: self.collection});
|
||||
App.modalRegion.show(view);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
define(
|
||||
[
|
||||
'backbone',
|
||||
'Quality/QualityProfileModel'
|
||||
], function (Backbone, QualityProfileModel) {
|
||||
|
||||
return Backbone.Collection.extend({
|
||||
model: QualityProfileModel,
|
||||
url : window.NzbDrone.ApiRoot + '/qualityprofiles/schema'
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<div class="quality-profile-item">
|
||||
<div>
|
||||
<h3 name="name"></h3>
|
||||
<span class="btn-group pull-right">
|
||||
<button class="btn btn-mini btn-icon-only x-edit"><i class="icon-nd-edit"/></button>
|
||||
<button class="btn btn-mini btn-icon-only x-delete"><i class="icon-nd-delete"/></button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{allowedLabeler}}
|
||||
</div>
|
||||
@@ -0,0 +1,71 @@
|
||||
'use strict';
|
||||
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/Quality/Profile/EditQualityProfileView',
|
||||
'Settings/Quality/Profile/DeleteView',
|
||||
'Series/SeriesCollection',
|
||||
'Mixins/AsModelBoundView',
|
||||
'Settings/Quality/Profile/AllowedLabeler',
|
||||
'bootstrap',
|
||||
|
||||
], function (App, Marionette, EditProfileView, DeleteProfileView, SeriesCollection, AsModelBoundView) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template: 'Settings/Quality/Profile/QualityProfileTemplate',
|
||||
tagName : 'li',
|
||||
|
||||
ui: {
|
||||
'progressbar' : '.progress .bar',
|
||||
'deleteButton': '.x-delete'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-edit' : '_editProfile',
|
||||
'click .x-delete': '_deleteProfile'
|
||||
},
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(this.model, 'sync', this.render);
|
||||
this.listenTo(SeriesCollection, 'all', this._updateDisableStatus)
|
||||
},
|
||||
|
||||
_editProfile: function () {
|
||||
var view = new EditProfileView({ model: this.model, profileCollection: this.model.collection });
|
||||
App.modalRegion.show(view);
|
||||
},
|
||||
|
||||
_deleteProfile: function () {
|
||||
if (this._isQualityInUse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var view = new DeleteProfileView({ model: this.model });
|
||||
App.modalRegion.show(view);
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this._updateDisableStatus();
|
||||
},
|
||||
|
||||
_updateDisableStatus: function () {
|
||||
if (this._isQualityInUse()) {
|
||||
this.ui.deleteButton.addClass('disabled');
|
||||
this.ui.deleteButton.attr('title', 'Can\'t delete quality profiles attached to a series.');
|
||||
}
|
||||
else {
|
||||
this.ui.deleteButton.removeClass('disabled');
|
||||
this.ui.deleteButton.attr('title', 'Delete Quality Profile');
|
||||
}
|
||||
},
|
||||
|
||||
_isQualityInUse: function () {
|
||||
return SeriesCollection.where({'qualityProfileId': this.model.id}).length !== 0;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
"use strict";
|
||||
|
||||
define(
|
||||
[
|
||||
'marionette',
|
||||
'Quality/QualityProfileCollection',
|
||||
'Settings/Quality/Profile/QualityProfileCollectionView',
|
||||
'Quality/QualitySizeCollection',
|
||||
'Settings/Quality/Size/QualitySizeCollectionView'
|
||||
], function (Marionette, QualityProfileCollection, QualityProfileCollectionView, QualitySizeCollection, QualitySizeCollectionView) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Settings/Quality/QualityLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
qualityProfile : '#quality-profile',
|
||||
qualitySize : '#quality-size'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.settings = options.settings;
|
||||
QualityProfileCollection.fetch();
|
||||
this.qualitySizeCollection = new QualitySizeCollection();
|
||||
this.qualitySizeCollection.fetch();
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
this.qualityProfile.show(new QualityProfileCollectionView({collection: QualityProfileCollection}));
|
||||
this.qualitySize.show(new QualitySizeCollectionView({collection: this.qualitySizeCollection}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<div class="row">
|
||||
<div class="span12" id="quality-profile"/>
|
||||
</div>
|
||||
<!--<div class="row">
|
||||
<div class="span12" id="quality-size"/>
|
||||
</div>-->
|
||||
@@ -0,0 +1,4 @@
|
||||
<fieldset>
|
||||
<legend>Quality Size Limits</legend>
|
||||
<ul class="quality-sizes"/>
|
||||
</fieldset>
|
||||
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
define(['marionette', 'Settings/Quality/Size/QualitySizeView'], function (Marionette, QualitySizeView) {
|
||||
return Marionette.CompositeView.extend({
|
||||
itemView : QualitySizeView,
|
||||
itemViewContainer: '.quality-sizes',
|
||||
template : 'Settings/Quality/Size/QualitySizeCollectionTemplate'
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="quality-size-item">
|
||||
<h2 class="center-block">{{name}}</h2>
|
||||
<div class="size">
|
||||
<div class="size-value-wrapper">
|
||||
<div>
|
||||
<span class="label label-large label-warning x-size-thirty"
|
||||
name="thirtyMinuteSize"
|
||||
title="Maximum size for a 30 minute episode">
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="label label-large label-info x-size-sixty"
|
||||
name="sixtyMinuteSize"
|
||||
title="Maximum size for a 60 minute episode">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" name="maxSize" class="knob x-knob" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
define(['marionette', 'Mixins/AsModelBoundView', 'filesize', 'jquery.knob' ], function (Marionette, AsModelBoundView, Filesize) {
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
template : 'Settings/Quality/Size/QualitySizeTemplate',
|
||||
tagName : 'li',
|
||||
|
||||
ui: {
|
||||
knob : '.x-knob',
|
||||
thirtyMinuteSize: '.x-size-thirty',
|
||||
sixtyMinuteSize : '.x-size-sixty'
|
||||
},
|
||||
|
||||
events: {
|
||||
'change .x-knob': '_changeMaxSize'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
this.qualityProfileCollection = options.qualityProfiles;
|
||||
this.filesize = Filesize;
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.ui.knob.knob({
|
||||
min : 0,
|
||||
max : 200,
|
||||
step : 1,
|
||||
cursor : 25,
|
||||
width : 150,
|
||||
stopper : true,
|
||||
displayInput : false
|
||||
});
|
||||
|
||||
this._changeMaxSize();
|
||||
},
|
||||
|
||||
_changeMaxSize: function () {
|
||||
var maxSize = this.model.get('maxSize');
|
||||
var bytes = maxSize * 1024 * 1024;
|
||||
var thirty = Filesize(bytes * 30, 1, false);
|
||||
var sixty = Filesize(bytes * 60, 1, false);
|
||||
|
||||
if (parseInt(maxSize) === 0) {
|
||||
thirty = 'No Limit';
|
||||
sixty = 'No Limit';
|
||||
}
|
||||
|
||||
this.ui.thirtyMinuteSize.html(thirty);
|
||||
this.ui.sixtyMinuteSize.html(sixty);
|
||||
}
|
||||
});
|
||||
|
||||
return AsModelBoundView.call(view);
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
@import "../../Shared/Styles/card";
|
||||
|
||||
.quality-profiles, .quality-sizes {
|
||||
li {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.quality-profile-item {
|
||||
|
||||
.card;
|
||||
|
||||
width: 300px;
|
||||
height: 120px;
|
||||
padding: 10px 15px;
|
||||
|
||||
h3 {
|
||||
margin-top: 0px;
|
||||
display: inline-block;
|
||||
width: 240px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&.add-card {
|
||||
.center {
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quality-size-item {
|
||||
|
||||
.card;
|
||||
text-align: center;
|
||||
|
||||
width: 200px;
|
||||
height: 220px;
|
||||
padding: 10px 15px;
|
||||
|
||||
h3 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.size {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
margin: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.knob {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.size-value-wrapper {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
width: 100%;
|
||||
|
||||
div {
|
||||
margin-top: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#quality-size {
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
'use strict';
|
||||
define(
|
||||
[
|
||||
'app',
|
||||
'marionette',
|
||||
'Settings/SettingsModel',
|
||||
'Settings/General/GeneralSettingsModel',
|
||||
'Settings/MediaManagement/Naming/Model',
|
||||
'Settings/MediaManagement/MediaManagementLayout',
|
||||
'Settings/Quality/QualityLayout',
|
||||
'Settings/Indexers/IndexerLayout',
|
||||
'Settings/Indexers/Collection',
|
||||
'Settings/DownloadClient/Layout',
|
||||
'Settings/Notifications/CollectionView',
|
||||
'Settings/Notifications/Collection',
|
||||
'Settings/General/GeneralView',
|
||||
'Shared/LoadingView',
|
||||
'Config'
|
||||
], function (App,
|
||||
Marionette,
|
||||
SettingsModel,
|
||||
GeneralSettingsModel,
|
||||
NamingModel,
|
||||
MediaManagementLayout,
|
||||
QualityLayout,
|
||||
IndexerLayout,
|
||||
IndexerCollection,
|
||||
DownloadClientLayout,
|
||||
NotificationCollectionView,
|
||||
NotificationCollection,
|
||||
GeneralView,
|
||||
LoadingView,
|
||||
Config) {
|
||||
return Marionette.Layout.extend({
|
||||
template: 'Settings/SettingsLayoutTemplate',
|
||||
|
||||
regions: {
|
||||
mediaManagement : '#media-management',
|
||||
quality : '#quality',
|
||||
indexers : '#indexers',
|
||||
downloadClient : '#download-client',
|
||||
notifications : '#notifications',
|
||||
general : '#general',
|
||||
loading : '#loading-region'
|
||||
},
|
||||
|
||||
ui: {
|
||||
mediaManagementTab : '.x-media-management-tab',
|
||||
qualityTab : '.x-quality-tab',
|
||||
indexersTab : '.x-indexers-tab',
|
||||
downloadClientTab : '.x-download-client-tab',
|
||||
notificationsTab : '.x-notifications-tab',
|
||||
generalTab : '.x-general-tab',
|
||||
advancedSettings : '.x-advanced-settings'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .x-media-management-tab' : '_showMediaManagement',
|
||||
'click .x-quality-tab' : '_showQuality',
|
||||
'click .x-indexers-tab' : '_showIndexers',
|
||||
'click .x-download-client-tab' : '_showDownloadClient',
|
||||
'click .x-notifications-tab' : '_showNotifications',
|
||||
'click .x-general-tab' : '_showGeneral',
|
||||
'click .x-save-settings' : '_save',
|
||||
'change .x-advanced-settings' : '_toggleAdvancedSettings'
|
||||
},
|
||||
|
||||
initialize: function (options) {
|
||||
if (options.action) {
|
||||
this.action = options.action.toLowerCase();
|
||||
}
|
||||
},
|
||||
|
||||
onRender: function () {
|
||||
this.loading.show(new LoadingView());
|
||||
var self = this;
|
||||
|
||||
this.settings = new SettingsModel();
|
||||
this.generalSettings = new GeneralSettingsModel();
|
||||
this.namingSettings = new NamingModel();
|
||||
this.indexerSettings = new IndexerCollection();
|
||||
this.notificationSettings = new NotificationCollection();
|
||||
|
||||
$.when(this.settings.fetch(),
|
||||
this.generalSettings.fetch(),
|
||||
this.namingSettings.fetch(),
|
||||
this.indexerSettings.fetch(),
|
||||
this.notificationSettings.fetch()
|
||||
).done(function () {
|
||||
self.loading.$el.hide();
|
||||
self.mediaManagement.show(new MediaManagementLayout({ settings: self.settings, namingSettings: self.namingSettings }));
|
||||
self.quality.show(new QualityLayout({ settings: self.settings }));
|
||||
self.indexers.show(new IndexerLayout({ settings: self.settings, indexersCollection: self.indexerSettings }));
|
||||
self.downloadClient.show(new DownloadClientLayout({ model: self.settings }));
|
||||
self.notifications.show(new NotificationCollectionView({ collection: self.notificationSettings }));
|
||||
self.general.show(new GeneralView({ model: self.generalSettings }));
|
||||
});
|
||||
|
||||
this._setAdvancedSettingsState();
|
||||
},
|
||||
|
||||
onShow: function () {
|
||||
switch (this.action) {
|
||||
case 'quality':
|
||||
this._showQuality();
|
||||
break;
|
||||
case 'indexers':
|
||||
this._showIndexers();
|
||||
break;
|
||||
case 'downloadclient':
|
||||
this._showDownloadClient();
|
||||
break;
|
||||
case 'connect':
|
||||
this._showNotifications();
|
||||
break;
|
||||
case 'notifications':
|
||||
this._showNotifications();
|
||||
break;
|
||||
case 'general':
|
||||
this._showGeneral();
|
||||
break;
|
||||
default:
|
||||
this._showMediaManagement();
|
||||
}
|
||||
},
|
||||
|
||||
_showMediaManagement: function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.mediaManagementTab.tab('show');
|
||||
this._navigate('settings/mediamanagement');
|
||||
},
|
||||
|
||||
_showQuality: function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.qualityTab.tab('show');
|
||||
this._navigate('settings/quality');
|
||||
},
|
||||
|
||||
_showIndexers: function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.indexersTab.tab('show');
|
||||
this._navigate('settings/indexers');
|
||||
},
|
||||
|
||||
_showDownloadClient: function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.downloadClientTab.tab('show');
|
||||
this._navigate('settings/downloadclient');
|
||||
},
|
||||
|
||||
_showNotifications: function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.notificationsTab.tab('show');
|
||||
this._navigate('settings/connect');
|
||||
},
|
||||
|
||||
_showGeneral: function (e) {
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
this.ui.generalTab.tab('show');
|
||||
this._navigate('settings/general');
|
||||
},
|
||||
|
||||
_navigate:function(route){
|
||||
require(['Router'], function(){
|
||||
App.Router.navigate(route);
|
||||
});
|
||||
},
|
||||
|
||||
_save: function () {
|
||||
App.vent.trigger(App.Commands.SaveSettings);
|
||||
},
|
||||
|
||||
_setAdvancedSettingsState: function () {
|
||||
var checked = Config.getValueBoolean('advancedSettings');
|
||||
this.ui.advancedSettings.prop('checked', checked);
|
||||
|
||||
if (checked) {
|
||||
this.$el.addClass('show-advanced-settings');
|
||||
}
|
||||
},
|
||||
|
||||
_toggleAdvancedSettings: function () {
|
||||
var checked = this.ui.advancedSettings.prop('checked');
|
||||
Config.setValue('advancedSettings', checked);
|
||||
|
||||
if (checked) {
|
||||
this.$el.addClass('show-advanced-settings');
|
||||
}
|
||||
|
||||
else {
|
||||
this.$el.removeClass('show-advanced-settings');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<ul class="nav nav-tabs" id="myTab">
|
||||
<li><a href="#media-management" class="x-media-management-tab no-router">Media Management</a></li>
|
||||
<li><a href="#quality" class="x-quality-tab no-router">Quality</a></li>
|
||||
<li><a href="#indexers" class="x-indexers-tab no-router">Indexers</a></li>
|
||||
<li><a href="#download-client" class="x-download-client-tab no-router">Download Client</a></li>
|
||||
<li><a href="#notifications" class="x-notifications-tab no-router">Connect</a></li>
|
||||
<li><a href="#general" class="x-general-tab no-router">General</a></li>
|
||||
<li class="pull-right"><button class="btn btn-primary x-save-settings">Save</button></li>
|
||||
<li class="pull-right advanced-settings-toggle">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" class="x-advanced-settings"/>
|
||||
<p>
|
||||
<span>Show</span>
|
||||
<span>Hide</span>
|
||||
</p>
|
||||
<div class="btn btn-warning slide-button"/>
|
||||
</label>
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-nd-form-info" title="Show advanced options"/>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="media-management"></div>
|
||||
<div class="tab-pane" id="quality"></div>
|
||||
<div class="tab-pane" id="indexers"></div>
|
||||
<div class="tab-pane" id="download-client"></div>
|
||||
<div class="tab-pane" id="notifications"></div>
|
||||
<div class="tab-pane" id="general"></div>
|
||||
</div>
|
||||
|
||||
<div id="loading-region"></div>
|
||||
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
define(['app',
|
||||
'Settings/SettingsModelBase'], function (App, SettingsModelBase) {
|
||||
return SettingsModelBase.extend({
|
||||
url : window.NzbDrone.ApiRoot + '/settings',
|
||||
successMessage: 'Settings saved',
|
||||
errorMessage : 'Failed to save settings'
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
define(['app',
|
||||
'backbone.deepmodel',
|
||||
'Mixins/AsChangeTrackingModel',
|
||||
'Shared/Messenger'], function (App, DeepModel, AsChangeTrackingModel, Messenger) {
|
||||
var model = DeepModel.DeepModel.extend({
|
||||
|
||||
initialize: function () {
|
||||
this.listenTo(App.vent, App.Commands.SaveSettings, this.saveSettings);
|
||||
},
|
||||
|
||||
saveSettings: function () {
|
||||
|
||||
if (!this.isSaved) {
|
||||
|
||||
var savePromise = this.save();
|
||||
|
||||
Messenger.monitor(
|
||||
{
|
||||
promise : savePromise,
|
||||
successMessage: this.successMessage,
|
||||
errorMessage : this.errorMessage
|
||||
});
|
||||
|
||||
return savePromise;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return AsChangeTrackingModel.call(model);
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
@import "../Content/Bootstrap/variables";
|
||||
@import "../Shared/Styles/clickable.less";
|
||||
@import "Indexers/indexers";
|
||||
@import "Quality/quality";
|
||||
@import "Notifications/notifications";
|
||||
|
||||
li.save-and-add {
|
||||
.clickable;
|
||||
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 20px;
|
||||
color: rgb(51, 51, 51);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
li.save-and-add:hover {
|
||||
text-decoration: none;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: rgb(0, 129, 194);
|
||||
}
|
||||
|
||||
.add-card {
|
||||
.clickable;
|
||||
color: #adadad;
|
||||
font-size: 50px;
|
||||
text-align: center;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.center {
|
||||
display: inline-block;
|
||||
padding: 5px 20px 0px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
i {
|
||||
.clickable;
|
||||
}
|
||||
}
|
||||
|
||||
.naming-example {
|
||||
display: inline-block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.advanced-settings-toggle {
|
||||
margin-right: 40px;
|
||||
|
||||
.checkbox {
|
||||
width : 100px;
|
||||
margin-left : 0px;
|
||||
display : inline-block;
|
||||
padding-top : 0px;
|
||||
margin-bottom : 0px;
|
||||
margin-top : -1px;
|
||||
}
|
||||
|
||||
.help-inline-checkbox {
|
||||
display : inline-block;
|
||||
margin-top : -23px;
|
||||
margin-bottom : 0;
|
||||
vertical-align : middle;
|
||||
}
|
||||
}
|
||||
|
||||
.advanced-setting {
|
||||
display: none;
|
||||
|
||||
.control-label {
|
||||
color: @warningText;
|
||||
}
|
||||
}
|
||||
|
||||
.show-advanced-settings {
|
||||
.advanced-setting {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user