? GetByXPath(string query, int nodeContextId, int? parentId, UmbracoEntityTypes type)
{
- // TODO: Rename this!!! It's misleading, it should be GetByXPath
-
-
if (type != UmbracoEntityTypes.Document)
{
throw new ArgumentException("Get by query is only compatible with entities of type Document");
}
-
- var q = ParseXPathQuery(query, nodeContextId);
+ var q = ParseXPathQuery(query, nodeContextId, parentId);
IPublishedContent? node = _publishedContentQuery.ContentSingleAtXPath(q);
if (node == null)
@@ -546,10 +552,11 @@ public class EntityController : UmbracoAuthorizedJsonController
}
// PP: Work in progress on the query parser
- private string ParseXPathQuery(string query, int id) =>
+ private string ParseXPathQuery(string query, int id, int? parentId) =>
UmbracoXPathPathSyntaxParser.ParseXPathQuery(
query,
id,
+ parentId,
nodeid =>
{
IEntitySlim? ent = _entityService.Get(nodeid);
diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
index a3c96cf805..6f46268b5b 100644
--- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
+++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js
@@ -318,9 +318,22 @@ function entityResource($q, $http, umbRequestHelper) {
'Failed to retrieve entity data for ids ' + ids);
},
+ /**
+ * @deprecated use getByXPath instead.
+ */
+ getByQuery: function (query, nodeContextId, type) {
+ return umbRequestHelper.resourcePromise(
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "entityApiBaseUrl",
+ "GetByQuery",
+ [{ query: query }, { nodeContextId: nodeContextId }, { type: type }])),
+ 'Failed to retrieve entity data for query ' + query);
+ },
+
/**
* @ngdoc method
- * @name umbraco.resources.entityResource#getByQuery
+ * @name umbraco.resources.entityResource#getByXPath
* @methodOf umbraco.resources.entityResource
*
* @description
@@ -329,7 +342,7 @@ function entityResource($q, $http, umbRequestHelper) {
* ##usage
*
* //get content by xpath
- * entityResource.getByQuery("$current", -1, "Document")
+ * entityResource.getByXPath("$current", -1, -1, "Document")
* .then(function(ent) {
* var myDoc = ent;
* alert('its here!');
@@ -338,17 +351,18 @@ function entityResource($q, $http, umbRequestHelper) {
*
* @param {string} query xpath to use in query
* @param {Int} nodeContextId id id to start from
+ * @param {Int} parentId id id of the parent to the starting point
* @param {string} type Object type name
* @returns {Promise} resourcePromise object containing the entity.
*
*/
- getByQuery: function (query, nodeContextId, type) {
+ getByXPath: function (query, nodeContextId, parentId, type) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
- "GetByQuery",
- [{ query: query }, { nodeContextId: nodeContextId }, { type: type }])),
+ "GetByXPath",
+ [{ query: query }, { nodeContextId: nodeContextId }, { parentId: parentId }, { type: type }])),
'Failed to retrieve entity data for query ' + query);
},
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
index abb81c38dc..20661d5d1c 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js
@@ -639,10 +639,9 @@
mapToPropertyModel(this.settings, this.settingsData);
}
};
-
// first time instant update of label.
- blockObject.label = (blockObject.config.label || blockObject.content?.contentTypeName) ?? "" ;
- blockObject.index = 0;
+ blockObject.label = blockObject.content?.contentTypeName || "";
+ blockObject.index = 0;
if (blockObject.config.label && blockObject.config.label !== "" && blockObject.config.unsupported !== true) {
var labelElement = $('', { text: blockObject.config.label});
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
index 09b9d2c98b..52f147bce0 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
@@ -245,9 +245,12 @@ function contentPickerController($scope, $q, $routeParams, $location, entityReso
dialogOptions.startNodeId = -1;
}
else if ($scope.model.config.startNode.query) {
- //if we have a query for the startnode, we will use that.
- var rootId = editorState.current.id;
- entityResource.getByQuery($scope.model.config.startNode.query, rootId, "Document").then(function (ent) {
+ entityResource.getByXPath(
+ $scope.model.config.startNode.query,
+ editorState.current.id,
+ editorState.current.parentId,
+ "Document"
+ ).then(function (ent) {
dialogOptions.startNodeId = ($scope.model.config.idType === "udi" ? ent.udi : ent.id).toString();
});
}
diff --git a/templates/UmbracoProject/UmbracoProject.csproj b/templates/UmbracoProject/UmbracoProject.csproj
index 0989706f68..d50f95a907 100644
--- a/templates/UmbracoProject/UmbracoProject.csproj
+++ b/templates/UmbracoProject/UmbracoProject.csproj
@@ -26,8 +26,6 @@
false
false
-
-
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs
index 750ac885e0..f214c05446 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentBuilderTests.cs
@@ -1,6 +1,7 @@
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core.DeliveryApi;
+using Umbraco.Cms.Core.Models.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.PublishedCache;
@@ -61,10 +62,36 @@ public class ContentBuilderTests : DeliveryApiTests
var customNameProvider = new Mock();
customNameProvider.Setup(n => n.GetName(content.Object)).Returns($"Custom name for: {content.Object.Name}");
- var builder = new ApiContentBuilder(customNameProvider.Object, Mock.Of(), CreateOutputExpansionStrategyAccessor());
+ var routeBuilder = new Mock();
+ routeBuilder
+ .Setup(r => r.Build(content.Object, It.IsAny()))
+ .Returns(new ApiContentRoute(content.Object.UrlSegment!, new ApiContentStartItem(Guid.NewGuid(), "/")));
+
+ var builder = new ApiContentBuilder(customNameProvider.Object, routeBuilder.Object, CreateOutputExpansionStrategyAccessor());
var result = builder.Build(content.Object);
Assert.NotNull(result);
Assert.AreEqual("Custom name for: The page", result.Name);
}
+
+ [Test]
+ public void ContentBuilder_ReturnsNullForUnRoutableContent()
+ {
+ var content = new Mock();
+
+ var contentType = new Mock();
+ contentType.SetupGet(c => c.Alias).Returns("thePageType");
+
+ ConfigurePublishedContentMock(content, Guid.NewGuid(), "The page", "the-page", contentType.Object, Array.Empty());
+
+ var routeBuilder = new Mock();
+ routeBuilder
+ .Setup(r => r.Build(content.Object, It.IsAny()))
+ .Returns((ApiContentRoute)null);
+
+ var builder = new ApiContentBuilder(new ApiContentNameProvider(), routeBuilder.Object, CreateOutputExpansionStrategyAccessor());
+ var result = builder.Build(content.Object);
+
+ Assert.Null(result);
+ }
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs
index 202026ed30..90774c5e25 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/ContentRouteBuilderTests.cs
@@ -2,6 +2,7 @@
using NUnit.Framework;
using Umbraco.Cms.Core.DeliveryApi;
using Umbraco.Cms.Core.Models;
+using Umbraco.Cms.Core.Models.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PublishedCache;
using Umbraco.Cms.Core.Routing;
@@ -21,6 +22,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath);
var result = builder.Build(root);
+ Assert.IsNotNull(result);
Assert.AreEqual("/", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root", result.StartItem.Path);
@@ -38,6 +40,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath);
var result = builder.Build(child);
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root", result.StartItem.Path);
@@ -58,6 +61,7 @@ public class ContentRouteBuilderTests : DeliveryApiTests
var builder = CreateApiContentRouteBuilder(hideTopLevelNodeFromPath);
var result = builder.Build(grandchild);
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child/the-grandchild", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root", result.StartItem.Path);
@@ -74,11 +78,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests
var builder = CreateApiContentRouteBuilder(false);
var result = builder.Build(child, "en-us");
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child-en-us", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root-en-us", result.StartItem.Path);
result = builder.Build(child, "da-dk");
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child-da-dk", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root-da-dk", result.StartItem.Path);
@@ -95,11 +101,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests
var builder = CreateApiContentRouteBuilder(false);
var result = builder.Build(child, "en-us");
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root-en-us", result.StartItem.Path);
result = builder.Build(child, "da-dk");
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root-da-dk", result.StartItem.Path);
@@ -116,11 +124,13 @@ public class ContentRouteBuilderTests : DeliveryApiTests
var builder = CreateApiContentRouteBuilder(false);
var result = builder.Build(child, "en-us");
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child-en-us", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root", result.StartItem.Path);
result = builder.Build(child, "da-dk");
+ Assert.IsNotNull(result);
Assert.AreEqual("/the-child-da-dk", result.Path);
Assert.AreEqual(rootKey, result.StartItem.Id);
Assert.AreEqual("the-root", result.StartItem.Path);
@@ -144,36 +154,18 @@ public class ContentRouteBuilderTests : DeliveryApiTests
[TestCase("#")]
public void FallsBackToContentPathIfUrlProviderCannotResolveUrl(string resolvedUrl)
{
- var publishedUrlProviderMock = new Mock();
- publishedUrlProviderMock
- .Setup(p => p.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
- .Returns(resolvedUrl);
+ var result = GetUnRoutableRoute(resolvedUrl, "/the/content/route");
+ Assert.IsNotNull(result);
+ Assert.AreEqual("/the/content/route", result.Path);
+ }
- var publishedContentCacheMock = new Mock();
- publishedContentCacheMock
- .Setup(c => c.GetRouteById(It.IsAny(), It.IsAny()))
- .Returns("/the/content/route");
-
- var publishedSnapshotMock = new Mock();
- publishedSnapshotMock
- .SetupGet(s => s.Content)
- .Returns(publishedContentCacheMock.Object);
- var publishedSnapshot = publishedSnapshotMock.Object;
-
- var publishedSnapshotAccessorMock = new Mock();
- publishedSnapshotAccessorMock
- .Setup(a => a.TryGetPublishedSnapshot(out publishedSnapshot))
- .Returns(true);
-
- var content = SetupVariantPublishedContent("The Content", Guid.NewGuid());
-
- var builder = new ApiContentRouteBuilder(
- publishedUrlProviderMock.Object,
- CreateGlobalSettings(),
- Mock.Of(),
- publishedSnapshotAccessorMock.Object);
-
- Assert.AreEqual("/the/content/route", builder.Build(content).Path);
+ [TestCase("")]
+ [TestCase(" ")]
+ [TestCase("#")]
+ public void YieldsNullForUnRoutableContent(string contentPath)
+ {
+ var result = GetUnRoutableRoute(contentPath, contentPath);
+ Assert.IsNull(result);
}
[TestCase(true)]
@@ -253,4 +245,38 @@ public class ContentRouteBuilderTests : DeliveryApiTests
CreateGlobalSettings(hideTopLevelNodeFromPath),
Mock.Of(),
Mock.Of());
+
+ private IApiContentRoute? GetUnRoutableRoute(string publishedUrl, string routeById)
+ {
+ var publishedUrlProviderMock = new Mock();
+ publishedUrlProviderMock
+ .Setup(p => p.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
+ .Returns(publishedUrl);
+
+ var publishedContentCacheMock = new Mock();
+ publishedContentCacheMock
+ .Setup(c => c.GetRouteById(It.IsAny(), It.IsAny()))
+ .Returns(routeById);
+
+ var publishedSnapshotMock = new Mock();
+ publishedSnapshotMock
+ .SetupGet(s => s.Content)
+ .Returns(publishedContentCacheMock.Object);
+ var publishedSnapshot = publishedSnapshotMock.Object;
+
+ var publishedSnapshotAccessorMock = new Mock();
+ publishedSnapshotAccessorMock
+ .Setup(a => a.TryGetPublishedSnapshot(out publishedSnapshot))
+ .Returns(true);
+
+ var content = SetupVariantPublishedContent("The Content", Guid.NewGuid());
+
+ var builder = new ApiContentRouteBuilder(
+ publishedUrlProviderMock.Object,
+ CreateGlobalSettings(),
+ Mock.Of(),
+ publishedSnapshotAccessorMock.Object);
+
+ return builder.Build(content);
+ }
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs
index 23dc5bb8b3..c02526054e 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MediaPickerWithCropsValueConverterTests.cs
@@ -2,7 +2,6 @@ using Moq;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.DeliveryApi;
-using Umbraco.Cms.Core.DeliveryApi.Accessors;
using Umbraco.Cms.Core.Models.DeliveryApi;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.PropertyEditors;
@@ -63,32 +62,15 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false) as IEnumerable;
Assert.NotNull(result);
Assert.AreEqual(1, result.Count());
- Assert.AreEqual("My media", result.First().Name);
- Assert.AreEqual("my-media", result.First().Url);
- Assert.AreEqual(".jpg", result.First().Extension);
- Assert.AreEqual(200, result.First().Width);
- Assert.AreEqual(400, result.First().Height);
- Assert.AreEqual(800, result.First().Bytes);
- Assert.NotNull(result.First().FocalPoint);
- Assert.AreEqual(".jpg", result.First().Extension);
- Assert.AreEqual(200, result.First().Width);
- Assert.AreEqual(400, result.First().Height);
- Assert.AreEqual(800, result.First().Bytes);
- Assert.AreEqual(.2m, result.First().FocalPoint.Left);
- Assert.AreEqual(.4m, result.First().FocalPoint.Top);
- Assert.NotNull(result.First().Crops);
- Assert.AreEqual(1, result.First().Crops.Count());
- Assert.AreEqual("one", result.First().Crops.First().Alias);
- Assert.AreEqual(100, result.First().Crops.First().Height);
- Assert.AreEqual(200, result.First().Crops.First().Width);
- Assert.NotNull(result.First().Crops.First().Coordinates);
- Assert.AreEqual(1m, result.First().Crops.First().Coordinates.X1);
- Assert.AreEqual(2m, result.First().Crops.First().Coordinates.X2);
- Assert.AreEqual(10m, result.First().Crops.First().Coordinates.Y1);
- Assert.AreEqual(20m, result.First().Crops.First().Coordinates.Y2);
- Assert.NotNull(result.First().Properties);
- Assert.AreEqual(1, result.First().Properties.Count);
- Assert.AreEqual("My alt text", result.First().Properties["altText"]);
+ var first = result.Single();
+ ValidateMedia(first, "My media", "my-media", ".jpg", 200, 400, 800);
+ ValidateFocalPoint(first.FocalPoint, .2m, .4m);
+ Assert.NotNull(first.Crops);
+ Assert.AreEqual(1, first.Crops.Count());
+ ValidateCrop(first.Crops.First(), "one", 200, 100, 1m, 2m, 10m, 20m);
+ Assert.NotNull(first.Properties);
+ Assert.AreEqual(1, first.Properties.Count);
+ Assert.AreEqual("My alt text", first.Properties["altText"]);
}
[Test]
@@ -138,52 +120,147 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false) as IEnumerable;
Assert.NotNull(result);
Assert.AreEqual(2, result.Count());
+ var first = result.First();
+ var last = result.Last();
- Assert.AreEqual("My media", result.First().Name);
- Assert.AreEqual("my-media", result.First().Url);
- Assert.AreEqual(".jpg", result.First().Extension);
- Assert.AreEqual(200, result.First().Width);
- Assert.AreEqual(400, result.First().Height);
- Assert.AreEqual(800, result.First().Bytes);
- Assert.NotNull(result.First().FocalPoint);
- Assert.AreEqual(.2m, result.First().FocalPoint.Left);
- Assert.AreEqual(.4m, result.First().FocalPoint.Top);
- Assert.NotNull(result.First().Crops);
- Assert.AreEqual(1, result.First().Crops.Count());
- Assert.AreEqual("one", result.First().Crops.First().Alias);
- Assert.AreEqual(100, result.First().Crops.First().Height);
- Assert.AreEqual(200, result.First().Crops.First().Width);
- Assert.NotNull(result.First().Crops.First().Coordinates);
- Assert.AreEqual(1m, result.First().Crops.First().Coordinates.X1);
- Assert.AreEqual(2m, result.First().Crops.First().Coordinates.X2);
- Assert.AreEqual(10m, result.First().Crops.First().Coordinates.Y1);
- Assert.AreEqual(20m, result.First().Crops.First().Coordinates.Y2);
- Assert.NotNull(result.First().Properties);
- Assert.AreEqual(1, result.First().Properties.Count);
- Assert.AreEqual("My alt text", result.First().Properties["altText"]);
+ ValidateMedia(first, "My media", "my-media", ".jpg", 200, 400, 800);
+ ValidateFocalPoint(first.FocalPoint, .2m, .4m);
+ Assert.NotNull(first.Crops);
+ Assert.AreEqual(1, first.Crops.Count());
+ ValidateCrop(first.Crops.First(), "one", 200, 100, 1m, 2m, 10m, 20m);
+ Assert.NotNull(first.Properties);
+ Assert.AreEqual(1, first.Properties.Count);
+ Assert.AreEqual("My alt text", first.Properties["altText"]);
- Assert.AreEqual("My other media", result.Last().Name);
- Assert.AreEqual("my-other-media", result.Last().Url);
- Assert.AreEqual(".png", result.Last().Extension);
- Assert.AreEqual(800, result.Last().Width);
- Assert.AreEqual(600, result.Last().Height);
- Assert.AreEqual(200, result.Last().Bytes);
- Assert.NotNull(result.Last().FocalPoint);
- Assert.AreEqual(.8m, result.Last().FocalPoint.Left);
- Assert.AreEqual(.6m, result.Last().FocalPoint.Top);
- Assert.NotNull(result.Last().Crops);
- Assert.AreEqual(1, result.Last().Crops.Count());
- Assert.AreEqual("one", result.Last().Crops.Last().Alias);
- Assert.AreEqual(100, result.Last().Crops.Last().Height);
- Assert.AreEqual(200, result.Last().Crops.Last().Width);
- Assert.NotNull(result.Last().Crops.Last().Coordinates);
- Assert.AreEqual(40m, result.Last().Crops.Last().Coordinates.X1);
- Assert.AreEqual(20m, result.Last().Crops.Last().Coordinates.X2);
- Assert.AreEqual(2m, result.Last().Crops.Last().Coordinates.Y1);
- Assert.AreEqual(1m, result.Last().Crops.Last().Coordinates.Y2);
- Assert.NotNull(result.Last().Properties);
- Assert.AreEqual(1, result.Last().Properties.Count);
- Assert.AreEqual("My other alt text", result.Last().Properties["altText"]);
+ ValidateMedia(last, "My other media", "my-other-media", ".png", 800, 600, 200);
+ ValidateFocalPoint(last.FocalPoint, .8m, .6m);
+ Assert.NotNull(last.Crops);
+ Assert.AreEqual(1, last.Crops.Count());
+ ValidateCrop(last.Crops.First(), "one", 200, 100, 40m, 20m, 2m, 1m);
+ Assert.NotNull(last.Properties);
+ Assert.AreEqual(1, last.Properties.Count);
+ Assert.AreEqual("My other alt text", last.Properties["altText"]);
+ }
+
+ [Test]
+ public void MediaPickerWithCropsValueConverter_MergesMediaCropsWithLocalCrops()
+ {
+ var publishedPropertyType = SetupMediaPropertyType(false);
+ var mediaCrops = new ImageCropperValue
+ {
+ Crops = new[]
+ {
+ new ImageCropperValue.ImageCropperCrop
+ {
+ Alias = "mediaOne",
+ Width = 111,
+ Height = 222,
+ Coordinates = new ImageCropperValue.ImageCropperCropCoordinates { X1 = 2m, X2 = 4m, Y1 = 20m, Y2 = 40m }
+ }
+ },
+ FocalPoint = new ImageCropperValue.ImageCropperFocalPoint { Left = .9m, Top = .1m }
+ };
+ var mediaKey = SetupMedia("Some media", ".123", 123, 456, "My alt text", 789, mediaCrops);
+
+ var serializer = new JsonNetSerializer();
+
+ var valueConverter = MediaPickerWithCropsValueConverter();
+ Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType));
+
+ var inter = serializer.Serialize(new[]
+ {
+ new MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.MediaWithCropsDto
+ {
+ Key = Guid.NewGuid(),
+ MediaKey = mediaKey,
+ Crops = new []
+ {
+ new ImageCropperValue.ImageCropperCrop
+ {
+ Alias = "one",
+ Coordinates = new ImageCropperValue.ImageCropperCropCoordinates { X1 = 1m, X2 = 2m, Y1 = 10m, Y2 = 20m }
+ }
+ }
+ }
+ });
+
+ var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false) as IEnumerable;
+ Assert.NotNull(result);
+ Assert.AreEqual(1, result.Count());
+ var mediaWithCrops = result.Single();
+ ValidateMedia(mediaWithCrops, "Some media", "some-media", ".123", 123, 456, 789);
+
+ // no local focal point, should revert to media focal point
+ ValidateFocalPoint(mediaWithCrops.FocalPoint, .9m, .1m);
+
+ // media crops should be merged with local crops
+ Assert.NotNull(mediaWithCrops.Crops);
+ Assert.AreEqual(2, mediaWithCrops.Crops.Count());
+
+ // local crops should be first, media crops should be last
+ ValidateCrop(mediaWithCrops.Crops.First(), "one", 200, 100, 1m, 2m, 10m, 20m);
+ ValidateCrop(mediaWithCrops.Crops.Last(), "mediaOne", 111, 222, 2m, 4m, 20m, 40m);
+ }
+
+
+ [Test]
+ public void MediaPickerWithCropsValueConverter_LocalCropsAndFocalPointTakesPrecedenceOverMediaCropsAndFocalPoint()
+ {
+ var publishedPropertyType = SetupMediaPropertyType(false);
+ var mediaCrops = new ImageCropperValue
+ {
+ Crops = new[]
+ {
+ new ImageCropperValue.ImageCropperCrop
+ {
+ Alias = "one",
+ Width = 111,
+ Height = 222,
+ Coordinates = new ImageCropperValue.ImageCropperCropCoordinates { X1 = 2m, X2 = 4m, Y1 = 20m, Y2 = 40m }
+ }
+ },
+ FocalPoint = new ImageCropperValue.ImageCropperFocalPoint { Left = .9m, Top = .1m }
+ };
+ var mediaKey = SetupMedia("Some media", ".123", 123, 456, "My alt text", 789, mediaCrops);
+
+ var serializer = new JsonNetSerializer();
+
+ var valueConverter = MediaPickerWithCropsValueConverter();
+ Assert.AreEqual(typeof(IEnumerable), valueConverter.GetDeliveryApiPropertyValueType(publishedPropertyType));
+
+ var inter = serializer.Serialize(new[]
+ {
+ new MediaPicker3PropertyEditor.MediaPicker3PropertyValueEditor.MediaWithCropsDto
+ {
+ Key = Guid.NewGuid(),
+ MediaKey = mediaKey,
+ Crops = new []
+ {
+ new ImageCropperValue.ImageCropperCrop
+ {
+ Alias = "one",
+ Coordinates = new ImageCropperValue.ImageCropperCropCoordinates { X1 = 1m, X2 = 2m, Y1 = 10m, Y2 = 20m }
+ }
+ },
+ FocalPoint = new ImageCropperValue.ImageCropperFocalPoint { Left = .2m, Top = .3m }
+ }
+ });
+
+ var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false) as IEnumerable;
+ Assert.NotNull(result);
+ Assert.AreEqual(1, result.Count());
+ var mediaWithCrops = result.Single();
+ ValidateMedia(mediaWithCrops, "Some media", "some-media", ".123", 123, 456, 789);
+
+ // local focal point should take precedence over media focal point
+ ValidateFocalPoint(mediaWithCrops.FocalPoint, .2m, .3m);
+
+ // media crops should be discarded when merging with local crops (matching aliases, local ones take precedence)
+ Assert.NotNull(mediaWithCrops.Crops);
+ Assert.AreEqual(1, mediaWithCrops.Crops.Count());
+
+ // local crops should be first, media crops should be last
+ ValidateCrop(mediaWithCrops.Crops.First(), "one", 200, 100, 1m, 2m, 10m, 20m);
}
[TestCase("")]
@@ -194,8 +271,6 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
{
var publishedPropertyType = SetupMediaPropertyType(false);
- var serializer = new JsonNetSerializer();
-
var valueConverter = MediaPickerWithCropsValueConverter();
var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false) as IEnumerable;
@@ -211,8 +286,6 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
{
var publishedPropertyType = SetupMediaPropertyType(true);
- var serializer = new JsonNetSerializer();
-
var valueConverter = MediaPickerWithCropsValueConverter();
var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType, PropertyCacheLevel.Element, inter, false) as IEnumerable;
@@ -241,7 +314,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
return publishedPropertyType.Object;
}
- private Guid SetupMedia(string name, string extension, int width, int height, string altText, int bytes)
+ private Guid SetupMedia(string name, string extension, int width, int height, string altText, int bytes, ImageCropperValue? imageCropperValue = null)
{
var publishedMediaType = new Mock();
publishedMediaType.SetupGet(c => c.ItemType).Returns(PublishedItemType.Media);
@@ -266,6 +339,7 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
AddProperty(Constants.Conventions.Media.Width, width);
AddProperty(Constants.Conventions.Media.Height, height);
AddProperty(Constants.Conventions.Media.Bytes, bytes);
+ AddProperty(Constants.Conventions.Media.File, imageCropperValue);
AddProperty("altText", altText);
PublishedMediaCacheMock
@@ -281,4 +355,49 @@ public class MediaPickerWithCropsValueConverterTests : PropertyValueConverterTes
return mediaKey;
}
+
+ private void ValidateMedia(
+ ApiMediaWithCrops actual,
+ string expectedName,
+ string expectedUrl,
+ string expectedExtension,
+ int expectedWidth,
+ int expectedHeight,
+ int expectedBytes)
+ {
+ Assert.AreEqual(expectedName, actual.Name);
+ Assert.AreEqual(expectedUrl, actual.Url);
+ Assert.AreEqual(expectedExtension, actual.Extension);
+ Assert.AreEqual(expectedWidth, actual.Width);
+ Assert.AreEqual(expectedHeight, actual.Height);
+ Assert.AreEqual(expectedBytes, actual.Bytes);
+
+ }
+
+ private void ValidateFocalPoint(ImageCropperValue.ImageCropperFocalPoint? actual, decimal expectedLeft, decimal expectedTop)
+ {
+ Assert.NotNull(actual);
+ Assert.AreEqual(expectedLeft, actual.Left);
+ Assert.AreEqual(expectedTop, actual.Top);
+ }
+
+ private void ValidateCrop(
+ ImageCropperValue.ImageCropperCrop actual,
+ string expectedAlias,
+ int expectedWidth,
+ int expectedHeight,
+ decimal expectedX1,
+ decimal expectedX2,
+ decimal expectedY1,
+ decimal expectedY2)
+ {
+ Assert.AreEqual(expectedAlias, actual.Alias);
+ Assert.AreEqual(expectedWidth, actual.Width);
+ Assert.AreEqual(expectedHeight, actual.Height);
+ Assert.NotNull(actual.Coordinates);
+ Assert.AreEqual(expectedX1, actual.Coordinates.X1);
+ Assert.AreEqual(expectedX2, actual.Coordinates.X2);
+ Assert.AreEqual(expectedY1, actual.Coordinates.Y1);
+ Assert.AreEqual(expectedY2, actual.Coordinates.Y2);
+ }
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs
index 320f16dbf6..276df9a223 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/DeliveryApi/MultiNodeTreePickerValueConverterTests.cs
@@ -15,13 +15,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.DeliveryApi;
[TestFixture]
public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTests
{
- private MultiNodeTreePickerValueConverter MultiNodeTreePickerValueConverter()
+ private MultiNodeTreePickerValueConverter MultiNodeTreePickerValueConverter(IApiContentRouteBuilder? routeBuilder = null)
{
var expansionStrategyAccessor = CreateOutputExpansionStrategyAccessor();
var contentNameProvider = new ApiContentNameProvider();
var apiUrProvider = new ApiMediaUrlProvider(PublishedUrlProvider);
- var routeBuilder = new ApiContentRouteBuilder(PublishedUrlProvider, CreateGlobalSettings(), Mock.Of(), Mock.Of());
+ routeBuilder = routeBuilder ?? new ApiContentRouteBuilder(PublishedUrlProvider, CreateGlobalSettings(), Mock.Of(), Mock.Of());
return new MultiNodeTreePickerValueConverter(
PublishedSnapshotAccessor,
Mock.Of(),
@@ -93,7 +93,9 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest
}
[Test]
- public void MultiNodeTreePickerValueConverter_RendersContentProperties()
+ [TestCase(Constants.UdiEntityType.Document)]
+ [TestCase("content")]
+ public void MultiNodeTreePickerValueConverter_RendersContentProperties(string entityType)
{
var content = new Mock();
@@ -112,7 +114,7 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest
.Setup(pcc => pcc.GetById(key))
.Returns(content.Object);
- var publishedDataType = MultiNodePickerPublishedDataType(false, Constants.UdiEntityType.Document);
+ var publishedDataType = MultiNodePickerPublishedDataType(false, entityType);
var publishedPropertyType = new Mock();
publishedPropertyType.SetupGet(p => p.DataType).Returns(publishedDataType);
@@ -274,4 +276,21 @@ public class MultiNodeTreePickerValueConverterTests : PropertyValueConverterTest
Assert.NotNull(result);
Assert.IsEmpty(result);
}
+
+ [Test]
+ public void MultiNodeTreePickerValueConverter_YieldsNothingForUnRoutableContent()
+ {
+ var publishedDataType = MultiNodePickerPublishedDataType(false, Constants.UdiEntityType.Document);
+ var publishedPropertyType = new Mock();
+ publishedPropertyType.SetupGet(p => p.DataType).Returns(publishedDataType);
+
+ // mocking the route builder will make it yield null values for all routes, so there is no need to setup anything on the mock
+ var routeBuilder = new Mock();
+ var valueConverter = MultiNodeTreePickerValueConverter(routeBuilder.Object);
+
+ var inter = new Udi[] { new GuidUdi(Constants.UdiEntityType.Document, PublishedContent.Key) };
+ var result = valueConverter.ConvertIntermediateToDeliveryApiObject(Mock.Of(), publishedPropertyType.Object, PropertyCacheLevel.Element, inter, false) as IEnumerable;
+ Assert.NotNull(result);
+ Assert.IsEmpty(result);
+ }
}