From c73b7f42d91717e51c3bc295ba6c0b40f53330f6 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 7 Nov 2023 16:02:20 +0100 Subject: [PATCH 1/5] Move localdb file management of PublishedSnapshotService into itself. (#15085) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move localdb file management of PublishedSnapshotService into itself. Added a way for the PublishedSnapshot service to clean up it's local files so (for example) Upgrade migrations have a reliable way of removing known invalid cache files without running into locking issues * Small rename to differentiate existing method from simple getter * Fix breaking change Long live default implementations 🎉 * Another breaking change fix --------- Co-authored-by: Sven Geusens --- .../IPublishedSnapshotService.cs | 4 ++++ .../Migrations/Upgrade/V_12_0_0/ResetCache.cs | 20 ++++++++++++---- .../PublishedSnapshotService.cs | 24 ++++++++++++++++--- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs index 5bd5ff23cc..8e661aa758 100644 --- a/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs @@ -125,4 +125,8 @@ public interface IPublishedSnapshotService : IDisposable /// Cleans up unused snapshots /// Task CollectAsync(); + + void ResetLocalDb() + { + } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs index b55b4c4ca7..0e41ad89ca 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs @@ -1,22 +1,34 @@ -using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.PublishedCache; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_12_0_0; public class ResetCache : MigrationBase { private readonly IHostingEnvironment _hostingEnvironment; + private readonly IPublishedSnapshotService _publishedSnapshotService; + [Obsolete("Use ctor with all params - This will be removed in Umbraco 14.")] public ResetCache(IMigrationContext context, IHostingEnvironment hostingEnvironment) - : base(context) => + : this(context, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public ResetCache(IMigrationContext context, IHostingEnvironment hostingEnvironment, IPublishedSnapshotService publishedSnapshotService) + : base(context) + { _hostingEnvironment = hostingEnvironment; + _publishedSnapshotService = publishedSnapshotService; + } protected override void Migrate() { RebuildCache = true; var distCacheFolderAbsolutePath = Path.Combine(_hostingEnvironment.LocalTempPath, "DistCache"); - var nuCacheFolderAbsolutePath = Path.Combine(_hostingEnvironment.LocalTempPath, "NuCache"); DeleteAllFilesInFolder(distCacheFolderAbsolutePath); - DeleteAllFilesInFolder(nuCacheFolderAbsolutePath); + _publishedSnapshotService.ResetLocalDb(); } private void DeleteAllFilesInFolder(string path) diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 68b50cfd91..a7f8c42823 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -70,6 +70,8 @@ internal class PublishedSnapshotService : IPublishedSnapshotService private long _mediaGen; private ContentStore _mediaStore = null!; + private string LocalFilePath => Path.Combine(_hostingEnvironment.LocalTempPath, "NuCache"); + public PublishedSnapshotService( PublishedSnapshotServiceOptions options, ISyncBootStateAccessor syncBootStateAccessor, @@ -475,6 +477,22 @@ internal class PublishedSnapshotService : IPublishedSnapshotService return GetUid(_mediaStore, id); } + + public void ResetLocalDb() + { + _logger.LogInformation( + "Resetting NuCache local db"); + var path = LocalFilePath; + if (Directory.Exists(path) is false) + { + return; + } + + MainDomRelease(); + Directory.Delete(path, true); + MainDomRegister(); + } + /// /// Lazily populates the stores only when they are first requested /// @@ -603,7 +621,7 @@ internal class PublishedSnapshotService : IPublishedSnapshotService /// private void MainDomRegister() { - var path = GetLocalFilesPath(); + var path = GetAndEnsureLocalFilesPathExists(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); @@ -652,9 +670,9 @@ internal class PublishedSnapshotService : IPublishedSnapshotService } } - private string GetLocalFilesPath() + private string GetAndEnsureLocalFilesPathExists() { - var path = Path.Combine(_hostingEnvironment.LocalTempPath, "NuCache"); + var path = LocalFilePath; if (!Directory.Exists(path)) { From 87380902f06ebc170a76edff1f0af509a7674208 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 28 Nov 2023 09:35:34 +0100 Subject: [PATCH 2/5] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 1c88857220..617555fa04 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "12.3.3", + "version": "12.3.4", "assemblyVersion": { "precision": "build" }, From 016709eecdd1b63ad0f9c0d107aad992d15eca50 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 30 Nov 2023 10:36:53 +0100 Subject: [PATCH 3/5] Add query string info to links in API output (#15327) --- .../Models/DeliveryApi/ApiLink.cs | 23 +++++++++--- .../MultiUrlPickerValueConverter.cs | 6 ++-- .../MultiUrlPickerValueConverterTests.cs | 35 +++++++++++++++++++ 3 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Core/Models/DeliveryApi/ApiLink.cs b/src/Umbraco.Core/Models/DeliveryApi/ApiLink.cs index d255f6ab32..fcfe12b339 100644 --- a/src/Umbraco.Core/Models/DeliveryApi/ApiLink.cs +++ b/src/Umbraco.Core/Models/DeliveryApi/ApiLink.cs @@ -2,19 +2,32 @@ namespace Umbraco.Cms.Core.Models.DeliveryApi; public sealed class ApiLink { + [Obsolete("Please use the overload that accepts a query string. Will be removed in V14.")] public static ApiLink Content(string title, string? target, Guid destinationId, string destinationType, IApiContentRoute route) - => new(LinkType.Content, null, title, target, destinationId, destinationType, route); + => Content(title, queryString: null, target, destinationId, destinationType, route); + public static ApiLink Content(string title, string? queryString, string? target, Guid destinationId, string destinationType, IApiContentRoute route) + => new(LinkType.Content, url: null, queryString, title, target, destinationId, destinationType, route); + + [Obsolete("Please use the overload that accepts a query string. Will be removed in V14.")] public static ApiLink Media(string title, string url, string? target, Guid destinationId, string destinationType) - => new(LinkType.Media, url, title, target, destinationId, destinationType, null); + => Media(title, url, queryString: null, target, destinationId, destinationType); + public static ApiLink Media(string title, string url, string? queryString, string? target, Guid destinationId, string destinationType) + => new(LinkType.Media, url, queryString, title, target, destinationId, destinationType, route: null); + + [Obsolete("Please use the overload that accepts a query string. Will be removed in V14.")] public static ApiLink External(string? title, string url, string? target) - => new(LinkType.External, url, title, target, null, null, null); + => External(title, url, queryString: null, target); - private ApiLink(LinkType linkType, string? url, string? title, string? target, Guid? destinationId, string? destinationType, IApiContentRoute? route) + public static ApiLink External(string? title, string url, string? queryString, string? target) + => new(LinkType.External, url, queryString, title, target, null, null, null); + + private ApiLink(LinkType linkType, string? url, string? queryString, string? title, string? target, Guid? destinationId, string? destinationType, IApiContentRoute? route) { LinkType = linkType; Url = url; + QueryString = queryString; Title = title; Target = target; DestinationId = destinationId; @@ -24,6 +37,8 @@ public sealed class ApiLink public string? Url { get; } + public string? QueryString { get; } + public string? Title { get; } public string? Target { get; } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs index dfc64bdad5..61cc0e1c7d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MultiUrlPickerValueConverter.cs @@ -185,6 +185,7 @@ public class MultiUrlPickerValueConverter : PropertyValueConverterBase, IDeliver ? null : ApiLink.Content( item.Name.IfNullOrWhiteSpace(_apiContentNameProvider.GetName(content)), + item.QueryString, item.Target, content.Key, content.ContentType.Alias, @@ -195,12 +196,13 @@ public class MultiUrlPickerValueConverter : PropertyValueConverterBase, IDeliver ? null : ApiLink.Media( item.Name.IfNullOrWhiteSpace(_apiContentNameProvider.GetName(media)), - _apiMediaUrlProvider.GetUrl(media), + $"{_apiMediaUrlProvider.GetUrl(media)}{item.QueryString}", + item.QueryString, item.Target, media.Key, media.ContentType.Alias); default: - return ApiLink.External(item.Name, $"{item.Url}{item.QueryString}", item.Target); + return ApiLink.External(item.Name, $"{item.Url}{item.QueryString}", item.QueryString, item.Target); } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiUrlPickerValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiUrlPickerValueConverterTests.cs index 818f55861d..a3bd140ce6 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiUrlPickerValueConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiUrlPickerValueConverterTests.cs @@ -157,11 +157,45 @@ public class MultiUrlPickerValueConverterTests : PropertyValueConverterTests var link = result.First(); Assert.AreEqual("The link", link.Title); Assert.AreEqual("https://umbraco.com/?something=true", link.Url); + Assert.AreEqual("?something=true", link.QueryString); Assert.AreEqual(LinkType.External, link.LinkType); Assert.AreEqual("_blank", link.Target); Assert.Null(link.Route); } + [Test] + public void MultiUrlPickerValueConverter_AppliesExplicitConfigurationToMediaLink() + { + var publishedDataType = new PublishedDataType(123, "test", new Lazy(() => new MultiUrlPickerConfiguration { MaxNumber = 1 })); + var publishedPropertyType = new Mock(); + publishedPropertyType.SetupGet(p => p.DataType).Returns(publishedDataType); + + var valueConverter = MultiUrlPickerValueConverter(); + + var inter = Serializer().Serialize(new[] + { + new MultiUrlPickerValueEditor.LinkDto + { + Udi = new GuidUdi(Constants.UdiEntityType.Media, PublishedMedia.Key), + Name = "Custom link name", + QueryString = "?something=true", + Target = "_blank" + } + }); + var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType.Object, PropertyCacheLevel.Element, inter, false, false) as IEnumerable; + Assert.NotNull(result); + Assert.AreEqual(1, result.Count()); + var link = result.First(); + Assert.AreEqual("Custom link name", link.Title); + Assert.AreEqual(PublishedMedia.Key, link.DestinationId); + Assert.AreEqual("TheMediaType", link.DestinationType); + Assert.AreEqual("the-media-url?something=true", link.Url); + Assert.AreEqual(LinkType.Media, link.LinkType); + Assert.AreEqual("_blank", link.Target); + Assert.AreEqual("?something=true", link.QueryString); + Assert.AreEqual(null, link.Route); + } + [Test] public void MultiUrlPickerValueConverter_AppliesExplicitConfigurationToContentLink() { @@ -190,6 +224,7 @@ public class MultiUrlPickerValueConverterTests : PropertyValueConverterTests Assert.AreEqual("/the-page-url", link.Route!.Path); Assert.AreEqual(LinkType.Content, link.LinkType); Assert.AreEqual("_blank", link.Target); + Assert.AreEqual("?something=true", link.QueryString); Assert.Null(link.Url); } From 528b7d346a8526a04991cb93e6103af56467142d Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 30 Nov 2023 11:32:49 +0100 Subject: [PATCH 4/5] Rollback some #14816 changes to fix 2FA (#15317) --- .../Controllers/AuthenticationController.cs | 2 +- .../src/views/common/login-2fa.html | 20 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs index fe105bee67..beb8787c79 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/AuthenticationController.cs @@ -526,7 +526,7 @@ public class AuthenticationController : UmbracoApiControllerBase await _signInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.IsPersistent, model.RememberClient); if (result.Succeeded) { - return GetUserDetail(_userService.GetByUsername(user.UserName)); + return Ok(GetUserDetail(_userService.GetByUsername(user.UserName))); } if (result.IsLockedOut) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/login-2fa.html b/src/Umbraco.Web.UI.Client/src/views/common/login-2fa.html index be11a78d76..5d8b9cd3fd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/login-2fa.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/login-2fa.html @@ -1,6 +1,6 @@ -