Document and Media restore endpoints (#15681)

* Added Endpoint to get Document restore information

* Add media version of original paent recycle bin

update document permission
regisered interfaces

* Added restore endpoints

* PR feedback and other cleanup

* Update orignal root parent logic & shrank return model

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Sven Geusens
2024-02-22 17:37:27 +01:00
committed by GitHub
parent 8027873438
commit 00dade48f6
16 changed files with 473 additions and 2 deletions

View File

@@ -0,0 +1,68 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Security.Authorization.Content;
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Document.Item;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Actions;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Querying.RecycleBin;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Document.RecycleBin;
[ApiVersion("1.0")]
public class OriginalParentDocumentRecycleBinController : DocumentRecycleBinControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IDocumentPresentationFactory _documentPresentationFactory;
private readonly IDocumentRecycleBinQueryService _documentRecycleBinQueryService;
public OriginalParentDocumentRecycleBinController(
IEntityService entityService,
IAuthorizationService authorizationService,
IDocumentPresentationFactory documentPresentationFactory,
IDocumentRecycleBinQueryService documentRecycleBinQueryService)
: base(entityService, documentPresentationFactory)
{
_authorizationService = authorizationService;
_documentPresentationFactory = documentPresentationFactory;
_documentRecycleBinQueryService = documentRecycleBinQueryService;
}
[HttpGet("{id:guid}/original-parent")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(ReferenceByIdModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> OriginalParent(Guid id)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.RecycleBin(ActionBrowse.ActionLetter),
AuthorizationPolicies.ContentPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType> getParentAttempt = await _documentRecycleBinQueryService.GetOriginalParentAsync(id);
return getParentAttempt.Success switch
{
true when getParentAttempt.Status == RecycleBinQueryResultType.Success
=> Ok(new ReferenceByIdModel(getParentAttempt.Result!.Key)),
true when getParentAttempt.Status == RecycleBinQueryResultType.ParentIsRoot
=> Ok(null),
_ => MapAttemptFailure(getParentAttempt.Status),
};
}
private IActionResult MapAttemptFailure(RecycleBinQueryResultType status)
=> MapRecycleBinQueryAttemptFailure(status, "document");
}

View File

@@ -0,0 +1,65 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Security.Authorization.Content;
using Umbraco.Cms.Api.Management.ViewModels.Media;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Actions;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Document.RecycleBin;
[ApiVersion("1.0")]
public class RestoreDocumentRecycleBinController : DocumentRecycleBinControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IContentEditingService _contentEditingService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public RestoreDocumentRecycleBinController(
IAuthorizationService authorizationService,
IContentEditingService contentEditingService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IEntityService entityService,
IDocumentPresentationFactory documentPresentationFactory)
: base(entityService,documentPresentationFactory)
{
_authorizationService = authorizationService;
_contentEditingService = contentEditingService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
[HttpPut("{id:guid}/restore")]
[MapToApiVersion("1.0")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Restore(Guid id, MoveMediaRequestModel moveDocumentRequestModel)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
ContentPermissionResource.RecycleBin(ActionMove.ActionLetter),
AuthorizationPolicies.ContentPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
Attempt<IContent?, ContentEditingOperationStatus> result = await _contentEditingService.RestoreAsync(
id,
moveDocumentRequestModel.Target?.Id,
CurrentUserKey(_backOfficeSecurityAccessor));
return result.Success
? Ok()
: ContentEditingOperationStatusResult(result.Status);
}
}

View File

@@ -0,0 +1,67 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Security.Authorization.Media;
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Media.Item;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Querying.RecycleBin;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Media.RecycleBin;
[ApiVersion("1.0")]
public class OriginalParentMediaRecycleBinController : MediaRecycleBinControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IMediaPresentationFactory _mediaPresentationFactory;
private readonly IMediaRecycleBinQueryService _mediaRecycleBinQueryService;
public OriginalParentMediaRecycleBinController(
IEntityService entityService,
IAuthorizationService authorizationService,
IMediaPresentationFactory mediaPresentationFactory,
IMediaRecycleBinQueryService mediaRecycleBinQueryService)
: base(entityService, mediaPresentationFactory)
{
_authorizationService = authorizationService;
_mediaPresentationFactory = mediaPresentationFactory;
_mediaRecycleBinQueryService = mediaRecycleBinQueryService;
}
[HttpGet("{id:guid}/original-parent")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(ReferenceByIdModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> OriginalParent(Guid id)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
MediaPermissionResource.RecycleBin(),
AuthorizationPolicies.MediaPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
Attempt<IMediaEntitySlim?, RecycleBinQueryResultType> getParentAttempt = await _mediaRecycleBinQueryService.GetOriginalParentAsync(id);
return getParentAttempt.Success switch
{
true when getParentAttempt.Status == RecycleBinQueryResultType.Success
=> Ok(new ReferenceByIdModel(getParentAttempt.Result!.Key)),
true when getParentAttempt.Status == RecycleBinQueryResultType.ParentIsRoot
=> Ok(null),
_ => MapAttemptFailure(getParentAttempt.Status),
};
}
private IActionResult MapAttemptFailure(RecycleBinQueryResultType status)
=> MapRecycleBinQueryAttemptFailure(status, "media item");
}

View File

@@ -0,0 +1,64 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.Security.Authorization.Media;
using Umbraco.Cms.Api.Management.ViewModels.Media;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;
using Umbraco.Extensions;
namespace Umbraco.Cms.Api.Management.Controllers.Media.RecycleBin;
[ApiVersion("1.0")]
public class RestoreMediaRecycleBinController : MediaRecycleBinControllerBase
{
private readonly IAuthorizationService _authorizationService;
private readonly IMediaEditingService _mediaEditingService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
public RestoreMediaRecycleBinController(
IAuthorizationService authorizationService,
IMediaEditingService mediaEditingService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
IEntityService entityService,
IMediaPresentationFactory mediaPresentationFactory)
: base(entityService,mediaPresentationFactory)
{
_authorizationService = authorizationService;
_mediaEditingService = mediaEditingService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
[HttpPut("{id:guid}/restore")]
[MapToApiVersion("1.0")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Restore(Guid id, MoveMediaRequestModel moveDocumentRequestModel)
{
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
User,
MediaPermissionResource.RecycleBin(),
AuthorizationPolicies.MediaPermissionByResource);
if (!authorizationResult.Succeeded)
{
return Forbidden();
}
Attempt<IMedia?, ContentEditingOperationStatus> result = await _mediaEditingService.RestoreAsync(
id,
moveDocumentRequestModel.Target?.Id,
CurrentUserKey(_backOfficeSecurityAccessor));
return result.Success
? Ok()
: ContentEditingOperationStatusResult(result.Status);
}
}

View File

@@ -8,6 +8,7 @@ using Umbraco.Cms.Api.Management.ViewModels.RecycleBin;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Querying.RecycleBin;
namespace Umbraco.Cms.Api.Management.Controllers.RecycleBin;
@@ -94,6 +95,31 @@ public abstract class RecycleBinControllerBase<TItem> : ContentControllerBase
.Build()),
});
protected IActionResult MapRecycleBinQueryAttemptFailure(RecycleBinQueryResultType status, string contentType)
=> OperationStatusResult(status, problemDetailsBuilder => status switch
{
RecycleBinQueryResultType.NotFound => NotFound(problemDetailsBuilder
.WithTitle($"The {contentType} could not be found")
.Build()),
RecycleBinQueryResultType.NotTrashed => BadRequest(problemDetailsBuilder
.WithTitle($"The {contentType} is not trashed")
.WithDetail($"The {contentType} needs to be trashed for the parent-before-recycled relation to be created.")
.Build()),
RecycleBinQueryResultType.NoParentRecycleRelation => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
.WithTitle("The parent relation could not be found")
.WithDetail($"The relation between the parent and the {contentType} that should have been created when the {contentType} was deleted could not be found.")
.Build()),
RecycleBinQueryResultType.ParentNotFound => NotFound(problemDetailsBuilder
.WithTitle($"The original {contentType} parent could not be found")
.Build()),
RecycleBinQueryResultType.ParentIsTrashed => NotFound(problemDetailsBuilder
.WithTitle($"The original {contentType} parent is in the recycle bin")
.Build()),
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
.WithTitle("Unknown recycle bin query type.")
.Build()),
});
private IEntitySlim[] GetPagedRootEntities(long pageNumber, int pageSize, out long totalItems)
{
IEntitySlim[] rootEntities = _entityService

View File

@@ -37,6 +37,7 @@ using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.ContentTypeEditing;
using Umbraco.Cms.Core.DynamicRoot;
using Umbraco.Cms.Core.Services.FileSystem;
using Umbraco.Cms.Core.Services.Querying.RecycleBin;
using Umbraco.Cms.Core.Sync;
using Umbraco.Cms.Core.Telemetry;
using Umbraco.Cms.Core.Templates;
@@ -376,10 +377,13 @@ namespace Umbraco.Cms.Core.DependencyInjection
Services.AddUnique<IDataTypeConfigurationCache, DataTypeConfigurationCache>();
Services.AddNotificationHandler<DataTypeCacheRefresherNotification, DataTypeConfigurationCacheRefresher>();
//Two factor providers
// Two factor providers
Services.AddUnique<ITwoFactorLoginService, TwoFactorLoginService>();
Services.AddUnique<IUserTwoFactorLoginService, UserTwoFactorLoginService>();
// Add Query services
Services.AddUnique<IDocumentRecycleBinQueryService, DocumentRecycleBinQueryService>();
Services.AddUnique<IMediaRecycleBinQueryService, MediaRecycleBinQueryService>();
}
}
}

View File

@@ -106,6 +106,9 @@ internal sealed class ContentEditingService
public async Task<Attempt<IContent?, ContentEditingOperationStatus>> MoveAsync(Guid key, Guid? parentKey, Guid userKey)
=> await HandleMoveAsync(key, parentKey, userKey);
public async Task<Attempt<IContent?, ContentEditingOperationStatus>> RestoreAsync(Guid key, Guid? parentKey, Guid userKey)
=> await HandleMoveAsync(key, parentKey, userKey, true);
public async Task<Attempt<IContent?, ContentEditingOperationStatus>> CopyAsync(Guid key, Guid? parentKey, bool relateToOriginal, bool includeDescendants, Guid userKey)
=> await HandleCopyAsync(key, parentKey, relateToOriginal, includeDescendants, userKey);

View File

@@ -169,7 +169,7 @@ internal abstract class ContentEditingServiceBase<TContent, TContentType, TConte
return OperationResultToAttempt(content, deleteResult);
}
protected async Task<Attempt<TContent?, ContentEditingOperationStatus>> HandleMoveAsync(Guid key, Guid? parentKey, Guid userKey)
protected async Task<Attempt<TContent?, ContentEditingOperationStatus>> HandleMoveAsync(Guid key, Guid? parentKey, Guid userKey, bool mustBeInRecycleBin = false)
{
using ICoreScope scope = CoreScopeProvider.CreateCoreScope();
TContent? content = ContentService.GetById(key);
@@ -178,6 +178,11 @@ internal abstract class ContentEditingServiceBase<TContent, TContentType, TConte
return await Task.FromResult(Attempt.FailWithStatus(ContentEditingOperationStatus.NotFound, content));
}
if (mustBeInRecycleBin && content.Trashed is false)
{
return await Task.FromResult(Attempt.FailWithStatus<TContent?, ContentEditingOperationStatus>(ContentEditingOperationStatus.NotInTrash, content));
}
TContentType contentType = ContentTypeService.Get(content.ContentType.Key)!;
TContent? parent = TryGetAndValidateParent(parentKey, contentType, out ContentEditingOperationStatus operationStatus);

View File

@@ -34,4 +34,6 @@ public interface IContentEditingService
/// Deletes a Content Item whether it is in the recycle bin or not.
/// </summary>
Task<Attempt<IContent?, ContentEditingOperationStatus>> DeleteAsync(Guid key, Guid userKey);
Task<Attempt<IContent?, ContentEditingOperationStatus>> RestoreAsync(Guid key, Guid? parentKey, Guid userKey);
}

View File

@@ -25,4 +25,6 @@ public interface IMediaEditingService
Task<ContentEditingOperationStatus> SortAsync(Guid? parentKey, IEnumerable<SortingModel> sortingModels, Guid userKey);
Task<Attempt<IMedia?, ContentEditingOperationStatus>> DeleteFromRecycleBinAsync(Guid key, Guid userKey);
Task<Attempt<IMedia?, ContentEditingOperationStatus>> RestoreAsync(Guid key, Guid? parentKey, Guid userKey);
}

View File

@@ -92,6 +92,9 @@ internal sealed class MediaEditingService
public async Task<Attempt<IMedia?, ContentEditingOperationStatus>> MoveAsync(Guid key, Guid? parentKey, Guid userKey)
=> await HandleMoveAsync(key, parentKey, userKey);
public async Task<Attempt<IMedia?, ContentEditingOperationStatus>> RestoreAsync(Guid key, Guid? parentKey, Guid userKey)
=> await HandleMoveAsync(key, parentKey, userKey, true);
public async Task<ContentEditingOperationStatus> SortAsync(Guid? parentKey, IEnumerable<SortingModel> sortingModels, Guid userKey)
=> await HandleSortAsync(parentKey, sortingModels, userKey);

View File

@@ -0,0 +1,65 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Scoping;
namespace Umbraco.Cms.Core.Services.Querying.RecycleBin;
public class DocumentRecycleBinQueryService : IDocumentRecycleBinQueryService
{
private readonly IEntityService _entityService;
private readonly ICoreScopeProvider _scopeProvider;
private readonly IRelationService _relationService;
public DocumentRecycleBinQueryService(
IEntityService entityService,
ICoreScopeProvider scopeProvider,
IRelationService relationService)
{
_entityService = entityService;
_scopeProvider = scopeProvider;
_relationService = relationService;
}
public Task<Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>> GetOriginalParentAsync(Guid trashedDocumentId)
{
using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
if (_entityService.Get(trashedDocumentId, UmbracoObjectTypes.Document) is not IDocumentEntitySlim entity)
{
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.NotFound));
}
if (entity.Trashed is false)
{
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.NotTrashed));
}
IEnumerable<IRelation> relationsByChild = _relationService.GetByChildId(entity.Id);
IRelation? parentRecycleRelation = relationsByChild.FirstOrDefault(
r => r.RelationType.Alias == Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias);
if (parentRecycleRelation is null)
{
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.NoParentRecycleRelation));
}
if (parentRecycleRelation.ParentId == Constants.System.Root)
{
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Succeed(RecycleBinQueryResultType.ParentIsRoot, null));
}
var parent =
_entityService.Get(parentRecycleRelation.ParentId, UmbracoObjectTypes.Document) as IDocumentEntitySlim;
if (parent is null)
{
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.ParentNotFound));
}
if (parent.Trashed)
{
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.ParentNotFound, parent));
}
return Task.FromResult(Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>.Succeed(RecycleBinQueryResultType.Success, parent));
}
}

View File

@@ -0,0 +1,8 @@
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Services.Querying.RecycleBin;
public interface IDocumentRecycleBinQueryService
{
Task<Attempt<IDocumentEntitySlim?, RecycleBinQueryResultType>> GetOriginalParentAsync(Guid trashedDocumentId);
}

View File

@@ -0,0 +1,8 @@
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Services.Querying.RecycleBin;
public interface IMediaRecycleBinQueryService
{
Task<Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>> GetOriginalParentAsync(Guid trashedMediaId);
}

View File

@@ -0,0 +1,66 @@
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Scoping;
namespace Umbraco.Cms.Core.Services.Querying.RecycleBin;
public class MediaRecycleBinQueryService : IMediaRecycleBinQueryService
{
private readonly IEntityService _entityService;
private readonly ICoreScopeProvider _scopeProvider;
private readonly IRelationService _relationService;
public MediaRecycleBinQueryService(
IEntityService entityService,
ICoreScopeProvider scopeProvider,
IRelationService relationService)
{
_entityService = entityService;
_scopeProvider = scopeProvider;
_relationService = relationService;
}
public Task<Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>> GetOriginalParentAsync(Guid trashedMediaId)
{
using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
if (_entityService.Get(trashedMediaId, UmbracoObjectTypes.Media) is not IMediaEntitySlim entity)
{
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.NotFound));
}
if (entity.Trashed is false)
{
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.NotTrashed));
}
IEnumerable<IRelation> relationsByChild = _relationService.GetByChildId(entity.Id);
IRelation? parentRecycleRelation = relationsByChild.FirstOrDefault(
r => r.RelationType.Alias == Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias);
if (parentRecycleRelation is null)
{
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.NoParentRecycleRelation));
}
if (parentRecycleRelation.ParentId == Constants.System.Root)
{
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Succeed(RecycleBinQueryResultType.ParentIsRoot, null));
}
var parent =
_entityService.Get(parentRecycleRelation.ParentId, UmbracoObjectTypes.Media) as IMediaEntitySlim;
if (parent is null)
{
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.ParentNotFound));
}
if (parent.Trashed)
{
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Fail(RecycleBinQueryResultType.ParentNotFound, parent));
}
return Task.FromResult(Attempt<IMediaEntitySlim?, RecycleBinQueryResultType>.Succeed(RecycleBinQueryResultType.Success, parent));
}
}

View File

@@ -0,0 +1,15 @@
namespace Umbraco.Cms.Core.Services.Querying.RecycleBin;
/// <summary>
/// < 10 = Success
/// </summary>
public enum RecycleBinQueryResultType
{
Success = 0,
ParentIsRoot = 2,
NotFound = 11,
NotTrashed = 12,
NoParentRecycleRelation = 13,
ParentNotFound = 14,
ParentIsTrashed = 15,
}