porting 7.6-rc1 into 8
This commit is contained in:
@@ -65,7 +65,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
if (route == null) throw new ArgumentNullException(nameof(route));
|
||||
|
||||
// try to get from cache if not previewing
|
||||
var contentId = (preview || _routesCache == null) ? 0 : _routesCache.GetNodeId(route);
|
||||
var contentId = preview || _routesCache == null ? 0 : _routesCache.GetNodeId(route);
|
||||
|
||||
// if found id in cache then get corresponding content
|
||||
// and clear cache if not found - for whatever reason
|
||||
@@ -101,7 +101,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
var deepest = DomainHelper.ExistsDomainInPath(_domainCache.GetAll(false), content.Path, domainRootNodeId) == false;
|
||||
|
||||
if (deepest)
|
||||
_routesCache.Store(content.Id, route);
|
||||
_routesCache.Store(content.Id, route, true); // trusted route
|
||||
}
|
||||
|
||||
public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null)
|
||||
@@ -112,7 +112,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
public virtual string GetRouteById(bool preview, int contentId)
|
||||
{
|
||||
// try to get from cache if not previewing
|
||||
var route = (preview || _routesCache == null) ? null : _routesCache.GetRoute(contentId);
|
||||
var route = preview || _routesCache == null ? null : _routesCache.GetRoute(contentId);
|
||||
|
||||
// if found in cache then return
|
||||
if (route != null)
|
||||
@@ -125,38 +125,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
if (route == null)
|
||||
return null;
|
||||
|
||||
// find the content back, detect routes collisions: we should find ourselves back,
|
||||
// else it means that another content with "higher priority" is sharing the same route.
|
||||
// perf impact:
|
||||
// - non-colliding, adds one complete "by route" lookup, only on the first time a url is computed (then it's cached anyways)
|
||||
// - colliding, adds one "by route" lookup, the first time the url is computed, then one dictionary looked each time it is computed again
|
||||
// assuming no collisions, the impact is one complete "by route" lookup the first time each url is computed
|
||||
//
|
||||
// U4-9121 - this lookup is too expensive when computing a large amount of urls on a front-end (eg menu)
|
||||
// ... thinking about moving the lookup out of the path into its own async task, so we are not reporting errors
|
||||
// in the back-office anymore, but at least we are not polluting the cache
|
||||
// instead, refactored DeterminedIdByRoute to stop using XPath, with a 16x improvement according to benchmarks
|
||||
// will it be enough?
|
||||
// cache the route BUT do NOT trust it as it can be a colliding route
|
||||
// meaning if we GetRouteById again, we'll get it from cache, but it
|
||||
// won't be used for inbound routing
|
||||
if (preview == false)
|
||||
_routesCache.Store(contentId, route, false);
|
||||
|
||||
var loopId = preview ? 0 : (_routesCache?.GetNodeId(route) ?? 0); // might be cached already in case of collision
|
||||
if (loopId == 0)
|
||||
{
|
||||
var content = DetermineIdByRoute(preview, route, GlobalSettings.HideTopLevelNodeFromPath);
|
||||
|
||||
// add the other route to cache so next time we have it already
|
||||
if (content != null && preview == false)
|
||||
AddToCacheIfDeepestRoute(content, route);
|
||||
|
||||
loopId = content?.Id ?? 0; // though... 0 here would be quite weird?
|
||||
}
|
||||
|
||||
// cache if we have a route and not previewing and it's not a colliding route
|
||||
// (the result of DetermineRouteById is always the deepest route)
|
||||
if (/*route != null &&*/ preview == false && loopId == contentId)
|
||||
_routesCache?.Store(contentId, route);
|
||||
|
||||
// return route if no collision, else report collision
|
||||
return loopId == contentId ? route : ("err/" + loopId);
|
||||
return route;
|
||||
}
|
||||
|
||||
public string GetRouteById(int contentId)
|
||||
@@ -166,10 +141,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
IPublishedContent DetermineIdByRoute(bool preview, string route, bool hideTopLevelNode)
|
||||
{
|
||||
if (route == null) throw new ArgumentNullException(nameof(route));
|
||||
|
||||
//the route always needs to be lower case because we only store the urlName attribute in lower case
|
||||
route = route.ToLowerInvariant();
|
||||
route = route?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(route));
|
||||
|
||||
var pos = route.IndexOf('/');
|
||||
var path = pos == 0 ? route : route.Substring(pos);
|
||||
@@ -177,18 +150,25 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
//check if we can find the node in our xml cache
|
||||
var id = NavigateRoute(preview, startNodeId, path, hideTopLevelNode);
|
||||
if (id > 0) return GetById(preview, id);
|
||||
return id > 0 ? GetById(preview, id) : null;
|
||||
}
|
||||
|
||||
// if hideTopLevelNodePath is true then for url /foo we looked for /*/foo
|
||||
// but maybe that was the url of a non-default top-level node, so we also
|
||||
// have to look for /foo (see note in ApplyHideTopLevelNodeFromPath).
|
||||
if (hideTopLevelNode && path.Length > 1 && path.IndexOf('/', 1) < 0)
|
||||
private static XmlElement GetXmlElementChildWithLowestSortOrder(XmlNode element)
|
||||
{
|
||||
XmlElement elt = null;
|
||||
var min = int.MaxValue;
|
||||
foreach (var n in element.ChildNodes)
|
||||
{
|
||||
var id2 = NavigateRoute(preview, startNodeId, path, false);
|
||||
if (id2 > 0) return GetById(preview, id2);
|
||||
}
|
||||
var e = n as XmlElement;
|
||||
if (e == null) continue;
|
||||
|
||||
return null;
|
||||
var sortOrder = int.Parse(e.GetAttribute("sortOrder"));
|
||||
if (sortOrder >= min) continue;
|
||||
|
||||
min = sortOrder;
|
||||
elt = e;
|
||||
}
|
||||
return elt;
|
||||
}
|
||||
|
||||
private int NavigateRoute(bool preview, int startNodeId, string path, bool hideTopLevelNode)
|
||||
@@ -205,17 +185,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
return elt == null ? -1 : startNodeId;
|
||||
}
|
||||
|
||||
elt = null;
|
||||
var min = int.MaxValue;
|
||||
foreach (var e in xml.DocumentElement.ChildNodes.OfType<XmlElement>())
|
||||
{
|
||||
var sortOrder = int.Parse(e.GetAttribute("sortOrder"));
|
||||
if (sortOrder < min)
|
||||
{
|
||||
min = sortOrder;
|
||||
elt = e;
|
||||
}
|
||||
}
|
||||
elt = GetXmlElementChildWithLowestSortOrder(xml.DocumentElement);
|
||||
return elt == null ? -1 : int.Parse(elt.GetAttribute("id"));
|
||||
}
|
||||
|
||||
@@ -229,12 +199,19 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
if (hideTopLevelNode && startNodeId <= 0)
|
||||
{
|
||||
foreach (var e in elt.ChildNodes.OfType<XmlElement>())
|
||||
//Don't use OfType<T> or Cast<T>, this is critical code, all ChildNodes are XmlElement so explicitly cast
|
||||
// https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198
|
||||
foreach (var n in elt.ChildNodes)
|
||||
{
|
||||
var e = n as XmlElement;
|
||||
if (e == null) continue;
|
||||
|
||||
var id = NavigateElementRoute(e, urlParts);
|
||||
if (id > 0) return id;
|
||||
}
|
||||
return -1;
|
||||
|
||||
if (urlParts.Length > 1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return NavigateElementRoute(elt, urlParts);
|
||||
@@ -247,15 +224,25 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
while (found && i < urlParts.Length)
|
||||
{
|
||||
found = false;
|
||||
foreach (var child in elt.ChildNodes.OfType<XmlElement>())
|
||||
//Don't use OfType<T> or Cast<T>, this is critical code, all ChildNodes are XmlElement so explicitly cast
|
||||
// https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198
|
||||
var sortOrder = -1;
|
||||
foreach (var o in elt.ChildNodes)
|
||||
{
|
||||
var child = o as XmlElement;
|
||||
if (child == null) continue;
|
||||
|
||||
var noNode = child.GetAttributeNode("isDoc") == null;
|
||||
if (noNode) continue;
|
||||
if (child.GetAttribute("urlName") != urlParts[i]) continue;
|
||||
|
||||
found = true;
|
||||
|
||||
var so = int.Parse(child.GetAttribute("sortOrder"));
|
||||
if (sortOrder >= 0 && so >= sortOrder) continue;
|
||||
|
||||
sortOrder = so;
|
||||
elt = child;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
@@ -285,7 +272,20 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (hasDomains == false && GlobalSettings.HideTopLevelNodeFromPath)
|
||||
ApplyHideTopLevelNodeFromPath(node, pathParts, preview);
|
||||
{
|
||||
if (node.Parent == null)
|
||||
{
|
||||
var rootNode = GetByRoute(preview, "/", true);
|
||||
if (rootNode == null)
|
||||
throw new Exception("Failed to get node at /.");
|
||||
if (rootNode.Id == node.Id) // remove only if we're the default node
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
pathParts.RemoveAt(pathParts.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// assemble the route
|
||||
pathParts.Reverse();
|
||||
@@ -295,30 +295,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
return route;
|
||||
}
|
||||
|
||||
void ApplyHideTopLevelNodeFromPath(IPublishedContent content, IList<string> segments, bool preview)
|
||||
{
|
||||
// in theory if hideTopLevelNodeFromPath is true, then there should be only once
|
||||
// top-level node, or else domains should be assigned. but for backward compatibility
|
||||
// we add this check - we look for the document matching "/" and if it's not us, then
|
||||
// we do not hide the top level path
|
||||
// it has to be taken care of in GetByRoute too so if
|
||||
// "/foo" fails (looking for "/*/foo") we try also "/foo".
|
||||
// this does not make much sense anyway esp. if both "/foo/" and "/bar/foo" exist, but
|
||||
// that's the way it works pre-4.10 and we try to be backward compat for the time being
|
||||
if (content.Parent == null)
|
||||
{
|
||||
var rootNode = GetByRoute(preview, "/", true);
|
||||
if (rootNode == null)
|
||||
throw new Exception("Failed to get node at /.");
|
||||
if (rootNode.Id == content.Id) // remove only if we're the default node
|
||||
segments.RemoveAt(segments.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
segments.RemoveAt(segments.Count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region XPath Strings
|
||||
|
||||
Reference in New Issue
Block a user