Compare commits

...
8 Commits
12 changed files with 2994 additions and 2892 deletions
+3 -1
View File
@@ -338,7 +338,9 @@ stages:
# Integration Tests (SQL Server)
- job:
timeoutInMinutes: 120
condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
# We are currently encountering issues when running SQL Server Linux tests Microsoft.Data.SqlClient.SqlException (0x80131904)
# condition: or(eq(stageDependencies.Build.A.outputs['build.NBGV_PublicRelease'], 'True'), ${{parameters.sqlServerIntegrationTests}})
condition: eq(${{parameters.sqlServerIntegrationTests}}, True)
displayName: Integration Tests (SQL Server)
strategy:
matrix:
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.DeliveryApi;
namespace Umbraco.Cms.Api.Delivery.Services;
@@ -11,5 +11,5 @@ internal sealed class RequestPreviewService : RequestHeaderHandler, IRequestPrev
}
/// <inheritdoc />
public bool IsPreview() => GetHeaderValue("Preview") == "true";
public bool IsPreview() => string.Equals(GetHeaderValue("Preview"), "true", StringComparison.OrdinalIgnoreCase);
}
@@ -87,7 +87,7 @@ public class MacroRenderingController : UmbracoAuthorizedJsonController
[HttpGet]
public async Task<IActionResult> GetMacroResultAsHtmlForEditor(string macroAlias, int pageId,
[FromQuery] IDictionary<string, object> macroParams) =>
await GetMacroResultAsHtml(macroAlias, pageId, macroParams);
await GetMacroResultAsHtml(macroAlias, pageId.ToString(), macroParams);
/// <summary>
/// Gets a rendered macro as HTML for rendering in the rich text editor.
@@ -98,11 +98,24 @@ public class MacroRenderingController : UmbracoAuthorizedJsonController
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
[NonAction]
[Obsolete("This endpoint is no longer used.")]
public async Task<IActionResult> GetMacroResultAsHtmlForEditor(MacroParameterModel model) =>
await GetMacroResultAsHtml(model.MacroAlias, model.PageId.ToString(), model.MacroParams);
/// <summary>
/// Gets a rendered macro as HTML for rendering in the rich text editor.
/// Using HTTP POST instead of GET allows for more parameters to be passed as it's not dependent on URL-length
/// limitations like GET.
/// The method using GET is kept to maintain backwards compatibility
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> GetMacroResultAsHtmlForEditor(MacroParameterModel2 model) =>
await GetMacroResultAsHtml(model.MacroAlias, model.PageId, model.MacroParams);
private async Task<IActionResult> GetMacroResultAsHtml(string? macroAlias, int pageId,
IDictionary<string, object>? macroParams)
private async Task<IActionResult> GetMacroResultAsHtml(string? macroAlias, string pageId, IDictionary<string, object>? macroParams)
{
IMacro? m = macroAlias is null ? null : _macroService.GetByAlias(macroAlias);
if (m == null)
@@ -111,11 +124,11 @@ public class MacroRenderingController : UmbracoAuthorizedJsonController
}
IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext();
IPublishedContent? publishedContent = umbracoContext.Content?.GetById(true, pageId);
IPublishedContent? publishedContent = GetPagePublishedContent(pageId, umbracoContext);
//if it isn't supposed to be rendered in the editor then return an empty string
//currently we cannot render a macro if the page doesn't yet exist
if (pageId == -1 || publishedContent == null || m.DontRender)
if (publishedContent == null || m.DontRender)
{
//need to create a specific content result formatted as HTML since this controller has been configured
//with only json formatters.
@@ -149,6 +162,21 @@ public class MacroRenderingController : UmbracoAuthorizedJsonController
}
}
private static IPublishedContent? GetPagePublishedContent(string pageId, IUmbracoContext umbracoContext)
{
if (int.TryParse(pageId, NumberStyles.Integer, CultureInfo.InvariantCulture, out int pageIdAsInt))
{
return umbracoContext.Content?.GetById(true, pageIdAsInt);
}
if (Guid.TryParse(pageId, out Guid pageIdAsGuid))
{
return umbracoContext.Content?.GetById(true, pageIdAsGuid);
}
return null;
}
[HttpPost]
public IActionResult CreatePartialViewMacroWithFile(CreatePartialViewMacroWithFileModel model)
{
@@ -180,6 +208,7 @@ public class MacroRenderingController : UmbracoAuthorizedJsonController
return Ok();
}
[Obsolete("This model is no longer used and has been replaced with MacroParameterModel2 that changes the type of the PageId property.")]
public class MacroParameterModel
{
public string? MacroAlias { get; set; }
@@ -187,6 +216,13 @@ public class MacroRenderingController : UmbracoAuthorizedJsonController
public IDictionary<string, object>? MacroParams { get; set; }
}
public class MacroParameterModel2
{
public string? MacroAlias { get; set; }
public string PageId { get; set; } = string.Empty;
public IDictionary<string, object>? MacroParams { get; set; }
}
public class CreatePartialViewMacroWithFileModel
{
public string? Filename { get; set; }
@@ -217,10 +217,8 @@ public partial class PreviewController : Controller
// are we attempting a redirect to the default route (by ID with optional culture)?
Match match = DefaultPreviewRedirectRegex().Match(redir ?? string.Empty);
if (match.Success)
if (match.Success && int.TryParse(match.Groups["id"].Value, out int id))
{
var id = int.Parse(match.Groups["id"].Value);
// first try to resolve the published URL
if (_umbracoContextAccessor.TryGetUmbracoContext(out IUmbracoContext? umbracoContext) &&
umbracoContext.Content is not null)
@@ -98,16 +98,6 @@ public class StaticFilesTreeController : TreeController
private void AddPhysicalFiles(string path, FormCollection queryStrings, TreeNodeCollection nodes)
{
IEnumerable<string> files = _fileSystem.GetFiles(path)
.Where(x => x.StartsWith(AppPlugins) || x.StartsWith(Webroot));
foreach (var file in files)
{
var name = Path.GetFileName(file);
TreeNode node = CreateTreeNode(WebUtility.UrlEncode(file), path, queryStrings, name, Constants.Icons.DefaultIcon, false);
nodes.Add(node);
}
IEnumerable<string> directories = _fileSystem.GetDirectories(path);
foreach (var directory in directories)
@@ -117,6 +107,16 @@ public class StaticFilesTreeController : TreeController
TreeNode node = CreateTreeNode(WebUtility.UrlEncode(directory), path, queryStrings, name, Constants.Icons.Folder, hasChildren);
nodes.Add(node);
}
IEnumerable<string> files = _fileSystem.GetFiles(path)
.Where(x => x.StartsWith(AppPlugins) || x.StartsWith(Webroot));
foreach (var file in files)
{
var name = Path.GetFileName(file);
TreeNode node = CreateTreeNode(WebUtility.UrlEncode(file), path, queryStrings, name, Constants.Icons.DefaultIcon, false);
nodes.Add(node);
}
}
private void AddWebRootFiles(string path, FormCollection queryStrings, TreeNodeCollection nodes)
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -19,8 +19,8 @@
},
"dependencies": {
"@microsoft/signalr": "8.0.7",
"@umbraco-ui/uui": "1.12.2",
"@umbraco-ui/uui-css": "1.12.1",
"@umbraco-ui/uui": "1.13.0",
"@umbraco-ui/uui-css": "1.13.0",
"ace-builds": "1.31.1",
"angular": "1.8.3",
"angular-animate": "1.8.3",
+2377 -2365
View File
File diff suppressed because it is too large Load Diff
+25 -25
View File
@@ -1,29 +1,29 @@
{
"name": "login",
"private": true,
"name": "login",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"watch": "tsc && vite build --watch",
"preview": "vite preview"
},
"engines": {
"node": ">=20.8",
"npm": ">=10.1"
},
"dependencies": {
"lit": "^3.1.2",
"msw": "^2.2.0",
"rxjs": "^7.8.1"
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"watch": "tsc && vite build --watch",
"preview": "vite preview"
},
"devDependencies": {
"@umbraco-ui/uui": "1.12.2",
"@umbraco-ui/uui-css": "1.12.1",
"typescript": "^5.3.3",
"vite": "^5.1.7"
},
"msw": {
"workerDirectory": "public"
}
"engines": {
"node": ">=20.8",
"npm": ">=10.1"
},
"dependencies": {
"lit": "^3.1.2",
"msw": "^2.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@umbraco-ui/uui": "1.13.0",
"@umbraco-ui/uui-css": "1.13.0",
"typescript": "^5.3.3",
"vite": "^5.1.7"
},
"msw": {
"workerDirectory": "public"
}
}
@@ -1,4 +1,4 @@
using System.Reflection;
using System.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Routing;
@@ -126,8 +126,20 @@ internal class EagerMatcherPolicy : MatcherPolicy, IEndpointSelectorPolicy
return;
}
// If it's an attribute routed IVirtualPageController with a Host attribute we should ignore if the host doesn't match the current request.
// Maybe we would expect that it wouldn't be in the provided CandidateSet, but it will be included just based on the Route.
// See: https://github.com/umbraco/Umbraco-CMS/issues/16816
if (controllerTypeInfo is not null && controllerTypeInfo.IsType<IVirtualPageController>())
{
HostAttribute? hostAttribute = controllerTypeInfo.GetCustomAttribute<HostAttribute>();
if (hostAttribute is not null && hostAttribute.Hosts.InvariantContains(httpContext.Request.Host.Value) is false)
{
continue;
}
}
// If it's an UmbracoPageController we need to do some domain routing.
// We need to do this in oder to handle cultures for our Dictionary.
// We need to do this in order to handle cultures for our Dictionary.
// This is because UmbracoPublishedContentCultureProvider is ued to set the Thread.CurrentThread.CurrentUICulture
// The CultureProvider is run before the actual routing, this means that our UmbracoVirtualPageFilterAttribute is hit AFTER the culture is set.
// Meaning we have to route the domain part already now, this is not pretty, but it beats having to look for content we know doesn't exist.
@@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Http;
using Moq;
using NUnit.Framework;
using Umbraco.Cms.Api.Delivery.Services;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Cms.Api.Delivery.Services;
[TestFixture]
public class RequestPreviewServiceTests
{
[TestCase(null, false)]
[TestCase("", false)]
[TestCase("false", false)]
[TestCase("true", true)]
[TestCase("True", true)]
public void IsPreview_Returns_Expected_Result(string? headerValue, bool expected)
{
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers["Preview"] = headerValue;
var httpContextAccessorMock = new Mock<IHttpContextAccessor>();
httpContextAccessorMock
.Setup(x => x.HttpContext)
.Returns(httpContext);
var sut = new RequestPreviewService(httpContextAccessorMock.Object);
var result = sut.IsPreview();
Assert.AreEqual(expected, result);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "13.8.0-rc",
"version": "13.8.0",
"assemblyVersion": {
"precision": "build"
},