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:
Bjarke Berg
2021-11-22 19:43:20 +01:00
533 changed files with 22641 additions and 49115 deletions

View File

@@ -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
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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; }
}
}

View 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);
}
}
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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;