Skip to content

Commit b502e29

Browse files
committed
Merge remote-tracking branch 'origin/release/17.0'
2 parents c1a8500 + b68a6a9 commit b502e29

File tree

19 files changed

+659
-266
lines changed

19 files changed

+659
-266
lines changed

src/Umbraco.Core/Configuration/Models/HostingSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ public class HostingSettings
2525
[DefaultValue(StaticLocalTempStorageLocation)]
2626
public LocalTempStorage LocalTempStorageLocation { get; set; } = Enum.Parse<LocalTempStorage>(StaticLocalTempStorageLocation);
2727

28+
/// <summary>
29+
/// Gets or sets a value for the location of temporary file uploads.
30+
/// </summary>
31+
/// <value>/umbraco/Data/TEMP/TemporaryFile if nothing is specified.</value>
32+
public string? TemporaryFileUploadLocation { get; set; }
33+
2834
/// <summary>
2935
/// Gets or sets a value indicating whether umbraco is running in [debug mode].
3036
/// </summary>

src/Umbraco.Core/Hosting/IHostingEnvironment.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,12 @@ public interface IHostingEnvironment
4343
string LocalTempPath { get; }
4444

4545
/// <summary>
46-
/// The web application's hosted path
46+
/// Gets the location of temporary file uploads.
47+
/// </summary>
48+
public string TemporaryFileUploadPath => Path.Combine(LocalTempPath, "TemporaryFile");
49+
50+
/// <summary>
51+
/// The web application's hosted path.
4752
/// </summary>
4853
/// <remarks>
4954
/// In most cases this will return "/" but if the site is hosted in a virtual directory then this will return the

src/Umbraco.Infrastructure/Persistence/Repositories/Implement/LocalFileSystemTemporaryFileRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public LocalFileSystemTemporaryFileRepository(
2727

2828
private DirectoryInfo GetRootDirectory()
2929
{
30-
var path = Path.Combine(_hostingEnvironment.LocalTempPath, "TemporaryFile");
30+
var path = _hostingEnvironment.TemporaryFileUploadPath;
3131

3232
if (!Directory.Exists(path))
3333
{

src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/RteBlockRenderingValueConverter.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,19 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType
7272
// to be cached at the published snapshot level, because we have no idea what the block renderings may depend on actually.
7373
PropertyCacheLevel.Snapshot;
7474

75+
/// <inheritdoc />
76+
public override bool? IsValue(object? value, PropertyValueLevel level)
77+
=> level switch
78+
{
79+
// we cannot determine if an RTE has a value at source level, because some RTEs might
80+
// be saved with an "empty" representation like {"markup":"","blocks":null}.
81+
PropertyValueLevel.Source => null,
82+
// we assume the RTE has a value if the intermediate value has markup beyond an empty paragraph tag.
83+
PropertyValueLevel.Inter => value is IRichTextEditorIntermediateValue { Markup.Length: > 0 } intermediateValue
84+
&& intermediateValue.Markup != "<p></p>",
85+
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
86+
};
87+
7588
// to counterweigh the cache level, we're going to do as much of the heavy lifting as we can while converting source to intermediate
7689
public override object? ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object? source, bool preview)
7790
{

src/Umbraco.PublishedCache.HybridCache/Factories/PublishedContentFactory.cs

Lines changed: 4 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
using Microsoft.Extensions.Logging;
2-
using Umbraco.Cms.Core.Cache;
31
using Umbraco.Cms.Core.Extensions;
42
using Umbraco.Cms.Core.Models;
53
using Umbraco.Cms.Core.Models.PublishedContent;
64
using Umbraco.Cms.Core.PublishedCache;
7-
using Umbraco.Extensions;
85

96
namespace Umbraco.Cms.Infrastructure.HybridCache.Factories;
107

@@ -16,49 +13,23 @@ internal sealed class PublishedContentFactory : IPublishedContentFactory
1613
private readonly IElementsCache _elementsCache;
1714
private readonly IVariationContextAccessor _variationContextAccessor;
1815
private readonly IPublishedContentTypeCache _publishedContentTypeCache;
19-
private readonly ILogger<PublishedContentFactory> _logger;
20-
private readonly AppCaches _appCaches;
2116

2217
/// <summary>
2318
/// Initializes a new instance of the <see cref="PublishedContentFactory"/> class.
2419
/// </summary>
2520
public PublishedContentFactory(
2621
IElementsCache elementsCache,
2722
IVariationContextAccessor variationContextAccessor,
28-
IPublishedContentTypeCache publishedContentTypeCache,
29-
ILogger<PublishedContentFactory> logger,
30-
AppCaches appCaches)
23+
IPublishedContentTypeCache publishedContentTypeCache)
3124
{
3225
_elementsCache = elementsCache;
3326
_variationContextAccessor = variationContextAccessor;
3427
_publishedContentTypeCache = publishedContentTypeCache;
35-
_logger = logger;
36-
_appCaches = appCaches;
3728
}
3829

3930
/// <inheritdoc/>
4031
public IPublishedContent? ToIPublishedContent(ContentCacheNode contentCacheNode, bool preview)
4132
{
42-
var cacheKey = $"{nameof(PublishedContentFactory)}DocumentCache_{contentCacheNode.Id}_{preview}_{contentCacheNode.Data?.VersionDate.Ticks ?? 0}";
43-
IPublishedContent? publishedContent = null;
44-
if (_appCaches.RequestCache.IsAvailable)
45-
{
46-
publishedContent = _appCaches.RequestCache.GetCacheItem<IPublishedContent?>(cacheKey);
47-
if (publishedContent is not null)
48-
{
49-
_logger.LogDebug(
50-
"Using cached IPublishedContent for document {ContentCacheNodeName} ({ContentCacheNodeId}).",
51-
contentCacheNode.Data?.Name ?? "No Name",
52-
contentCacheNode.Id);
53-
return publishedContent;
54-
}
55-
}
56-
57-
_logger.LogDebug(
58-
"Creating IPublishedContent for document {ContentCacheNodeName} ({ContentCacheNodeId}).",
59-
contentCacheNode.Data?.Name ?? "No Name",
60-
contentCacheNode.Id);
61-
6233
IPublishedContentType contentType =
6334
_publishedContentTypeCache.Get(PublishedItemType.Content, contentCacheNode.ContentTypeId);
6435
var contentNode = new ContentNode(
@@ -71,44 +42,19 @@ public PublishedContentFactory(
7142
preview ? contentCacheNode.Data : null,
7243
preview ? null : contentCacheNode.Data);
7344

74-
publishedContent = GetModel(contentNode, preview);
45+
IPublishedContent? publishedContent = GetModel(contentNode, preview);
7546

7647
if (preview)
7748
{
7849
publishedContent ??= GetPublishedContentAsDraft(publishedContent);
7950
}
8051

81-
if (_appCaches.RequestCache.IsAvailable && publishedContent is not null)
82-
{
83-
_appCaches.RequestCache.Set(cacheKey, publishedContent);
84-
}
85-
8652
return publishedContent;
8753
}
8854

8955
/// <inheritdoc/>
9056
public IPublishedContent? ToIPublishedMedia(ContentCacheNode contentCacheNode)
9157
{
92-
var cacheKey = $"{nameof(PublishedContentFactory)}MediaCache_{contentCacheNode.Id}";
93-
IPublishedContent? publishedContent = null;
94-
if (_appCaches.RequestCache.IsAvailable)
95-
{
96-
publishedContent = _appCaches.RequestCache.GetCacheItem<IPublishedContent?>(cacheKey);
97-
if (publishedContent is not null)
98-
{
99-
_logger.LogDebug(
100-
"Using cached IPublishedContent for media {ContentCacheNodeName} ({ContentCacheNodeId}).",
101-
contentCacheNode.Data?.Name ?? "No Name",
102-
contentCacheNode.Id);
103-
return publishedContent;
104-
}
105-
}
106-
107-
_logger.LogDebug(
108-
"Creating IPublishedContent for media {ContentCacheNodeName} ({ContentCacheNodeId}).",
109-
contentCacheNode.Data?.Name ?? "No Name",
110-
contentCacheNode.Id);
111-
11258
IPublishedContentType contentType =
11359
_publishedContentTypeCache.Get(PublishedItemType.Media, contentCacheNode.ContentTypeId);
11460
var contentNode = new ContentNode(
@@ -121,40 +67,12 @@ public PublishedContentFactory(
12167
null,
12268
contentCacheNode.Data);
12369

124-
publishedContent = GetModel(contentNode, false);
125-
126-
if (_appCaches.RequestCache.IsAvailable && publishedContent is not null)
127-
{
128-
_appCaches.RequestCache.Set(cacheKey, publishedContent);
129-
}
130-
131-
return publishedContent;
70+
return GetModel(contentNode, false);
13271
}
13372

13473
/// <inheritdoc/>
13574
public IPublishedMember ToPublishedMember(IMember member)
13675
{
137-
string cacheKey = $"{nameof(PublishedContentFactory)}MemberCache_{member.Id}";
138-
IPublishedMember? publishedMember = null;
139-
if (_appCaches.RequestCache.IsAvailable)
140-
{
141-
publishedMember = _appCaches.RequestCache.GetCacheItem<IPublishedMember?>(cacheKey);
142-
if (publishedMember is not null)
143-
{
144-
_logger.LogDebug(
145-
"Using cached IPublishedMember for member {MemberName} ({MemberId}).",
146-
member.Username,
147-
member.Id);
148-
149-
return publishedMember;
150-
}
151-
}
152-
153-
_logger.LogDebug(
154-
"Creating IPublishedMember for member {MemberName} ({MemberId}).",
155-
member.Username,
156-
member.Id);
157-
15876
IPublishedContentType contentType =
15977
_publishedContentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId);
16078

@@ -179,14 +97,7 @@ public IPublishedMember ToPublishedMember(IMember member)
17997
contentType,
18098
null,
18199
contentData);
182-
publishedMember = new PublishedMember(member, contentNode, _elementsCache, _variationContextAccessor);
183-
184-
if (_appCaches.RequestCache.IsAvailable)
185-
{
186-
_appCaches.RequestCache.Set(cacheKey, publishedMember);
187-
}
188-
189-
return publishedMember;
100+
return new PublishedMember(member, contentNode, _elementsCache, _variationContextAccessor);
190101
}
191102

192103
private static Dictionary<string, PropertyData[]> GetPropertyValues(IPublishedContentType contentType, IMember member)

src/Umbraco.PublishedCache.HybridCache/Services/DocumentCacheService.cs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#if DEBUG
22
using System.Diagnostics;
33
#endif
4+
using System.Collections.Concurrent;
45
using Microsoft.Extensions.Caching.Hybrid;
56
using Microsoft.Extensions.Logging;
67
using Microsoft.Extensions.Options;
@@ -35,6 +36,8 @@ internal sealed class DocumentCacheService : IDocumentCacheService
3536
private readonly ILogger<DocumentCacheService> _logger;
3637
private HashSet<Guid>? _seedKeys;
3738

39+
private readonly ConcurrentDictionary<string, IPublishedContent> _publishedContentCache = [];
40+
3841
private HashSet<Guid> SeedKeys
3942
{
4043
get
@@ -108,6 +111,11 @@ public DocumentCacheService(
108111
{
109112
var cacheKey = GetCacheKey(key, preview);
110113

114+
if (preview is false && _publishedContentCache.TryGetValue(cacheKey, out IPublishedContent? cached))
115+
{
116+
return cached;
117+
}
118+
111119
ContentCacheNode? contentCacheNode = await _hybridCache.GetOrCreateAsync(
112120
cacheKey,
113121
async cancel =>
@@ -137,7 +145,13 @@ public DocumentCacheService(
137145
return null;
138146
}
139147

140-
return _publishedContentFactory.ToIPublishedContent(contentCacheNode, preview).CreateModel(_publishedModelFactory);
148+
IPublishedContent? result = _publishedContentFactory.ToIPublishedContent(contentCacheNode, preview).CreateModel(_publishedModelFactory);
149+
if (result is not null)
150+
{
151+
_publishedContentCache[cacheKey] = result;
152+
}
153+
154+
return result;
141155
}
142156

143157
private bool GetPreview() => _previewService.IsInPreview();
@@ -174,7 +188,9 @@ public async Task RefreshMemoryCacheAsync(Guid key)
174188
ContentCacheNode? publishedNode = await _databaseCacheRepository.GetContentSourceAsync(key, false);
175189
if (publishedNode is not null && _publishStatusQueryService.HasPublishedAncestorPath(publishedNode.Key))
176190
{
177-
await _hybridCache.SetAsync(GetCacheKey(publishedNode.Key, false), publishedNode, GetEntryOptions(publishedNode.Key, false), GenerateTags(key));
191+
var cacheKey = GetCacheKey(publishedNode.Key, false);
192+
await _hybridCache.SetAsync(cacheKey, publishedNode, GetEntryOptions(publishedNode.Key, false), GenerateTags(key));
193+
_publishedContentCache.Remove(cacheKey, out _);
178194
}
179195

180196
scope.Complete();
@@ -183,7 +199,7 @@ public async Task RefreshMemoryCacheAsync(Guid key)
183199
public async Task RemoveFromMemoryCacheAsync(Guid key)
184200
{
185201
await _hybridCache.RemoveAsync(GetCacheKey(key, true));
186-
await _hybridCache.RemoveAsync(GetCacheKey(key, false));
202+
await ClearPublishedCacheAsync(key);
187203
}
188204

189205
public async Task SeedAsync(CancellationToken cancellationToken)
@@ -300,7 +316,7 @@ public async Task RefreshContentAsync(IContent content)
300316

301317
if (content.PublishedState == PublishedState.Unpublishing)
302318
{
303-
await _hybridCache.RemoveAsync(GetCacheKey(publishedCacheNode.Key, false));
319+
await ClearPublishedCacheAsync(publishedCacheNode.Key);
304320
}
305321
}
306322

@@ -338,12 +354,19 @@ public async Task RebuildMemoryCacheByContentTypeAsync(IEnumerable<int> contentT
338354

339355
foreach (ContentCacheNode content in contentByContentTypeKey)
340356
{
341-
_hybridCache.RemoveAsync(GetCacheKey(content.Key, true)).GetAwaiter().GetResult();
357+
await _hybridCache.RemoveAsync(GetCacheKey(content.Key, true));
342358

343359
if (content.IsDraft is false)
344360
{
345-
_hybridCache.RemoveAsync(GetCacheKey(content.Key, false)).GetAwaiter().GetResult();
361+
await ClearPublishedCacheAsync(content.Key);
346362
}
347363
}
348364
}
365+
366+
private async Task ClearPublishedCacheAsync(Guid key)
367+
{
368+
var cacheKey = GetCacheKey(key, false);
369+
await _hybridCache.RemoveAsync(cacheKey);
370+
_publishedContentCache.Remove(cacheKey, out _);
371+
}
349372
}

0 commit comments

Comments
 (0)