Skip to content

Commit b12620a

Browse files
committed
Merge branch 'release/13.6' into v13/dev
2 parents 1b050eb + d1af59f commit b12620a

File tree

17 files changed

+1277
-994
lines changed

17 files changed

+1277
-994
lines changed

src/Umbraco.Cms.Api.Delivery/Controllers/Content/ContentApiControllerBase.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace Umbraco.Cms.Api.Delivery.Controllers.Content;
1515
[ApiExplorerSettings(GroupName = "Content")]
1616
[LocalizeFromAcceptLanguageHeader]
1717
[ValidateStartItem]
18+
[AddVaryHeader]
1819
[OutputCache(PolicyName = Constants.DeliveryApi.OutputCache.ContentCachePolicy)]
1920
public abstract class ContentApiControllerBase : DeliveryApiControllerBase
2021
{
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.AspNetCore.Mvc.Filters;
2+
3+
namespace Umbraco.Cms.Api.Delivery.Filters;
4+
5+
public sealed class AddVaryHeaderAttribute : ActionFilterAttribute
6+
{
7+
private const string Vary = "Accept-Language, Preview, Start-Item";
8+
9+
public override void OnResultExecuting(ResultExecutingContext context)
10+
=> context.HttpContext.Response.Headers.Vary = context.HttpContext.Response.Headers.Vary.Count > 0
11+
? $"{context.HttpContext.Response.Headers.Vary}, {Vary}"
12+
: Vary;
13+
}

src/Umbraco.Cms.Api.Delivery/Querying/Filters/ContentTypeFilter.cs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using Umbraco.Cms.Api.Delivery.Indexing.Filters;
22
using Umbraco.Cms.Core.DeliveryApi;
3-
using Umbraco.Extensions;
43

54
namespace Umbraco.Cms.Api.Delivery.Querying.Filters;
65

@@ -15,15 +14,15 @@ public bool CanHandle(string query)
1514
/// <inheritdoc/>
1615
public FilterOption BuildFilterOption(string filter)
1716
{
18-
var alias = filter.Substring(ContentTypeSpecifier.Length);
17+
var filterValue = filter.Substring(ContentTypeSpecifier.Length);
18+
var negate = filterValue.StartsWith('!');
19+
var aliases = filterValue.TrimStart('!').Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
1920

2021
return new FilterOption
2122
{
2223
FieldName = ContentTypeFilterIndexer.FieldName,
23-
Values = alias.IsNullOrWhiteSpace() == false
24-
? new[] { alias.TrimStart('!') }
25-
: Array.Empty<string>(),
26-
Operator = alias.StartsWith('!')
24+
Values = aliases,
25+
Operator = negate
2726
? FilterOperation.IsNot
2827
: FilterOperation.Is
2928
};

src/Umbraco.Cms.Api.Delivery/Querying/Selectors/AncestorsSelector.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using Microsoft.Extensions.DependencyInjection;
12
using Umbraco.Cms.Api.Delivery.Indexing.Selectors;
23
using Umbraco.Cms.Core.DeliveryApi;
4+
using Umbraco.Cms.Core.DependencyInjection;
35
using Umbraco.Cms.Core.Models.PublishedContent;
46
using Umbraco.Cms.Core.PublishedCache;
57
using Umbraco.Extensions;
@@ -10,10 +12,21 @@ public sealed class AncestorsSelector : QueryOptionBase, ISelectorHandler
1012
{
1113
private const string AncestorsSpecifier = "ancestors:";
1214
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
15+
private readonly IRequestPreviewService _requestPreviewService;
1316

14-
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor, IRequestRoutingService requestRoutingService)
15-
: base(publishedSnapshotAccessor, requestRoutingService) =>
17+
[Obsolete("Please use the non-obsolete constructor. Will be removed in V17.")]
18+
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor,
19+
IRequestRoutingService requestRoutingService)
20+
: this(publishedSnapshotAccessor, requestRoutingService, StaticServiceProvider.Instance.GetRequiredService<IRequestPreviewService>())
21+
{
22+
}
23+
24+
public AncestorsSelector(IPublishedSnapshotAccessor publishedSnapshotAccessor, IRequestRoutingService requestRoutingService, IRequestPreviewService requestPreviewService)
25+
: base(publishedSnapshotAccessor, requestRoutingService)
26+
{
1627
_publishedSnapshotAccessor = publishedSnapshotAccessor;
28+
_requestPreviewService = requestPreviewService;
29+
}
1730

1831
/// <inheritdoc />
1932
public bool CanHandle(string query)
@@ -37,10 +50,20 @@ public SelectorOption BuildSelectorOption(string selector)
3750
};
3851
}
3952

40-
IPublishedSnapshot publishedSnapshot = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot();
53+
IPublishedContentCache contentCache = _publishedSnapshotAccessor.GetRequiredPublishedSnapshot()?.Content
54+
?? throw new InvalidOperationException("Could not obtain the content cache");
55+
56+
IPublishedContent? contentItem = contentCache.GetById(_requestPreviewService.IsPreview(), id.Value);
4157

42-
IPublishedContent contentItem = publishedSnapshot.Content?.GetById((Guid)id)
43-
?? throw new InvalidOperationException("Could not obtain the content cache");
58+
if (contentItem is null)
59+
{
60+
// no such content item, make sure the selector does not yield any results
61+
return new SelectorOption
62+
{
63+
FieldName = AncestorsSelectorIndexer.FieldName,
64+
Values = Array.Empty<string>()
65+
};
66+
}
4467

4568
var ancestorKeys = contentItem.Ancestors().Select(a => a.Key.ToString("D")).ToArray();
4669

src/Umbraco.Core/EmbeddedResources/Lang/en.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@
167167
<key alias="move">Media moved</key>
168168
<key alias="copy">Media copied</key>
169169
<key alias="save">Media saved</key>
170+
<key alias="new">Media created</key>
170171
</area>
171172
<area alias="auditTrails">
172173
<key alias="atViewingFor">Viewing for</key>
@@ -190,6 +191,7 @@
190191
<key alias="smallPublishVariant">Publish</key>
191192
<key alias="smallMove">Move</key>
192193
<key alias="smallSave">Save</key>
194+
<key alias="smallNew">New</key>
193195
<key alias="smallSaveVariant">Save</key>
194196
<key alias="smallDelete">Delete</key>
195197
<key alias="smallUnpublish">Unpublish</key>

src/Umbraco.Core/EmbeddedResources/Lang/en_us.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<key alias="move">Media moved</key>
166166
<key alias="copy">Media copied</key>
167167
<key alias="save">Media saved</key>
168+
<key alias="new">Media created</key>
168169
</area>
169170
<area alias="auditTrails">
170171
<key alias="atViewingFor">Viewing for</key>
@@ -189,6 +190,7 @@
189190
<key alias="smallPublishVariant">Publish</key>
190191
<key alias="smallMove">Move</key>
191192
<key alias="smallSave">Save</key>
193+
<key alias="smallNew">New</key>
192194
<key alias="smallSaveVariant">Save</key>
193195
<key alias="smallDelete">Delete</key>
194196
<key alias="smallUnpublish">Unpublish</key>

src/Umbraco.Infrastructure/DeliveryApi/ApiRichTextElementParser.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ private T ParseElement<T>(HtmlNode element, IPublishedSnapshot publishedSnapshot
101101
// - non-#comment nodes
102102
// - non-#text nodes
103103
// - non-empty #text nodes
104+
// - empty #text between inline elements (see #17037)
104105
HtmlNode[] childNodes = element.ChildNodes
105-
.Where(c => c.Name != CommentNodeName && (c.Name != TextNodeName || string.IsNullOrWhiteSpace(c.InnerText) is false))
106+
.Where(c => c.Name != CommentNodeName && (c.Name != TextNodeName || c.NextSibling is not null || string.IsNullOrWhiteSpace(c.InnerText) is false))
106107
.ToArray();
107108

108109
var tag = TagName(element);

src/Umbraco.Web.BackOffice/Controllers/MediaController.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,8 @@ public async Task<IActionResult> PostMove(MoveOrCopy move)
471471
null); // media are all invariant
472472

473473
// we will continue to save if model state is invalid, however we cannot save if critical data is missing.
474-
// TODO: Allowing media to be saved when it is invalid is odd - media doesn't have a publish phase so suddenly invalid data is allowed to be 'live'
474+
// this is a design decision, to continue to be able to save invalid content
475+
// as you can do the same with documents. This will be removed in a future version.
475476
if (!ModelState.IsValid)
476477
{
477478
// check for critical data validation issues, we can't continue saving if this data is invalid
@@ -495,10 +496,10 @@ public async Task<IActionResult> PostMove(MoveOrCopy move)
495496
// return the updated model
496497
MediaItemDisplay? display = _umbracoMapper.Map<MediaItemDisplay>(contentItem.PersistedContent);
497498

498-
// lastly, if it is not valid, add the model state to the outgoing object and throw a 403
499+
// lastly, if it is not valid, add the model state to the outgoing object.
499500
if (!ModelState.IsValid)
500501
{
501-
return ValidationProblem(display, ModelState, StatusCodes.Status403Forbidden);
502+
return ValidationProblem(display, ModelState);
502503
}
503504

504505
// put the correct msgs in

src/Umbraco.Web.Common/Routing/UmbracoVirtualPageRoute.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
using Microsoft.AspNetCore.Mvc.Filters;
66
using Microsoft.AspNetCore.Routing;
77
using Microsoft.Extensions.DependencyInjection;
8+
using Umbraco.Cms.Core.DependencyInjection;
89
using Umbraco.Cms.Core.Models.PublishedContent;
910
using Umbraco.Cms.Core.Routing;
11+
using Umbraco.Cms.Core.Web;
1012
using Umbraco.Cms.Web.Common.Controllers;
1113
using Umbraco.Cms.Web.Common.Extensions;
1214

@@ -21,6 +23,7 @@ public class UmbracoVirtualPageRoute : IUmbracoVirtualPageRoute
2123
private readonly LinkParser _linkParser;
2224
private readonly UriUtility _uriUtility;
2325
private readonly IPublishedRouter _publishedRouter;
26+
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
2427

2528
/// <summary>
2629
/// Constructor.
@@ -29,16 +32,29 @@ public class UmbracoVirtualPageRoute : IUmbracoVirtualPageRoute
2932
/// <param name="linkParser">The link parser.</param>
3033
/// <param name="uriUtility">The Uri utility.</param>
3134
/// <param name="publishedRouter">The published router.</param>
35+
/// <param name="umbracoContextAccessor">The umbraco context accessor.</param>
3236
public UmbracoVirtualPageRoute(
3337
EndpointDataSource endpointDataSource,
3438
LinkParser linkParser,
3539
UriUtility uriUtility,
36-
IPublishedRouter publishedRouter)
40+
IPublishedRouter publishedRouter,
41+
IUmbracoContextAccessor umbracoContextAccessor)
3742
{
3843
_endpointDataSource = endpointDataSource;
3944
_linkParser = linkParser;
4045
_uriUtility = uriUtility;
4146
_publishedRouter = publishedRouter;
47+
_umbracoContextAccessor = umbracoContextAccessor;
48+
}
49+
50+
[Obsolete("Please use constructor that takes an IUmbracoContextAccessor instead, scheduled for removal in v17")]
51+
public UmbracoVirtualPageRoute(
52+
EndpointDataSource endpointDataSource,
53+
LinkParser linkParser,
54+
UriUtility uriUtility,
55+
IPublishedRouter publishedRouter)
56+
: this(endpointDataSource, linkParser, uriUtility, publishedRouter, StaticServiceProvider.Instance.GetRequiredService<IUmbracoContextAccessor>())
57+
{
4258
}
4359

4460
/// <summary>
@@ -157,7 +173,8 @@ public async Task<IPublishedRequest> CreatePublishedRequest(HttpContext httpCont
157173
requestBuilder.SetPublishedContent(publishedContent);
158174
_publishedRouter.RouteDomain(requestBuilder);
159175

160-
return requestBuilder.Build();
176+
// Ensure the culture and domain is set correctly for the published request
177+
return await _publishedRouter.RouteRequestAsync(requestBuilder, new RouteRequestOptions(Core.Routing.RouteDirection.Inbound));
161178
}
162179

163180
/// <summary>
@@ -171,6 +188,12 @@ public async Task SetRouteValues(HttpContext httpContext, IPublishedContent publ
171188
{
172189
IPublishedRequest publishedRequest = await CreatePublishedRequest(httpContext, publishedContent);
173190

191+
// Ensure the published request is set to the UmbracoContext
192+
if (_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? umbracoContext))
193+
{
194+
umbracoContext.PublishedRequest = publishedRequest;
195+
}
196+
174197
var umbracoRouteValues = new UmbracoRouteValues(
175198
publishedRequest,
176199
controllerActionDescriptor);

0 commit comments

Comments
 (0)