diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs
index a0e10589a..a56e72b1f 100644
--- a/NzbDrone.Common/DiskProvider.cs
+++ b/NzbDrone.Common/DiskProvider.cs
@@ -225,5 +225,15 @@ namespace NzbDrone.Common
return new FileInfo(path).Length;
}
+
+ public virtual void FileSetLastWriteTimeUtc(string path, DateTime dateTime)
+ {
+ File.SetLastWriteTimeUtc(path, dateTime);
+ }
+
+ public virtual void DirectorySetLastWriteTimeUtc(string path, DateTime dateTime)
+ {
+ Directory.SetLastWriteTimeUtc(path, dateTime);
+ }
}
}
\ No newline at end of file
diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
index 695e3e405..a6efa6cf9 100644
--- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
+++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj
@@ -123,6 +123,10 @@
+
+
+
+
diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs
new file mode 100644
index 000000000..dd4255406
--- /dev/null
+++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/CleanupFixture.cs
@@ -0,0 +1,107 @@
+// ReSharper disable RedundantUsingDirective
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using FizzWare.NBuilder;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Model;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Providers.Core;
+using NzbDrone.Core.Repository;
+using NzbDrone.Core.Repository.Quality;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common.AutoMoq;
+using PetaPoco;
+using TvdbLib.Data;
+
+namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
+{
+ [TestFixture]
+ // ReSharper disable InconsistentNaming
+ public class CleanupFixture : CoreTest
+ {
+ private const string RecycleBin = @"C:\Test\RecycleBin";
+
+ private void WithExpired()
+ {
+ Mocker.GetMock().Setup(s => s.GetLastDirectoryWrite(It.IsAny()))
+ .Returns(DateTime.UtcNow.AddDays(-10));
+
+ Mocker.GetMock().Setup(s => s.GetLastFileWrite(It.IsAny()))
+ .Returns(DateTime.UtcNow.AddDays(-10));
+ }
+
+ private void WithNonExpired()
+ {
+ Mocker.GetMock().Setup(s => s.GetLastDirectoryWrite(It.IsAny()))
+ .Returns(DateTime.UtcNow.AddDays(-3));
+
+ Mocker.GetMock().Setup(s => s.GetLastFileWrite(It.IsAny()))
+ .Returns(DateTime.UtcNow.AddDays(-3));
+ }
+
+ [SetUp]
+ public void Setup()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(RecycleBin);
+
+ Mocker.GetMock().Setup(s => s.GetDirectories(RecycleBin))
+ .Returns(new [] { @"C:\Test\RecycleBin\Folder1", @"C:\Test\RecycleBin\Folder2", @"C:\Test\RecycleBin\Folder3" });
+
+ Mocker.GetMock().Setup(s => s.GetFiles(RecycleBin, SearchOption.TopDirectoryOnly))
+ .Returns(new [] { @"C:\Test\RecycleBin\File1.avi", @"C:\Test\RecycleBin\File2.mkv" });
+ }
+
+ [Test]
+ public void should_return_if_recycleBin_not_configured()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(String.Empty);
+
+ Mocker.Resolve().Cleanup();
+
+ Mocker.GetMock().Verify(v => v.GetDirectories(It.IsAny()), Times.Never());
+ }
+
+ [Test]
+ public void should_delete_all_expired_folders()
+ {
+ WithExpired();
+ Mocker.Resolve().Cleanup();
+
+ Mocker.GetMock().Verify(v => v.DeleteFolder(It.IsAny(), true), Times.Exactly(3));
+ }
+
+ [Test]
+ public void should_delete_all_expired_files()
+ {
+ WithExpired();
+ Mocker.Resolve().Cleanup();
+
+ Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Exactly(2));
+ }
+
+ [Test]
+ public void should_not_delete_all_non_expired_folders()
+ {
+ WithNonExpired();
+ Mocker.Resolve().Cleanup();
+
+ Mocker.GetMock().Verify(v => v.DeleteFolder(It.IsAny(), true), Times.Never());
+ }
+
+ [Test]
+ public void should_not_delete_all_non_expired_files()
+ {
+ WithNonExpired();
+ Mocker.Resolve().Cleanup();
+
+ Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Never());
+ }
+ }
+}
diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs
new file mode 100644
index 000000000..e4cd1fd3b
--- /dev/null
+++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteDirectoryFixture.cs
@@ -0,0 +1,89 @@
+// ReSharper disable RedundantUsingDirective
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using FizzWare.NBuilder;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Model;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Providers.Core;
+using NzbDrone.Core.Repository;
+using NzbDrone.Core.Repository.Quality;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common.AutoMoq;
+using PetaPoco;
+using TvdbLib.Data;
+
+namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
+{
+ [TestFixture]
+ // ReSharper disable InconsistentNaming
+ public class DeleteDirectoryFixture : CoreTest
+ {
+ private void WithRecycleBin()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(@"C:\Test\Recycle Bin");
+ }
+
+ private void WithoutRecycleBin()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(String.Empty);
+ }
+
+ [Test]
+ public void should_use_delete_when_recycleBin_is_not_configured()
+ {
+ WithoutRecycleBin();
+
+ var path = @"C:\Test\TV\30 Rock";
+
+ Mocker.Resolve().DeleteDirectory(path);
+
+ Mocker.GetMock().Verify(v => v.DeleteFolder(path, true), Times.Once());
+ }
+
+ [Test]
+ public void should_use_move_when_recycleBin_is_configured()
+ {
+ WithRecycleBin();
+
+ var path = @"C:\Test\TV\30 Rock";
+
+ Mocker.Resolve().DeleteDirectory(path);
+
+ Mocker.GetMock().Verify(v => v.MoveDirectory(path, @"C:\Test\Recycle Bin\30 Rock"), Times.Once());
+ }
+
+ [Test]
+ public void should_call_directorySetLastWriteTime()
+ {
+ WithRecycleBin();
+
+ var path = @"C:\Test\TV\30 Rock";
+
+ Mocker.Resolve().DeleteDirectory(path);
+
+ Mocker.GetMock().Verify(v => v.DirectorySetLastWriteTimeUtc(@"C:\Test\Recycle Bin\30 Rock", It.IsAny()), Times.Once());
+ }
+
+ [Test]
+ public void should_call_fileSetLastWriteTime_for_each_file()
+ {
+ WithRecycleBin();
+ var path = @"C:\Test\TV\30 Rock";
+
+ Mocker.GetMock().Setup(s => s.GetFiles(@"C:\Test\Recycle Bin\30 Rock", SearchOption.AllDirectories))
+ .Returns(new[]{ "File1", "File2", "File3" });
+
+ Mocker.Resolve().DeleteDirectory(path);
+
+ Mocker.GetMock().Verify(v => v.FileSetLastWriteTimeUtc(It.IsAny(), It.IsAny()), Times.Exactly(3));
+ }
+ }
+}
diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs
new file mode 100644
index 000000000..5ac5c3378
--- /dev/null
+++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/DeleteFileFixture.cs
@@ -0,0 +1,75 @@
+// ReSharper disable RedundantUsingDirective
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using FizzWare.NBuilder;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Model;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Providers.Core;
+using NzbDrone.Core.Repository;
+using NzbDrone.Core.Repository.Quality;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common.AutoMoq;
+using PetaPoco;
+using TvdbLib.Data;
+
+namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
+{
+ [TestFixture]
+ // ReSharper disable InconsistentNaming
+ public class DeleteFileFixture : CoreTest
+ {
+ private void WithRecycleBin()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(@"C:\Test\Recycle Bin");
+ }
+
+ private void WithoutRecycleBin()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(String.Empty);
+ }
+
+ [Test]
+ public void should_use_delete_when_recycleBin_is_not_configured()
+ {
+ WithoutRecycleBin();
+
+ var path = @"C:\Test\TV\30 Rock\S01E01.avi";
+
+ Mocker.Resolve().DeleteFile(path);
+
+ Mocker.GetMock().Verify(v => v.DeleteFile(path), Times.Once());
+ }
+
+ [Test]
+ public void should_use_move_when_recycleBin_is_configured()
+ {
+ WithRecycleBin();
+
+ var path = @"C:\Test\TV\30 Rock\S01E01.avi";
+
+ Mocker.Resolve().DeleteFile(path);
+
+ Mocker.GetMock().Verify(v => v.MoveFile(path, @"C:\Test\Recycle Bin\S01E01.avi"), Times.Once());
+ }
+
+ [Test]
+ public void should_call_fileSetLastWriteTime_for_each_file()
+ {
+ WithRecycleBin();
+ var path = @"C:\Test\TV\30 Rock\S01E01.avi";
+
+
+ Mocker.Resolve().DeleteFile(path);
+
+ Mocker.GetMock().Verify(v => v.FileSetLastWriteTimeUtc(@"C:\Test\Recycle Bin\S01E01.avi", It.IsAny()), Times.Once());
+ }
+ }
+}
diff --git a/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs
new file mode 100644
index 000000000..1835dda2a
--- /dev/null
+++ b/NzbDrone.Core.Test/ProviderTests/RecycleBinProviderTests/EmptyFixture.cs
@@ -0,0 +1,69 @@
+// ReSharper disable RedundantUsingDirective
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+using FizzWare.NBuilder;
+using FluentAssertions;
+using Moq;
+using NUnit.Framework;
+using NzbDrone.Common;
+using NzbDrone.Core.Model;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Providers.Core;
+using NzbDrone.Core.Repository;
+using NzbDrone.Core.Repository.Quality;
+using NzbDrone.Core.Test.Framework;
+using NzbDrone.Test.Common.AutoMoq;
+using PetaPoco;
+using TvdbLib.Data;
+
+namespace NzbDrone.Core.Test.ProviderTests.RecycleBinProviderTests
+{
+ [TestFixture]
+ // ReSharper disable InconsistentNaming
+ public class EmptyFixture : CoreTest
+ {
+ private const string RecycleBin = @"C:\Test\RecycleBin";
+
+ [SetUp]
+ public void Setup()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(RecycleBin);
+
+ Mocker.GetMock().Setup(s => s.GetDirectories(RecycleBin))
+ .Returns(new [] { @"C:\Test\RecycleBin\Folder1", @"C:\Test\RecycleBin\Folder2", @"C:\Test\RecycleBin\Folder3" });
+
+ Mocker.GetMock().Setup(s => s.GetFiles(RecycleBin, SearchOption.TopDirectoryOnly))
+ .Returns(new [] { @"C:\Test\RecycleBin\File1.avi", @"C:\Test\RecycleBin\File2.mkv" });
+ }
+
+ [Test]
+ public void should_return_if_recycleBin_not_configured()
+ {
+ Mocker.GetMock().SetupGet(s => s.RecycleBin).Returns(String.Empty);
+
+ Mocker.Resolve().Empty();
+
+ Mocker.GetMock().Verify(v => v.GetDirectories(It.IsAny()), Times.Never());
+ }
+
+ [Test]
+ public void should_delete_all_folders()
+ {
+ Mocker.Resolve().Empty();
+
+ Mocker.GetMock().Verify(v => v.DeleteFolder(It.IsAny(), true), Times.Exactly(3));
+ }
+
+ [Test]
+ public void should_delete_all_files()
+ {
+ Mocker.Resolve().Empty();
+
+ Mocker.GetMock().Verify(v => v.DeleteFile(It.IsAny()), Times.Exactly(2));
+ }
+ }
+}
diff --git a/NzbDrone.Core/Jobs/CleanupRecycleBinJob.cs b/NzbDrone.Core/Jobs/CleanupRecycleBinJob.cs
new file mode 100644
index 000000000..f8c62cdb3
--- /dev/null
+++ b/NzbDrone.Core/Jobs/CleanupRecycleBinJob.cs
@@ -0,0 +1,36 @@
+using System.Linq;
+using System;
+using Ninject;
+using NLog;
+using NzbDrone.Core.Model.Notification;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Providers.Converting;
+
+namespace NzbDrone.Core.Jobs
+{
+ public class CleanupRecycleBinJob : IJob
+ {
+ private readonly RecycleBinProvider _recycleBinProvider;
+
+ [Inject]
+ public CleanupRecycleBinJob(RecycleBinProvider recycleBinProvider)
+ {
+ _recycleBinProvider = recycleBinProvider;
+ }
+
+ public string Name
+ {
+ get { return "Cleanup Recycle Bin"; }
+ }
+
+ public TimeSpan DefaultInterval
+ {
+ get { return TimeSpan.FromDays(24); }
+ }
+
+ public void Start(ProgressNotification notification, int targetId, int secondaryTargetId)
+ {
+ _recycleBinProvider.Cleanup();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NzbDrone.Core/Jobs/DeleteSeriesJob.cs b/NzbDrone.Core/Jobs/DeleteSeriesJob.cs
index f3555fbc3..ac910e146 100644
--- a/NzbDrone.Core/Jobs/DeleteSeriesJob.cs
+++ b/NzbDrone.Core/Jobs/DeleteSeriesJob.cs
@@ -11,15 +11,15 @@ namespace NzbDrone.Core.Jobs
public class DeleteSeriesJob : IJob
{
private readonly SeriesProvider _seriesProvider;
- private readonly DiskProvider _diskProvider;
+ private readonly RecycleBinProvider _recycleBinProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject]
- public DeleteSeriesJob(SeriesProvider seriesProvider, DiskProvider diskProvider)
+ public DeleteSeriesJob(SeriesProvider seriesProvider, RecycleBinProvider recycleBinProvider)
{
_seriesProvider = seriesProvider;
- _diskProvider = diskProvider;
+ _recycleBinProvider = recycleBinProvider;
}
public string Name
@@ -54,7 +54,7 @@ namespace NzbDrone.Core.Jobs
{
notification.CurrentMessage = String.Format("Deleting files from disk for series '{0}'", title);
- _diskProvider.DeleteFolder(series.Path, true);
+ _recycleBinProvider.DeleteDirectory(series.Path);
notification.CurrentMessage = String.Format("Successfully deleted files from disk for series '{0}'", title);
}
diff --git a/NzbDrone.Core/Jobs/EmptyRecycleBinJob.cs b/NzbDrone.Core/Jobs/EmptyRecycleBinJob.cs
new file mode 100644
index 000000000..46d390492
--- /dev/null
+++ b/NzbDrone.Core/Jobs/EmptyRecycleBinJob.cs
@@ -0,0 +1,36 @@
+using System.Linq;
+using System;
+using Ninject;
+using NLog;
+using NzbDrone.Core.Model.Notification;
+using NzbDrone.Core.Providers;
+using NzbDrone.Core.Providers.Converting;
+
+namespace NzbDrone.Core.Jobs
+{
+ public class EmptyRecycleBinJob : IJob
+ {
+ private readonly RecycleBinProvider _recycleBinProvider;
+
+ [Inject]
+ public EmptyRecycleBinJob(RecycleBinProvider recycleBinProvider)
+ {
+ _recycleBinProvider = recycleBinProvider;
+ }
+
+ public string Name
+ {
+ get { return "Empty Recycle Bin"; }
+ }
+
+ public TimeSpan DefaultInterval
+ {
+ get { return TimeSpan.FromTicks(0); }
+ }
+
+ public void Start(ProgressNotification notification, int targetId, int secondaryTargetId)
+ {
+ _recycleBinProvider.Empty();
+ }
+ }
+}
\ No newline at end of file
diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj
index 2bdf59ae4..abfe30b93 100644
--- a/NzbDrone.Core/NzbDrone.Core.csproj
+++ b/NzbDrone.Core/NzbDrone.Core.csproj
@@ -253,6 +253,8 @@
+
+
@@ -297,6 +299,7 @@
+
diff --git a/NzbDrone.Core/Providers/Core/ConfigProvider.cs b/NzbDrone.Core/Providers/Core/ConfigProvider.cs
index 06e4770f5..0a371981d 100644
--- a/NzbDrone.Core/Providers/Core/ConfigProvider.cs
+++ b/NzbDrone.Core/Providers/Core/ConfigProvider.cs
@@ -526,6 +526,12 @@ namespace NzbDrone.Core.Providers.Core
set { SetValue("PneumaticDirectory", value); }
}
+ public virtual string RecycleBin
+ {
+ get { return GetValue("RecycleBin", String.Empty); }
+ set { SetValue("RecycleBin", value); }
+ }
+
private string GetValue(string key)
{
return GetValue(key, String.Empty);
diff --git a/NzbDrone.Core/Providers/DiskScanProvider.cs b/NzbDrone.Core/Providers/DiskScanProvider.cs
index 215b17f63..bf9e2db24 100644
--- a/NzbDrone.Core/Providers/DiskScanProvider.cs
+++ b/NzbDrone.Core/Providers/DiskScanProvider.cs
@@ -23,12 +23,14 @@ namespace NzbDrone.Core.Providers
private readonly DownloadProvider _downloadProvider;
private readonly SignalRProvider _signalRProvider;
private readonly ConfigProvider _configProvider;
+ private readonly RecycleBinProvider _recycleBinProvider;
[Inject]
public DiskScanProvider(DiskProvider diskProvider, EpisodeProvider episodeProvider,
SeriesProvider seriesProvider, MediaFileProvider mediaFileProvider,
ExternalNotificationProvider externalNotificationProvider, DownloadProvider downloadProvider,
- SignalRProvider signalRProvider, ConfigProvider configProvider)
+ SignalRProvider signalRProvider, ConfigProvider configProvider,
+ RecycleBinProvider recycleBinProvider)
{
_diskProvider = diskProvider;
_episodeProvider = episodeProvider;
@@ -38,6 +40,7 @@ namespace NzbDrone.Core.Providers
_downloadProvider = downloadProvider;
_signalRProvider = signalRProvider;
_configProvider = configProvider;
+ _recycleBinProvider = recycleBinProvider;
}
public DiskScanProvider()
@@ -135,7 +138,7 @@ namespace NzbDrone.Core.Providers
{
Logger.Debug("Deleting the existing file(s) on disk to upgrade to: {0}", filePath);
//Do the delete for files where there is already an episode on disk
- episodes.Where(e => e.EpisodeFile != null).Select(e => e.EpisodeFile.Path).Distinct().ToList().ForEach(p => _diskProvider.DeleteFile(p));
+ episodes.Where(e => e.EpisodeFile != null).Select(e => e.EpisodeFile.Path).Distinct().ToList().ForEach(p => _recycleBinProvider.DeleteFile(p));
}
else
diff --git a/NzbDrone.Core/Providers/RecycleBinProvider.cs b/NzbDrone.Core/Providers/RecycleBinProvider.cs
new file mode 100644
index 000000000..698f38619
--- /dev/null
+++ b/NzbDrone.Core/Providers/RecycleBinProvider.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using NLog;
+using Ninject;
+using NzbDrone.Common;
+using NzbDrone.Core.Providers.Core;
+
+namespace NzbDrone.Core.Providers
+{
+ public class RecycleBinProvider
+ {
+ private readonly DiskProvider _diskProvider;
+ private readonly ConfigProvider _configProvider;
+
+ private static readonly Logger logger = LogManager.GetCurrentClassLogger();
+
+ [Inject]
+ public RecycleBinProvider(DiskProvider diskProvider, ConfigProvider configProvider)
+ {
+ _diskProvider = diskProvider;
+ _configProvider = configProvider;
+ }
+
+ public RecycleBinProvider()
+ {
+ }
+
+ public virtual void DeleteDirectory(string path)
+ {
+ logger.Trace("Attempting to send '{0}' to recycling bin", path);
+ var recyclingBin = _configProvider.RecycleBin;
+
+ if (String.IsNullOrWhiteSpace(recyclingBin))
+ {
+ logger.Info("Recycling Bin has not been configured, deleting permanently.");
+ _diskProvider.DeleteFolder(path, true);
+ logger.Trace("Folder has been permanently deleted: {0}", path);
+ }
+
+ else
+ {
+ var destination = Path.Combine(recyclingBin, new DirectoryInfo(path).Name);
+
+ logger.Trace("Moving '{0}' to '{1}'", path, destination);
+ _diskProvider.MoveDirectory(path, destination);
+
+ logger.Trace("Setting last accessed: {0}", path);
+ _diskProvider.DirectorySetLastWriteTimeUtc(destination, DateTime.UtcNow);
+ foreach(var file in _diskProvider.GetFiles(destination, SearchOption.AllDirectories))
+ {
+ _diskProvider.FileSetLastWriteTimeUtc(file, DateTime.UtcNow);
+ }
+
+ logger.Trace("Folder has been moved to the recycling bin: {0}", destination);
+ }
+ }
+
+ public virtual void DeleteFile(string path)
+ {
+ logger.Trace("Attempting to send '{0}' to recycling bin", path);
+ var recyclingBin = _configProvider.RecycleBin;
+
+ if (String.IsNullOrWhiteSpace(recyclingBin))
+ {
+ logger.Info("Recycling Bin has not been configured, deleting permanently.");
+ _diskProvider.DeleteFile(path);
+ logger.Trace("File has been permanently deleted: {0}", path);
+ }
+
+ else
+ {
+ var destination = Path.Combine(recyclingBin, new FileInfo(path).Name);
+
+ logger.Trace("Moving '{0}' to '{1}'", path, destination);
+ _diskProvider.MoveFile(path, destination);
+ _diskProvider.FileSetLastWriteTimeUtc(destination, DateTime.UtcNow);
+ logger.Trace("File has been moved to the recycling bin: {0}", destination);
+ }
+ }
+
+ public virtual void Empty()
+ {
+ if (String.IsNullOrWhiteSpace(_configProvider.RecycleBin))
+ {
+ logger.Info("Recycle Bin has not been configured, cannot empty.");
+ return;
+ }
+
+ logger.Info("Removing all items from the recycling bin");
+
+ foreach (var folder in _diskProvider.GetDirectories(_configProvider.RecycleBin))
+ {
+ _diskProvider.DeleteFolder(folder, true);
+ }
+
+ foreach (var file in _diskProvider.GetFiles(_configProvider.RecycleBin, SearchOption.TopDirectoryOnly))
+ {
+ _diskProvider.DeleteFile(file);
+ }
+
+ logger.Trace("Recycling Bin has been emptied.");
+ }
+
+ public virtual void Cleanup()
+ {
+ if (String.IsNullOrWhiteSpace(_configProvider.RecycleBin))
+ {
+ logger.Info("Recycle Bin has not been configured, cannot cleanup.");
+ return;
+ }
+
+ logger.Info("Removing items older than 7 days from the recycling bin");
+
+ foreach (var folder in _diskProvider.GetDirectories(_configProvider.RecycleBin))
+ {
+ if (_diskProvider.GetLastDirectoryWrite(folder).AddDays(7) > DateTime.UtcNow)
+ {
+ logger.Trace("Folder hasn't expired yet, skipping: {0}", folder);
+ continue;
+ }
+
+ _diskProvider.DeleteFolder(folder, true);
+ }
+
+ foreach (var file in _diskProvider.GetFiles(_configProvider.RecycleBin, SearchOption.TopDirectoryOnly))
+ {
+ if (_diskProvider.GetLastFileWrite(file).AddDays(7) > DateTime.UtcNow)
+ {
+ logger.Trace("File hasn't expired yet, skipping: {0}", file);
+ continue;
+ }
+
+ _diskProvider.DeleteFile(file);
+ }
+
+ logger.Trace("Recycling Bin has been cleaned up.");
+ }
+ }
+}