mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
parent
492b114510
commit
0f2bba0615
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Api.Notifications
|
||||
{
|
||||
@ -9,6 +8,11 @@ public class NotificationResource : ProviderResource
|
||||
public bool OnGrab { get; set; }
|
||||
public bool OnDownload { get; set; }
|
||||
public bool OnUpgrade { get; set; }
|
||||
public bool OnRename { get; set; }
|
||||
public bool SupportsOnGrab { get; set; }
|
||||
public bool SupportsOnDownload { get; set; }
|
||||
public bool SupportsOnUpgrade { get; set; }
|
||||
public bool SupportsOnRename { get; set; }
|
||||
public string TestCommand { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
}
|
||||
|
@ -177,6 +177,7 @@
|
||||
<Compile Include="Extensions\PathExtensions.cs" />
|
||||
<Compile Include="Processes\PidFileProvider.cs" />
|
||||
<Compile Include="Processes\ProcessOutput.cs" />
|
||||
<Compile Include="Processes\ProcessOutputLine.cs" />
|
||||
<Compile Include="Processes\ProcessProvider.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Properties\SharedAssemblyInfo.cs" />
|
||||
|
@ -1,17 +1,32 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Common.Processes
|
||||
{
|
||||
public class ProcessOutput
|
||||
{
|
||||
public List<String> Standard { get; private set; }
|
||||
public List<String> Error { get; private set; }
|
||||
public int ExitCode { get; set; }
|
||||
public List<ProcessOutputLine> Lines { get; set; }
|
||||
|
||||
public ProcessOutput()
|
||||
{
|
||||
Standard = new List<string>();
|
||||
Error = new List<string>();
|
||||
Lines = new List<ProcessOutputLine>();
|
||||
}
|
||||
|
||||
public List<ProcessOutputLine> Standard
|
||||
{
|
||||
get
|
||||
{
|
||||
return Lines.Where(c => c.Level == ProcessOutputLevel.Standard).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<ProcessOutputLine> Error
|
||||
{
|
||||
get
|
||||
{
|
||||
return Lines.Where(c => c.Level == ProcessOutputLevel.Error).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
29
src/NzbDrone.Common/Processes/ProcessOutputLine.cs
Normal file
29
src/NzbDrone.Common/Processes/ProcessOutputLine.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace NzbDrone.Common.Processes
|
||||
{
|
||||
public class ProcessOutputLine
|
||||
{
|
||||
public ProcessOutputLevel Level { get; set; }
|
||||
public string Content { get; set; }
|
||||
public DateTime Time { get; set; }
|
||||
|
||||
public ProcessOutputLine(ProcessOutputLevel level, string content)
|
||||
{
|
||||
Level = level;
|
||||
Content = content;
|
||||
Time = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("{0} - {1} - {2}", Time, Level, Content);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ProcessOutputLevel
|
||||
{
|
||||
Standard = 0,
|
||||
Error = 1
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@ -24,9 +26,9 @@ public interface IProcessProvider
|
||||
Boolean Exists(int processId);
|
||||
Boolean Exists(string processName);
|
||||
ProcessPriorityClass GetCurrentProcessPriority();
|
||||
Process Start(string path, string args = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null);
|
||||
Process SpawnNewProcess(string path, string args = null);
|
||||
ProcessOutput StartAndCapture(string path, string args = null);
|
||||
Process Start(string path, string args = null, StringDictionary environmentVariables = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null);
|
||||
Process SpawnNewProcess(string path, string args = null, StringDictionary environmentVariables = null);
|
||||
ProcessOutput StartAndCapture(string path, string args = null, StringDictionary environmentVariables = null);
|
||||
}
|
||||
|
||||
public class ProcessProvider : IProcessProvider
|
||||
@ -104,7 +106,7 @@ public void OpenDefaultBrowser(string url)
|
||||
process.Start();
|
||||
}
|
||||
|
||||
public Process Start(string path, string args = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null)
|
||||
public Process Start(string path, string args = null, StringDictionary environmentVariables = null, Action<string> onOutputDataReceived = null, Action<string> onErrorDataReceived = null)
|
||||
{
|
||||
if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
@ -123,6 +125,13 @@ public Process Start(string path, string args = null, Action<string> onOutputDat
|
||||
RedirectStandardInput = true
|
||||
};
|
||||
|
||||
if (environmentVariables != null)
|
||||
{
|
||||
foreach (DictionaryEntry environmentVariable in environmentVariables)
|
||||
{
|
||||
startInfo.EnvironmentVariables.Add(environmentVariable.Key.ToString(), environmentVariable.Value.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug("Starting {0} {1}", path, args);
|
||||
|
||||
@ -163,7 +172,7 @@ public Process Start(string path, string args = null, Action<string> onOutputDat
|
||||
return process;
|
||||
}
|
||||
|
||||
public Process SpawnNewProcess(string path, string args = null)
|
||||
public Process SpawnNewProcess(string path, string args = null, StringDictionary environmentVariables = null)
|
||||
{
|
||||
if (OsInfo.IsMonoRuntime && path.EndsWith(".exe", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
@ -184,10 +193,14 @@ public Process SpawnNewProcess(string path, string args = null)
|
||||
return process;
|
||||
}
|
||||
|
||||
public ProcessOutput StartAndCapture(string path, string args = null)
|
||||
public ProcessOutput StartAndCapture(string path, string args = null, StringDictionary environmentVariables = null)
|
||||
{
|
||||
var output = new ProcessOutput();
|
||||
Start(path, args, s => output.Standard.Add(s), error => output.Error.Add(error)).WaitForExit();
|
||||
var process = Start(path, args, environmentVariables, s => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Standard, s)),
|
||||
error => output.Lines.Add(new ProcessOutputLine(ProcessOutputLevel.Error, error)));
|
||||
|
||||
process.WaitForExit();
|
||||
output.ExitCode = process.ExitCode;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ public void should_not_update_library_if_disabled()
|
||||
{
|
||||
(Subject.Definition.Settings as SynologyIndexerSettings).UpdateLibrary = false;
|
||||
|
||||
Subject.AfterRename(_series);
|
||||
Subject.OnRename(_series);
|
||||
|
||||
Mocker.GetMock<ISynologyIndexerProxy>()
|
||||
.Verify(v => v.UpdateFolder(_series.Path), Times.Never());
|
||||
@ -93,7 +93,7 @@ public void should_add_new_episode_on_upgrade()
|
||||
[Test]
|
||||
public void should_update_entire_series_folder_on_rename()
|
||||
{
|
||||
Subject.AfterRename(_series);
|
||||
Subject.OnRename(_series);
|
||||
|
||||
Mocker.GetMock<ISynologyIndexerProxy>()
|
||||
.Verify(v => v.UpdateFolder(@"C:\Test\".AsOsAgnostic()), Times.Once());
|
||||
|
@ -145,7 +145,7 @@ public void should_start_update_client()
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IProcessProvider>()
|
||||
.Verify(c => c.Start(It.IsAny<string>(), It.Is<String>(s => s.StartsWith("12")), null, null), Times.Once());
|
||||
.Verify(c => c.Start(It.IsAny<string>(), It.Is<String>(s => s.StartsWith("12")), null, null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -179,7 +179,7 @@ public void should_run_script_if_configured()
|
||||
|
||||
Subject.Execute(new ApplicationUpdateCommand());
|
||||
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Once());
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null, null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -193,7 +193,7 @@ public void should_throw_if_script_is_not_set()
|
||||
Assert.Throws<CommandFailedException>(() => Subject.Execute(new ApplicationUpdateCommand()));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null, null), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -207,7 +207,7 @@ public void should_throw_if_script_is_null()
|
||||
Assert.Throws<CommandFailedException>(() => Subject.Execute(new ApplicationUpdateCommand()));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null, null), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -225,7 +225,7 @@ public void should_throw_if_script_path_does_not_exist()
|
||||
Assert.Throws<CommandFailedException>(() => Subject.Execute(new ApplicationUpdateCommand()));
|
||||
|
||||
ExceptionVerification.ExpectedErrors(1);
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null), Times.Never());
|
||||
Mocker.GetMock<IProcessProvider>().Verify(v => v.Start(scriptPath, It.IsAny<String>(), null, null, null), Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NzbDrone.Core.Annotations
|
||||
{
|
||||
@ -10,10 +11,10 @@ public FieldDefinitionAttribute(int order)
|
||||
Order = order;
|
||||
}
|
||||
|
||||
public Int32 Order { get; private set; }
|
||||
public String Label { get; set; }
|
||||
public String HelpText { get; set; }
|
||||
public String HelpLink { get; set; }
|
||||
public int Order { get; private set; }
|
||||
public string Label { get; set; }
|
||||
public string HelpText { get; set; }
|
||||
public string HelpLink { get; set; }
|
||||
public FieldType Type { get; set; }
|
||||
public Boolean Advanced { get; set; }
|
||||
public Type SelectOptions { get; set; }
|
||||
|
@ -0,0 +1,21 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(87)]
|
||||
public class add_on_rename_to_notifcations : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Notifications").AddColumn("OnRename").AsBoolean().Nullable();
|
||||
|
||||
Execute.Sql("UPDATE Notifications SET OnRename = OnDownload WHERE Implementation IN ('PlexServer', 'Xbmc', 'MediaBrowser')");
|
||||
Execute.Sql("UPDATE Notifications SET OnRename = 0 WHERE Implementation NOT IN ('PlexServer', 'Xbmc', 'MediaBrowser')");
|
||||
|
||||
Alter.Table("Notifications").AlterColumn("OnRename").AsBoolean().NotNullable();
|
||||
|
||||
Execute.Sql("UPDATE Notifications SET OnGrab = 0 WHERE Implementation = 'PlexServer'");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(89)]
|
||||
public class add_on_rename_to_notifcations : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("Notifications").AddColumn("OnRename").AsBoolean().Nullable();
|
||||
|
||||
Execute.Sql("UPDATE Notifications SET OnRename = OnDownload WHERE Implementation IN ('PlexServer', 'Xbmc', 'MediaBrowser')");
|
||||
Execute.Sql("UPDATE Notifications SET OnRename = 0 WHERE Implementation NOT IN ('PlexServer', 'Xbmc', 'MediaBrowser')");
|
||||
|
||||
Alter.Table("Notifications").AlterColumn("OnRename").AsBoolean().NotNullable();
|
||||
|
||||
Execute.Sql("UPDATE Notifications SET OnGrab = 0 WHERE Implementation = 'PlexServer'");
|
||||
}
|
||||
}
|
||||
}
|
@ -53,7 +53,12 @@ public static void Map()
|
||||
.Ignore(i => i.SupportsRss)
|
||||
.Ignore(i => i.SupportsSearch);
|
||||
|
||||
Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications");
|
||||
Mapper.Entity<NotificationDefinition>().RegisterDefinition("Notifications")
|
||||
.Ignore(i => i.SupportsOnGrab)
|
||||
.Ignore(i => i.SupportsOnDownload)
|
||||
.Ignore(i => i.SupportsOnUpgrade)
|
||||
.Ignore(i => i.SupportsOnRename);
|
||||
|
||||
Mapper.Entity<MetadataDefinition>().RegisterDefinition("Metadata");
|
||||
|
||||
Mapper.Entity<DownloadClientDefinition>().RegisterDefinition("DownloadClients")
|
||||
|
58
src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
Normal file
58
src/NzbDrone.Core/Notifications/CustomScript/CustomScript.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Core.Tv;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.CustomScript
|
||||
{
|
||||
public class CustomScript : NotificationBase<CustomScriptSettings>
|
||||
{
|
||||
private readonly ICustomScriptService _customScriptService;
|
||||
|
||||
public CustomScript(ICustomScriptService customScriptService)
|
||||
{
|
||||
_customScriptService = customScriptService;
|
||||
}
|
||||
|
||||
public override string Link
|
||||
{
|
||||
get { return "https://github.com/Sonarr/Sonarr/wiki/Custom-Post-Processing-Scripts"; }
|
||||
}
|
||||
|
||||
public override void OnGrab(string message)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnDownload(DownloadMessage message)
|
||||
{
|
||||
_customScriptService.OnDownload(message.Series, message.EpisodeFile, Settings);
|
||||
}
|
||||
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
_customScriptService.OnRename(series, Settings);
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Custom Script";
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnGrab
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Common.Processes;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.CustomScript
|
||||
{
|
||||
public interface ICustomScriptService
|
||||
{
|
||||
void OnDownload(Series series, EpisodeFile episodeFile, CustomScriptSettings settings);
|
||||
void OnRename(Series series, CustomScriptSettings settings);
|
||||
ValidationFailure Test(CustomScriptSettings settings);
|
||||
}
|
||||
|
||||
public class CustomScriptService : ICustomScriptService
|
||||
{
|
||||
private readonly IProcessProvider _processProvider;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public CustomScriptService(IProcessProvider processProvider, IDiskProvider diskProvider, Logger logger)
|
||||
{
|
||||
_processProvider = processProvider;
|
||||
_diskProvider = diskProvider;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnDownload(Series series, EpisodeFile episodeFile, CustomScriptSettings settings)
|
||||
{
|
||||
var environmentVariables = new StringDictionary();
|
||||
|
||||
environmentVariables.Add("Sonarr.EventType", "Download");
|
||||
environmentVariables.Add("Sonarr.Series.Id", series.Id.ToString());
|
||||
environmentVariables.Add("Sonarr.Series.Title", series.Title);
|
||||
environmentVariables.Add("Sonarr.Series.Path", series.Path);
|
||||
environmentVariables.Add("Sonarr.Series.TvdbId", series.TvdbId.ToString());
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.Id", episodeFile.Id.ToString());
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.RelativePath", episodeFile.RelativePath);
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.Path", Path.Combine(series.Path, episodeFile.RelativePath));
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.SeasonNumber", episodeFile.SeasonNumber.ToString());
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.EpisodeNumbers", String.Join(",", episodeFile.Episodes.Value.Select(e => e.EpisodeNumber)));
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.EpisodeAirDates", String.Join(",", episodeFile.Episodes.Value.Select(e => e.AirDate)));
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.EpisodeAirDatesUtc", String.Join(",", episodeFile.Episodes.Value.Select(e => e.AirDateUtc)));
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.Quality", episodeFile.Quality.Quality.Name);
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.QualityVersion", episodeFile.Quality.Revision.Version.ToString());
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.ReleaseGroup", episodeFile.ReleaseGroup ?? String.Empty);
|
||||
environmentVariables.Add("Sonarr.EpisodeFile.SceneName", episodeFile.SceneName ?? String.Empty);
|
||||
|
||||
ExecuteScript(environmentVariables, settings);
|
||||
}
|
||||
|
||||
public void OnRename(Series series, CustomScriptSettings settings)
|
||||
{
|
||||
var environmentVariables = new StringDictionary();
|
||||
|
||||
environmentVariables.Add("Sonarr.EventType", "Rename");
|
||||
environmentVariables.Add("Sonarr.Series.Id", series.Id.ToString());
|
||||
environmentVariables.Add("Sonarr.Series.Title", series.Title);
|
||||
environmentVariables.Add("Sonarr.Series.Path", series.Path);
|
||||
environmentVariables.Add("Sonarr.Series.TvdbId", series.TvdbId.ToString());
|
||||
|
||||
ExecuteScript(environmentVariables, settings);
|
||||
}
|
||||
|
||||
public ValidationFailure Test(CustomScriptSettings settings)
|
||||
{
|
||||
if (!_diskProvider.FileExists(settings.Path))
|
||||
{
|
||||
return new NzbDroneValidationFailure("Path", "File does not exist");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void ExecuteScript(StringDictionary environmentVariables, CustomScriptSettings settings)
|
||||
{
|
||||
_logger.Debug("Executing external script: {0}", settings.Path);
|
||||
|
||||
var process = _processProvider.StartAndCapture(settings.Path, settings.Arguments, environmentVariables);
|
||||
|
||||
_logger.Debug("Executed external script: {0} - Status: {1}", settings.Path, process.ExitCode);
|
||||
_logger.Debug("Script Output: \r\n{0}", String.Join("\r\n", process.Lines));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
using NzbDrone.Core.Validation.Paths;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.CustomScript
|
||||
{
|
||||
public class CustomScriptSettingsValidator : AbstractValidator<CustomScriptSettings>
|
||||
{
|
||||
public CustomScriptSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.Path).IsValidPath();
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomScriptSettings : IProviderConfig
|
||||
{
|
||||
private static readonly CustomScriptSettingsValidator Validator = new CustomScriptSettingsValidator();
|
||||
|
||||
[FieldDefinition(0, Label = "Path", Type = FieldType.Path)]
|
||||
public String Path { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Arguments", HelpText = "Arguments to pass to the script")]
|
||||
public String Arguments { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_emailService.SendEmail(Settings, subject, body);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -48,6 +48,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -33,7 +33,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_growlService.SendNotification(title, message.Message, "DOWNLOAD", Settings.Host, Settings.Port, Settings.Password);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -45,6 +45,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -9,6 +9,10 @@ public interface INotification : IProvider
|
||||
|
||||
void OnGrab(string message);
|
||||
void OnDownload(DownloadMessage message);
|
||||
void AfterRename(Series series);
|
||||
void OnRename(Series series);
|
||||
bool SupportsOnGrab { get; }
|
||||
bool SupportsOnDownload { get; }
|
||||
bool SupportsOnUpgrade { get; }
|
||||
bool SupportsOnRename { get; }
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
}
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
|
@ -40,8 +40,13 @@ public IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
public abstract string Link { get; }
|
||||
|
||||
public abstract void OnGrab(string message);
|
||||
public abstract void OnDownload(DownloadMessage message);
|
||||
public abstract void AfterRename(Series series);
|
||||
public abstract void OnDownload(DownloadMessage message);
|
||||
public abstract void OnRename(Series series);
|
||||
|
||||
public virtual bool SupportsOnGrab { get { return true; } }
|
||||
public virtual bool SupportsOnDownload { get { return true; } }
|
||||
public virtual bool SupportsOnUpgrade { get { return true; } }
|
||||
public virtual bool SupportsOnRename { get { return true; } }
|
||||
|
||||
protected TSettings Settings
|
||||
{
|
||||
|
@ -11,12 +11,17 @@ public NotificationDefinition()
|
||||
Tags = new HashSet<Int32>();
|
||||
}
|
||||
|
||||
public Boolean OnGrab { get; set; }
|
||||
public Boolean OnDownload { get; set; }
|
||||
public Boolean OnUpgrade { get; set; }
|
||||
public HashSet<Int32> Tags { get; set; }
|
||||
public bool OnGrab { get; set; }
|
||||
public bool OnDownload { get; set; }
|
||||
public bool OnUpgrade { get; set; }
|
||||
public bool OnRename { get; set; }
|
||||
public bool SupportsOnGrab { get; set; }
|
||||
public bool SupportsOnDownload { get; set; }
|
||||
public bool SupportsOnUpgrade { get; set; }
|
||||
public bool SupportsOnRename { get; set; }
|
||||
public HashSet<int> Tags { get; set; }
|
||||
|
||||
public override Boolean Enable
|
||||
public override bool Enable
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -12,6 +12,7 @@ public interface INotificationFactory : IProviderFactory<INotification, Notifica
|
||||
List<INotification> OnGrabEnabled();
|
||||
List<INotification> OnDownloadEnabled();
|
||||
List<INotification> OnUpgradeEnabled();
|
||||
List<INotification> OnRenameEnabled();
|
||||
}
|
||||
|
||||
public class NotificationFactory : ProviderFactory<INotification, NotificationDefinition>, INotificationFactory
|
||||
@ -35,5 +36,22 @@ public List<INotification> OnUpgradeEnabled()
|
||||
{
|
||||
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnUpgrade).ToList();
|
||||
}
|
||||
|
||||
public List<INotification> OnRenameEnabled()
|
||||
{
|
||||
return GetAvailableProviders().Where(n => ((NotificationDefinition)n.Definition).OnRename).ToList();
|
||||
}
|
||||
|
||||
public override NotificationDefinition GetProviderCharacteristics(INotification provider, NotificationDefinition definition)
|
||||
{
|
||||
definition = base.GetProviderCharacteristics(provider, definition);
|
||||
|
||||
definition.SupportsOnGrab = provider.SupportsOnGrab;
|
||||
definition.SupportsOnDownload = provider.SupportsOnDownload;
|
||||
definition.SupportsOnUpgrade = provider.SupportsOnUpgrade;
|
||||
definition.SupportsOnRename = provider.SupportsOnRename;
|
||||
|
||||
return definition;
|
||||
}
|
||||
}
|
||||
}
|
@ -137,19 +137,19 @@ public void Handle(EpisodeDownloadedEvent message)
|
||||
|
||||
public void Handle(SeriesRenamedEvent message)
|
||||
{
|
||||
foreach (var notification in _notificationFactory.OnDownloadEnabled())
|
||||
foreach (var notification in _notificationFactory.OnRenameEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ShouldHandleSeries(notification.Definition, message.Series))
|
||||
{
|
||||
notification.AfterRename(message.Series);
|
||||
notification.OnRename(message.Series);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.WarnException("Unable to send AfterRename notification to: " + notification.Definition.Name, ex);
|
||||
_logger.WarnException("Unable to send OnRename notification to: " + notification.Definition.Name, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_proxy.SendNotification(title, message.Message, Settings.ApiKey, (NotifyMyAndroidPriority)Settings.Priority);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -46,6 +46,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -31,7 +31,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_plexClientService.Notify(Settings, header, message.Message);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -43,6 +43,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -39,7 +39,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
Notify(Settings, header, message.Message);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
|
||||
}
|
||||
@ -52,6 +52,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -28,7 +28,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
UpdateIfEnabled(message.Series);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
UpdateIfEnabled(series);
|
||||
}
|
||||
@ -49,6 +49,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnGrab
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -34,7 +34,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_prowlService.SendNotification(title, message.Message, Settings.ApiKey, (NotificationPriority)Settings.Priority);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -46,6 +46,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -33,7 +33,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_proxy.SendNotification(title, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -45,6 +45,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -34,7 +34,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_proxy.SendNotification(title, message.Message, Settings);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -46,6 +46,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -33,7 +33,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
_proxy.SendNotification(title, message.Message, Settings.ApiKey, Settings.UserKey, (PushoverPriority)Settings.Priority, Settings.Sound);
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
}
|
||||
|
||||
@ -45,6 +45,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -45,7 +45,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
}
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
@ -61,6 +61,14 @@ public override string Name
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SupportsOnRename
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
@ -40,7 +40,7 @@ public override void OnDownload(DownloadMessage message)
|
||||
UpdateAndClean(message.Series, message.OldFiles.Any());
|
||||
}
|
||||
|
||||
public override void AfterRename(Series series)
|
||||
public override void OnRename(Series series)
|
||||
{
|
||||
UpdateAndClean(series);
|
||||
}
|
||||
|
@ -254,6 +254,7 @@
|
||||
<Compile Include="Datastore\Migration\074_disable_eztv.cs" />
|
||||
<Compile Include="Datastore\Migration\073_clear_ratings.cs" />
|
||||
<Compile Include="Datastore\Migration\077_add_add_options_to_series.cs" />
|
||||
<Compile Include="Datastore\Migration\089_add_on_rename_to_notifcations.cs" />
|
||||
<Compile Include="Datastore\Migration\085_expand_transmission_urlbase.cs" />
|
||||
<Compile Include="Datastore\Migration\086_pushbullet_device_ids.cs" />
|
||||
<Compile Include="Datastore\Migration\081_move_dot_prefix_to_transmission_category.cs" />
|
||||
@ -710,6 +711,9 @@
|
||||
<Compile Include="Notifications\Plex\Models\PlexSectionItem.cs" />
|
||||
<Compile Include="Notifications\Plex\Models\PlexSection.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexAuthenticationException.cs" />
|
||||
<Compile Include="Notifications\CustomScript\CustomScript.cs" />
|
||||
<Compile Include="Notifications\CustomScript\CustomScriptService.cs" />
|
||||
<Compile Include="Notifications\CustomScript\CustomScriptSettings.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexHomeTheater.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexHomeTheaterSettings.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexClientService.cs" />
|
||||
|
@ -56,11 +56,11 @@ private bool IsRegistered()
|
||||
|
||||
if (output == null || !output.Standard.Any()) return false;
|
||||
|
||||
var hashLine = output.Standard.SingleOrDefault(line => CertificateHashRegex.IsMatch(line));
|
||||
var hashLine = output.Standard.SingleOrDefault(line => CertificateHashRegex.IsMatch(line.Content));
|
||||
|
||||
if (hashLine != null)
|
||||
{
|
||||
var match = CertificateHashRegex.Match(hashLine);
|
||||
var match = CertificateHashRegex.Match(hashLine.Content);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
@ -73,7 +73,7 @@ private bool IsRegistered()
|
||||
}
|
||||
}
|
||||
|
||||
return output.Standard.Any(line => line.Contains(ipPort));
|
||||
return output.Standard.Any(line => line.Content.Contains(ipPort));
|
||||
}
|
||||
|
||||
private void Unregister()
|
||||
|
@ -139,7 +139,7 @@ private List<UrlAcl> GetRegisteredUrls()
|
||||
|
||||
return output.Standard.Select(line =>
|
||||
{
|
||||
var match = UrlAclRegex.Match(line);
|
||||
var match = UrlAclRegex.Match(line.Content);
|
||||
|
||||
if (match.Success)
|
||||
{
|
||||
|
@ -85,7 +85,7 @@ public void KillAll()
|
||||
private void Start(string outputNzbdroneConsoleExe)
|
||||
{
|
||||
var args = "-nobrowser -data=\"" + AppData + "\"";
|
||||
_nzbDroneProcess = _processProvider.Start(outputNzbdroneConsoleExe, args, OnOutputDataReceived, OnOutputDataReceived);
|
||||
_nzbDroneProcess = _processProvider.Start(outputNzbdroneConsoleExe, args, null, OnOutputDataReceived, OnOutputDataReceived);
|
||||
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ public void should_start_console_if_app_type_was_service_but_start_failed_becaus
|
||||
|
||||
Subject.Start(AppType.Service, targetFolder);
|
||||
|
||||
Mocker.GetMock<IProcessProvider>().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", "/" + StartupContext.NO_BROWSER), Times.Once());
|
||||
Mocker.GetMock<IProcessProvider>().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", "/" + StartupContext.NO_BROWSER, null), Times.Once());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
@ -27,9 +27,10 @@ module.exports = Marionette.ItemView.extend({
|
||||
|
||||
this.model.set({
|
||||
id : undefined,
|
||||
onGrab : true,
|
||||
onDownload : true,
|
||||
onUpgrade : true
|
||||
onGrab : this.model.get('supportsOnGrab'),
|
||||
onDownload : this.model.get('supportsOnDownload'),
|
||||
onUpgrade : this.model.get('supportsOnUpgrade'),
|
||||
onRename : this.model.get('supportsOnRename')
|
||||
});
|
||||
|
||||
var editView = new EditView({
|
||||
@ -47,9 +48,10 @@ module.exports = Marionette.ItemView.extend({
|
||||
|
||||
this.model.set({
|
||||
id : undefined,
|
||||
onGrab : true,
|
||||
onDownload : true,
|
||||
onUpgrade : true
|
||||
onGrab : this.model.get('supportsOnGrab'),
|
||||
onDownload : this.model.get('supportsOnDownload'),
|
||||
onUpgrade : this.model.get('supportsOnUpgrade'),
|
||||
onRename : this.model.get('supportsOnRename')
|
||||
});
|
||||
|
||||
var editView = new EditView({
|
||||
|
@ -6,6 +6,7 @@ var AsValidatedView = require('../../../Mixins/AsValidatedView');
|
||||
var AsEditModalView = require('../../../Mixins/AsEditModalView');
|
||||
require('../../../Form/FormBuilder');
|
||||
require('../../../Mixins/TagInput');
|
||||
require('../../../Mixins/FileBrowser');
|
||||
require('bootstrap.tagsinput');
|
||||
|
||||
var view = Marionette.ItemView.extend({
|
||||
@ -15,7 +16,9 @@ var view = Marionette.ItemView.extend({
|
||||
onDownloadToggle : '.x-on-download',
|
||||
onUpgradeSection : '.x-on-upgrade',
|
||||
tags : '.x-tags',
|
||||
formTag : '.x-form-tag'
|
||||
modalBody : '.modal-body',
|
||||
formTag : '.x-form-tag',
|
||||
path : '.x-path'
|
||||
},
|
||||
|
||||
events : {
|
||||
@ -43,6 +46,14 @@ var view = Marionette.ItemView.extend({
|
||||
});
|
||||
},
|
||||
|
||||
onShow : function() {
|
||||
if (this.ui.path.length > 0) {
|
||||
this.ui.modalBody.addClass('modal-overflow');
|
||||
}
|
||||
|
||||
this.ui.path.fileBrowser();
|
||||
},
|
||||
|
||||
_onAfterSave : function() {
|
||||
this.targetCollection.add(this.model, { merge : true });
|
||||
vent.trigger(vent.Commands.CloseModalCommand);
|
||||
|
@ -23,7 +23,7 @@
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="onGrab"/>
|
||||
<input type="checkbox" name="onGrab" {{#unless supportsOnGrab}}disabled="disabled"{{/unless}}/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
@ -45,7 +45,7 @@
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="onDownload" class="x-on-download"/>
|
||||
<input type="checkbox" name="onDownload" class="x-on-download" {{#unless supportsOnDownload}}disabled="disabled"{{/unless}}/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
@ -67,7 +67,7 @@
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="onUpgrade"/>
|
||||
<input type="checkbox" name="onUpgrade" {{#unless supportsOnUpgrade}}disabled="disabled"{{/unless}}/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
@ -83,6 +83,28 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group x-on-upgrade">
|
||||
<label class="col-sm-3 control-label">On Rename</label>
|
||||
|
||||
<div class="col-sm-5">
|
||||
<div class="input-group">
|
||||
<label class="checkbox toggle well">
|
||||
<input type="checkbox" name="onRename" {{#unless supportsOnRename}}disabled="disabled"{{/unless}}/>
|
||||
<p>
|
||||
<span>Yes</span>
|
||||
<span>No</span>
|
||||
</p>
|
||||
|
||||
<div class="btn btn-primary slide-button"/>
|
||||
</label>
|
||||
|
||||
<span class="help-inline-checkbox">
|
||||
<i class="icon-sonarr-form-info" title="Be notified when episodes are renamed"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">Filter Series Tags</label>
|
||||
|
||||
|
@ -4,16 +4,44 @@
|
||||
</div>
|
||||
|
||||
<div class="settings">
|
||||
{{#if onGrab}}
|
||||
<span class="label label-success">On Grab</span>
|
||||
{{#if supportsOnGrab}}
|
||||
{{#if onGrab}}
|
||||
<span class="label label-success">On Grab</span>
|
||||
{{else}}
|
||||
<span class="label label-default">On Grab</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="label label-default">On Grab</span>
|
||||
<span class="label label-default label-disabled">On Grab</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if onDownload}}
|
||||
<span class="label label-success">On Download</span>
|
||||
{{#if supportsOnDownload}}
|
||||
{{#if onDownload}}
|
||||
<span class="label label-success">On Download</span>
|
||||
{{else}}
|
||||
<span class="label label-default">On Download</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="label label-default">On Download</span>
|
||||
<span class="label label-default label-disabled">On Download</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if supportsOnUpgrade}}
|
||||
{{#if onUpgrade}}
|
||||
<span class="label label-success">On Upgrade</span>
|
||||
{{else}}
|
||||
<span class="label label-default">On Upgrade</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="label label-default label-disabled">On Upgrade</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if supportsOnRename}}
|
||||
{{#if onRename}}
|
||||
<span class="label label-success">On Rename</span>
|
||||
{{else}}
|
||||
<span class="label label-default">On Rename</span>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<span class="label label-default label-disabled">On Rename</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,11 +10,17 @@
|
||||
.clickable;
|
||||
|
||||
width: 290px;
|
||||
height: 90px;
|
||||
height: 115px;
|
||||
padding: 20px 20px;
|
||||
|
||||
.settings {
|
||||
margin-top: 5px;
|
||||
|
||||
.label {
|
||||
display : inline-block;
|
||||
margin-bottom : 2px;
|
||||
padding : 4px 6px 3px 6px;
|
||||
}
|
||||
}
|
||||
|
||||
&.add-card {
|
||||
|
Loading…
Reference in New Issue
Block a user