Merge remote-tracking branch 'origin/v9/dev' into v10/dev
# Conflicts: # build/azure-pipelines.yml # src/Umbraco.Core/Routing/DefaultUrlProvider.cs # src/Umbraco.Core/Routing/UrlProviderExtensions.cs # src/Umbraco.Infrastructure/Migrations/Install/DatabaseSchemaCreator.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs # src/Umbraco.Infrastructure/Services/Implement/ContentService.cs # src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs # src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs # src/Umbraco.Web.UI.Client/package-lock.json # tests/Umbraco.Tests.AcceptanceTest/package-lock.json # tests/Umbraco.Tests.AcceptanceTest/package.json # tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs
This commit is contained in:
@@ -13,19 +13,24 @@ using Umbraco.Cms.Core.Xml;
|
||||
using Umbraco.Cms.Core.Xml.XPath;
|
||||
using Umbraco.Cms.Infrastructure.PublishedCache.Navigable;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
public class ContentCache : PublishedCacheBase, IPublishedContentCache, INavigableData, IDisposable
|
||||
{
|
||||
private readonly IDomainCache _domainCache;
|
||||
private readonly IAppCache _elementsCache;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ContentStore.Snapshot _snapshot;
|
||||
private readonly IAppCache _snapshotCache;
|
||||
private readonly IAppCache _elementsCache;
|
||||
private readonly IDomainCache _domainCache;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose() => _snapshot.Dispose();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
// TODO: figure this out
|
||||
@@ -33,7 +38,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// it's too late for UmbracoContext which has captured previewDefault and stuff into these ctor vars
|
||||
// but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache
|
||||
|
||||
public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IOptions<GlobalSettings> globalSettings, IVariationContextAccessor variationContextAccessor)
|
||||
public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache,
|
||||
IAppCache elementsCache, IDomainCache domainCache, IOptions<GlobalSettings> globalSettings,
|
||||
IVariationContextAccessor variationContextAccessor)
|
||||
: base(previewDefault)
|
||||
{
|
||||
_snapshot = snapshot;
|
||||
@@ -59,18 +66,23 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// at the moment we try our best to be backward compatible, but really,
|
||||
// should get rid of hideTopLevelNode and other oddities entirely, eventually
|
||||
|
||||
public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null)
|
||||
{
|
||||
return GetByRoute(PreviewDefault, route, hideTopLevelNode, culture);
|
||||
}
|
||||
public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) =>
|
||||
GetByRoute(PreviewDefault, route, hideTopLevelNode, culture);
|
||||
|
||||
public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null)
|
||||
public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null,
|
||||
string culture = null)
|
||||
{
|
||||
if (route == null) throw new ArgumentNullException(nameof(route));
|
||||
if (route == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(route));
|
||||
}
|
||||
|
||||
var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache;
|
||||
|
||||
IAppCache cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing
|
||||
? _elementsCache
|
||||
: _snapshotCache;
|
||||
var key = CacheKeys.ContentCacheContentByRoute(route, preview, culture);
|
||||
return cache.GetCacheItem<IPublishedContent>(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture));
|
||||
return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture));
|
||||
}
|
||||
|
||||
private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, string culture)
|
||||
@@ -108,8 +120,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// hideTopLevelNode = support legacy stuff, look for /*/path/to/node
|
||||
// else normal, look for /path/to/node
|
||||
content = hideTopLevelNode.Value
|
||||
? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture)).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0])
|
||||
: GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]);
|
||||
? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture))
|
||||
.FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0])
|
||||
: GetAtRoot(preview)
|
||||
.FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]);
|
||||
content = FollowRoute(content, parts, 1, culture);
|
||||
}
|
||||
|
||||
@@ -118,59 +132,72 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// have to look for /foo (see note in ApplyHideTopLevelNodeFromPath).
|
||||
if (content == null && hideTopLevelNode.Value && parts.Length == 1)
|
||||
{
|
||||
content = GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]);
|
||||
content = GetAtRoot(preview)
|
||||
.FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
public string GetRouteById(int contentId, string culture = null)
|
||||
{
|
||||
return GetRouteById(PreviewDefault, contentId, culture);
|
||||
}
|
||||
public string GetRouteById(int contentId, string culture = null) =>
|
||||
GetRouteById(PreviewDefault, contentId, culture);
|
||||
|
||||
public string GetRouteById(bool preview, int contentId, string culture = null)
|
||||
{
|
||||
var cache = (preview == false || PublishedSnapshotService.FullCacheWhenPreviewing) ? _elementsCache : _snapshotCache;
|
||||
IAppCache cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing
|
||||
? _elementsCache
|
||||
: _snapshotCache;
|
||||
var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, culture);
|
||||
return cache.GetCacheItem<string>(key, () => GetRouteByIdInternal(preview, contentId, null, culture));
|
||||
return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture));
|
||||
}
|
||||
|
||||
private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string culture)
|
||||
{
|
||||
var node = GetById(preview, contentId);
|
||||
IPublishedContent node = GetById(preview, contentId);
|
||||
if (node == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings
|
||||
|
||||
// walk up from that node until we hit a node with a domain,
|
||||
// or we reach the content root, collecting URLs in the way
|
||||
var pathParts = new List<string>();
|
||||
var n = node;
|
||||
IPublishedContent n = node;
|
||||
var urlSegment = n.UrlSegment(_variationContextAccessor, culture);
|
||||
var hasDomains = _domainCache.HasAssigned(n.Id);
|
||||
var hasDomains = _domainCache.GetAssignedWithCulture(culture, n.Id);
|
||||
while (hasDomains == false && n != null) // n is null at root
|
||||
{
|
||||
// no segment indicates this is not published when this is a variant
|
||||
if (urlSegment.IsNullOrWhiteSpace()) return null;
|
||||
if (urlSegment.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
pathParts.Add(urlSegment);
|
||||
|
||||
// move to parent node
|
||||
n = n.Parent;
|
||||
if (n != null)
|
||||
{
|
||||
urlSegment = n.UrlSegment(_variationContextAccessor, culture);
|
||||
}
|
||||
|
||||
hasDomains = n != null && _domainCache.HasAssigned(n.Id);
|
||||
hasDomains = n != null && _domainCache.GetAssignedWithCulture(culture, n.Id);
|
||||
}
|
||||
|
||||
// at this point this will be the urlSegment of the root, no segment indicates this is not published when this is a variant
|
||||
if (urlSegment.IsNullOrWhiteSpace()) return null;
|
||||
if (urlSegment.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// no domain, respect HideTopLevelNodeFromPath for legacy purposes
|
||||
if (hasDomains == false && hideTopLevelNode.Value)
|
||||
{
|
||||
ApplyHideTopLevelNodeFromPath(node, pathParts, preview);
|
||||
}
|
||||
|
||||
// assemble the route
|
||||
pathParts.Reverse();
|
||||
@@ -182,7 +209,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
return route;
|
||||
}
|
||||
|
||||
private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList<string> parts, int start, string culture)
|
||||
private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList<string> parts, int start,
|
||||
string culture)
|
||||
{
|
||||
var i = start;
|
||||
while (content != null && i < parts.Count)
|
||||
@@ -194,6 +222,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
return urlSegment == part;
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -209,11 +238,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// 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);
|
||||
IPublishedContent 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
|
||||
{
|
||||
@@ -227,13 +261,13 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
|
||||
public override IPublishedContent GetById(bool preview, int contentId)
|
||||
{
|
||||
var node = _snapshot.Get(contentId);
|
||||
ContentNode node = _snapshot.Get(contentId);
|
||||
return GetNodePublishedContent(node, preview);
|
||||
}
|
||||
|
||||
public override IPublishedContent GetById(bool preview, Guid contentId)
|
||||
{
|
||||
var node = _snapshot.Get(contentId);
|
||||
ContentNode node = _snapshot.Get(contentId);
|
||||
return GetNodePublishedContent(node, preview);
|
||||
}
|
||||
|
||||
@@ -241,18 +275,26 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
var guidUdi = contentId as GuidUdi;
|
||||
if (guidUdi == null)
|
||||
{
|
||||
throw new ArgumentException($"Udi must be of type {typeof(GuidUdi).Name}.", nameof(contentId));
|
||||
}
|
||||
|
||||
if (guidUdi.EntityType != Constants.UdiEntityType.Document)
|
||||
throw new ArgumentException($"Udi entity type must be \"{Constants.UdiEntityType.Document}\".", nameof(contentId));
|
||||
{
|
||||
throw new ArgumentException($"Udi entity type must be \"{Constants.UdiEntityType.Document}\".",
|
||||
nameof(contentId));
|
||||
}
|
||||
|
||||
return GetById(preview, guidUdi.Guid);
|
||||
}
|
||||
|
||||
public override bool HasById(bool preview, int contentId)
|
||||
{
|
||||
var n = _snapshot.Get(contentId);
|
||||
if (n == null) return false;
|
||||
ContentNode n = _snapshot.Get(contentId);
|
||||
if (n == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return preview || n.PublishedModel != null;
|
||||
}
|
||||
@@ -263,7 +305,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
// handle context culture for variant
|
||||
if (culture == null)
|
||||
{
|
||||
culture = _variationContextAccessor?.VariationContext?.Culture ?? "";
|
||||
}
|
||||
|
||||
// _snapshot.GetAtRoot() returns all ContentNode at root
|
||||
// both .Draft and .Published cannot be null at the same time
|
||||
@@ -272,13 +316,15 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// GetNodePublishedContent may return null if !preview and there is no
|
||||
// published model, so we need to filter these nulls out
|
||||
|
||||
var atRoot = _snapshot.GetAtRoot()
|
||||
IEnumerable<IPublishedContent> atRoot = _snapshot.GetAtRoot()
|
||||
.Select(n => GetNodePublishedContent(n, preview))
|
||||
.WhereNotNull();
|
||||
|
||||
// if a culture is specified, we must ensure that it is avail/published
|
||||
if (culture != "*")
|
||||
{
|
||||
atRoot = atRoot.Where(x => x.IsInvariantOrHasCulture(culture));
|
||||
}
|
||||
|
||||
return atRoot;
|
||||
}
|
||||
@@ -286,7 +332,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// both .Draft and .Published cannot be null at the same time
|
||||
|
||||
@@ -299,7 +347,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// this is for published content when previewing
|
||||
private static IPublishedContent GetPublishedContentAsDraft(IPublishedContent content /*, bool preview*/)
|
||||
{
|
||||
if (content == null /*|| preview == false*/) return null; //content;
|
||||
if (content == null /*|| preview == false*/)
|
||||
{
|
||||
return null; //content;
|
||||
}
|
||||
|
||||
// an object in the cache is either an IPublishedContentOrMedia,
|
||||
// or a model inheriting from PublishedContentExtended - in which
|
||||
@@ -309,12 +360,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
return inner.AsDraft();
|
||||
}
|
||||
|
||||
public override bool HasContent(bool preview)
|
||||
{
|
||||
return preview
|
||||
public override bool HasContent(bool preview) =>
|
||||
preview
|
||||
? _snapshot.IsEmpty == false
|
||||
: _snapshot.GetAtRoot().Any(x => x.PublishedModel != null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -322,21 +371,24 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
|
||||
public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars)
|
||||
{
|
||||
var navigator = CreateNavigator(preview);
|
||||
var iterator = navigator.Select(xpath, vars);
|
||||
XPathNavigator navigator = CreateNavigator(preview);
|
||||
XPathNodeIterator iterator = navigator.Select(xpath, vars);
|
||||
return GetSingleByXPath(iterator);
|
||||
}
|
||||
|
||||
public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars)
|
||||
{
|
||||
var navigator = CreateNavigator(preview);
|
||||
var iterator = navigator.Select(xpath, vars);
|
||||
XPathNavigator navigator = CreateNavigator(preview);
|
||||
XPathNodeIterator iterator = navigator.Select(xpath, vars);
|
||||
return GetSingleByXPath(iterator);
|
||||
}
|
||||
|
||||
private static IPublishedContent GetSingleByXPath(XPathNodeIterator iterator)
|
||||
{
|
||||
if (iterator.MoveNext() == false) return null;
|
||||
if (iterator.MoveNext() == false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var xnav = iterator.Current as NavigableNavigator;
|
||||
var xcontent = xnav?.UnderlyingObject as NavigableContent;
|
||||
@@ -345,15 +397,16 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
|
||||
public override IEnumerable<IPublishedContent> GetByXPath(bool preview, string xpath, XPathVariable[] vars)
|
||||
{
|
||||
var navigator = CreateNavigator(preview);
|
||||
var iterator = navigator.Select(xpath, vars);
|
||||
XPathNavigator navigator = CreateNavigator(preview);
|
||||
XPathNodeIterator iterator = navigator.Select(xpath, vars);
|
||||
return GetByXPath(iterator);
|
||||
}
|
||||
|
||||
public override IEnumerable<IPublishedContent> GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars)
|
||||
public override IEnumerable<IPublishedContent> GetByXPath(bool preview, XPathExpression xpath,
|
||||
XPathVariable[] vars)
|
||||
{
|
||||
var navigator = CreateNavigator(preview);
|
||||
var iterator = navigator.Select(xpath, vars);
|
||||
XPathNavigator navigator = CreateNavigator(preview);
|
||||
XPathNodeIterator iterator = navigator.Select(xpath, vars);
|
||||
return GetByXPath(iterator);
|
||||
}
|
||||
|
||||
@@ -364,7 +417,10 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
var xnav = iterator.Current as NavigableNavigator;
|
||||
var xcontent = xnav?.UnderlyingObject as NavigableContent;
|
||||
if (xcontent == null) continue;
|
||||
if (xcontent == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return xcontent.InnerContent;
|
||||
}
|
||||
@@ -395,14 +451,5 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key);
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_snapshot.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,13 +175,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
public IPublishedContent PublishedModel => GetModel(ref _publishedModel, _publishedData);
|
||||
|
||||
public ContentNodeKit ToKit()
|
||||
=> new ContentNodeKit
|
||||
{
|
||||
Node = this,
|
||||
ContentTypeId = ContentType.Id,
|
||||
|
||||
DraftData = _draftData,
|
||||
PublishedData = _publishedData
|
||||
};
|
||||
=> new ContentNodeKit(this, ContentType.Id, _draftData, _publishedData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,39 @@
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Infrastructure.PublishedCache.DataSource;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
// what's needed to actually build a content node
|
||||
public struct ContentNodeKit
|
||||
{
|
||||
[Obsolete("This will be changed to a property in future versions")]
|
||||
public ContentNode Node;
|
||||
|
||||
[Obsolete("This will be changed to a property in future versions")]
|
||||
public int ContentTypeId;
|
||||
|
||||
[Obsolete("This will be changed to a property in future versions")]
|
||||
public ContentData DraftData;
|
||||
|
||||
[Obsolete("This will be changed to a property in future versions")]
|
||||
public ContentData PublishedData;
|
||||
|
||||
public ContentNodeKit(ContentNode node, int contentTypeId, ContentData draftData, ContentData publishedData)
|
||||
{
|
||||
Node = node;
|
||||
ContentTypeId = contentTypeId;
|
||||
DraftData = draftData;
|
||||
PublishedData = publishedData;
|
||||
}
|
||||
|
||||
|
||||
public bool IsEmpty => Node == null;
|
||||
|
||||
public bool IsNull => ContentTypeId < 0;
|
||||
|
||||
public static ContentNodeKit Empty { get; } = new ContentNodeKit();
|
||||
public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 };
|
||||
public static ContentNodeKit Null { get; } = new ContentNodeKit(null, -1, null, null);
|
||||
|
||||
public void Build(
|
||||
IPublishedContentType contentType,
|
||||
@@ -41,12 +57,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
}
|
||||
|
||||
public ContentNodeKit Clone(IPublishedModelFactory publishedModelFactory)
|
||||
=> new ContentNodeKit
|
||||
{
|
||||
ContentTypeId = ContentTypeId,
|
||||
DraftData = DraftData,
|
||||
PublishedData = PublishedData,
|
||||
Node = new ContentNode(Node, publishedModelFactory)
|
||||
};
|
||||
=> new ContentNodeKit(new ContentNode(Node, publishedModelFactory), ContentTypeId, DraftData, PublishedData);
|
||||
|
||||
public ContentNodeKit Clone(IPublishedModelFactory publishedModelFactory, ContentData draftData, ContentData publishedData)
|
||||
=> new ContentNodeKit(new ContentNode(Node, publishedModelFactory), ContentTypeId, draftData, publishedData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
|
||||
private class WriteLockInfo
|
||||
{
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
// This is a field that is used for ref operations
|
||||
public bool Taken;
|
||||
#pragma warning restore IDE1006 // Naming Styles
|
||||
}
|
||||
|
||||
// a scope contextual that represents a locked writer to the dictionary
|
||||
@@ -524,7 +528,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
parent = GetParentLink(kit.Node, null);
|
||||
if (parent == null)
|
||||
{
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, could not find parent id={kit.Node.ParentContentId}.", kit.Node.Id, kit.Node.ParentContentId);
|
||||
_logger.LogWarning("Skip item id={kitNodeId}, could not find parent id={kitNodeParentContentId}.", kit.Node.Id, kit.Node.ParentContentId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -533,21 +537,21 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
// because the data sort operation is by path.
|
||||
if (parent.Value == null)
|
||||
{
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, no Data assigned for linked node with path {kit.Node.Path} and parent id {kit.Node.ParentContentId}. This can indicate data corruption for the Path value for node {kit.Node.Id}. See the Health Check dashboard in Settings to resolve data integrity issues.", kit.Node.Id, kit.Node.ParentContentId);
|
||||
_logger.LogWarning("Skip item id={kitNodeId}, no Data assigned for linked node with path {kitNodePath} and parent id {kitNodeParentContentId}. This can indicate data corruption for the Path value for node {kitNodeId}. See the Health Check dashboard in Settings to resolve data integrity issues.", kit.Node.Id, kit.Node.Path, kit.Node.ParentContentId, kit.Node.Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the kit is valid
|
||||
if (kit.DraftData == null && kit.PublishedData == null)
|
||||
{
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, both draft and published data are null.", kit.Node.Id);
|
||||
_logger.LogWarning("Skip item id={kitNodeId}, both draft and published data are null.", kit.Node.Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// unknown = bad
|
||||
if (_contentTypesById.TryGetValue(kit.ContentTypeId, out var link) == false || link.Value == null)
|
||||
{
|
||||
_logger.LogWarning("Skip item id={kit.Node.Id}, could not find content type id={kit.ContentTypeId}.", kit.Node.Id, kit.ContentTypeId);
|
||||
_logger.LogWarning("Skip item id={kitNodeId}, could not find content type id={kitContentTypeId}.", kit.Node.Id, kit.ContentTypeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -723,7 +727,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
previousNode = null; // there is no previous sibling
|
||||
}
|
||||
|
||||
_logger.LogDebug("Set {thisNode.Id} with parent {thisNode.ParentContentId}", thisNode.Id, thisNode.ParentContentId);
|
||||
_logger.LogDebug("Set {thisNodeId} with parent {thisNodeParentContentId}", thisNode.Id, thisNode.ParentContentId);
|
||||
SetValueLocked(_contentNodes, thisNode.Id, thisNode);
|
||||
|
||||
// if we are initializing from the database source ensure the local db is updated
|
||||
@@ -780,7 +784,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
ok = false;
|
||||
continue; // skip that one
|
||||
}
|
||||
_logger.LogDebug("Set {kit.Node.Id} with parent {kit.Node.ParentContentId}", kit.Node.Id, kit.Node.ParentContentId);
|
||||
_logger.LogDebug("Set {kitNodeId} with parent {kitNodeParentContentId}", kit.Node.Id, kit.Node.ParentContentId);
|
||||
SetValueLocked(_contentNodes, kit.Node.Id, kit.Node);
|
||||
|
||||
if (_localDb != null) RegisterChange(kit.Node.Id, kit);
|
||||
|
||||
@@ -13,11 +13,11 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
_dictionaryOfPropertyDataSerializer = dictionaryOfPropertyDataSerializer;
|
||||
if(_dictionaryOfPropertyDataSerializer == null)
|
||||
{
|
||||
_dictionaryOfPropertyDataSerializer = DefaultPropertiesSerializer;
|
||||
_dictionaryOfPropertyDataSerializer = s_defaultPropertiesSerializer;
|
||||
}
|
||||
}
|
||||
private static readonly DictionaryOfPropertyDataSerializer DefaultPropertiesSerializer = new DictionaryOfPropertyDataSerializer();
|
||||
private static readonly DictionaryOfCultureVariationSerializer DefaultCultureVariationsSerializer = new DictionaryOfCultureVariationSerializer();
|
||||
private static readonly DictionaryOfPropertyDataSerializer s_defaultPropertiesSerializer = new DictionaryOfPropertyDataSerializer();
|
||||
private static readonly DictionaryOfCultureVariationSerializer s_defaultCultureVariationsSerializer = new DictionaryOfCultureVariationSerializer();
|
||||
private readonly IDictionaryOfPropertyDataSerializer _dictionaryOfPropertyDataSerializer;
|
||||
|
||||
public ContentData ReadFrom(Stream stream)
|
||||
@@ -29,18 +29,9 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
var versionDate = PrimitiveSerializer.DateTime.ReadFrom(stream);
|
||||
var writerId = PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
var templateId = PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
return new ContentData
|
||||
{
|
||||
Published = published,
|
||||
Name = name,
|
||||
UrlSegment = urlSegment,
|
||||
VersionId = versionId,
|
||||
VersionDate = versionDate,
|
||||
WriterId = writerId,
|
||||
TemplateId = templateId == 0 ? (int?)null : templateId,
|
||||
Properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream), // TODO: We don't want to allocate empty arrays
|
||||
CultureInfos = DefaultCultureVariationsSerializer.ReadFrom(stream) // TODO: We don't want to allocate empty arrays
|
||||
};
|
||||
var properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays
|
||||
var cultureInfos = s_defaultCultureVariationsSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays
|
||||
return new ContentData(name, urlSegment, versionId, versionDate, writerId, templateId, published, properties, cultureInfos);
|
||||
}
|
||||
|
||||
public void WriteTo(ContentData value, Stream stream)
|
||||
@@ -53,7 +44,7 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
PrimitiveSerializer.Int32.WriteTo(value.WriterId, stream);
|
||||
PrimitiveSerializer.Int32.WriteTo(value.TemplateId ?? 0, stream);
|
||||
_dictionaryOfPropertyDataSerializer.WriteTo(value.Properties, stream);
|
||||
DefaultCultureVariationsSerializer.WriteTo(value.CultureInfos, stream);
|
||||
s_defaultCultureVariationsSerializer.WriteTo(value.CultureInfos, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.IO;
|
||||
using System.IO;
|
||||
using CSharpTest.Net.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
@@ -10,19 +10,17 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
_contentDataSerializer = contentDataSerializer;
|
||||
if(_contentDataSerializer == null)
|
||||
{
|
||||
_contentDataSerializer = DefaultDataSerializer;
|
||||
_contentDataSerializer = s_defaultDataSerializer;
|
||||
}
|
||||
}
|
||||
static readonly ContentDataSerializer DefaultDataSerializer = new ContentDataSerializer();
|
||||
static readonly ContentDataSerializer s_defaultDataSerializer = new ContentDataSerializer();
|
||||
private readonly ContentDataSerializer _contentDataSerializer;
|
||||
|
||||
//static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer();
|
||||
|
||||
public ContentNodeKit ReadFrom(Stream stream)
|
||||
{
|
||||
var kit = new ContentNodeKit
|
||||
{
|
||||
Node = new ContentNode(
|
||||
var contentNode = new ContentNode(
|
||||
PrimitiveSerializer.Int32.ReadFrom(stream), // id
|
||||
PrimitiveSerializer.Guid.ReadFrom(stream), // uid
|
||||
PrimitiveSerializer.Int32.ReadFrom(stream), // level
|
||||
@@ -31,15 +29,27 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
PrimitiveSerializer.Int32.ReadFrom(stream), // parent id
|
||||
PrimitiveSerializer.DateTime.ReadFrom(stream), // date created
|
||||
PrimitiveSerializer.Int32.ReadFrom(stream) // creator id
|
||||
),
|
||||
ContentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream)
|
||||
};
|
||||
);
|
||||
|
||||
int contentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream);
|
||||
var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream);
|
||||
ContentData draftData = null;
|
||||
ContentData publishedData = null;
|
||||
if (hasDraft)
|
||||
kit.DraftData = _contentDataSerializer.ReadFrom(stream);
|
||||
{
|
||||
draftData = _contentDataSerializer.ReadFrom(stream);
|
||||
}
|
||||
var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream);
|
||||
if (hasPublished)
|
||||
kit.PublishedData = _contentDataSerializer.ReadFrom(stream);
|
||||
{
|
||||
publishedData = _contentDataSerializer.ReadFrom(stream);
|
||||
}
|
||||
var kit = new ContentNodeKit(
|
||||
contentNode,
|
||||
contentTypeId,
|
||||
draftData,
|
||||
publishedData);
|
||||
|
||||
return kit;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,19 +8,38 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource
|
||||
/// </summary>
|
||||
public class ContentData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string UrlSegment { get; set; }
|
||||
public int VersionId { get; set; }
|
||||
public DateTime VersionDate { get; set; }
|
||||
public int WriterId { get; set; }
|
||||
public int? TemplateId { get; set; }
|
||||
public bool Published { get; set; }
|
||||
[Obsolete("Use ctor with all params, as the pros should be immutable")]
|
||||
public ContentData()
|
||||
{
|
||||
|
||||
public IDictionary<string, PropertyData[]> Properties { get; set; }
|
||||
}
|
||||
|
||||
public ContentData(string name, string urlSegment, int versionId, DateTime versionDate, int writerId, int? templateId, bool published, IDictionary<string, PropertyData[]> properties, IReadOnlyDictionary<string, CultureVariation> cultureInfos)
|
||||
{
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
UrlSegment = urlSegment;
|
||||
VersionId = versionId;
|
||||
VersionDate = versionDate;
|
||||
WriterId = writerId;
|
||||
TemplateId = templateId;
|
||||
Published = published;
|
||||
Properties = properties ?? throw new ArgumentNullException(nameof(properties));
|
||||
CultureInfos = cultureInfos;
|
||||
}
|
||||
|
||||
public string Name { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
public string UrlSegment { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
public int VersionId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
public DateTime VersionDate { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
public int WriterId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
public int? TemplateId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
public bool Published { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
|
||||
public IDictionary<string, PropertyData[]> Properties { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
|
||||
/// <summary>
|
||||
/// The collection of language Id to name for the content item
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, CultureVariation> CultureInfos { get; set; }
|
||||
public IReadOnlyDictionary<string, CultureVariation> CultureInfos { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; }
|
||||
}
|
||||
}
|
||||
|
||||
15
src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs
Normal file
15
src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
{
|
||||
public static class DomainCacheExtensions
|
||||
{
|
||||
public static bool GetAssignedWithCulture(this IDomainCache domainCache, string culture, int documentId, bool includeWildcards = false)
|
||||
{
|
||||
var assigned = domainCache.GetAssigned(documentId, includeWildcards);
|
||||
|
||||
return culture is null ? assigned.Any() : assigned.Any(x => x.Culture == culture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -820,18 +820,16 @@ AND cmsContentNu.nodeId IS NULL
|
||||
bool published = false;
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published);
|
||||
|
||||
d = new ContentData
|
||||
{
|
||||
Name = dto.EditName,
|
||||
Published = published,
|
||||
TemplateId = dto.EditTemplateId == 0 ? (int?)null : dto.EditTemplateId,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.EditVersionDate,
|
||||
WriterId = dto.EditWriterId,
|
||||
Properties = deserializedContent.PropertyData, // TODO: We don't want to allocate empty arrays
|
||||
CultureInfos = deserializedContent.CultureData,
|
||||
UrlSegment = deserializedContent.UrlSegment
|
||||
};
|
||||
d = new ContentData(
|
||||
dto.EditName,
|
||||
deserializedContent.UrlSegment,
|
||||
dto.VersionId,
|
||||
dto.EditVersionDate,
|
||||
dto.EditWriterId,
|
||||
dto.EditTemplateId,
|
||||
published,
|
||||
deserializedContent.PropertyData,
|
||||
deserializedContent.CultureData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,31 +849,23 @@ AND cmsContentNu.nodeId IS NULL
|
||||
bool published = true;
|
||||
var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw, published);
|
||||
|
||||
p = new ContentData
|
||||
{
|
||||
Name = dto.PubName,
|
||||
UrlSegment = deserializedContent.UrlSegment,
|
||||
Published = published,
|
||||
TemplateId = dto.PubTemplateId == 0 ? (int?)null : dto.PubTemplateId,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.PubVersionDate,
|
||||
WriterId = dto.PubWriterId,
|
||||
Properties = deserializedContent.PropertyData, // TODO: We don't want to allocate empty arrays
|
||||
CultureInfos = deserializedContent.CultureData
|
||||
};
|
||||
p = new ContentData(
|
||||
dto.PubName,
|
||||
deserializedContent.UrlSegment,
|
||||
dto.VersionId,
|
||||
dto.PubVersionDate,
|
||||
dto.PubWriterId,
|
||||
dto.PubTemplateId,
|
||||
published,
|
||||
deserializedContent.PropertyData,
|
||||
deserializedContent.CultureData);
|
||||
}
|
||||
}
|
||||
|
||||
var n = new ContentNode(dto.Id, dto.Key,
|
||||
dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId);
|
||||
|
||||
var s = new ContentNodeKit
|
||||
{
|
||||
Node = n,
|
||||
ContentTypeId = dto.ContentTypeId,
|
||||
DraftData = d,
|
||||
PublishedData = p
|
||||
};
|
||||
var s = new ContentNodeKit(n, dto.ContentTypeId, d, p);
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -888,27 +878,21 @@ AND cmsContentNu.nodeId IS NULL
|
||||
bool published = true;
|
||||
var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published);
|
||||
|
||||
var p = new ContentData
|
||||
{
|
||||
Name = dto.EditName,
|
||||
Published = published,
|
||||
TemplateId = -1,
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.EditVersionDate,
|
||||
WriterId = dto.CreatorId, // what-else?
|
||||
Properties = deserializedMedia.PropertyData, // TODO: We don't want to allocate empty arrays
|
||||
CultureInfos = deserializedMedia.CultureData
|
||||
};
|
||||
var p = new ContentData(
|
||||
dto.EditName,
|
||||
null,
|
||||
dto.VersionId,
|
||||
dto.EditVersionDate,
|
||||
dto.CreatorId,
|
||||
-1,
|
||||
published,
|
||||
deserializedMedia.PropertyData,
|
||||
deserializedMedia.CultureData);
|
||||
|
||||
var n = new ContentNode(dto.Id, dto.Key,
|
||||
dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId);
|
||||
|
||||
var s = new ContentNodeKit
|
||||
{
|
||||
Node = n,
|
||||
ContentTypeId = dto.ContentTypeId,
|
||||
PublishedData = p
|
||||
};
|
||||
var s = new ContentNodeKit(n, dto.ContentTypeId, null, p);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -34,15 +34,8 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
IVariationContextAccessor variationContextAccessor,
|
||||
IPublishedModelFactory publishedModelFactory)
|
||||
{
|
||||
var d = new ContentData
|
||||
{
|
||||
Name = member.Name,
|
||||
Published = previewing,
|
||||
TemplateId = -1,
|
||||
VersionDate = member.UpdateDate,
|
||||
WriterId = member.CreatorId, // what else?
|
||||
Properties = GetPropertyValues(contentType, member)
|
||||
};
|
||||
var d = new ContentData(member.Name, null, 0, member.UpdateDate, member.CreatorId, -1, previewing, GetPropertyValues(contentType, member), null);
|
||||
|
||||
var n = new ContentNode(
|
||||
member.Id,
|
||||
member.Key,
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||
private readonly IDefaultCultureAccessor _defaultCultureAccessor;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IContentCacheDataSerializerFactory _contentCacheDataSerializerFactory;
|
||||
private readonly ContentDataSerializer _contentDataSerializer;
|
||||
private readonly NuCacheSettings _config;
|
||||
|
||||
@@ -93,7 +92,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
IPublishedModelFactory publishedModelFactory,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
IOptions<NuCacheSettings> config,
|
||||
IContentCacheDataSerializerFactory contentCacheDataSerializerFactory,
|
||||
ContentDataSerializer contentDataSerializer)
|
||||
{
|
||||
_options = options;
|
||||
@@ -111,7 +109,6 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache
|
||||
_defaultCultureAccessor = defaultCultureAccessor;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_contentCacheDataSerializerFactory = contentCacheDataSerializerFactory;
|
||||
_contentDataSerializer = contentDataSerializer;
|
||||
_config = config.Value;
|
||||
_publishedModelFactory = publishedModelFactory;
|
||||
|
||||
Reference in New Issue
Block a user