Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.ComponentModel.DataAnnotations;
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Common.ViewModels.Pagination;
using Umbraco.Cms.Api.Management.Factories;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentVersion;

[ApiVersion("1.0")]
public class AllDocumentVersionController : DocumentVersionControllerBase
{
private readonly IContentVersionService _contentVersionService;
private readonly IDocumentVersionPresentationFactory _documentVersionPresentationFactory;

public AllDocumentVersionController(
IContentVersionService contentVersionService,
IDocumentVersionPresentationFactory documentVersionPresentationFactory)
{
_contentVersionService = contentVersionService;
_documentVersionPresentationFactory = documentVersionPresentationFactory;
}

[MapToApiVersion("1.0")]
[HttpGet]
[ProducesResponseType(typeof(PagedViewModel<DocumentVersionItemResponseModel>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> All([Required] Guid documentId, string? culture, int skip = 0, int take = 100)
{
Attempt<PagedModel<ContentVersionMeta>?, ContentVersionOperationStatus> attempt =
await _contentVersionService.GetPagedContentVersionsAsync(documentId, culture, skip, take);

var pagedViewModel = new PagedViewModel<DocumentVersionItemResponseModel>
{
Total = attempt.Result!.Total,
Items = await _documentVersionPresentationFactory.CreateMultipleAsync(attempt.Result!.Items),
};

return attempt.Success
? Ok(pagedViewModel)
: MapFailure(attempt.Status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentVersion;

[ApiVersion("1.0")]
public class ByKeyDocumentVersionController : DocumentVersionControllerBase
{
private readonly IContentVersionService _contentVersionService;
private readonly IUmbracoMapper _umbracoMapper;

public ByKeyDocumentVersionController(
IContentVersionService contentVersionService,
IUmbracoMapper umbracoMapper)
{
_contentVersionService = contentVersionService;
_umbracoMapper = umbracoMapper;
}

[MapToApiVersion("1.0")]
[HttpGet("{id:guid}")]
[ProducesResponseType(typeof(DocumentVersionResponseModel), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> ByKey(Guid id)
{
Attempt<IContent?, ContentVersionOperationStatus> attempt =
await _contentVersionService.GetAsync(id);

return attempt.Success
? Ok(_umbracoMapper.Map<DocumentVersionResponseModel>(attempt.Result))
: MapFailure(attempt.Status);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.Routing;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Services.OperationStatus;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentVersion;

[VersionedApiBackOfficeRoute($"{Constants.UdiEntityType.Document}-version")]
[ApiExplorerSettings(GroupName = $"{nameof(Constants.UdiEntityType.Document)} Version")]
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocuments)]
public abstract class DocumentVersionControllerBase : ManagementApiControllerBase
{
protected IActionResult MapFailure(ContentVersionOperationStatus status)
=> OperationStatusResult(status, problemDetailsBuilder => status switch
{
ContentVersionOperationStatus.NotFound => NotFound(problemDetailsBuilder
.WithTitle("The requested version could not be found")
.Build()),
ContentVersionOperationStatus.ContentNotFound => NotFound(problemDetailsBuilder
.WithTitle("The requested document could not be found")
.Build()),
ContentVersionOperationStatus.InvalidSkipTake => SkipTakeToPagingProblem(),
ContentVersionOperationStatus.RollBackFailed => BadRequest(problemDetailsBuilder
.WithTitle("Rollback failed")
.WithDetail("An unspecified error occurred while rolling back the requested version. Please check the logs for additional information.")),
ContentVersionOperationStatus.RollBackCanceled => BadRequest(problemDetailsBuilder
.WithTitle("Request cancelled by notification")
.WithDetail("The request to roll back was cancelled by a notification handler.")
.Build()),
_ => StatusCode(StatusCodes.Status500InternalServerError, problemDetailsBuilder
.WithTitle("Unknown content version operation status.")
.Build()),
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentVersion;

[ApiVersion("1.0")]
public class RollbackDocumentVersionController : DocumentVersionControllerBase
{
private readonly IContentVersionService _contentVersionService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;

public RollbackDocumentVersionController(
IContentVersionService contentVersionService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_contentVersionService = contentVersionService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

[MapToApiVersion("1.0")]
[HttpPost("{id:guid}/rollback")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Rollback(Guid id, string? culture)
{
Attempt<ContentVersionOperationStatus> attempt =
await _contentVersionService.RollBackAsync(id, culture, CurrentUserKey(_backOfficeSecurityAccessor));

return attempt.Success
? Ok()
: MapFailure(attempt.Result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.OperationStatus;

namespace Umbraco.Cms.Api.Management.Controllers.DocumentVersion;

[ApiVersion("1.0")]
public class UpdatePreventCleanupDocumentVersionController : DocumentVersionControllerBase
{
private readonly IContentVersionService _contentVersionService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;

public UpdatePreventCleanupDocumentVersionController(
IContentVersionService contentVersionService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_contentVersionService = contentVersionService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}

[MapToApiVersion("1.0")]
[HttpPut("{id:guid}/prevent-cleanup")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Set(Guid id, bool preventCleanup)
{
Attempt<ContentVersionOperationStatus> attempt =
await _contentVersionService.SetPreventCleanupAsync(id, preventCleanup, CurrentUserKey(_backOfficeSecurityAccessor));

return attempt.Success
? Ok()
: MapFailure(attempt.Result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,13 @@ protected static IUser CurrentUser(IBackOfficeSecurityAccessor backOfficeSecurit
protected static IActionResult OperationStatusResult<TEnum>(TEnum status, Func<ProblemDetailsBuilder, IActionResult> result)
where TEnum : Enum
=> result(new ProblemDetailsBuilder().WithOperationStatus(status));

protected BadRequestObjectResult SkipTakeToPagingProblem() =>
BadRequest(new ProblemDetails
{
Title = "Invalid skip/take",
Detail = "Skip must be a multiple of take - i.e. skip = 10, take = 5",
Status = StatusCodes.Status400BadRequest,
Type = "Error",
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ internal static IUmbracoBuilder AddDocuments(this IUmbracoBuilder builder)
builder.Services.AddTransient<IDocumentEditingPresentationFactory, DocumentEditingPresentationFactory>();
builder.Services.AddTransient<IPublicAccessPresentationFactory, PublicAccessPresentationFactory>();
builder.Services.AddTransient<IDomainPresentationFactory, DomainPresentationFactory>();
builder.Services.AddTransient<IDocumentVersionPresentationFactory, DocumentVersionPresentationFactory>();

builder.WithCollectionBuilder<MapDefinitionCollectionBuilder>()
.Add<DocumentMapDefinition>()
.Add<DomainMapDefinition>();
.Add<DomainMapDefinition>()
.Add<DocumentVersionMapDefinition>();

return builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Extensions;

namespace Umbraco.Cms.Api.Management.Factories;

internal sealed class DocumentVersionPresentationFactory : IDocumentVersionPresentationFactory
{
private readonly IEntityService _entityService;
private readonly IUserIdKeyResolver _userIdKeyResolver;

public DocumentVersionPresentationFactory(
IEntityService entityService,
IUserIdKeyResolver userIdKeyResolver)
{
_entityService = entityService;
_userIdKeyResolver = userIdKeyResolver;
}

public async Task<DocumentVersionItemResponseModel> CreateAsync(ContentVersionMeta contentVersion) =>
new(
contentVersion.VersionId.ToGuid(), // this is a magic guid since versions do not have keys in the DB
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentId, UmbracoObjectTypes.Document).Result),
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType)
.Result),
new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)),
new DateTimeOffset(contentVersion.VersionDate, TimeSpan.Zero), // todo align with datetime offset rework
contentVersion.CurrentPublishedVersion,
contentVersion.CurrentDraftVersion,
contentVersion.PreventCleanup);

public async Task<IEnumerable<DocumentVersionItemResponseModel>> CreateMultipleAsync(IEnumerable<ContentVersionMeta> contentVersions) =>
await Task.WhenAll(contentVersions.Select(CreateAsync));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Core.Models;

namespace Umbraco.Cms.Api.Management.Factories;

public interface IDocumentVersionPresentationFactory
{
Task<DocumentVersionItemResponseModel> CreateAsync(ContentVersionMeta contentVersion);

Task<IEnumerable<DocumentVersionItemResponseModel>> CreateMultipleAsync(
IEnumerable<ContentVersionMeta> contentVersions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Umbraco.Cms.Api.Management.Mapping.Content;
using Umbraco.Cms.Api.Management.ViewModels;
using Umbraco.Cms.Api.Management.ViewModels.Document;
using Umbraco.Cms.Api.Management.ViewModels.DocumentType;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Extensions;

namespace Umbraco.Cms.Api.Management.Mapping.Document;

public class DocumentVersionMapDefinition : ContentMapDefinition<IContent, DocumentValueModel, DocumentVariantResponseModel>, IMapDefinition
{
public DocumentVersionMapDefinition(PropertyEditorCollection propertyEditorCollection)
: base(propertyEditorCollection)
{
}

public void DefineMaps(IUmbracoMapper mapper)
{
mapper.Define<IContent, DocumentVersionResponseModel>((_, _) => new DocumentVersionResponseModel(), Map);
}

private void Map(IContent source, DocumentVersionResponseModel target, MapperContext context)
{
target.Id = source.VersionId.ToGuid(); // this is a magic guid since versions do not have Guids in the DB
target.Document = new ReferenceByIdModel(source.Key);
target.DocumentType = context.Map<DocumentTypeReferenceResponseModel>(source.ContentType)!;
target.Values = MapValueViewModels(source.Properties);
target.Variants = MapVariantViewModels(
source,
(culture, _, documentVariantViewModel) =>
{
documentVariantViewModel.State = DocumentVariantStateHelper.GetState(source, culture);
documentVariantViewModel.PublishDate = culture == null
? source.PublishDate
: source.GetPublishDate(culture);
});
}
}
Loading