mirror of
https://github.com/Radarr/Radarr.git
synced 2024-09-17 15:02:34 +02:00
Added Auth, startup options to UI
Added caching to ConfigFileProvider,
This commit is contained in:
parent
8a5bd31da7
commit
4da6654440
@ -2,6 +2,7 @@
|
|||||||
using Nancy.Security;
|
using Nancy.Security;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.Model;
|
using NzbDrone.Common.Model;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Api.Authentication
|
namespace NzbDrone.Api.Authentication
|
||||||
{
|
{
|
||||||
|
@ -55,16 +55,34 @@ public class NamingConfigResource : RestResource
|
|||||||
public class SettingsModule : NzbDroneApiModule
|
public class SettingsModule : NzbDroneApiModule
|
||||||
{
|
{
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
|
private readonly IConfigFileProvider _configFileProvider;
|
||||||
|
|
||||||
public SettingsModule(IConfigService configService)
|
public SettingsModule(IConfigService configService, IConfigFileProvider configFileProvider)
|
||||||
: base("/settings")
|
: base("/settings")
|
||||||
{
|
{
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
Get["/"] = x => GetAllSettings();
|
_configFileProvider = configFileProvider;
|
||||||
Post["/"] = x => SaveSettings();
|
Get["/"] = x => GetGeneralSettings();
|
||||||
|
Post["/"] = x => SaveGeneralSettings();
|
||||||
|
|
||||||
|
Get["/host"] = x => GetHostSettings();
|
||||||
|
Post["/host"] = x => SaveHostSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response GetAllSettings()
|
private Response SaveHostSettings()
|
||||||
|
{
|
||||||
|
var request = Request.Body.FromJson<Dictionary<string, object>>();
|
||||||
|
_configFileProvider.SaveConfigDictionary(request);
|
||||||
|
|
||||||
|
return GetHostSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response GetHostSettings()
|
||||||
|
{
|
||||||
|
return _configFileProvider.GetConfigDictionary().AsResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response GetGeneralSettings()
|
||||||
{
|
{
|
||||||
var collection = Request.Query.Collection;
|
var collection = Request.Query.Collection;
|
||||||
|
|
||||||
@ -74,7 +92,7 @@ private Response GetAllSettings()
|
|||||||
return _configService.AllWithDefaults().AsResponse();
|
return _configService.AllWithDefaults().AsResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response SaveSettings()
|
private Response SaveGeneralSettings()
|
||||||
{
|
{
|
||||||
var request = Request.Body.FromJson<Dictionary<string, object>>();
|
var request = Request.Body.FromJson<Dictionary<string, object>>();
|
||||||
_configService.SaveValues(request);
|
_configService.SaveValues(request);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using FluentAssertions;
|
using System.Collections.Generic;
|
||||||
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
|
|
||||||
@ -59,6 +60,21 @@ public void remove_none_existing_should_break_things()
|
|||||||
{
|
{
|
||||||
_cachedString.Remove("Test");
|
_cachedString.Remove("Test");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void get_without_callback_should_throw_on_invalid_key()
|
||||||
|
{
|
||||||
|
Assert.Throws<KeyNotFoundException>(() => _cachedString.Get("InvalidKey"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_be_able_to_update_key()
|
||||||
|
{
|
||||||
|
_cachedString.Set("Key", "Old");
|
||||||
|
_cachedString.Set("Key", "New");
|
||||||
|
|
||||||
|
_cachedString.Get("Key").Should().Be("New");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Worker
|
public class Worker
|
||||||
|
@ -2,16 +2,17 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Test.CacheTests
|
namespace NzbDrone.Common.Test.CacheTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class CachedManagerFixture
|
public class CachedManagerFixture:TestBase<ICacheManger>
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void should_return_proper_type_of_cache()
|
public void should_return_proper_type_of_cache()
|
||||||
{
|
{
|
||||||
var result = CacheManger.GetCache<DateTime>(typeof(string));
|
var result = Subject.GetCache<DateTime>(typeof(string));
|
||||||
|
|
||||||
result.Should().BeOfType<Cached<DateTime>>();
|
result.Should().BeOfType<Cached<DateTime>>();
|
||||||
}
|
}
|
||||||
@ -20,8 +21,8 @@ public void should_return_proper_type_of_cache()
|
|||||||
[Test]
|
[Test]
|
||||||
public void multiple_calls_should_get_the_same_cache()
|
public void multiple_calls_should_get_the_same_cache()
|
||||||
{
|
{
|
||||||
var result1 = CacheManger.GetCache<DateTime>(typeof(string));
|
var result1 = Subject.GetCache<DateTime>(typeof(string));
|
||||||
var result2 = CacheManger.GetCache<DateTime>(typeof(string));
|
var result2 = Subject.GetCache<DateTime>(typeof(string));
|
||||||
|
|
||||||
result1.Should().BeSameAs(result2);
|
result1.Should().BeSameAs(result2);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Model;
|
using NzbDrone.Common.Model;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Test
|
namespace NzbDrone.Common.Test
|
||||||
@ -143,15 +144,18 @@ public void GetAuthenticationType_Basic()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Guid_should_return_the_same_every_time()
|
public void SaveDictionary_should_save_proper_value()
|
||||||
{
|
{
|
||||||
|
int port = 20555;
|
||||||
|
|
||||||
var firstResponse = Subject.Guid;
|
var dic = Subject.GetConfigDictionary();
|
||||||
var secondResponse = Subject.Guid;
|
dic["Port"] = 20555;
|
||||||
|
|
||||||
|
Subject.SaveConfigDictionary(dic);
|
||||||
|
|
||||||
|
|
||||||
|
Subject.Port.Should().Be(port);
|
||||||
secondResponse.Should().Be(firstResponse);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,20 +3,32 @@
|
|||||||
|
|
||||||
namespace NzbDrone.Common.Cache
|
namespace NzbDrone.Common.Cache
|
||||||
{
|
{
|
||||||
public static class CacheManger
|
public interface ICacheManger
|
||||||
{
|
{
|
||||||
private static readonly ICached<object> Cache;
|
ICached<T> GetCache<T>(Type type);
|
||||||
|
ICached<T> GetCache<T>(object host);
|
||||||
|
}
|
||||||
|
|
||||||
static CacheManger()
|
public class CacheManger : ICacheManger
|
||||||
|
{
|
||||||
|
private readonly ICached<object> _cache;
|
||||||
|
|
||||||
|
public CacheManger()
|
||||||
{
|
{
|
||||||
Cache = new Cached<object>();
|
_cache = new Cached<object>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICached<T> GetCache<T>(Type type)
|
public ICached<T> GetCache<T>(Type type)
|
||||||
{
|
{
|
||||||
Ensure.That(() => type).IsNotNull();
|
Ensure.That(() => type).IsNotNull();
|
||||||
|
|
||||||
return (ICached<T>)Cache.Get(type.FullName, () => new Cached<T>());
|
return (ICached<T>)_cache.Get(type.FullName, () => new Cached<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICached<T> GetCache<T>(object host)
|
||||||
|
{
|
||||||
|
return GetCache<T>(host.GetType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NzbDrone.Common.EnsureThat;
|
using NzbDrone.Common.EnsureThat;
|
||||||
|
|
||||||
namespace NzbDrone.Common.Cache
|
namespace NzbDrone.Common.Cache
|
||||||
@ -16,7 +17,12 @@ public Cached()
|
|||||||
public void Set(string key, T value)
|
public void Set(string key, T value)
|
||||||
{
|
{
|
||||||
Ensure.That(() => key).IsNotNullOrWhiteSpace();
|
Ensure.That(() => key).IsNotNullOrWhiteSpace();
|
||||||
_store.TryAdd(key, value);
|
_store[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Get(string key)
|
||||||
|
{
|
||||||
|
return Get(key, () => { throw new KeyNotFoundException(key); });
|
||||||
}
|
}
|
||||||
|
|
||||||
public T Get(string key, Func<T> function)
|
public T Get(string key, Func<T> function)
|
||||||
|
@ -9,5 +9,6 @@ public interface ICached<T>
|
|||||||
bool ContainsKey(string key);
|
bool ContainsKey(string key);
|
||||||
void Clear();
|
void Clear();
|
||||||
void Remove(string key);
|
void Remove(string key);
|
||||||
|
T Get(string key);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,151 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
using NzbDrone.Common.Model;
|
|
||||||
|
|
||||||
namespace NzbDrone.Common
|
|
||||||
{
|
|
||||||
public interface IConfigFileProvider
|
|
||||||
{
|
|
||||||
Guid Guid { get; }
|
|
||||||
int Port { get; set; }
|
|
||||||
bool LaunchBrowser { get; set; }
|
|
||||||
AuthenticationType AuthenticationType { get; set; }
|
|
||||||
string BasicAuthUsername { get; set; }
|
|
||||||
string BasicAuthPassword { get; set; }
|
|
||||||
int GetValueInt(string key, int defaultValue);
|
|
||||||
bool GetValueBoolean(string key, bool defaultValue);
|
|
||||||
string GetValue(string key, object defaultValue);
|
|
||||||
T GetValueEnum<T>(string key, T defaultValue);
|
|
||||||
void SetValue(string key, object value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ConfigFileProvider : IConfigFileProvider
|
|
||||||
{
|
|
||||||
private readonly IEnvironmentProvider _environmentProvider;
|
|
||||||
|
|
||||||
private readonly string _configFile;
|
|
||||||
|
|
||||||
public ConfigFileProvider(IEnvironmentProvider environmentProvider)
|
|
||||||
{
|
|
||||||
_environmentProvider = environmentProvider;
|
|
||||||
_configFile = _environmentProvider.GetConfigPath();
|
|
||||||
|
|
||||||
CreateDefaultConfigFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Guid Guid
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var key = "Guid";
|
|
||||||
if (string.IsNullOrWhiteSpace(GetValue(key, string.Empty)))
|
|
||||||
{
|
|
||||||
SetValue(key, Guid.NewGuid().ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return Guid.Parse(GetValue(key, string.Empty));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual int Port
|
|
||||||
{
|
|
||||||
get { return GetValueInt("Port", 8989); }
|
|
||||||
set { SetValue("Port", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool LaunchBrowser
|
|
||||||
{
|
|
||||||
get { return GetValueBoolean("LaunchBrowser", true); }
|
|
||||||
set { SetValue("LaunchBrowser", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual AuthenticationType AuthenticationType
|
|
||||||
{
|
|
||||||
get { return GetValueEnum("AuthenticationType", AuthenticationType.Anonymous); }
|
|
||||||
set { SetValue("AuthenticationType", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string BasicAuthUsername
|
|
||||||
{
|
|
||||||
get { return GetValue("BasicAuthUsername", ""); }
|
|
||||||
set { SetValue("BasicAuthUsername", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string BasicAuthPassword
|
|
||||||
{
|
|
||||||
get { return GetValue("BasicAuthPassword", ""); }
|
|
||||||
set { SetValue("BasicAuthPassword", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual int GetValueInt(string key, int defaultValue)
|
|
||||||
{
|
|
||||||
return Convert.ToInt32(GetValue(key, defaultValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool GetValueBoolean(string key, bool defaultValue)
|
|
||||||
{
|
|
||||||
return Convert.ToBoolean(GetValue(key, defaultValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetValueEnum<T>(string key, T defaultValue)
|
|
||||||
{
|
|
||||||
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string GetValue(string key, object defaultValue)
|
|
||||||
{
|
|
||||||
var xDoc = XDocument.Load(_configFile);
|
|
||||||
var config = xDoc.Descendants("Config").Single();
|
|
||||||
|
|
||||||
var parentContainer = config;
|
|
||||||
|
|
||||||
var valueHolder = parentContainer.Descendants(key).ToList();
|
|
||||||
|
|
||||||
if (valueHolder.Count() == 1)
|
|
||||||
return valueHolder.First().Value;
|
|
||||||
|
|
||||||
//Save the value
|
|
||||||
SetValue(key, defaultValue);
|
|
||||||
|
|
||||||
//return the default value
|
|
||||||
return defaultValue.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetValue(string key, object value)
|
|
||||||
{
|
|
||||||
var xDoc = XDocument.Load(_configFile);
|
|
||||||
var config = xDoc.Descendants("Config").Single();
|
|
||||||
|
|
||||||
var parentContainer = config;
|
|
||||||
|
|
||||||
var keyHolder = parentContainer.Descendants(key);
|
|
||||||
|
|
||||||
if (keyHolder.Count() != 1)
|
|
||||||
parentContainer.Add(new XElement(key, value));
|
|
||||||
|
|
||||||
else
|
|
||||||
parentContainer.Descendants(key).Single().Value = value.ToString();
|
|
||||||
|
|
||||||
xDoc.Save(_configFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetValue(string key, Enum value)
|
|
||||||
{
|
|
||||||
SetValue(key, value.ToString().ToLower());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateDefaultConfigFile()
|
|
||||||
{
|
|
||||||
if (!File.Exists(_configFile))
|
|
||||||
{
|
|
||||||
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
|
||||||
|
|
||||||
xDoc.Add(new XElement("Config"));
|
|
||||||
|
|
||||||
xDoc.Save(_configFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -55,10 +55,6 @@
|
|||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Interop.NetFwTypeLib">
|
|
||||||
<HintPath>..\Libraries\Interop.NetFwTypeLib.dll</HintPath>
|
|
||||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Ionic.Zip">
|
<Reference Include="Ionic.Zip">
|
||||||
<HintPath>..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll</HintPath>
|
<HintPath>..\packages\DotNetZip.1.9.1.8\lib\net20\Ionic.Zip.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
@ -75,8 +71,6 @@
|
|||||||
<Reference Include="System.Configuration.Install" />
|
<Reference Include="System.Configuration.Install" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.ServiceProcess" />
|
<Reference Include="System.ServiceProcess" />
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ArchiveProvider.cs" />
|
<Compile Include="ArchiveProvider.cs" />
|
||||||
@ -136,7 +130,6 @@
|
|||||||
<Compile Include="ServiceFactory.cs" />
|
<Compile Include="ServiceFactory.cs" />
|
||||||
<Compile Include="StringExtention.cs" />
|
<Compile Include="StringExtention.cs" />
|
||||||
<Compile Include="HttpProvider.cs" />
|
<Compile Include="HttpProvider.cs" />
|
||||||
<Compile Include="IConfigFileProvider.cs" />
|
|
||||||
<Compile Include="ConsoleService.cs" />
|
<Compile Include="ConsoleService.cs" />
|
||||||
<Compile Include="Contract\ReportBase.cs" />
|
<Compile Include="Contract\ReportBase.cs" />
|
||||||
<Compile Include="Contract\ParseErrorReport.cs" />
|
<Compile Include="Contract\ParseErrorReport.cs" />
|
||||||
@ -149,12 +142,10 @@
|
|||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Properties\SharedAssemblyInfo.cs" />
|
<Compile Include="Properties\SharedAssemblyInfo.cs" />
|
||||||
<Compile Include="RestProvider.cs" />
|
<Compile Include="RestProvider.cs" />
|
||||||
<Compile Include="FirewallAdapter.cs" />
|
|
||||||
<Compile Include="IServiceProvider.cs" />
|
<Compile Include="IServiceProvider.cs" />
|
||||||
<Compile Include="TinyIoC.cs" />
|
<Compile Include="TinyIoC.cs" />
|
||||||
<Compile Include="TryParseExtension.cs" />
|
<Compile Include="TryParseExtension.cs" />
|
||||||
<Compile Include="UdpProvider.cs" />
|
<Compile Include="UdpProvider.cs" />
|
||||||
<Compile Include="UrlAclAdapter.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="packages.config">
|
<None Include="packages.config">
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
namespace NzbDrone.Common
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NzbDrone.Common
|
||||||
{
|
{
|
||||||
public static class StringExtension
|
public static class StringExtension
|
||||||
{
|
{
|
||||||
@ -13,5 +16,10 @@ public static string NullSafe(this string target)
|
|||||||
{
|
{
|
||||||
return ((object)target).NullSafe().ToString();
|
return ((object)target).NullSafe().ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string FirstCharToUpper(this string input)
|
||||||
|
{
|
||||||
|
return input.First().ToString().ToUpper() + String.Join("", input.Skip(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,7 +32,6 @@ public class UpdateServiceFixture : CoreTest<UpdateService>
|
|||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IEnvironmentProvider>().SetupGet(c => c.SystemTemp).Returns(TempFolder);
|
Mocker.GetMock<IEnvironmentProvider>().SetupGet(c => c.SystemTemp).Returns(TempFolder);
|
||||||
Mocker.GetMock<IConfigFileProvider>().SetupGet(c => c.Guid).Returns(_clientGuid);
|
|
||||||
Mocker.GetMock<IUpdatePackageProvider>().Setup(c => c.GetLatestUpdate()).Returns(_updatePackage);
|
Mocker.GetMock<IUpdatePackageProvider>().Setup(c => c.GetLatestUpdate()).Returns(_updatePackage);
|
||||||
|
|
||||||
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
|
Mocker.GetMock<IProcessProvider>().Setup(c => c.GetCurrentProcess()).Returns(new ProcessInfo { Id = 12 });
|
||||||
|
189
NzbDrone.Core/Configuration/ConfigFileProvider.cs
Normal file
189
NzbDrone.Core/Configuration/ConfigFileProvider.cs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Xml.Linq;
|
||||||
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Model;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Configuration
|
||||||
|
{
|
||||||
|
public interface IConfigFileProvider
|
||||||
|
{
|
||||||
|
Dictionary<string, object> GetConfigDictionary();
|
||||||
|
void SaveConfigDictionary(Dictionary<string, object> configValues);
|
||||||
|
|
||||||
|
int Port { get; set; }
|
||||||
|
bool LaunchBrowser { get; set; }
|
||||||
|
AuthenticationType AuthenticationType { get; set; }
|
||||||
|
string BasicAuthUsername { get; set; }
|
||||||
|
string BasicAuthPassword { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConfigFileProvider : IConfigFileProvider
|
||||||
|
{
|
||||||
|
private readonly IEnvironmentProvider _environmentProvider;
|
||||||
|
private readonly ICached<string> _cache;
|
||||||
|
|
||||||
|
private readonly string _configFile;
|
||||||
|
|
||||||
|
public ConfigFileProvider(IEnvironmentProvider environmentProvider, ICacheManger cacheManger)
|
||||||
|
{
|
||||||
|
_environmentProvider = environmentProvider;
|
||||||
|
_cache = cacheManger.GetCache<string>(this);
|
||||||
|
_configFile = _environmentProvider.GetConfigPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, object> GetConfigDictionary()
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, object>(StringComparer.InvariantCultureIgnoreCase);
|
||||||
|
|
||||||
|
var type = GetType();
|
||||||
|
var properties = type.GetProperties();
|
||||||
|
|
||||||
|
foreach (var propertyInfo in properties)
|
||||||
|
{
|
||||||
|
var value = propertyInfo.GetValue(this, null);
|
||||||
|
|
||||||
|
dict.Add(propertyInfo.Name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveConfigDictionary(Dictionary<string, object> configValues)
|
||||||
|
{
|
||||||
|
_cache.Clear();
|
||||||
|
|
||||||
|
var allWithDefaults = GetConfigDictionary();
|
||||||
|
|
||||||
|
foreach (var configValue in configValues)
|
||||||
|
{
|
||||||
|
object currentValue;
|
||||||
|
allWithDefaults.TryGetValue(configValue.Key, out currentValue);
|
||||||
|
if (currentValue == null) continue;
|
||||||
|
|
||||||
|
var equal = configValue.Value.ToString().Equals(currentValue.ToString());
|
||||||
|
|
||||||
|
if (!equal)
|
||||||
|
{
|
||||||
|
SetValue(configValue.Key.FirstCharToUpper(), configValue.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Port
|
||||||
|
{
|
||||||
|
get { return GetValueInt("Port", 8989); }
|
||||||
|
set { SetValue("Port", value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LaunchBrowser
|
||||||
|
{
|
||||||
|
get { return GetValueBoolean("LaunchBrowser", true); }
|
||||||
|
set { SetValue("LaunchBrowser", value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthenticationType AuthenticationType
|
||||||
|
{
|
||||||
|
get { return GetValueEnum("AuthenticationType", AuthenticationType.Anonymous); }
|
||||||
|
set { SetValue("AuthenticationType", value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BasicAuthUsername
|
||||||
|
{
|
||||||
|
get { return GetValue("BasicAuthUsername", ""); }
|
||||||
|
set { SetValue("BasicAuthUsername", value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BasicAuthPassword
|
||||||
|
{
|
||||||
|
get { return GetValue("BasicAuthPassword", ""); }
|
||||||
|
set { SetValue("BasicAuthPassword", value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetValueInt(string key, int defaultValue)
|
||||||
|
{
|
||||||
|
return Convert.ToInt32(GetValue(key, defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetValueBoolean(string key, bool defaultValue)
|
||||||
|
{
|
||||||
|
return Convert.ToBoolean(GetValue(key, defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetValueEnum<T>(string key, T defaultValue)
|
||||||
|
{
|
||||||
|
return (T)Enum.Parse(typeof(T), GetValue(key, defaultValue), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetValue(string key, object defaultValue)
|
||||||
|
{
|
||||||
|
return _cache.Get(key, () =>
|
||||||
|
{
|
||||||
|
EnsureDefaultConfigFile();
|
||||||
|
|
||||||
|
var xDoc = XDocument.Load(_configFile);
|
||||||
|
var config = xDoc.Descendants("Config").Single();
|
||||||
|
|
||||||
|
var parentContainer = config;
|
||||||
|
|
||||||
|
var valueHolder = parentContainer.Descendants(key).ToList();
|
||||||
|
|
||||||
|
if (valueHolder.Count() == 1)
|
||||||
|
return valueHolder.First().Value;
|
||||||
|
|
||||||
|
//Save the value
|
||||||
|
SetValue(key, defaultValue);
|
||||||
|
|
||||||
|
//return the default value
|
||||||
|
return defaultValue.ToString();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(string key, object value)
|
||||||
|
{
|
||||||
|
EnsureDefaultConfigFile();
|
||||||
|
|
||||||
|
var xDoc = XDocument.Load(_configFile);
|
||||||
|
var config = xDoc.Descendants("Config").Single();
|
||||||
|
|
||||||
|
var parentContainer = config;
|
||||||
|
|
||||||
|
var keyHolder = parentContainer.Descendants(key);
|
||||||
|
|
||||||
|
if (keyHolder.Count() != 1)
|
||||||
|
{
|
||||||
|
parentContainer.Add(new XElement(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parentContainer.Descendants(key).Single().Value = value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache.Set(key, value.ToString());
|
||||||
|
|
||||||
|
xDoc.Save(_configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetValue(string key, Enum value)
|
||||||
|
{
|
||||||
|
SetValue(key, value.ToString().ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDefaultConfigFile()
|
||||||
|
{
|
||||||
|
if (!File.Exists(_configFile))
|
||||||
|
{
|
||||||
|
var xDoc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
|
||||||
|
xDoc.Add(new XElement("Config"));
|
||||||
|
xDoc.Save(_configFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,23 @@ public Dictionary<String, Object> AllWithDefaults()
|
|||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveValues(Dictionary<string, object> configValues)
|
||||||
|
{
|
||||||
|
var allWithDefaults = AllWithDefaults();
|
||||||
|
|
||||||
|
foreach (var configValue in configValues)
|
||||||
|
{
|
||||||
|
object currentValue;
|
||||||
|
allWithDefaults.TryGetValue(configValue.Key, out currentValue);
|
||||||
|
if (currentValue == null) continue;
|
||||||
|
|
||||||
|
var equal = configValue.Value.ToString().Equals(currentValue.ToString());
|
||||||
|
|
||||||
|
if (!equal)
|
||||||
|
SetValue(configValue.Key, configValue.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String SabHost
|
public String SabHost
|
||||||
{
|
{
|
||||||
get { return GetValue("SabHost", "localhost"); }
|
get { return GetValue("SabHost", "localhost"); }
|
||||||
@ -132,18 +149,6 @@ public string UpdateUrl
|
|||||||
set { SetValue("UpdateUrl", value); }
|
set { SetValue("UpdateUrl", value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TwitterAccessToken
|
|
||||||
{
|
|
||||||
get { return GetValue("TwitterAccessToken", String.Empty); }
|
|
||||||
set { SetValue("TwitterAccessToken", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string TwitterAccessTokenSecret
|
|
||||||
{
|
|
||||||
get { return GetValue("TwitterAccessTokenSecret", String.Empty); }
|
|
||||||
set { SetValue("TwitterAccessTokenSecret", value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EnableBacklogSearching
|
public bool EnableBacklogSearching
|
||||||
{
|
{
|
||||||
get { return GetValueBoolean("EnableBacklogSearching"); }
|
get { return GetValueBoolean("EnableBacklogSearching"); }
|
||||||
@ -365,22 +370,7 @@ public void SetValue(string key, Enum value)
|
|||||||
SetValue(key, value.ToString().ToLower());
|
SetValue(key, value.ToString().ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveValues(Dictionary<string, object> configValues)
|
|
||||||
{
|
|
||||||
var allWithDefaults = AllWithDefaults();
|
|
||||||
|
|
||||||
foreach (var configValue in configValues)
|
|
||||||
{
|
|
||||||
object currentValue;
|
|
||||||
allWithDefaults.TryGetValue(configValue.Key, out currentValue);
|
|
||||||
if (currentValue == null) continue;
|
|
||||||
|
|
||||||
var equal = configValue.Value.ToString().Equals(currentValue.ToString());
|
|
||||||
|
|
||||||
if (!equal)
|
|
||||||
SetValue(configValue.Key, configValue.Value.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureCache()
|
private void EnsureCache()
|
||||||
{
|
{
|
||||||
|
@ -23,8 +23,6 @@ public interface IConfigService
|
|||||||
bool UseSeasonFolder { get; set; }
|
bool UseSeasonFolder { get; set; }
|
||||||
string SortingSeasonFolderFormat { get; set; }
|
string SortingSeasonFolderFormat { get; set; }
|
||||||
int DefaultQualityProfile { get; set; }
|
int DefaultQualityProfile { get; set; }
|
||||||
string TwitterAccessToken { get; set; }
|
|
||||||
string TwitterAccessTokenSecret { get; set; }
|
|
||||||
bool EnableBacklogSearching { get; set; }
|
bool EnableBacklogSearching { get; set; }
|
||||||
bool AutoIgnorePreviouslyDownloadedEpisodes { get; set; }
|
bool AutoIgnorePreviouslyDownloadedEpisodes { get; set; }
|
||||||
int Retention { get; set; }
|
int Retention { get; set; }
|
||||||
|
@ -178,6 +178,7 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Annotations\FieldDefinitionAttribute.cs" />
|
<Compile Include="Annotations\FieldDefinitionAttribute.cs" />
|
||||||
<Compile Include="Configuration\Config.cs" />
|
<Compile Include="Configuration\Config.cs" />
|
||||||
|
<Compile Include="Configuration\ConfigFileProvider.cs" />
|
||||||
<Compile Include="Configuration\ConfigRepository.cs" />
|
<Compile Include="Configuration\ConfigRepository.cs" />
|
||||||
<Compile Include="Configuration\IConfigService.cs" />
|
<Compile Include="Configuration\IConfigService.cs" />
|
||||||
<Compile Include="Constants.cs" />
|
<Compile Include="Constants.cs" />
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.Messaging;
|
using NzbDrone.Common.Messaging;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Update.Commands;
|
using NzbDrone.Core.Update.Commands;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Update
|
namespace NzbDrone.Core.Update
|
||||||
@ -75,7 +76,7 @@ private void InstallUpdate(UpdatePackage updatePackage)
|
|||||||
var startInfo = new ProcessStartInfo
|
var startInfo = new ProcessStartInfo
|
||||||
{
|
{
|
||||||
FileName = _environmentProvider.GetUpdateClientExePath(),
|
FileName = _environmentProvider.GetUpdateClientExePath(),
|
||||||
Arguments = string.Format("{0} {1}", _processProvider.GetCurrentProcess().Id, _configFileProvider.Guid)
|
Arguments = string.Format("{0} {1}", _processProvider.GetCurrentProcess().Id)
|
||||||
};
|
};
|
||||||
|
|
||||||
var process = _processProvider.Start(startInfo);
|
var process = _processProvider.Start(startInfo);
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
using NzbDrone.Api.RootFolders;
|
using NzbDrone.Api.RootFolders;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
using NzbDrone.Common.Composition;
|
using NzbDrone.Common.Composition;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Integration.Test.Client;
|
using NzbDrone.Integration.Test.Client;
|
||||||
using NzbDrone.Owin;
|
using NzbDrone.Owin;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
using NLog;
|
using NLog;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Common.Messaging;
|
using NzbDrone.Common.Messaging;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
using NzbDrone.Test.Common.AutoMoq;
|
using NzbDrone.Test.Common.AutoMoq;
|
||||||
@ -20,6 +21,8 @@ public abstract class TestBase<TSubject> : TestBase where TSubject : class
|
|||||||
public void CoreTestSetup()
|
public void CoreTestSetup()
|
||||||
{
|
{
|
||||||
_subject = null;
|
_subject = null;
|
||||||
|
|
||||||
|
Mocker.SetConstant<ICacheManger>(new CacheManger());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TSubject Subject
|
protected TSubject Subject
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
using System.ServiceProcess;
|
using System.ServiceProcess;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Host;
|
||||||
using NzbDrone.Owin;
|
using NzbDrone.Owin;
|
||||||
|
|
||||||
namespace NzbDrone
|
namespace NzbDrone
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NetFwTypeLib;
|
using NetFwTypeLib;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Common
|
namespace NzbDrone.Host
|
||||||
{
|
{
|
||||||
public interface IFirewallAdapter
|
public interface IFirewallAdapter
|
||||||
{
|
{
|
@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
|
|
||||||
namespace NzbDrone.Common
|
namespace NzbDrone.Host
|
||||||
{
|
{
|
||||||
public interface IUrlAclAdapter
|
public interface IUrlAclAdapter
|
||||||
{
|
{
|
@ -89,6 +89,10 @@
|
|||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\FluentMigrator.1.0.6.0\tools\FluentMigrator.Runner.dll</HintPath>
|
<HintPath>..\packages\FluentMigrator.1.0.6.0\tools\FluentMigrator.Runner.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Interop.NetFwTypeLib">
|
||||||
|
<HintPath>..\Libraries\Interop.NetFwTypeLib.dll</HintPath>
|
||||||
|
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||||
|
</Reference>
|
||||||
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
<Reference Include="Microsoft.AspNet.SignalR.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.0.1\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
|
<HintPath>..\packages\Microsoft.AspNet.SignalR.Core.1.0.1\lib\net40\Microsoft.AspNet.SignalR.Core.dll</HintPath>
|
||||||
@ -137,6 +141,8 @@
|
|||||||
<Compile Include="ApplicationServer.cs">
|
<Compile Include="ApplicationServer.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Host\FirewallAdapter.cs" />
|
||||||
|
<Compile Include="Host\UrlAclAdapter.cs" />
|
||||||
<Compile Include="MainAppContainerBuilder.cs" />
|
<Compile Include="MainAppContainerBuilder.cs" />
|
||||||
<Compile Include="ApplicationModes.cs" />
|
<Compile Include="ApplicationModes.cs" />
|
||||||
<Compile Include="AppMain.cs" />
|
<Compile Include="AppMain.cs" />
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
using Microsoft.Owin.Hosting;
|
using Microsoft.Owin.Hosting;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Owin.MiddleWare;
|
using NzbDrone.Owin.MiddleWare;
|
||||||
using Owin;
|
using Owin;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
16
UI/Settings/General/GeneralSettingsModel.js
Normal file
16
UI/Settings/General/GeneralSettingsModel.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
"use strict";
|
||||||
|
define(['app'], function () {
|
||||||
|
NzbDrone.Settings.General.GeneralSettingsModel = Backbone.Model.extend({
|
||||||
|
url: NzbDrone.Constants.ApiRoot + '/settings/host',
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
this.on('change', function () {
|
||||||
|
this.isSaved = false;
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
this.on('sync', function () {
|
||||||
|
this.isSaved = true;
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
70
UI/Settings/General/GeneralTemplate.html
Normal file
70
UI/Settings/General/GeneralTemplate.html
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<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="text" placeholder="8989" name="port"/>
|
||||||
|
<span>
|
||||||
|
<i class="icon-exclamation-sign danger" title="Requires restart to take effect"></i>
|
||||||
|
</span>
|
||||||
|
</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"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<span class="help-inline-checkbox">
|
||||||
|
<i class="icon-question-sign" title="Open a web browser and navigate to NzbDrone homepage on app start. Has no effect if installed as a windows service"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Security</legend>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Authentication</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<select class="inputClass" name="authenticationType">
|
||||||
|
<option value="Anonymous">Anonymous</option>
|
||||||
|
<option value="Basic">Basic</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Username</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" placeholder="Username" name="basicAuthUsername"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Password</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<input type="password" name="basicAuthPassword"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
31
UI/Settings/General/GeneralView.js
Normal file
31
UI/Settings/General/GeneralView.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
define(['app', 'Settings/SettingsModel', 'Shared/Messenger'], function () {
|
||||||
|
|
||||||
|
NzbDrone.Settings.General.GeneralView = Backbone.Marionette.ItemView.extend({
|
||||||
|
template: 'Settings/General/GeneralTemplate',
|
||||||
|
|
||||||
|
initialize: function () {
|
||||||
|
|
||||||
|
NzbDrone.vent.on(NzbDrone.Commands.SaveSettings, this.saveSettings, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
saveSettings: function () {
|
||||||
|
if (!this.model.isSaved) {
|
||||||
|
this.model.save(undefined, this.syncNotification("Naming Settings Saved", "Couldn't Save Naming Settings"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
syncNotification: function (success, error) {
|
||||||
|
return {
|
||||||
|
success: function () {
|
||||||
|
NzbDrone.Shared.Messenger.show({message: 'General Settings Saved'});
|
||||||
|
},
|
||||||
|
error : function () {
|
||||||
|
NzbDrone.Shared.Messenger.show({message: "Couldn't Save General Settings", type: 'error'});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
@ -1,191 +1,198 @@
|
|||||||
<fieldset>
|
<div class="form-horizontal">
|
||||||
<legend>Episode Naming</legend>
|
<fieldset>
|
||||||
|
<legend>Episode Naming</legend>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Use Scene Name</label>
|
<label class="control-label">Use Scene Name</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="useSceneName"/>
|
<input type="checkbox" name="useSceneName"/>
|
||||||
<p>
|
|
||||||
<span>On</span>
|
|
||||||
<span>Off</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="btn btn-primary slide-button"></div>
|
<p>
|
||||||
</label>
|
<span>On</span>
|
||||||
|
<span>Off</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Use the scene name, ignoring all other naming settings"></i>
|
<i class="icon-question-sign" title="Use the scene name, ignoring all other naming settings"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Include Series Title</label>
|
<label class="control-label">Include Series Title</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="includeSeriesTitle"/>
|
<input type="checkbox" name="includeSeriesTitle"/>
|
||||||
<p>
|
|
||||||
<span>On</span>
|
|
||||||
<span>Off</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="btn btn-primary slide-button"></div>
|
<p>
|
||||||
</label>
|
<span>On</span>
|
||||||
|
<span>Off</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Should filenames contain the series name when renamed?"></i>
|
<i class="icon-question-sign" title="Should filenames contain the series name when renamed?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Include Episode Title</label>
|
<label class="control-label">Include Episode Title</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="includeEpisodeTitle"/>
|
<input type="checkbox" name="includeEpisodeTitle"/>
|
||||||
<p>
|
|
||||||
<span>On</span>
|
|
||||||
<span>Off</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="btn btn-primary slide-button"></div>
|
<p>
|
||||||
</label>
|
<span>On</span>
|
||||||
|
<span>Off</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Should filenames contain the episode name when renamed?"></i>
|
<i class="icon-question-sign" title="Should filenames contain the episode name when renamed?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Include Quality</label>
|
<label class="control-label">Include Quality</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="includeQuality"/>
|
<input type="checkbox" name="includeQuality"/>
|
||||||
<p>
|
|
||||||
<span>On</span>
|
|
||||||
<span>Off</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="btn btn-primary slide-button"></div>
|
<p>
|
||||||
</label>
|
<span>On</span>
|
||||||
|
<span>Off</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Should filenames have the include the quality?"></i>
|
<i class="icon-question-sign" title="Should filenames have the include the quality?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Replace Spaces</label>
|
<label class="control-label">Replace Spaces</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<label class="checkbox toggle well">
|
<label class="checkbox toggle well">
|
||||||
<input type="checkbox" name="replaceSpaces"/>
|
<input type="checkbox" name="replaceSpaces"/>
|
||||||
<p>
|
|
||||||
<span>On</span>
|
|
||||||
<span>Off</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="btn btn-primary slide-button"></div>
|
<p>
|
||||||
</label>
|
<span>On</span>
|
||||||
|
<span>Off</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="btn btn-primary slide-button"></div>
|
||||||
|
</label>
|
||||||
|
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Do you want to replace spaces in the filename with periods?"></i>
|
<i class="icon-question-sign" title="Do you want to replace spaces in the filename with periods?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Season Folder Format</label>
|
<label class="control-label">Season Folder Format</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<input type="text" placeholder="Season %s" name="seasonFolderFormat"/>
|
<input type="text" placeholder="Season %s" name="seasonFolderFormat"/>
|
||||||
<span class="help-inline">
|
<span class="help-inline">
|
||||||
<i class="icon-question-sign" title="How should season folders be named? (Use %0s to pad to two digits)"></i>
|
<i class="icon-question-sign" title="How should season folders be named? (Use %0s to pad to two digits)"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Separator</label>
|
<label class="control-label">Separator</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<select class="inputClass x-backlog-setting" name="separator">
|
<select class="inputClass x-backlog-setting" name="separator">
|
||||||
<option value=" - ">Dash</option>
|
<option value=" - ">Dash</option>
|
||||||
<option value=" ">Space</option>
|
<option value=" ">Space</option>
|
||||||
<option value=".">Period</option>
|
<option value=".">Period</option>
|
||||||
</select>
|
</select>
|
||||||
<span class="help-inline">
|
<span class="help-inline">
|
||||||
<i class="icon-question-sign" title="How should NzbDrone separate sections of the filename?"></i>
|
<i class="icon-question-sign" title="How should NzbDrone separate sections of the filename?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Numbering Style</label>
|
<label class="control-label">Numbering Style</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<select class="inputClass x-backlog-setting" name="numberStyle">
|
<select class="inputClass x-backlog-setting" name="numberStyle">
|
||||||
<option value="0">1x05</option>
|
<option value="0">1x05</option>
|
||||||
<option value="1">01x05</option>
|
<option value="1">01x05</option>
|
||||||
<option value="2">S01E05</option>
|
<option value="2">S01E05</option>
|
||||||
<option value="3">s01e05</option>
|
<option value="3">s01e05</option>
|
||||||
</select>
|
</select>
|
||||||
<span class="help-inline">
|
<span class="help-inline">
|
||||||
<i class="icon-question-sign" title="What numbering style do you want?"></i>
|
<i class="icon-question-sign" title="What numbering style do you want?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class="control-label">Multi-Episode Style</label>
|
<label class="control-label">Multi-Episode Style</label>
|
||||||
|
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<select class="inputClass x-backlog-setting" name="multiEpisodeStyle">
|
<select class="inputClass x-backlog-setting" name="multiEpisodeStyle">
|
||||||
<option value="0">Extend</option>
|
<option value="0">Extend</option>
|
||||||
<option value="1">Duplicate</option>
|
<option value="1">Duplicate</option>
|
||||||
<option value="2">Repeat</option>
|
<option value="2">Repeat</option>
|
||||||
<option value="3">Scene</option>
|
<option value="3">Scene</option>
|
||||||
</select>
|
</select>
|
||||||
<span class="help-inline">
|
<span class="help-inline">
|
||||||
<i class="icon-question-sign" title="How will multi-episode files be named?"></i>
|
<i class="icon-question-sign" title="How will multi-episode files be named?"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
<fieldset>
|
|
||||||
<legend>Metadata</legend>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label">XBMC</label>
|
|
||||||
|
|
||||||
<div class="controls">
|
|
||||||
<div class="switch">
|
|
||||||
<input type="checkbox" name="metadataXbmcEnabled"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Metadata</legend>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">XBMC</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<div class="switch">
|
||||||
|
<input type="checkbox" name="metadataXbmcEnabled"/>
|
||||||
|
</div>
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Enable creating metadata for XBMC"></i>
|
<i class="icon-question-sign" title="Enable creating metadata for XBMC"></i>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label">Use Banners</label>
|
|
||||||
|
|
||||||
<div class="controls">
|
|
||||||
<div class="switch">
|
|
||||||
<input type="checkbox" name="metadataUseBanners"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label">Use Banners</label>
|
||||||
|
|
||||||
|
<div class="controls">
|
||||||
|
<div class="switch">
|
||||||
|
<input type="checkbox" name="metadataUseBanners"/>
|
||||||
|
</div>
|
||||||
<span class="help-inline-checkbox">
|
<span class="help-inline-checkbox">
|
||||||
<i class="icon-question-sign" title="Use banners instead of posters?"></i>
|
<i class="icon-question-sign" title="Use banners instead of posters?"></i>
|
||||||
</span>
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</fieldset>
|
||||||
</fieldset>
|
</div>
|
@ -3,7 +3,6 @@ define(['app', 'Settings/Naming/NamingModel'], function () {
|
|||||||
|
|
||||||
NzbDrone.Settings.Naming.NamingView = Backbone.Marionette.ItemView.extend({
|
NzbDrone.Settings.Naming.NamingView = Backbone.Marionette.ItemView.extend({
|
||||||
template : 'Settings/Naming/NamingTemplate',
|
template : 'Settings/Naming/NamingTemplate',
|
||||||
className: 'form-horizontal',
|
|
||||||
|
|
||||||
ui: {
|
ui: {
|
||||||
tooltip: '[class^="help-inline"] i'
|
tooltip: '[class^="help-inline"] i'
|
||||||
|
@ -6,7 +6,8 @@ define([
|
|||||||
'Settings/Indexers/CollectionView',
|
'Settings/Indexers/CollectionView',
|
||||||
'Settings/DownloadClient/DownloadClientView',
|
'Settings/DownloadClient/DownloadClientView',
|
||||||
'Settings/Notifications/CollectionView',
|
'Settings/Notifications/CollectionView',
|
||||||
'Settings/System/SystemView',
|
'Settings/General/GeneralView',
|
||||||
|
'Settings/General/GeneralSettingsModel',
|
||||||
'Settings/Misc/MiscView'
|
'Settings/Misc/MiscView'
|
||||||
],
|
],
|
||||||
function () {
|
function () {
|
||||||
@ -19,7 +20,7 @@ define([
|
|||||||
indexers : '#indexers',
|
indexers : '#indexers',
|
||||||
downloadClient: '#download-client',
|
downloadClient: '#download-client',
|
||||||
notifications : '#notifications',
|
notifications : '#notifications',
|
||||||
system : '#system',
|
general : '#general',
|
||||||
misc : '#misc'
|
misc : '#misc'
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ define([
|
|||||||
indexersTab : '.x-indexers-tab',
|
indexersTab : '.x-indexers-tab',
|
||||||
downloadClientTab: '.x-download-client-tab',
|
downloadClientTab: '.x-download-client-tab',
|
||||||
notificationsTab : '.x-notifications-tab',
|
notificationsTab : '.x-notifications-tab',
|
||||||
systemTab : '.x-system-tab',
|
generalTab : '.x-general-tab',
|
||||||
miscTab : '.x-misc-tab'
|
miscTab : '.x-misc-tab'
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ define([
|
|||||||
'click .x-indexers-tab' : 'showIndexers',
|
'click .x-indexers-tab' : 'showIndexers',
|
||||||
'click .x-download-client-tab': 'showDownloadClient',
|
'click .x-download-client-tab': 'showDownloadClient',
|
||||||
'click .x-notifications-tab' : 'showNotifications',
|
'click .x-notifications-tab' : 'showNotifications',
|
||||||
'click .x-system-tab' : 'showSystem',
|
'click .x-general-tab' : 'showGeneral',
|
||||||
'click .x-misc-tab' : 'showMisc',
|
'click .x-misc-tab' : 'showMisc',
|
||||||
'click .x-save-settings' : 'save'
|
'click .x-save-settings' : 'save'
|
||||||
},
|
},
|
||||||
@ -89,13 +90,13 @@ define([
|
|||||||
NzbDrone.Router.navigate('settings/notifications');
|
NzbDrone.Router.navigate('settings/notifications');
|
||||||
},
|
},
|
||||||
|
|
||||||
showSystem: function (e) {
|
showGeneral: function (e) {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.ui.systemTab.tab('show');
|
this.ui.generalTab.tab('show');
|
||||||
NzbDrone.Router.navigate('settings/system');
|
NzbDrone.Router.navigate('settings/general');
|
||||||
},
|
},
|
||||||
|
|
||||||
showMisc: function (e) {
|
showMisc: function (e) {
|
||||||
@ -111,6 +112,9 @@ define([
|
|||||||
this.settings = new NzbDrone.Settings.SettingsModel();
|
this.settings = new NzbDrone.Settings.SettingsModel();
|
||||||
this.settings.fetch();
|
this.settings.fetch();
|
||||||
|
|
||||||
|
this.generalSettings = new NzbDrone.Settings.General.GeneralSettingsModel();
|
||||||
|
this.generalSettings.fetch();
|
||||||
|
|
||||||
this.namingSettings = new NzbDrone.Settings.Naming.NamingModel();
|
this.namingSettings = new NzbDrone.Settings.Naming.NamingModel();
|
||||||
this.namingSettings.fetch();
|
this.namingSettings.fetch();
|
||||||
|
|
||||||
@ -131,7 +135,7 @@ define([
|
|||||||
this.indexers.show(new NzbDrone.Settings.Indexers.CollectionView({collection: this.indexerSettings}));
|
this.indexers.show(new NzbDrone.Settings.Indexers.CollectionView({collection: this.indexerSettings}));
|
||||||
this.downloadClient.show(new NzbDrone.Settings.DownloadClient.DownloadClientView({model: this.settings}));
|
this.downloadClient.show(new NzbDrone.Settings.DownloadClient.DownloadClientView({model: this.settings}));
|
||||||
this.notifications.show(new NzbDrone.Settings.Notifications.CollectionView({collection: this.notificationSettings}));
|
this.notifications.show(new NzbDrone.Settings.Notifications.CollectionView({collection: this.notificationSettings}));
|
||||||
this.system.show(new NzbDrone.Settings.System.SystemView({model: this.settings}));
|
this.general.show(new NzbDrone.Settings.General.GeneralView({model: this.generalSettings}));
|
||||||
this.misc.show(new NzbDrone.Settings.Misc.MiscView({model: this.settings}));
|
this.misc.show(new NzbDrone.Settings.Misc.MiscView({model: this.settings}));
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -149,8 +153,8 @@ define([
|
|||||||
case 'notifications':
|
case 'notifications':
|
||||||
this.showNotifications();
|
this.showNotifications();
|
||||||
break;
|
break;
|
||||||
case 'system':
|
case 'general':
|
||||||
this.showSystem();
|
this.showGeneral();
|
||||||
break;
|
break;
|
||||||
case 'misc':
|
case 'misc':
|
||||||
this.showMisc();
|
this.showMisc();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<li><a href="#indexers" class="x-indexers-tab">Indexers</a></li>
|
<li><a href="#indexers" class="x-indexers-tab">Indexers</a></li>
|
||||||
<li><a href="#download-client" class="x-download-client-tab">Download Client</a></li>
|
<li><a href="#download-client" class="x-download-client-tab">Download Client</a></li>
|
||||||
<li><a href="#notifications" class="x-notifications-tab">Notifications</a></li>
|
<li><a href="#notifications" class="x-notifications-tab">Notifications</a></li>
|
||||||
<li><a href="#system" class="x-system-tab">System</a></li>
|
<li><a href="#general" class="x-general-tab">general</a></li>
|
||||||
<li><a href="#misc" class="x-misc-tab">Misc</a></li>
|
<li><a href="#misc" class="x-misc-tab">Misc</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<div class="tab-pane" id="indexers">Indexer Settings</div>
|
<div class="tab-pane" id="indexers">Indexer Settings</div>
|
||||||
<div class="tab-pane" id="download-client">Download Client Settings</div>
|
<div class="tab-pane" id="download-client">Download Client Settings</div>
|
||||||
<div class="tab-pane" id="notifications">Notification Settings</div>
|
<div class="tab-pane" id="notifications">Notification Settings</div>
|
||||||
<div class="tab-pane" id="system">System Settings</div>
|
<div class="tab-pane" id="general">general Settings</div>
|
||||||
<div class="tab-pane" id="misc">Misc Settings</div>
|
<div class="tab-pane" id="misc">Misc Settings</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
<div>
|
|
||||||
System settings will go here
|
|
||||||
</div>
|
|
@ -1,11 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
define([
|
|
||||||
'app', 'Settings/SettingsModel'
|
|
||||||
|
|
||||||
], function () {
|
|
||||||
|
|
||||||
NzbDrone.Settings.System.SystemView = Backbone.Marionette.ItemView.extend({
|
|
||||||
template: 'Settings/System/SystemTemplate'
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user