diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 921a7e75dd..880488e935 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -483,6 +483,12 @@ namespace Umbraco.Core.Persistence.Repositories return GetByVersion(dto.ContentVersionDto.VersionId); } + public bool EmptyRecycleBin() + { + var repo = new RecycleBinRepository(UnitOfWork); + return repo.EmptyRecycleBin(NodeObjectTypeId); + } + #endregion /// diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index 667395c3b4..95924e88be 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -20,5 +20,11 @@ namespace Umbraco.Core.Persistence.Repositories /// Query to execute against published versions /// An enumerable list of IEnumerable GetByPublishedVersion(IQuery query); + + /// + /// Empties the Recycle Bin for deleted Content + /// + /// True if the Recycle Bin was successfully emptied and all items deleted otherwise False + bool EmptyRecycleBin(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs index 0b92f182d8..8c016c8818 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaRepository.cs @@ -4,6 +4,10 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IMediaRepository : IRepositoryVersionable { - + /// + /// Empties the Recycle Bin for media Content + /// + /// True if the Recycle Bin was successfully emptied and all items deleted otherwise False + bool EmptyRecycleBin(); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs index e27fac1ba3..e2d9725849 100644 --- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs @@ -356,6 +356,16 @@ namespace Umbraco.Core.Persistence.Repositories #endregion + #region Implementation of IMediaRepository + + public bool EmptyRecycleBin() + { + var repo = new RecycleBinRepository(UnitOfWork); + return repo.EmptyRecycleBin(NodeObjectTypeId); + } + + #endregion + private PropertyCollection GetPropertyCollection(int id, Guid versionId, IMediaType contentType, DateTime createDate, DateTime updateDate) { var sql = new Sql(); diff --git a/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs new file mode 100644 index 0000000000..d879503485 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Logging; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal class RecycleBinRepository + { + private readonly IDatabaseUnitOfWork _unitOfWork; + + public RecycleBinRepository(IDatabaseUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public bool EmptyRecycleBin(Guid nodeObjectType) + { + try + { + var db = _unitOfWork.Database; + + //Issue query to get all trashed content or media that has the Upload field as a property + //The value for each field is stored in a list: FilesToDelete() + //Alias: Constants.Conventions.Media.File and ControlId: Constants.PropertyEditors.UploadField + var sql = new Sql(); + sql.Select("DISTINCT(dataNvarchar)") + .From() + .InnerJoin().On(left => left.NodeId, right => right.NodeId) + .InnerJoin().On(left => left.PropertyTypeId, right => right.Id) + .InnerJoin().On(left => left.DataTypeId, right => right.DataTypeId) + .Where("umbracoNode.trashed = '1' AND umbracoNode.nodeObjectType = @NodeObjectType AND dataNvarchar IS NOT NULL AND (cmsPropertyType.Alias = @FileAlias OR cmsDataType.controlId = @ControlId)", + new { FileAlias = Constants.Conventions.Media.File, NodeObjectType = nodeObjectType, ControlId = Constants.PropertyEditors.UploadField }); + + var files = db.Fetch(sql); + + //Construct and execute delete statements for all trashed items by 'nodeObjectType' + var deletes = new List + { + FormatDeleteStatement("umbracoUser2NodeNotify", "nodeId"), + FormatDeleteStatement("umbracoUser2NodePermission", "nodeId"), + FormatDeleteStatement("umbracoRelation", "parentId"), + FormatDeleteStatement("umbracoRelation", "childId"), + FormatDeleteStatement("cmsTagRelationship", "nodeId"), + FormatDeleteStatement("umbracoDomains", "domainRootStructureID"), + FormatDeleteStatement("cmsDocument", "NodeId"), + FormatDeleteStatement("cmsPropertyData", "contentNodeId"), + FormatDeleteStatement("cmsPreviewXml", "nodeId"), + FormatDeleteStatement("cmsContentVersion", "ContentId"), + FormatDeleteStatement("cmsContentXml", "nodeID"), + FormatDeleteStatement("cmsContent", "NodeId"), + "DELETE FROM umbracoNode WHERE trashed = '1' AND nodeObjectType = @NodeObjectType" + }; + + foreach (var delete in deletes) + { + db.Execute(delete, new { NodeObjectType = nodeObjectType }); + } + + //Trigger (internal) event with list of files to delete - RecycleBinEmptied + + return true; + } + catch (Exception ex) + { + LogHelper.Error("An error occurred while emptying the Recycle Bin: " + ex.Message, ex); + return false; + } + } + + private string FormatDeleteStatement(string tableName, string keyName) + { + return + string.Format( + "DELETE FROM {0} FROM {0} as TB1 INNER JOIN umbracoNode as TB2 ON TB1.{1} = TB2.id WHERE TB2.trashed = '1' AND TB2.nodeObjectType = @NodeObjectType", + tableName, keyName); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index f4011e90a8..5fa9177947 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -497,6 +497,7 @@ + diff --git a/src/umbraco.sln b/src/umbraco.sln index eba2ad7b9b..76001aa9c6 100644 --- a/src/umbraco.sln +++ b/src/umbraco.sln @@ -162,7 +162,4 @@ Global {73529637-28F5-419C-A6BB-D094E39DE614} = {DD32977B-EF54-475B-9A1B-B97A502C6E58} {B555AAE6-0F56-442F-AC9F-EF497DB38DE7} = {DD32977B-EF54-475B-9A1B-B97A502C6E58} EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal