From 3acda751b95fe3c5d1bcc7a0da96ff4b8570ec78 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:51:59 +0100 Subject: [PATCH] v14: Add Preview controller (#15481) * Add Preview controller * Implement PreviewService * Add tests for endpoints. * Add Authorization to previews * Apply suggestions from code review Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> --------- Co-authored-by: Elitsa Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> --- .../Preview/EndPreviewController.cs | 23 ++++++++++++++ .../Preview/EnterPreviewController.cs | 23 ++++++++++++++ .../Preview/PreviewControllerBase.cs | 13 ++++++++ .../DependencyInjection/UmbracoBuilder.cs | 1 + src/Umbraco.Core/Services/IPreviewService.cs | 14 +++++++++ src/Umbraco.Core/Services/PreviewService.cs | 15 ++++++++++ .../ManagementApi/Preview/EndPreviewTests.cs | 30 +++++++++++++++++++ .../Preview/EnterPreviewTests.cs | 30 +++++++++++++++++++ 8 files changed, 149 insertions(+) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Preview/EndPreviewController.cs create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Preview/EnterPreviewController.cs create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/Preview/PreviewControllerBase.cs create mode 100644 src/Umbraco.Core/Services/IPreviewService.cs create mode 100644 src/Umbraco.Core/Services/PreviewService.cs create mode 100644 tests/Umbraco.Tests.Integration/ManagementApi/Preview/EndPreviewTests.cs create mode 100644 tests/Umbraco.Tests.Integration/ManagementApi/Preview/EnterPreviewTests.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Preview/EndPreviewController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Preview/EndPreviewController.cs new file mode 100644 index 0000000000..8b3532f6ab --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Preview/EndPreviewController.cs @@ -0,0 +1,23 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.Preview; + +[ApiVersion("1.0")] +public class EndPreviewController : PreviewControllerBase +{ + private readonly IPreviewService _previewService; + + public EndPreviewController(IPreviewService previewService) => _previewService = previewService; + + [HttpDelete] + [MapToApiVersion("1.0")] + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult End() + { + _previewService.EndPreview(); + return Ok(); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Preview/EnterPreviewController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Preview/EnterPreviewController.cs new file mode 100644 index 0000000000..80a569eb44 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Preview/EnterPreviewController.cs @@ -0,0 +1,23 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.Preview; + +[ApiVersion("1.0")] +public class EnterPreviewController : PreviewControllerBase +{ + private readonly IPreviewService _previewService; + + public EnterPreviewController(IPreviewService previewService) => _previewService = previewService; + + [HttpPost] + [MapToApiVersion("1.0")] + [ProducesResponseType(StatusCodes.Status200OK)] + public IActionResult Enter() + { + _previewService.EnterPreview(); + return Ok(); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Preview/PreviewControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Preview/PreviewControllerBase.cs new file mode 100644 index 0000000000..0534624bd6 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/Preview/PreviewControllerBase.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Routing; +using Umbraco.Cms.Web.Common.Authorization; + +namespace Umbraco.Cms.Api.Management.Controllers.Preview; + +[ApiController] +[VersionedApiBackOfficeRoute("preview")] +[ApiExplorerSettings(GroupName = "Preview")] +public class PreviewControllerBase : ManagementApiControllerBase +{ +} diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 1ca693ae19..9347994407 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -348,6 +348,7 @@ namespace Umbraco.Cms.Core.DependencyInjection Services.AddSingleton(); Services.AddSingleton(); + Services.AddUnique(); // Register a noop IHtmlSanitizer to be replaced Services.AddUnique(); diff --git a/src/Umbraco.Core/Services/IPreviewService.cs b/src/Umbraco.Core/Services/IPreviewService.cs new file mode 100644 index 0000000000..2bcd4e496c --- /dev/null +++ b/src/Umbraco.Core/Services/IPreviewService.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Cms.Core.Services; + +public interface IPreviewService +{ + /// + /// Enters preview mode for a given user that calls this + /// + void EnterPreview(); + + /// + /// Exits preview mode for a given user that calls this + /// + void EndPreview(); +} diff --git a/src/Umbraco.Core/Services/PreviewService.cs b/src/Umbraco.Core/Services/PreviewService.cs new file mode 100644 index 0000000000..30316faff7 --- /dev/null +++ b/src/Umbraco.Core/Services/PreviewService.cs @@ -0,0 +1,15 @@ +using Umbraco.Cms.Core.Web; + +namespace Umbraco.Cms.Core.Services; + +public class PreviewService : IPreviewService +{ + private readonly ICookieManager _cookieManager; + + public PreviewService(ICookieManager cookieManager) => _cookieManager = cookieManager; + + public void EnterPreview() => _cookieManager.SetCookieValue(Constants.Web.PreviewCookieName, "preview"); + + + public void EndPreview() => _cookieManager.ExpireCookie(Constants.Web.PreviewCookieName); +} diff --git a/tests/Umbraco.Tests.Integration/ManagementApi/Preview/EndPreviewTests.cs b/tests/Umbraco.Tests.Integration/ManagementApi/Preview/EndPreviewTests.cs new file mode 100644 index 0000000000..52c0536d86 --- /dev/null +++ b/tests/Umbraco.Tests.Integration/ManagementApi/Preview/EndPreviewTests.cs @@ -0,0 +1,30 @@ +using System.Linq.Expressions; +using System.Net; +using NUnit.Framework; +using Umbraco.Cms.Api.Management.Controllers.Preview; +using Umbraco.Cms.Core; + +namespace Umbraco.Cms.Tests.Integration.ManagementApi.Preview; + +public class EndPreviewTests : ManagementApiTest +{ + protected override Expression> MethodSelector => + x => x.End(); + + + [Test] + public virtual async Task As_Admin_I_Have_Access() + { + await AuthenticateClientAsync(Client, "admin@umbraco.com", "1234567890", true); + + var response = await Client.DeleteAsync(Url); + + // Check if the set cookie header is sent + var doesHeaderExist = response.Headers.TryGetValues("Set-Cookie", out var setCookieValues) && + setCookieValues.Any(value => value.Contains($"{Constants.Web.PreviewCookieName}=; expires")); + + Assert.IsTrue(doesHeaderExist); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, await response.Content.ReadAsStringAsync()); + } + +} diff --git a/tests/Umbraco.Tests.Integration/ManagementApi/Preview/EnterPreviewTests.cs b/tests/Umbraco.Tests.Integration/ManagementApi/Preview/EnterPreviewTests.cs new file mode 100644 index 0000000000..3b8913ec69 --- /dev/null +++ b/tests/Umbraco.Tests.Integration/ManagementApi/Preview/EnterPreviewTests.cs @@ -0,0 +1,30 @@ +using System.Linq.Expressions; +using System.Net; +using NUnit.Framework; +using Umbraco.Cms.Api.Management.Controllers.Preview; +using Umbraco.Cms.Core; + +namespace Umbraco.Cms.Tests.Integration.ManagementApi.Preview; + +public class EnterPreviewTests : ManagementApiTest +{ + protected override Expression> MethodSelector => + x => x.Enter(); + + + [Test] + public virtual async Task As_Admin_I_Have_Access() + { + await AuthenticateClientAsync(Client, "admin@umbraco.com", "1234567890", true); + + var response = await Client.PostAsync(Url, null); + + // Check if the set cookie header is sent + var doesHeaderExist = response.Headers.TryGetValues("Set-Cookie", out var setCookieValues) && + setCookieValues.Any(value => value.Contains($"{Constants.Web.PreviewCookieName}=preview; path=/")); + + Assert.IsTrue(doesHeaderExist); + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, await response.Content.ReadAsStringAsync()); + } + +}