From b3bedb4efee8866a7c5cfee9be2414adcf8f0a8a Mon Sep 17 00:00:00 2001 From: karlmacklin <353806+karlmacklin@users.noreply.github.com> Date: Mon, 15 May 2023 08:42:58 +0200 Subject: [PATCH 1/4] XPath can unambiguously use $site/$parent (#14127) * XPath can unambiguously use $site/$parent * add deprecation notices and obsolete methods * Update deprecation description text/instruction Co-authored-by: Mole * Small spelling fix on deprecation description * keep depr. getByQuery and handle legacy usage --------- Co-authored-by: Kalle Macklin Co-authored-by: Mole --- .../Xml/UmbracoXPathPathSyntaxParser.cs | 53 ++++++++++++++----- .../Routing/NotFoundHandlerHelper.cs | 1 + .../Controllers/EntityController.cs | 21 +++++--- .../src/common/resources/entity.resource.js | 24 +++++++-- .../contentpicker/contentpicker.controller.js | 9 ++-- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs b/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs index bb5c186ca6..2a01d42dc7 100644 --- a/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs +++ b/src/Umbraco.Core/Xml/UmbracoXPathPathSyntaxParser.cs @@ -8,13 +8,23 @@ namespace Umbraco.Cms.Core.Xml; /// public class UmbracoXPathPathSyntaxParser { + [Obsolete("This will be removed in Umbraco 13. Use ParseXPathQuery which accepts a parentId instead")] + public static string ParseXPathQuery( + string xpathExpression, + int? nodeContextId, + Func?> getPath, + Func publishedContentExists) => ParseXPathQuery(xpathExpression, nodeContextId, null, getPath, publishedContentExists); + /// /// Parses custom umbraco xpath expression /// /// The Xpath expression /// - /// The current node id context of executing the query - null if there is no current node, in which case - /// some of the parameters like $current, $parent, $site will be disabled + /// The current node id context of executing the query - null if there is no current node. + /// + /// + /// The parent node id of the current node id context of executing the query. With this we can determine the + /// $parent and $site parameters even if the current node is not yet published. /// /// The callback to create the nodeId path, given a node Id /// The callback to return whether a published node exists based on Id @@ -22,6 +32,7 @@ public class UmbracoXPathPathSyntaxParser public static string ParseXPathQuery( string xpathExpression, int? nodeContextId, + int? parentId, Func?> getPath, Func publishedContentExists) { @@ -84,19 +95,27 @@ public class UmbracoXPathPathSyntaxParser // parseable items: var vars = new Dictionary>(); - // These parameters must have a node id context - if (nodeContextId.HasValue) + if (parentId.HasValue) + { + vars.Add("$parent", q => + { + var path = getPath(parentId.Value)?.ToArray(); + var closestPublishedAncestorId = getClosestPublishedAncestor(path); + return q.Replace("$parent", string.Format(rootXpath, closestPublishedAncestorId)); + }); + + vars.Add("$site", q => + { + var closestPublishedAncestorId = getClosestPublishedAncestor(getPath(parentId.Value)); + return q.Replace( + "$site", + string.Format(rootXpath, closestPublishedAncestorId) + "/ancestor-or-self::*[@level = 1]"); + }); + } + else if (nodeContextId.HasValue) { - vars.Add("$current", q => - { - var closestPublishedAncestorId = getClosestPublishedAncestor(getPath(nodeContextId.Value)); - return q.Replace("$current", string.Format(rootXpath, closestPublishedAncestorId)); - }); - vars.Add("$parent", q => { - // remove the first item in the array if its the current node - // this happens when current is published, but we are looking for its parent specifically var path = getPath(nodeContextId.Value)?.ToArray(); if (path?[0] == nodeContextId.ToString()) { @@ -116,6 +135,16 @@ public class UmbracoXPathPathSyntaxParser }); } + // These parameters must have a node id context + if (nodeContextId.HasValue) + { + vars.Add("$current", q => + { + var closestPublishedAncestorId = getClosestPublishedAncestor(getPath(nodeContextId.Value)); + return q.Replace("$current", string.Format(rootXpath, closestPublishedAncestorId)); + }); + } + // TODO: This used to just replace $root with string.Empty BUT, that would never work // the root is always "/root . Need to confirm with Per why this was string.Empty before! vars.Add("$root", q => q.Replace("$root", "/root")); diff --git a/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs index f945f03b28..04ca6b4bb2 100644 --- a/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs +++ b/src/Umbraco.Infrastructure/Routing/NotFoundHandlerHelper.cs @@ -79,6 +79,7 @@ internal class NotFoundHandlerHelper var xpathResult = UmbracoXPathPathSyntaxParser.ParseXPathQuery( errorPage.ContentXPath!, domainContentId, + null, nodeid => { IEntitySlim? ent = entityService.Get(nodeid); diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index 36e7d46df1..9c4f6aa258 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -555,6 +555,15 @@ public class EntityController : UmbracoAuthorizedJsonController return Ok(returnUrl); } + /// + /// Gets an entity by a xpath query - OBSOLETE + /// + /// + /// + /// + /// + [Obsolete("This will be removed in Umbraco 13. Use GetByXPath instead")] + public ActionResult? GetByQuery(string query, int nodeContextId, UmbracoEntityTypes type) => GetByXPath(query, nodeContextId, null, type); /// /// Gets an entity by a xpath query @@ -562,19 +571,16 @@ public class EntityController : UmbracoAuthorizedJsonController /// /// /// + /// /// - public ActionResult? GetByQuery(string query, int nodeContextId, UmbracoEntityTypes type) + public ActionResult? 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) @@ -586,10 +592,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 3b2618a82b..1c6f8d6184 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/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();
         });
     }

From 16f448a802f352a9859bf3c45dcca626ed097be9 Mon Sep 17 00:00:00 2001
From: Dhanesh Kumar Mj <58820887+dKumarmj@users.noreply.github.com>
Date: Fri, 19 May 2023 14:51:40 +0530
Subject: [PATCH 2/4] [Fix] Block editor labels showing Angular JS on first
 load. (#14143)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Co-authored-by: Dhanesh Kumar <“dhanesh.kumar@phases.io”>
(cherry picked from commit 58695b6e9fdafc0539a757e6d842e44f618d833d)
---
 .../src/common/services/blockeditormodelobject.service.js    | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

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}); From b67c7fbe772037acdc7d806d7bb9576a7ef58b5b Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 22 May 2023 15:15:19 +0200 Subject: [PATCH 3/4] Cherry pick b8d6613bd8d382759c50cdab1bdd305d626642d6 accidentally kept both lines instead of just the one, this fixes the problem. --- templates/UmbracoProject/UmbracoProject.csproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/UmbracoProject/UmbracoProject.csproj b/templates/UmbracoProject/UmbracoProject.csproj index ea6db75798..7ae1dee47d 100644 --- a/templates/UmbracoProject/UmbracoProject.csproj +++ b/templates/UmbracoProject/UmbracoProject.csproj @@ -26,8 +26,6 @@ false false - - From e79246368bdce66744da7b8d1cf225eb130bc62b Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Tue, 23 May 2023 09:38:01 +0200 Subject: [PATCH 4/4] Update JSON schema package references for Forms and Deploy. (#14285) --- src/JsonSchema/JsonSchema.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JsonSchema/JsonSchema.csproj b/src/JsonSchema/JsonSchema.csproj index 3d31dd265d..613283bd69 100644 --- a/src/JsonSchema/JsonSchema.csproj +++ b/src/JsonSchema/JsonSchema.csproj @@ -12,7 +12,7 @@ - - + +