From 204bbd5c46c9e2283125e66d22aea83d5b7e5f46 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Thu, 5 Dec 2019 10:41:58 +0100 Subject: [PATCH] Moved IOHelper to abstractions --- src/Umbraco.Abstractions/IO/IIOHelper.cs | 7 -- .../IO/IOHelper.cs | 11 --- .../IO/MediaFileSystem.cs | 13 ++-- src/Umbraco.Abstractions/StringExtensions.cs | 76 +++++++++++++++++++ src/Umbraco.Configuration/ConfigsFactory.cs | 1 + src/Umbraco.Core/ContentExtensions.cs | 5 +- src/Umbraco.Core/StringExtensions.cs | 75 +----------------- src/Umbraco.Core/Umbraco.Core.csproj | 1 - src/Umbraco.Tests/IO/FileSystemsTests.cs | 2 + src/Umbraco.Tests/Models/MediaXmlTest.cs | 4 +- .../PropertyEditors/ImageCropperTest.cs | 6 +- .../Strings/StringExtensionsTests.cs | 2 +- src/Umbraco.Tests/TestHelpers/TestHelper.cs | 3 + src/Umbraco.Tests/TestHelpers/TestObjects.cs | 5 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 7 +- src/Umbraco.Web/UmbracoApplicationBase.cs | 1 + 16 files changed, 107 insertions(+), 112 deletions(-) rename src/{Umbraco.Core => Umbraco.Abstractions}/IO/IOHelper.cs (95%) diff --git a/src/Umbraco.Abstractions/IO/IIOHelper.cs b/src/Umbraco.Abstractions/IO/IIOHelper.cs index 8b090f7f50..2d535a8d1e 100644 --- a/src/Umbraco.Abstractions/IO/IIOHelper.cs +++ b/src/Umbraco.Abstractions/IO/IIOHelper.cs @@ -59,13 +59,6 @@ namespace Umbraco.Core.IO /// void SetRootDirectory(string rootPath); - /// - /// Check to see if filename passed has any special chars in it and strips them to create a safe filename. Used to overcome an issue when Umbraco is used in IE in an intranet environment. - /// - /// The filename passed to the file handler from the upload field. - /// A safe filename without any path specific chars. - string SafeFileName(string filePath); - void EnsurePathExists(string path); /// diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Abstractions/IO/IOHelper.cs similarity index 95% rename from src/Umbraco.Core/IO/IOHelper.cs rename to src/Umbraco.Abstractions/IO/IOHelper.cs index 1b0fed9c0f..672228e3ca 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Abstractions/IO/IOHelper.cs @@ -255,17 +255,6 @@ namespace Umbraco.Core.IO _rootDir = rootPath; } - /// - /// Check to see if filename passed has any special chars in it and strips them to create a safe filename. Used to overcome an issue when Umbraco is used in IE in an intranet environment. - /// - /// The filename passed to the file handler from the upload field. - /// A safe filename without any path specific chars. - public string SafeFileName(string filePath) - { - // use string extensions - return filePath.ToSafeFileName(); - } - public void EnsurePathExists(string path) { var absolutePath = MapPath(path); diff --git a/src/Umbraco.Abstractions/IO/MediaFileSystem.cs b/src/Umbraco.Abstractions/IO/MediaFileSystem.cs index 02564820c5..cd978cc392 100644 --- a/src/Umbraco.Abstractions/IO/MediaFileSystem.cs +++ b/src/Umbraco.Abstractions/IO/MediaFileSystem.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Strings; namespace Umbraco.Core.IO { @@ -15,20 +16,18 @@ namespace Umbraco.Core.IO public class MediaFileSystem : FileSystemWrapper, IMediaFileSystem { private readonly IMediaPathScheme _mediaPathScheme; - private readonly IContentSection _contentConfig; private readonly ILogger _logger; - private readonly IIOHelper _ioHelper; + private readonly IShortStringHelper _shortStringHelper; /// /// Initializes a new instance of the class. /// - public MediaFileSystem(IFileSystem innerFileSystem, IContentSection contentConfig, IMediaPathScheme mediaPathScheme, ILogger logger, IIOHelper ioHelper) + public MediaFileSystem(IFileSystem innerFileSystem, IMediaPathScheme mediaPathScheme, ILogger logger, IShortStringHelper shortStringHelper) : base(innerFileSystem) { - _contentConfig = contentConfig; _mediaPathScheme = mediaPathScheme; _logger = logger; - _ioHelper = ioHelper; + _shortStringHelper = shortStringHelper; } /// @@ -65,7 +64,7 @@ namespace Umbraco.Core.IO { filename = Path.GetFileName(filename); if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _ioHelper.SafeFileName(filename.ToLowerInvariant()); + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); } @@ -75,7 +74,7 @@ namespace Umbraco.Core.IO { filename = Path.GetFileName(filename); if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _ioHelper.SafeFileName(filename.ToLowerInvariant()); + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); } diff --git a/src/Umbraco.Abstractions/StringExtensions.cs b/src/Umbraco.Abstractions/StringExtensions.cs index e9a69f40a5..37a6ae4258 100644 --- a/src/Umbraco.Abstractions/StringExtensions.cs +++ b/src/Umbraco.Abstractions/StringExtensions.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; +using Umbraco.Core.IO; namespace Umbraco.Core { @@ -1213,5 +1214,80 @@ namespace Umbraco.Core /// public static string NullOrWhiteSpaceAsNull(this string text) => string.IsNullOrWhiteSpace(text) ? null : text; + + /// + /// Ensures that a path has `~/` as prefix + /// + /// + /// + public static string EnsurePathIsApplicationRootPrefixed(this string path) + { + if (path.StartsWith("~/")) + return path; + if (path.StartsWith("/") == false && path.StartsWith("\\") == false) + path = string.Format("/{0}", path); + if (path.StartsWith("~") == false) + path = string.Format("~{0}", path); + return path; + } + + /// + /// Checks if a given path is a full path including drive letter + /// + /// + /// + // From: http://stackoverflow.com/a/35046453/5018 + public static bool IsFullPath(this string path) + { + return string.IsNullOrWhiteSpace(path) == false + && path.IndexOfAny(Path.GetInvalidPathChars().ToArray()) == -1 + && Path.IsPathRooted(path) + && Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false; + } + + /// + /// Based on the input string, this will detect if the string is a JS path or a JS snippet. + /// If a path cannot be determined, then it is assumed to be a snippet the original text is returned + /// with an invalid attempt, otherwise a valid attempt is returned with the resolved path + /// + /// + /// + /// + /// This is only used for legacy purposes for the Action.JsSource stuff and shouldn't be needed in v8 + /// + internal static Attempt DetectIsJavaScriptPath(this string input, IIOHelper ioHelper) + { + //validate that this is a url, if it is not, we'll assume that it is a text block and render it as a text + //block instead. + var isValid = true; + + if (Uri.IsWellFormedUriString(input, UriKind.RelativeOrAbsolute)) + { + //ok it validates, but so does alert('hello'); ! so we need to do more checks + + //here are the valid chars in a url without escaping + if (Regex.IsMatch(input, @"[^a-zA-Z0-9-._~:/?#\[\]@!$&'\(\)*\+,%;=]")) + isValid = false; + + //we'll have to be smarter and just check for certain js patterns now too! + var jsPatterns = new[] { @"\+\s*\=", @"\);", @"function\s*\(", @"!=", @"==" }; + if (jsPatterns.Any(p => Regex.IsMatch(input, p))) + isValid = false; + + if (isValid) + { + var resolvedUrlResult = ioHelper.TryResolveUrl(input); + //if the resolution was success, return it, otherwise just return the path, we've detected + // it's a path but maybe it's relative and resolution has failed, etc... in which case we're just + // returning what was given to us. + return resolvedUrlResult.Success + ? resolvedUrlResult + : Attempt.Succeed(input); + } + } + + return Attempt.Fail(input); + } + } } diff --git a/src/Umbraco.Configuration/ConfigsFactory.cs b/src/Umbraco.Configuration/ConfigsFactory.cs index e7ee72656f..6619da90a4 100644 --- a/src/Umbraco.Configuration/ConfigsFactory.cs +++ b/src/Umbraco.Configuration/ConfigsFactory.cs @@ -13,6 +13,7 @@ namespace Umbraco.Core.Configuration } public IHostingSettings HostingSettings { get; } = new HostingSettings(); + public IUmbracoSettingsSection UmbracoSettings { get; } public Configs Create(IIOHelper ioHelper) { diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 2a61c44323..db553c3b4a 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -11,6 +11,7 @@ using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; +using Umbraco.Core.Strings; namespace Umbraco.Core { @@ -136,9 +137,9 @@ namespace Umbraco.Core if (filename == null || filestream == null) return; // get a safe & clean filename - var ioHelper = Current.Factory.GetInstance(); + var shortStringHelper = Current.Factory.GetInstance(); - filename = ioHelper.SafeFileName(filename); + filename = shortStringHelper.CleanStringForSafeFileName(filename); if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index b7baa8cd60..f9f9199dad 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -14,51 +14,6 @@ namespace Umbraco.Core /// public static class StringExtensions { - /// - /// Based on the input string, this will detect if the string is a JS path or a JS snippet. - /// If a path cannot be determined, then it is assumed to be a snippet the original text is returned - /// with an invalid attempt, otherwise a valid attempt is returned with the resolved path - /// - /// - /// - /// - /// This is only used for legacy purposes for the Action.JsSource stuff and shouldn't be needed in v8 - /// - internal static Attempt DetectIsJavaScriptPath(this string input) - { - //validate that this is a url, if it is not, we'll assume that it is a text block and render it as a text - //block instead. - var isValid = true; - - if (Uri.IsWellFormedUriString(input, UriKind.RelativeOrAbsolute)) - { - //ok it validates, but so does alert('hello'); ! so we need to do more checks - - //here are the valid chars in a url without escaping - if (Regex.IsMatch(input, @"[^a-zA-Z0-9-._~:/?#\[\]@!$&'\(\)*\+,%;=]")) - isValid = false; - - //we'll have to be smarter and just check for certain js patterns now too! - var jsPatterns = new[] { @"\+\s*\=", @"\);", @"function\s*\(", @"!=", @"==" }; - if (jsPatterns.Any(p => Regex.IsMatch(input, p))) - isValid = false; - - if (isValid) - { - var ioHelper = Current.Factory.GetInstance(); - - var resolvedUrlResult = ioHelper.TryResolveUrl(input); - //if the resolution was success, return it, otherwise just return the path, we've detected - // it's a path but maybe it's relative and resolution has failed, etc... in which case we're just - // returning what was given to us. - return resolvedUrlResult.Success - ? resolvedUrlResult - : Attempt.Succeed(input); - } - } - - return Attempt.Fail(input); - } // FORMAT STRINGS @@ -227,34 +182,8 @@ namespace Umbraco.Core return Current.ShortStringHelper.CleanStringForSafeFileName(text, culture); } - /// - /// Checks if a given path is a full path including drive letter - /// - /// - /// - // From: http://stackoverflow.com/a/35046453/5018 - internal static bool IsFullPath(this string path) - { - return string.IsNullOrWhiteSpace(path) == false - && path.IndexOfAny(Path.GetInvalidPathChars().ToArray()) == -1 - && Path.IsPathRooted(path) - && Path.GetPathRoot(path).Equals(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) == false; - } - /// - /// Ensures that a path has `~/` as prefix - /// - /// - /// - internal static string EnsurePathIsApplicationRootPrefixed(this string path) - { - if (path.StartsWith("~/")) - return path; - if (path.StartsWith("/") == false && path.StartsWith("\\") == false) - path = string.Format("/{0}", path); - if (path.StartsWith("~") == false) - path = string.Format("~{0}", path); - return path; - } + + } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4d3bf9ebc0..80690c7dd5 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -159,7 +159,6 @@ - diff --git a/src/Umbraco.Tests/IO/FileSystemsTests.cs b/src/Umbraco.Tests/IO/FileSystemsTests.cs index 76d1ce85fc..9358923f72 100644 --- a/src/Umbraco.Tests/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests/IO/FileSystemsTests.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Services; using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; using Umbraco.Core.Composing.CompositionExtensions; +using Umbraco.Core.Strings; using FileSystems = Umbraco.Core.IO.FileSystems; namespace Umbraco.Tests.IO @@ -34,6 +35,7 @@ namespace Umbraco.Tests.IO composition.Register(_ => Mock.Of()); composition.Register(_ => Mock.Of()); composition.Register(_ => Mock.Of()); + composition.Register(_ => TestHelper.ShortStringHelper); composition.Register(_ => TestHelper.IOHelper); composition.RegisterUnique(); composition.RegisterUnique(TestHelper.IOHelper); diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index d8557230b6..a68272c0ce 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -31,13 +31,13 @@ namespace Umbraco.Tests.Models // reference, so static ctor runs, so event handlers register // and then, this will reset the width, height... because the file does not exist, of course ;-( var logger = Mock.Of(); - var ioHelper = Mock.Of(); + var shortStringHelper = Mock.Of(); var scheme = Mock.Of(); var config = Mock.Of(); var dataTypeService = Mock.Of(); var localizationService = Mock.Of(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger, ioHelper); + var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, shortStringHelper); var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config, dataTypeService, localizationService); var media = MockedMedia.CreateMediaImage(mediaType, -1); diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index 58345e2064..5e18e8ce78 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -16,6 +16,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.PropertyEditors; using Umbraco.Core.PropertyEditors.ValueConverters; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Tests.Components; using Umbraco.Tests.TestHelpers; using Umbraco.Web.Models; @@ -78,10 +79,9 @@ namespace Umbraco.Tests.PropertyEditors var logger = Mock.Of(); var scheme = Mock.Of(); - var config = Mock.Of(); - var ioHelper = Mock.Of(); + var shortStringHelper = Mock.Of(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger, ioHelper); + var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, shortStringHelper); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new ImageCropperPropertyEditor(Mock.Of(), mediaFileSystem, Mock.Of(), Mock.Of(), Mock.Of(), TestHelper.IOHelper)) { Id = 1 }); diff --git a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs index 66d0a973a7..286f2fd9e4 100644 --- a/src/Umbraco.Tests/Strings/StringExtensionsTests.cs +++ b/src/Umbraco.Tests/Strings/StringExtensionsTests.cs @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Strings [TestCase("/Test.js function(){return true;}", false)] public void Detect_Is_JavaScript_Path(string input, bool result) { - var output = input.DetectIsJavaScriptPath(); + var output = input.DetectIsJavaScriptPath(IOHelper); Assert.AreEqual(result, output.Success); } diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs index ca52c0b222..fc14c04da8 100644 --- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs +++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs @@ -20,6 +20,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Net; using Umbraco.Web; @@ -82,6 +83,8 @@ namespace Umbraco.Tests.TestHelpers } } + public static IShortStringHelper ShortStringHelper => new DefaultShortStringHelper(new DefaultShortStringHelperConfig()); + public static IIOHelper IOHelper = new IOHelper(GetHostingEnvironment()); /// diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index d79ea3500e..50288f9ac3 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -108,9 +108,10 @@ namespace Umbraco.Tests.TestHelpers if (eventMessagesFactory == null) throw new ArgumentNullException(nameof(eventMessagesFactory)); var scheme = Mock.Of(); - var config = Mock.Of(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger, ioHelper); + var shortStringHelper = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, shortStringHelper); var externalLoginService = GetLazyService(factory, c => new ExternalLoginService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); var publicAccessService = GetLazyService(factory, c => new PublicAccessService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index c84089475e..eaf20bb810 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -104,6 +104,7 @@ namespace Umbraco.Tests.Testing protected ILogger Logger => Factory.GetInstance(); protected IIOHelper IOHelper { get; private set; } + protected IShortStringHelper ShortStringHelper { get; private set; } protected IUmbracoVersion UmbracoVersion { get; private set; } protected ITypeFinder TypeFinder { get; private set; } @@ -143,6 +144,7 @@ namespace Umbraco.Tests.Testing var (logger, profiler) = GetLoggers(Options.Logger); var proflogger = new ProfilingLogger(logger, profiler); IOHelper = TestHelper.IOHelper; + ShortStringHelper = TestHelper.ShortStringHelper; TypeFinder = new TypeFinder(logger); var appCaches = GetAppCaches(); @@ -160,6 +162,7 @@ namespace Umbraco.Tests.Testing Composition = new Composition(register, typeLoader, proflogger, ComponentTests.MockRuntimeState(RuntimeLevel.Run), TestHelper.GetConfigs(), TestHelper.IOHelper, AppCaches.NoCache); + Composition.RegisterUnique(ShortStringHelper); Composition.RegisterUnique(IOHelper); Composition.RegisterUnique(UmbracoVersion); Composition.RegisterUnique(TypeFinder); @@ -364,10 +367,8 @@ namespace Umbraco.Tests.Testing var logger = Mock.Of(); var scheme = Mock.Of(); - var config = Mock.Of(); - var ioHelper = Mock.Of(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger, ioHelper); + var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, logger, ShortStringHelper); Composition.RegisterUnique(factory => mediaFileSystem); // no factory (noop) diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs index d65006e6bb..313df4b619 100644 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ b/src/Umbraco.Web/UmbracoApplicationBase.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Hosting; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Logging.Serilog; +using Umbraco.Core.Strings; using Umbraco.Web.Hosting; namespace Umbraco.Web