Merge remote-tracking branch 'origin/v13/dev' into v14/dev
This commit is contained in:
@@ -355,8 +355,9 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
Services.AddSingleton<CompiledPackageXmlParser>();
|
||||
Services.AddUnique<IPreviewService, PreviewService>();
|
||||
|
||||
// Register a noop IHtmlSanitizer to be replaced
|
||||
// Register a noop IHtmlSanitizer & IMarkdownSanitizer to be replaced
|
||||
Services.AddUnique<IHtmlSanitizer, NoopHtmlSanitizer>();
|
||||
Services.AddUnique<IMarkdownSanitizer, NoopMarkdownSanitizer>();
|
||||
|
||||
Services.AddUnique<IPropertyTypeUsageService, PropertyTypeUsageService>();
|
||||
Services.AddUnique<IDataTypeUsageService, DataTypeUsageService>();
|
||||
|
||||
@@ -1015,6 +1015,8 @@
|
||||
<key alias="createHeader">Opret header</key>
|
||||
<key alias="deliveries">Leverancer</key>
|
||||
<key alias="noHeaders">Der er ikke tilføjet nogen webhook headers</key>
|
||||
<key alias="toggleDebug">Skift til debug mode for mere information.</key>
|
||||
<key alias="statusNotOk">Ikke OK status kode.</key>
|
||||
</area>
|
||||
<area alias="language">
|
||||
<key alias="cultureCode">Culture Code</key>
|
||||
|
||||
@@ -2032,6 +2032,8 @@ To manage your website, simply open the Umbraco backoffice and start adding cont
|
||||
<key alias="types">Types</key>
|
||||
<key alias="webhookKey">Webhook key</key>
|
||||
<key alias="retryCount">Retry count</key>
|
||||
<key alias="toggleDebug">Toggle debug mode for more information.</key>
|
||||
<key alias="statusNotOk">Not OK status code</key>
|
||||
</area>
|
||||
<area alias="languages">
|
||||
<key alias="addLanguage">Add language</key>
|
||||
|
||||
@@ -7,19 +7,21 @@ namespace Umbraco.Cms.Core.IO;
|
||||
|
||||
internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
{
|
||||
private static readonly string ShadowFsPath = Constants.SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs";
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private const string ShadowFsPath = "ShadowFs";
|
||||
|
||||
private readonly Func<bool?>? _isScoped;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly string _shadowPath;
|
||||
private readonly Func<bool?>? _isScoped;
|
||||
|
||||
private string? _shadowDir;
|
||||
private ShadowFileSystem? _shadowFileSystem;
|
||||
|
||||
public ShadowWrapper(IFileSystem innerFileSystem, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory, string shadowPath, Func<bool?>? isScoped = null)
|
||||
{
|
||||
InnerFileSystem = innerFileSystem;
|
||||
|
||||
_ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper));
|
||||
_hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment));
|
||||
_loggerFactory = loggerFactory;
|
||||
@@ -35,18 +37,19 @@ internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isScoped is not null && _shadowFileSystem is not null)
|
||||
Func<bool?>? isScoped = _isScoped;
|
||||
if (isScoped is not null && _shadowFileSystem is not null)
|
||||
{
|
||||
var isScoped = _isScoped!();
|
||||
bool? scoped = isScoped();
|
||||
|
||||
// if the filesystem is created *after* shadowing starts, it won't be shadowing
|
||||
// better not ignore that situation and raised a meaningful (?) exception
|
||||
if (isScoped.HasValue && isScoped.Value && _shadowFileSystem == null)
|
||||
// better not ignore that situation and raise a meaningful (?) exception
|
||||
if (scoped.HasValue && scoped.Value && _shadowFileSystem == null)
|
||||
{
|
||||
throw new Exception("The filesystems are shadowing, but this filesystem is not.");
|
||||
}
|
||||
|
||||
return isScoped.HasValue && isScoped.Value
|
||||
return scoped.HasValue && scoped.Value
|
||||
? _shadowFileSystem
|
||||
: InnerFileSystem;
|
||||
}
|
||||
@@ -56,8 +59,7 @@ internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IFileProvider? Create() =>
|
||||
InnerFileSystem.TryCreateFileProvider(out IFileProvider? fileProvider) ? fileProvider : null;
|
||||
public IFileProvider? Create() => InnerFileSystem.TryCreateFileProvider(out IFileProvider? fileProvider) ? fileProvider : null;
|
||||
|
||||
public IEnumerable<string> GetDirectories(string path) => FileSystem.GetDirectories(path);
|
||||
|
||||
@@ -69,8 +71,7 @@ internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
|
||||
public void AddFile(string path, Stream stream) => FileSystem.AddFile(path, stream);
|
||||
|
||||
public void AddFile(string path, Stream stream, bool overrideExisting) =>
|
||||
FileSystem.AddFile(path, stream, overrideExisting);
|
||||
public void AddFile(string path, Stream stream, bool overrideExisting) => FileSystem.AddFile(path, stream, overrideExisting);
|
||||
|
||||
public IEnumerable<string> GetFiles(string path) => FileSystem.GetFiles(path);
|
||||
|
||||
@@ -107,8 +108,7 @@ internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
{
|
||||
var id = GuidUtils.ToBase32String(Guid.NewGuid(), idLength);
|
||||
|
||||
var virt = ShadowFsPath + "/" + id;
|
||||
var shadowDir = hostingEnvironment.MapPathContentRoot(virt);
|
||||
var shadowDir = Path.Combine(hostingEnvironment.LocalTempPath, ShadowFsPath, id);
|
||||
if (Directory.Exists(shadowDir))
|
||||
{
|
||||
continue;
|
||||
@@ -129,10 +129,10 @@ internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
// note: no thread-safety here, because ShadowFs is thread-safe due to the check
|
||||
// on ShadowFileSystemsScope.None - and if None is false then we should be running
|
||||
// in a single thread anyways
|
||||
var virt = Path.Combine(ShadowFsPath, id, _shadowPath);
|
||||
_shadowDir = _hostingEnvironment.MapPathContentRoot(virt);
|
||||
var rootUrl = Path.Combine(ShadowFsPath, id, _shadowPath);
|
||||
_shadowDir = Path.Combine(_hostingEnvironment.LocalTempPath, rootUrl);
|
||||
Directory.CreateDirectory(_shadowDir);
|
||||
var tempfs = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, _loggerFactory.CreateLogger<PhysicalFileSystem>(), _shadowDir, _hostingEnvironment.ToAbsolute(virt));
|
||||
var tempfs = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, _loggerFactory.CreateLogger<PhysicalFileSystem>(), _shadowDir, rootUrl);
|
||||
_shadowFileSystem = new ShadowFileSystem(InnerFileSystem, tempfs);
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ internal class ShadowWrapper : IFileSystem, IFileProviderFactory
|
||||
|
||||
// shadowPath make be path/to/dir, remove each
|
||||
dir = dir!.Replace('/', Path.DirectorySeparatorChar);
|
||||
var min = _hostingEnvironment.MapPathContentRoot(ShadowFsPath).Length;
|
||||
var min = Path.Combine(_hostingEnvironment.LocalTempPath, ShadowFsPath).Length;
|
||||
var pos = dir.LastIndexOf(Path.DirectorySeparatorChar);
|
||||
while (pos > min)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
/// <summary>
|
||||
/// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for
|
||||
/// display in the editor
|
||||
/// </summary>
|
||||
internal class MarkDownPropertyValueEditor : DataValueEditor
|
||||
{
|
||||
private readonly IMarkdownSanitizer _markdownSanitizer;
|
||||
|
||||
public MarkDownPropertyValueEditor(
|
||||
ILocalizedTextService localizedTextService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper,
|
||||
DataEditorAttribute attribute,
|
||||
IMarkdownSanitizer markdownSanitizer)
|
||||
: base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute) => _markdownSanitizer = markdownSanitizer;
|
||||
|
||||
public override object? FromEditor(ContentPropertyData editorValue, object? currentValue)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(editorValue.Value?.ToString()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var sanitized = _markdownSanitizer.Sanitize(editorValue.Value.ToString()!);
|
||||
|
||||
return sanitized.NullOrWhiteSpaceAsNull();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -50,4 +51,11 @@ public class MarkdownPropertyEditor : DataEditor
|
||||
/// <inheritdoc />
|
||||
protected override IConfigurationEditor CreateConfigurationEditor() =>
|
||||
new MarkdownConfigurationEditor(_ioHelper, _editorConfigurationParser);
|
||||
|
||||
/// <summary>
|
||||
/// Create a custom value editor
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override IDataValueEditor CreateValueEditor() =>
|
||||
DataValueEditorFactory.Create<MarkDownPropertyValueEditor>(Attribute!);
|
||||
}
|
||||
|
||||
14
src/Umbraco.Core/Security/IMarkdownSanitizer.cs
Normal file
14
src/Umbraco.Core/Security/IMarkdownSanitizer.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Umbraco.Cms.Core.Security;
|
||||
|
||||
/// <summary>
|
||||
/// Sanitizer service for the markdown editor.
|
||||
/// </summary>
|
||||
public interface IMarkdownSanitizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Sanitizes Markdown
|
||||
/// </summary>
|
||||
/// <param name="markdown">Markdown to be sanitized</param>
|
||||
/// <returns>Sanitized Markdown</returns>
|
||||
string Sanitize(string markdown);
|
||||
}
|
||||
8
src/Umbraco.Core/Security/NoopMarkdownSanitizer.cs
Normal file
8
src/Umbraco.Core/Security/NoopMarkdownSanitizer.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Cms.Core.Security;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class NoopMarkdownSanitizer : IMarkdownSanitizer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Sanitize(string markdown) => markdown;
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Webhooks;
|
||||
using Umbraco.Cms.Web.Common.Models;
|
||||
|
||||
@@ -7,6 +11,24 @@ namespace Umbraco.Cms.Web.BackOffice.Mapping;
|
||||
|
||||
public class WebhookMapDefinition : IMapDefinition
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
|
||||
[Obsolete("Use non-obsolete constructor. This will be removed in Umbraco 15.")]
|
||||
public WebhookMapDefinition() : this(
|
||||
StaticServiceProvider.Instance.GetRequiredService<IHostingEnvironment>(),
|
||||
StaticServiceProvider.Instance.GetRequiredService<ILocalizedTextService>()
|
||||
)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public WebhookMapDefinition(IHostingEnvironment hostingEnvironment, ILocalizedTextService localizedTextService)
|
||||
{
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_localizedTextService = localizedTextService;
|
||||
}
|
||||
|
||||
public void DefineMaps(IUmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<WebhookViewModel, IWebhook>((_, _) => new Webhook(string.Empty), Map);
|
||||
@@ -40,13 +62,22 @@ public class WebhookMapDefinition : IMapDefinition
|
||||
target.EventAlias = source.EventAlias;
|
||||
target.Key = source.Key;
|
||||
target.RequestBody = source.RequestBody ?? string.Empty;
|
||||
target.ResponseBody = source.ResponseBody;
|
||||
target.RetryCount = source.RetryCount;
|
||||
target.StatusCode = source.StatusCode;
|
||||
target.Url = source.Url;
|
||||
target.RequestHeaders = source.RequestHeaders;
|
||||
target.ResponseHeaders = source.ResponseHeaders;
|
||||
target.WebhookKey = source.WebhookKey;
|
||||
target.ExceptionOccured = source.ExceptionOccured;
|
||||
|
||||
if (_hostingEnvironment.IsDebugMode)
|
||||
{
|
||||
target.ExceptionOccured = source.ExceptionOccured;
|
||||
target.ResponseBody = source.ResponseBody;
|
||||
target.ResponseHeaders = source.ResponseHeaders;
|
||||
target.StatusCode = source.StatusCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
target.ResponseBody = _localizedTextService.Localize("webhooks", "toggleDebug", Thread.CurrentThread.CurrentUICulture);
|
||||
target.StatusCode = source.StatusCode is "OK (200)" ? source.StatusCode : _localizedTextService.Localize("webhooks", "statusNotOk", Thread.CurrentThread.CurrentUICulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
Reference in New Issue
Block a user