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/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 @@ -
+
-
+
One last step
@@ -12,19 +12,19 @@
- - + -
+
Invalid code entered @@ -37,14 +37,14 @@ button-style="success" size="m" label-key="general_validate" - state="vm.stateValidateButton" - disabled="vm.code.length === 0"> + state="cvm.stateValidateButton" + disabled="cvm.code.length === 0"> + action="cvm.goBack()">
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); } diff --git a/version.json b/version.json index 5994a80c7d..220f9d3fc6 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.4.0", + "version": "12.4.0-rc", "assemblyVersion": { "precision": "build" },