From c9ebaadf2300768677d4000e723a6ae34ef126d7 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 27 Apr 2021 09:52:17 +0200 Subject: [PATCH] Netcore: File systems rework (#10181) * Allow IMediaFileSystem to be replace in the DI, or registered with inner filesystem * Remove GetFileSystem from Filesystems It was only used by tests. * Make MediaFileSystem inherit from PhysicalFileSystem directly * Remove FileSystemWrapper * Remove inner filesystem from MediaFileSystem * Add MediaFileManager and bare minimum to make it testable * Remove MediaFileSystem * Fix unit tests using MediaFileManager * Remove IFileSystem and rely only on FileSystem * Hide dangerous methods in FileSystems and do some cleaning * Apply stylecop warnings to MediaFileManager * Add FilesystemsCreator to Tests.Common This allows you to create an instance if FileSystems with your own specified IFileSystem for testing purposes outside our own test suite. * Allow the stylesheet filesystem to be replaced. * Fix tests * Don't save stylesheetWrapper in a temporary var * refactor(FileSystems): change how stylesheet filesystem is registered * fix(FileSystems): unable to overwrite media filesystem SetMediaFileSystem added the MediaManager as a Singleton instead of replacing the existing instance. * fix(FileSystems): calling AddFileSystems replaces MediaManager When calling AddFileSystems after SetMediaFileSystem the MediaManager gets replaced by the default PhysicalFileSystem, so instead of calling SetMediaFileSystem in AddFileSystems we now call TrySetMediaFileSystem instead. This method will not replace any existing instance of the MediaManager if there's already a MediaManager registered. * Use SetMediaFileSystem instead of TrySet, and rename AddFilesystems to ConfigureFileSystems Also don't call AddFileSystems again in ConfigureFilesystems * Don't wrap CSS filesystem twice * Add CreateShadowWrapperInternal to avoid casting * Throw UnauthorizedAccessException isntead of InvalidOperationException * Remove ResetShadowId Co-authored-by: Rasmus John Pedersen --- .../Events/QueuingEventDispatcher.cs | 8 +- .../Extensions/ContentExtensions.cs | 16 +- src/Umbraco.Core/IO/FileSystemExtensions.cs | 28 -- src/Umbraco.Core/IO/FileSystemWrapper.cs | 118 -------- src/Umbraco.Core/IO/FileSystems.cs | 261 +++++++++++------- src/Umbraco.Core/IO/IFileSystems.cs | 33 --- src/Umbraco.Core/IO/IMediaFileSystem.cs | 66 ----- src/Umbraco.Core/IO/IMediaPathScheme.cs | 6 +- src/Umbraco.Core/IO/MediaFileManager.cs | 224 +++++++++++++++ src/Umbraco.Core/IO/MediaFileSystem.cs | 126 --------- .../CombinedGuidsMediaPathScheme.cs | 4 +- .../OriginalMediaPathScheme.cs | 8 +- .../TwoGuidsMediaPathScheme.cs | 4 +- .../MediaPathSchemes/UniqueMediaPathScheme.cs | 4 +- .../Media/UploadAutoFillProperties.cs | 8 +- .../Models/Mapping/UserMapDefinition.cs | 12 +- src/Umbraco.Core/Models/UserExtensions.cs | 6 +- .../UmbracoBuilder.FileSystems.cs | 21 +- .../UmbracoBuilder.Uniques.cs | 47 +++- .../Implement/PartialViewMacroRepository.cs | 2 +- .../Implement/PartialViewRepository.cs | 2 +- .../Implement/ScriptRepository.cs | 2 +- .../Implement/StylesheetRepository.cs | 2 +- .../Implement/TemplateRepository.cs | 2 +- .../FileUploadPropertyEditor.cs | 22 +- .../FileUploadPropertyValueEditor.cs | 18 +- .../ImageCropperPropertyEditor.cs | 24 +- .../ImageCropperPropertyValueEditor.cs | 18 +- .../RichTextEditorPastedImages.cs | 17 +- src/Umbraco.Infrastructure/Scoping/Scope.cs | 16 +- .../Scoping/ScopeProvider.cs | 12 +- .../Services/Implement/MediaService.cs | 16 +- .../TestHelpers/FileSystemsCreator.cs | 37 +++ .../Umbraco.Core/IO/FileSystemsTests.cs | 41 +-- .../Umbraco.Core/IO/ShadowFileSystemTests.cs | 33 +-- .../Repositories/ContentTypeRepositoryTest.cs | 2 +- .../Repositories/DocumentRepositoryTest.cs | 2 +- .../PartialViewRepositoryTests.cs | 9 +- .../Repositories/ScriptRepositoryTest.cs | 11 +- .../Repositories/StylesheetRepositoryTest.cs | 12 +- .../Repositories/TemplateRepositoryTest.cs | 2 +- .../Scoping/ScopeFileSystemsTests.cs | 43 ++- .../Umbraco.Core/Components/ComponentTests.cs | 8 +- .../Scoping/ScopeEventDispatcherTests.cs | 8 +- .../Scoping/ScopeUnitTests.cs | 10 +- src/Umbraco.Tests/Models/MediaXmlTest.cs | 6 +- .../PublishedContentTestBase.cs | 5 +- .../PublishedContent/PublishedContentTests.cs | 3 +- .../Routing/MediaUrlProviderTests.cs | 8 +- .../TestHelpers/TestObjects-Mocks.cs | 28 +- src/Umbraco.Tests/TestHelpers/TestObjects.cs | 6 +- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 5 +- .../Controllers/CodeFileController.cs | 4 +- .../Controllers/CurrentUserController.cs | 8 +- .../Controllers/ImagesController.cs | 8 +- .../Controllers/LogController.cs | 8 +- .../Controllers/MediaController.cs | 8 +- .../Controllers/UsersController.cs | 22 +- .../Trees/PartialViewMacrosTreeController.cs | 2 +- .../Trees/PartialViewsTreeController.cs | 2 +- .../Trees/ScriptsTreeController.cs | 2 +- .../Trees/StylesheetsTreeController.cs | 2 +- src/Umbraco.Web/Composing/Current.cs | 2 +- 63 files changed, 740 insertions(+), 760 deletions(-) delete mode 100644 src/Umbraco.Core/IO/FileSystemWrapper.cs delete mode 100644 src/Umbraco.Core/IO/IFileSystems.cs delete mode 100644 src/Umbraco.Core/IO/IMediaFileSystem.cs create mode 100644 src/Umbraco.Core/IO/MediaFileManager.cs delete mode 100644 src/Umbraco.Core/IO/MediaFileSystem.cs create mode 100644 src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs diff --git a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs index de82fc1f24..e79cd67cd8 100644 --- a/src/Umbraco.Core/Events/QueuingEventDispatcher.cs +++ b/src/Umbraco.Core/Events/QueuingEventDispatcher.cs @@ -11,11 +11,11 @@ namespace Umbraco.Cms.Core.Events /// public class QueuingEventDispatcher : QueuingEventDispatcherBase { - private readonly IMediaFileSystem _mediaFileSystem; - public QueuingEventDispatcher(IMediaFileSystem mediaFileSystem) + private readonly MediaFileManager _mediaFileManager; + public QueuingEventDispatcher(MediaFileManager mediaFileManager) : base(true) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; } protected override void ScopeExitCompleted() @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.Events // but then where should it be (without making things too complicated)? var delete = e.Args as IDeletingMediaFilesEventArgs; if (delete != null && delete.MediaFilesToDelete.Count > 0) - _mediaFileSystem.DeleteMediaFiles(delete.MediaFilesToDelete); + _mediaFileManager.DeleteMediaFiles(delete.MediaFilesToDelete); } } diff --git a/src/Umbraco.Core/Extensions/ContentExtensions.cs b/src/Umbraco.Core/Extensions/ContentExtensions.cs index 7794d60b15..5aaad5e303 100644 --- a/src/Umbraco.Core/Extensions/ContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentExtensions.cs @@ -204,7 +204,7 @@ namespace Umbraco.Extensions /// /// Sets the posted file value of a property. /// - public static void SetValue(this IContentBase content, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + public static void SetValue(this IContentBase content, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { if (filename == null || filestream == null) return; @@ -212,10 +212,10 @@ namespace Umbraco.Extensions if (string.IsNullOrWhiteSpace(filename)) return; filename = filename.ToLower(); - SetUploadFile(content, mediaFileSystem, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); + SetUploadFile(content, mediaFileManager, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); } - private static void SetUploadFile(this IContentBase content, IMediaFileSystem mediaFileSystem, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + private static void SetUploadFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) { var property = GetProperty(content, contentTypeBaseServiceProvider, propertyTypeAlias); @@ -229,11 +229,11 @@ namespace Umbraco.Extensions // the property value is a JSON serialized image crop data set - grab the "src" property as the file source svalue = serializer.DeserializeSubset(svalue, "src"); } - oldpath = mediaFileSystem.GetRelativePath(svalue); + oldpath = mediaFileManager.FileSystem.GetRelativePath(svalue); } - var filepath = mediaFileSystem.StoreFile(content, property.PropertyType, filename, filestream, oldpath); - property.SetValue(mediaFileSystem.GetUrl(filepath), culture, segment); + var filepath = mediaFileManager.StoreFile(content, property.PropertyType, filename, filestream, oldpath); + property.SetValue(mediaFileManager.FileSystem.GetUrl(filepath), culture, segment); } // gets or creates a property for a content item. @@ -269,13 +269,13 @@ namespace Umbraco.Extensions /// the "folder number" that was assigned to the previous file referenced by the property, /// if any. /// - public static string StoreFile(this IContentBase content, IMediaFileSystem mediaFileSystem, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string filepath) + public static string StoreFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string filepath) { var contentType = contentTypeBaseServiceProvider.GetContentTypeOf(content); var propertyType = contentType .CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + "."); - return mediaFileSystem.StoreFile(content, propertyType, filename, filestream, filepath); + return mediaFileManager.StoreFile(content, propertyType, filename, filestream, filepath); } #endregion diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index e688ca22da..6f1f47007f 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -66,33 +66,5 @@ namespace Umbraco.Extensions } fs.DeleteFile(tempFile); } - - /// - /// Unwraps a filesystem. - /// - /// - /// A filesystem can be wrapped in a (public) or a (internal), - /// and this method deals with the various wrappers and - /// - public static IFileSystem Unwrap(this IFileSystem filesystem) - { - var unwrapping = true; - while (unwrapping) - { - switch (filesystem) - { - case FileSystemWrapper wrapper: - filesystem = wrapper.InnerFileSystem; - break; - case ShadowWrapper shadow: - filesystem = shadow.InnerFileSystem; - break; - default: - unwrapping = false; - break; - } - } - return filesystem; - } } } diff --git a/src/Umbraco.Core/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs deleted file mode 100644 index 34276131c7..0000000000 --- a/src/Umbraco.Core/IO/FileSystemWrapper.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// All custom file systems that are based upon another IFileSystem should inherit from FileSystemWrapper - /// - /// - /// An IFileSystem is generally used as a base file system, for example like a PhysicalFileSystem or an S3FileSystem. - /// Then, other custom file systems are wrapped upon these files systems like MediaFileSystem, etc... All of the custom - /// file systems must inherit from FileSystemWrapper. - /// - /// This abstract class just wraps the 'real' IFileSystem object passed in to its constructor. - /// - public abstract class FileSystemWrapper : IFileSystem - { - protected FileSystemWrapper(IFileSystem innerFileSystem) - { - InnerFileSystem = innerFileSystem; - } - - internal IFileSystem InnerFileSystem { get; set; } - - public virtual IEnumerable GetDirectories(string path) - { - return InnerFileSystem.GetDirectories(path); - } - - public virtual void DeleteDirectory(string path) - { - InnerFileSystem.DeleteDirectory(path); - } - - public virtual void DeleteDirectory(string path, bool recursive) - { - InnerFileSystem.DeleteDirectory(path, recursive); - } - - public virtual bool DirectoryExists(string path) - { - return InnerFileSystem.DirectoryExists(path); - } - - public virtual void AddFile(string path, Stream stream) - { - InnerFileSystem.AddFile(path, stream); - } - - public virtual void AddFile(string path, Stream stream, bool overrideExisting) - { - InnerFileSystem.AddFile(path, stream, overrideExisting); - } - - public virtual IEnumerable GetFiles(string path) - { - return InnerFileSystem.GetFiles(path); - } - - public virtual IEnumerable GetFiles(string path, string filter) - { - return InnerFileSystem.GetFiles(path, filter); - } - - public virtual Stream OpenFile(string path) - { - return InnerFileSystem.OpenFile(path); - } - - public virtual void DeleteFile(string path) - { - InnerFileSystem.DeleteFile(path); - } - - public virtual bool FileExists(string path) - { - return InnerFileSystem.FileExists(path); - } - - public virtual string GetRelativePath(string fullPathOrUrl) - { - return InnerFileSystem.GetRelativePath(fullPathOrUrl); - } - - public virtual string GetFullPath(string path) - { - return InnerFileSystem.GetFullPath(path); - } - - public virtual string GetUrl(string path) - { - return InnerFileSystem.GetUrl(path); - } - - public virtual DateTimeOffset GetLastModified(string path) - { - return InnerFileSystem.GetLastModified(path); - } - - public virtual DateTimeOffset GetCreated(string path) - { - return InnerFileSystem.GetCreated(path); - } - - public virtual long GetSize(string path) - { - return InnerFileSystem.GetSize(path); - } - - public virtual bool CanAddPhysical => InnerFileSystem.CanAddPhysical; - - public virtual void AddFile(string path, string physicalPath, bool overrideIfExists = true, bool copy = false) - { - InnerFileSystem.AddFile(path, physicalPath, overrideIfExists, copy); - } - } -} diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 52ee2fe0d3..1b4db6acec 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -1,23 +1,25 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; +using System.ComponentModel; using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; -using Umbraco.Extensions; namespace Umbraco.Cms.Core.IO { - public class FileSystems : IFileSystems + /// + /// Provides the system filesystems. + /// + public sealed class FileSystems { - private readonly IServiceProvider _container; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; private readonly IIOHelper _ioHelper; + private GlobalSettings _globalSettings; + private readonly IHostingEnvironment _hostingEnvironment; - private readonly ConcurrentDictionary> _filesystems = new ConcurrentDictionary>(); // wrappers for shadow support private ShadowWrapper _macroPartialFileSystem; @@ -38,93 +40,175 @@ namespace Umbraco.Cms.Core.IO #region Constructor // DI wants a public ctor - public FileSystems(IServiceProvider container, ILogger logger, ILoggerFactory loggerFactory, IIOHelper ioHelper, IOptions globalSettings, IHostingEnvironment hostingEnvironment) + public FileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment) { - _container = container; - _logger = logger; + _logger = loggerFactory.CreateLogger(); _loggerFactory = loggerFactory; _ioHelper = ioHelper; _globalSettings = globalSettings.Value; _hostingEnvironment = hostingEnvironment; } - // for tests only, totally unsafe - internal void Reset() + // Ctor for tests, allows you to set the various filesystems + internal FileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment, + IFileSystem macroPartialFileSystem, + IFileSystem partialViewsFileSystem, + IFileSystem stylesheetFileSystem, + IFileSystem scriptsFileSystem, + IFileSystem mvcViewFileSystem) : this(loggerFactory, ioHelper, globalSettings, hostingEnvironment) { - _shadowWrappers.Clear(); - _filesystems.Clear(); - Volatile.Write(ref _wkfsInitialized, false); - _shadowCurrentId = null; + _macroPartialFileSystem = CreateShadowWrapperInternal(macroPartialFileSystem, "macro-partials"); + _partialViewsFileSystem = CreateShadowWrapperInternal(partialViewsFileSystem, "partials"); + _stylesheetsFileSystem = CreateShadowWrapperInternal(stylesheetFileSystem, "css"); + _scriptsFileSystem = CreateShadowWrapperInternal(scriptsFileSystem, "scripts"); + _mvcViewsFileSystem = CreateShadowWrapperInternal(mvcViewFileSystem, "view"); + // Set initialized to true so the filesystems doesn't get overwritten. + _wkfsInitialized = true; + } - // for tests only, totally unsafe - internal static void ResetShadowId() - { - _shadowCurrentId = null; - } - - // set by the scope provider when taking control of filesystems + /// + /// Used be Scope provider to take control over the filesystems, should never be used for anything else. + /// + [EditorBrowsable(EditorBrowsableState.Never)] public Func IsScoped { get; set; } = () => false; #endregion #region Well-Known FileSystems - /// + /// + /// Gets the macro partials filesystem. + /// public IFileSystem MacroPartialsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _macroPartialFileSystem; } } - /// + /// + /// Gets the partial views filesystem. + /// public IFileSystem PartialViewsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _partialViewsFileSystem; } } - /// + /// + /// Gets the stylesheets filesystem. + /// public IFileSystem StylesheetsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _stylesheetsFileSystem; } } - /// + /// + /// Gets the scripts filesystem. + /// public IFileSystem ScriptsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _scriptsFileSystem; } } - /// + /// + /// Gets the MVC views filesystem. + /// public IFileSystem MvcViewsFileSystem { get { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } + return _mvcViewsFileSystem; } } - private void EnsureWellKnownFileSystems() + /// + /// Sets the stylesheet filesystem. + /// + /// + /// Be careful when using this, the root path and root url must be correct for this to work. + /// + /// The . + /// If the is null + /// Throws exception if the StylesheetFileSystem has already been initialized. + /// Throws exception if full path can't be resolved successfully. + public void SetStylesheetFilesystem(IFileSystem fileSystem) { - LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + if (fileSystem == null) + { + throw new ArgumentNullException(nameof(fileSystem)); + } + + if (_stylesheetsFileSystem != null) + { + throw new InvalidOperationException( + "The StylesheetFileSystem cannot be changed when it's already been initialized."); + } + + // Verify that _rootUrl/_rootPath is correct + // We have to do this because there's a tight coupling + // to the VirtualPath we get with CodeFileDisplay from the frontend. + try + { + var rootPath = fileSystem.GetFullPath("/css/"); + } + catch (UnauthorizedAccessException exception) + { + throw new UnauthorizedAccessException( + "Can't register the stylesheet filesystem, " + + "this is most likely caused by using a PhysicalFileSystem with an incorrect " + + "rootPath/rootUrl. RootPath must be \\wwwroot\\css" + + " and rootUrl must be /css", exception); + } + + _stylesheetsFileSystem = CreateShadowWrapperInternal(fileSystem, "css"); } + private void EnsureWellKnownFileSystems() => LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems); + // need to return something to LazyInitializer.EnsureInitialized // but it does not really matter what we return - here, null private object CreateWellKnownFileSystems() @@ -134,20 +218,28 @@ namespace Umbraco.Cms.Core.IO //TODO this is fucked, why do PhysicalFileSystem has a root url? Mvc views cannot be accessed by url! var macroPartialFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MacroPartials)); var partialViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.PartialViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.PartialViews)); - var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); var scriptsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoScriptsPath), _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoScriptsPath)); var mvcViewsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MvcViews), _hostingEnvironment.ToAbsolute(Constants.SystemDirectories.MvcViews)); _macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "macro-partials", IsScoped); _partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "partials", IsScoped); - _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "css", IsScoped); _scriptsFileSystem = new ShadowWrapper(scriptsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "scripts", IsScoped); _mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "views", IsScoped); + if (_stylesheetsFileSystem == null) + { + var stylesheetsFileSystem = new PhysicalFileSystem(_ioHelper, _hostingEnvironment, logger, + _hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoCssPath), + _hostingEnvironment.ToAbsolute(_globalSettings.UmbracoCssPath)); + + _stylesheetsFileSystem = new ShadowWrapper(stylesheetsFileSystem, _ioHelper, _hostingEnvironment, _loggerFactory, "css", IsScoped); + + _shadowWrappers.Add(_stylesheetsFileSystem); + } + // TODO: do we need a lock here? _shadowWrappers.Add(_macroPartialFileSystem); _shadowWrappers.Add(_partialViewsFileSystem); - _shadowWrappers.Add(_stylesheetsFileSystem); _shadowWrappers.Add(_scriptsFileSystem); _shadowWrappers.Add(_mvcViewsFileSystem); @@ -156,64 +248,6 @@ namespace Umbraco.Cms.Core.IO #endregion - #region Providers - - private readonly Dictionary _paths = new Dictionary(); - - // internal for tests - internal IReadOnlyDictionary Paths => _paths; - private GlobalSettings _globalSettings; - private readonly IHostingEnvironment _hostingEnvironment; - - /// - /// Gets a strongly-typed filesystem. - /// - /// The type of the filesystem. - /// A strongly-typed filesystem of the specified type. - /// - /// Note that any filesystem created by this method *after* shadowing begins, will *not* be - /// shadowing (and an exception will be thrown by the ShadowWrapper). - /// - public TFileSystem GetFileSystem(IFileSystem supporting) - where TFileSystem : FileSystemWrapper - { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - - return (TFileSystem) _filesystems.GetOrAdd(typeof(TFileSystem), _ => new Lazy(() => - { - var typeofTFileSystem = typeof(TFileSystem); - - // path must be unique and not collide with paths used in CreateWellKnownFileSystems - // for our well-known 'media' filesystem we can use the short 'media' path - // for others, put them under 'x/' and use ... something - string path; - if (typeofTFileSystem == typeof(MediaFileSystem)) - { - path = "media"; - } - else - { - lock (_paths) - { - if (!_paths.TryGetValue(typeofTFileSystem, out path)) - { - path = Guid.NewGuid().ToString("N").Substring(0, 6); - while (_paths.ContainsValue(path)) // this can't loop forever, right? - path = Guid.NewGuid().ToString("N").Substring(0, 6); - _paths[typeofTFileSystem] = path; - } - } - - path = "x/" + path; - } - - var shadowWrapper = CreateShadowWrapper(supporting, path); - return _container.CreateInstance(shadowWrapper); - })).Value; - } - - #endregion - #region Shadow // note @@ -221,9 +255,17 @@ namespace Umbraco.Cms.Core.IO // global shadow for the entire application, so great care should be taken to ensure that the // application is *not* doing anything else when using a shadow. + /// + /// Shadows the filesystem, should never be used outside the Scope class. + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] public ICompletable Shadow() { - if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); + if (Volatile.Read(ref _wkfsInitialized) == false) + { + EnsureWellKnownFileSystems(); + } var id = ShadowWrapper.CreateShadowId(_hostingEnvironment); return new ShadowFileSystems(this, id); // will invoke BeginShadow and EndShadow @@ -235,14 +277,18 @@ namespace Umbraco.Cms.Core.IO { // if we throw here, it means that something very wrong happened. if (_shadowCurrentId != null) + { throw new InvalidOperationException("Already shadowing."); + } _shadowCurrentId = id; _logger.LogDebug("Shadow '{ShadowId}'", _shadowCurrentId); - foreach (var wrapper in _shadowWrappers) + foreach (ShadowWrapper wrapper in _shadowWrappers) + { wrapper.Shadow(_shadowCurrentId); + } } } @@ -252,14 +298,19 @@ namespace Umbraco.Cms.Core.IO { // if we throw here, it means that something very wrong happened. if (_shadowCurrentId == null) + { throw new InvalidOperationException("Not shadowing."); + } + if (id != _shadowCurrentId) + { throw new InvalidOperationException("Not the current shadow."); + } _logger.LogDebug("UnShadow '{ShadowId}' {Status}", id, completed ? "complete" : "abort"); var exceptions = new List(); - foreach (var wrapper in _shadowWrappers) + foreach (ShadowWrapper wrapper in _shadowWrappers) { try { @@ -275,17 +326,31 @@ namespace Umbraco.Cms.Core.IO _shadowCurrentId = null; if (exceptions.Count > 0) + { throw new AggregateException(completed ? "Failed to apply all changes (see exceptions)." : "Failed to abort (see exceptions).", exceptions); + } } } - private ShadowWrapper CreateShadowWrapper(IFileSystem filesystem, string shadowPath) + /// + /// Creates a shadow wrapper for a filesystem, should never be used outside UmbracoBuilder or testing + /// + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public IFileSystem CreateShadowWrapper(IFileSystem filesystem, string shadowPath) => CreateShadowWrapperInternal(filesystem, shadowPath); + + private ShadowWrapper CreateShadowWrapperInternal(IFileSystem filesystem, string shadowPath) { lock (_shadowLocker) { var wrapper = new ShadowWrapper(filesystem, _ioHelper, _hostingEnvironment, _loggerFactory, shadowPath,() => IsScoped()); if (_shadowCurrentId != null) + { wrapper.Shadow(_shadowCurrentId); + } + _shadowWrappers.Add(wrapper); return wrapper; } diff --git a/src/Umbraco.Core/IO/IFileSystems.cs b/src/Umbraco.Core/IO/IFileSystems.cs deleted file mode 100644 index 3a169e33a3..0000000000 --- a/src/Umbraco.Core/IO/IFileSystems.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Umbraco.Cms.Core.IO -{ - /// - /// Provides the system filesystems. - /// - public interface IFileSystems - { - /// - /// Gets the macro partials filesystem. - /// - IFileSystem MacroPartialsFileSystem { get; } - - /// - /// Gets the partial views filesystem. - /// - IFileSystem PartialViewsFileSystem { get; } - - /// - /// Gets the stylesheets filesystem. - /// - IFileSystem StylesheetsFileSystem { get; } - - /// - /// Gets the scripts filesystem. - /// - IFileSystem ScriptsFileSystem { get; } - - /// - /// Gets the MVC views filesystem. - /// - IFileSystem MvcViewsFileSystem { get; } - } -} diff --git a/src/Umbraco.Core/IO/IMediaFileSystem.cs b/src/Umbraco.Core/IO/IMediaFileSystem.cs deleted file mode 100644 index eced74482e..0000000000 --- a/src/Umbraco.Core/IO/IMediaFileSystem.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// Provides methods allowing the manipulation of media files. - /// - public interface IMediaFileSystem : IFileSystem - { - /// - /// Delete media files. - /// - /// Files to delete (filesystem-relative paths). - void DeleteMediaFiles(IEnumerable files); - - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// With the old media path scheme, this CREATES a new media path each time it is invoked. - string GetMediaPath(string filename, Guid cuid, Guid puid); - - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// A previous file path. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// In the old, legacy, number-based scheme, we try to re-use the media folder - /// specified by . Else, we CREATE a new one. Each time we are invoked. - string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid); - - /// - /// Stores a media file associated to a property of a content item. - /// - /// The content item owning the media file. - /// The property type owning the media file. - /// The media file name. - /// A stream containing the media bytes. - /// An optional filesystem-relative filepath to the previous media file. - /// The filesystem-relative filepath to the media file. - /// - /// The file is considered "owned" by the content/propertyType. - /// If an is provided then that file (and associated thumbnails if any) is deleted - /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. - /// - string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath); - - /// - /// Copies a media file as a new media file, associated to a property of a content item. - /// - /// The content item owning the copy of the media file. - /// The property type owning the copy of the media file. - /// The filesystem-relative path to the source media file. - /// The filesystem-relative path to the copy of the media file. - string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath); - } -} diff --git a/src/Umbraco.Core/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs index bd48b21a16..2b2696ec5d 100644 --- a/src/Umbraco.Core/IO/IMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/IMediaPathScheme.cs @@ -10,13 +10,13 @@ namespace Umbraco.Cms.Core.IO /// /// Gets a media file path. /// - /// The media filesystem. + /// The media filesystem. /// The (content, media) item unique identifier. /// The property type unique identifier. /// The file name. /// A previous filename. /// The filesystem-relative complete file path. - string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); + string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); /// /// Gets the directory that can be deleted when the file is deleted. @@ -28,6 +28,6 @@ namespace Umbraco.Cms.Core.IO /// The directory, and anything below it, will be deleted. /// Can return null (or empty) when no directory should be deleted. /// - string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath); + string GetDeleteDirectory(MediaFileManager fileSystem, string filepath); } } diff --git a/src/Umbraco.Core/IO/MediaFileManager.cs b/src/Umbraco.Core/IO/MediaFileManager.cs new file mode 100644 index 0000000000..de89d7ca87 --- /dev/null +++ b/src/Umbraco.Core/IO/MediaFileManager.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Strings; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Core.IO +{ + public sealed class MediaFileManager + { + private readonly IMediaPathScheme _mediaPathScheme; + private readonly ILogger _logger; + private readonly IShortStringHelper _shortStringHelper; + + /// + /// Gets the media filesystem. + /// + public IFileSystem FileSystem { get; } + + public MediaFileManager( + IFileSystem fileSystem, + IMediaPathScheme mediaPathScheme, + ILogger logger, + IShortStringHelper shortStringHelper) + { + _mediaPathScheme = mediaPathScheme; + _logger = logger; + _shortStringHelper = shortStringHelper; + FileSystem = fileSystem; + } + + /// + /// Delete media files. + /// + /// Files to delete (filesystem-relative paths). + public void DeleteMediaFiles(IEnumerable files) + { + files = files.Distinct(); + + // kinda try to keep things under control + var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; + + Parallel.ForEach(files, options, file => + { + try + { + if (file.IsNullOrWhiteSpace()) + { + return; + } + + if (FileSystem.FileExists(file) == false) + { + return; + } + + FileSystem.DeleteFile(file); + + var directory = _mediaPathScheme.GetDeleteDirectory(this, file); + if (!directory.IsNullOrWhiteSpace()) + { + FileSystem.DeleteDirectory(directory, true); + } + } + catch (Exception e) + { + _logger.LogError(e, "Failed to delete media file '{File}'.", file); + } + }); + } + + #region Media Path + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// With the old media path scheme, this CREATES a new media path each time it is invoked. + public string GetMediaPath(string filename, Guid cuid, Guid puid) + { + filename = Path.GetFileName(filename); + if (filename == null) + { + throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); + } + + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); + + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); + } + + /// + /// Gets the file path of a media file. + /// + /// The file name. + /// A previous file path. + /// The unique identifier of the content/media owning the file. + /// The unique identifier of the property type owning the file. + /// The filesystem-relative path to the media file. + /// In the old, legacy, number-based scheme, we try to re-use the media folder + /// specified by . Else, we CREATE a new one. Each time we are invoked. + public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) + { + filename = Path.GetFileName(filename); + if (filename == null) + { + throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); + } + + filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); + + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); + } + + #endregion + + #region Associated Media Files + + /// + /// Stores a media file associated to a property of a content item. + /// + /// The content item owning the media file. + /// The property type owning the media file. + /// The media file name. + /// A stream containing the media bytes. + /// An optional filesystem-relative filepath to the previous media file. + /// The filesystem-relative filepath to the media file. + /// + /// The file is considered "owned" by the content/propertyType. + /// If an is provided then that file (and associated thumbnails if any) is deleted + /// before the new file is saved, and depending on the media path scheme, the folder may be reused for the new file. + /// + public string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (filename == null) + { + throw new ArgumentNullException(nameof(filename)); + } + + if (string.IsNullOrWhiteSpace(filename)) + { + throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); + } + + if (filestream == null) + { + throw new ArgumentNullException(nameof(filestream)); + } + + // clear the old file, if any + if (string.IsNullOrWhiteSpace(oldpath) == false) + { + FileSystem.DeleteFile(oldpath); + } + + // get the filepath, store the data + // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme + var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); + FileSystem.AddFile(filepath, filestream); + return filepath; + } + + /// + /// Copies a media file as a new media file, associated to a property of a content item. + /// + /// The content item owning the copy of the media file. + /// The property type owning the copy of the media file. + /// The filesystem-relative path to the source media file. + /// The filesystem-relative path to the copy of the media file. + public string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath) + { + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (sourcepath == null) + { + throw new ArgumentNullException(nameof(sourcepath)); + } + + if (string.IsNullOrWhiteSpace(sourcepath)) + { + throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); + } + + // ensure we have a file to copy + if (FileSystem.FileExists(sourcepath) == false) + { + return null; + } + + // get the filepath + var filename = Path.GetFileName(sourcepath); + var filepath = GetMediaPath(filename, content.Key, propertyType.Key); + FileSystem.CopyFile(sourcepath, filepath); + return filepath; + } + + #endregion + } +} diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs deleted file mode 100644 index 6d598941c5..0000000000 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Strings; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Core.IO -{ - /// - /// A custom file system provider for media - /// - public class MediaFileSystem : FileSystemWrapper, IMediaFileSystem - { - private readonly IMediaPathScheme _mediaPathScheme; - private readonly ILogger _logger; - private readonly IShortStringHelper _shortStringHelper; - - /// - /// Initializes a new instance of the class. - /// - public MediaFileSystem(IFileSystem innerFileSystem, IMediaPathScheme mediaPathScheme, ILogger logger, IShortStringHelper shortStringHelper) - : base(innerFileSystem) - { - _mediaPathScheme = mediaPathScheme; - _logger = logger; - _shortStringHelper = shortStringHelper; - } - - /// - public void DeleteMediaFiles(IEnumerable files) - { - files = files.Distinct(); - - // kinda try to keep things under control - var options = new ParallelOptions { MaxDegreeOfParallelism = 20 }; - - Parallel.ForEach(files, options, file => - { - try - { - if (file.IsNullOrWhiteSpace()) return; - if (FileExists(file) == false) return; - DeleteFile(file); - - var directory = _mediaPathScheme.GetDeleteDirectory(this, file); - if (!directory.IsNullOrWhiteSpace()) - DeleteDirectory(directory, true); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to delete media file '{File}'.", file); - } - }); - } - - #region Media Path - - /// - public string GetMediaPath(string filename, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); - } - - /// - public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); - } - - #endregion - - #region Associated Media Files - - /// - public string StoreFile(IContentBase content, IPropertyType propertyType, string filename, Stream filestream, string oldpath) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (filename == null) throw new ArgumentNullException(nameof(filename)); - if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename)); - if (filestream == null) throw new ArgumentNullException(nameof(filestream)); - - // clear the old file, if any - if (string.IsNullOrWhiteSpace(oldpath) == false) - DeleteFile(oldpath); - - // get the filepath, store the data - // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme - var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); - AddFile(filepath, filestream); - return filepath; - } - - /// - public string CopyFile(IContentBase content, IPropertyType propertyType, string sourcepath) - { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (propertyType == null) throw new ArgumentNullException(nameof(propertyType)); - if (sourcepath == null) throw new ArgumentNullException(nameof(sourcepath)); - if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath)); - - // ensure we have a file to copy - if (FileExists(sourcepath) == false) return null; - - // get the filepath - var filename = Path.GetFileName(sourcepath); - var filepath = GetMediaPath(filename, content.Key, propertyType.Key); - this.CopyFile(sourcepath, filepath); - return filepath; - } - - #endregion - } -} diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index a23468d5ac..f3c15a13d6 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class CombinedGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name @@ -23,6 +23,6 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => Path.GetDirectoryName(filepath); + public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) => Path.GetDirectoryName(filepath); } } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs index ea23bf0145..d1cea0c46b 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private bool _folderCounterInitialized; /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { string directory; if (previous != null) @@ -33,11 +33,11 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes var pos = previous.IndexOf(sep, StringComparison.Ordinal); var s = pos > 0 ? previous.Substring(0, pos) : null; - directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileSystem); + directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileManager.FileSystem); } else { - directory = GetNextDirectory(fileSystem); + directory = GetNextDirectory(fileManager.FileSystem); } if (directory == null) @@ -47,7 +47,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) + public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs index 2fffd4e7d6..203c8aaed8 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs @@ -12,13 +12,13 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class TwoGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { return Path.Combine(itemGuid.ToString("N"), propertyGuid.ToString("N"), filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) + public string GetDeleteDirectory(MediaFileManager fileManager, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs index faf94fb6e6..55a9cd4574 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private const int DirectoryLength = 8; /// - public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid); var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength); @@ -32,6 +32,6 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes /// race conditions. We'd need to implement locks in for /// this. /// - public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) => null; + public string GetDeleteDirectory(MediaFileManager fileManager, string filepath) => null; } } diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index dc5529d25f..32c8ddd98d 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -13,18 +13,18 @@ namespace Umbraco.Cms.Core.Media /// public class UploadAutoFillProperties { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ILogger _logger; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IImageDimensionExtractor _imageDimensionExtractor; public UploadAutoFillProperties( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, ILogger logger, IImageUrlGenerator imageUrlGenerator, IImageDimensionExtractor imageDimensionExtractor) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _imageUrlGenerator = imageUrlGenerator ?? throw new ArgumentNullException(nameof(imageUrlGenerator)); _imageDimensionExtractor = imageDimensionExtractor ?? throw new ArgumentNullException(nameof(imageDimensionExtractor)); @@ -69,7 +69,7 @@ namespace Umbraco.Cms.Core.Media // if anything goes wrong, just reset the properties try { - using (var filestream = _mediaFileSystem.OpenFile(filepath)) + using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath)) { var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period); var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null; diff --git a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs index 85da93fb31..4a19fbe530 100644 --- a/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/UserMapDefinition.cs @@ -28,12 +28,12 @@ namespace Umbraco.Cms.Core.Models.Mapping private readonly ActionCollection _actions; private readonly AppCaches _appCaches; private readonly GlobalSettings _globalSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IShortStringHelper _shortStringHelper; private readonly IImageUrlGenerator _imageUrlGenerator; public UserMapDefinition(ILocalizedTextService textService, IUserService userService, IEntityService entityService, ISectionService sectionService, - AppCaches appCaches, ActionCollection actions, IOptions globalSettings, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, + AppCaches appCaches, ActionCollection actions, IOptions globalSettings, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IImageUrlGenerator imageUrlGenerator) { _sectionService = sectionService; @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Core.Models.Mapping _actions = actions; _appCaches = appCaches; _globalSettings = globalSettings.Value; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _shortStringHelper = shortStringHelper; _imageUrlGenerator = imageUrlGenerator; } @@ -283,7 +283,7 @@ namespace Umbraco.Cms.Core.Models.Mapping private void Map(IUser source, UserDisplay target, MapperContext context) { target.AvailableCultures = _textService.GetSupportedCultures().ToDictionary(x => x.Name, x => x.DisplayName); - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.CalculatedStartContentIds = GetStartNodes(source.CalculateContentStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Document, "content/contentRoot", context); target.CalculatedStartMediaIds = GetStartNodes(source.CalculateMediaStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Media, "media/mediaRoot", context); target.CreateDate = source.CreateDate; @@ -314,7 +314,7 @@ namespace Umbraco.Cms.Core.Models.Mapping //Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost //Alternatively, if this is annoying the back office UI would need to be updated to request the avatars for the list of users separately so it doesn't look //like the load time is waiting. - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); @@ -333,7 +333,7 @@ namespace Umbraco.Cms.Core.Models.Mapping private void Map(IUser source, UserDetail target, MapperContext context) { target.AllowedSections = source.AllowedSections; - target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString(); target.Email = source.Email; target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash(); diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 6fb74c3b86..dd96faf298 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -21,11 +21,11 @@ namespace Umbraco.Cms.Core.Models /// /// /// - /// + /// /// /// A list of 5 different sized avatar URLs /// - public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, IMediaFileSystem mediaFileSystem, IImageUrlGenerator imageUrlGenerator) + public static string[] GetUserAvatarUrls(this IUser user, IAppCache cache, MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) { // If FIPS is required, never check the Gravatar service as it only supports MD5 hashing. // Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Core.Models } //use the custom avatar - var avatarUrl = mediaFileSystem.GetUrl(user.Avatar); + var avatarUrl = mediaFileManager.FileSystem.GetUrl(user.Avatar); return new[] { imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = ImageCropMode.Crop, Width = 30, Height = 30 }), diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs index b00b9a4985..edb8033f3d 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.FileSystems.cs @@ -19,10 +19,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection * Create an implementation of IFileSystem and register it as the underlying filesystem for * MediaFileSystem with the following extension on composition. * - * composition.SetMediaFileSystem(factory => FactoryMethodToReturnYourImplementation()) - * - * Alternatively you can just register an Implementation of IMediaFileSystem, however the - * extension above ensures that your IFileSystem implementation is wrapped by the "ShadowWrapper". + * builder.SetMediaFileSystem(factory => FactoryMethodToReturnYourImplementation()) * * WHAT IS SHADOWING * ----------------- @@ -37,23 +34,17 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection internal static IUmbracoBuilder AddFileSystems(this IUmbracoBuilder builder) { // register FileSystems, which manages all filesystems - // it needs to be registered (not only the interface) because it provides additional - // functionality eg for scoping, and is injected in the scope provider - whereas the - // interface is really for end-users to get access to filesystems. - builder.Services.AddUnique(factory => factory.CreateInstance(factory)); - - // register IFileSystems, which gives access too all filesystems - builder.Services.AddUnique(factory => factory.GetRequiredService()); + builder.Services.AddUnique(); // register the scheme for media paths builder.Services.AddUnique(); builder.SetMediaFileSystem(factory => { - var ioHelper = factory.GetRequiredService(); - var hostingEnvironment = factory.GetRequiredService(); - var logger = factory.GetRequiredService>(); - var globalSettings = factory.GetRequiredService>().Value; + IIOHelper ioHelper = factory.GetRequiredService(); + IHostingEnvironment hostingEnvironment = factory.GetRequiredService(); + ILogger logger = factory.GetRequiredService>(); + GlobalSettings globalSettings = factory.GetRequiredService>().Value; var rootPath = hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPath); var rootUrl = hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath); diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs index 5a6c8fe8f2..cbbaa6a3e0 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Uniques.cs @@ -1,5 +1,6 @@ using System; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Dictionary; using Umbraco.Cms.Core.IO; @@ -109,20 +110,48 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection } /// - /// Sets the underlying media filesystem. + /// Sets the filesystem used by the MediaFileManager /// /// A builder. - /// A filesystem factory. - /// - /// Using this helper will ensure that your IFileSystem implementation is wrapped by the ShadowWrapper - /// - public static void SetMediaFileSystem(this IUmbracoBuilder builder, Func filesystemFactory) - => builder.Services.AddUnique(factory => + /// Factory method to create an IFileSystem implementation used in the MediaFileManager + public static void SetMediaFileSystem(this IUmbracoBuilder builder, + Func filesystemFactory) => builder.Services.AddUnique( + provider => { - var fileSystems = factory.GetRequiredService(); - return fileSystems.GetFileSystem(filesystemFactory(factory)); + IFileSystem filesystem = filesystemFactory(provider); + // We need to use the Filesystems to create a shadow wrapper, + // because shadow wrapper requires the IsScoped delegate from the FileSystems. + // This is used by the scope provider when taking control of the filesystems. + FileSystems fileSystems = provider.GetRequiredService(); + IFileSystem shadow = fileSystems.CreateShadowWrapper(filesystem, "media"); + + return provider.CreateInstance(shadow); }); + /// + /// Register FileSystems with a method to configure the . + /// + /// A builder. + /// Method that configures the . + /// Throws exception if is null. + /// Throws exception if full path can't be resolved successfully. + public static void ConfigureFileSystems(this IUmbracoBuilder builder, + Action configure) + { + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + builder.Services.AddUnique( + provider => + { + FileSystems fileSystems = provider.CreateInstance(); + configure(provider, fileSystems); + return fileSystems; + }); + } + /// /// Sets the log viewer. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs index d13e4c6945..47123b790e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewMacroRepository.cs @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { internal class PartialViewMacroRepository : PartialViewRepository, IPartialViewMacroRepository { - public PartialViewMacroRepository(IFileSystems fileSystems, IIOHelper ioHelper) + public PartialViewMacroRepository(FileSystems fileSystems, IIOHelper ioHelper) : base(fileSystems.MacroPartialsFileSystem, ioHelper) { } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs index eee199e252..a1d2b218c4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/PartialViewRepository.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { private readonly IIOHelper _ioHelper; - public PartialViewRepository(IFileSystems fileSystems, IIOHelper ioHelper) + public PartialViewRepository(FileSystems fileSystems, IIOHelper ioHelper) : base(fileSystems.PartialViewsFileSystem) { _ioHelper = ioHelper; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs index 161b7a90a8..77b8ac9e20 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ScriptRepository.cs @@ -19,7 +19,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IIOHelper _ioHelper; private readonly GlobalSettings _globalSettings; - public ScriptRepository(IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public ScriptRepository(FileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) : base(fileSystems.ScriptsFileSystem) { _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs index 03c729b51c..ac255890bd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/StylesheetRepository.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IIOHelper _ioHelper; private readonly GlobalSettings _globalSettings; - public StylesheetRepository(ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) + public StylesheetRepository(ILogger logger, FileSystems fileSystems, IIOHelper ioHelper, IOptions globalSettings) : base(fileSystems.StylesheetsFileSystem) { _logger = logger; diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index ec54a6bef8..3aad92a012 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private readonly IFileSystem _viewsFileSystem; private readonly ViewHelper _viewHelper; - public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IFileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper) + public TemplateRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, FileSystems fileSystems, IIOHelper ioHelper, IShortStringHelper shortStringHelper) : base(scopeAccessor, cache, logger) { _ioHelper = ioHelper; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index a0bbf2a7fe..db895444c4 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -30,7 +30,7 @@ namespace Umbraco.Cms.Core.PropertyEditors INotificationHandler, INotificationHandler, INotificationHandler { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly UploadAutoFillProperties _uploadAutoFillProperties; private readonly IDataTypeService _dataTypeService; @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Core.PropertyEditors public FileUploadPropertyEditor( ILoggerFactory loggerFactory, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Core.PropertyEditors IContentService contentService) : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value; _dataTypeService = dataTypeService; _localizationService = localizationService; @@ -66,7 +66,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// The corresponding property value editor. protected override IDataValueEditor CreateValueEditor() { - var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileSystem, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Options.Create(_contentSettings), JsonSerializer); + var editor = new FileUploadPropertyValueEditor(Attribute, _mediaFileManager, _dataTypeService, _localizationService, _localizedTextService, ShortStringHelper, Options.Create(_contentSettings), JsonSerializer); editor.Validators.Add(new UploadFileTypeValidator(_localizedTextService, Options.Create(_contentSettings))); return editor; } @@ -115,12 +115,12 @@ namespace Umbraco.Cms.Core.PropertyEditors //check if the published value contains data and return it var propVal = propertyValue.PublishedValue; if (propVal != null && propVal is string str1 && !str1.IsNullOrWhiteSpace()) - yield return _mediaFileSystem.GetRelativePath(str1); + yield return _mediaFileManager.FileSystem.GetRelativePath(str1); //check if the edited value contains data and return it propVal = propertyValue.EditedValue; if (propVal != null && propVal is string str2 && !str2.IsNullOrWhiteSpace()) - yield return _mediaFileSystem.GetRelativePath(str2); + yield return _mediaFileManager.FileSystem.GetRelativePath(str2); } } @@ -142,9 +142,9 @@ namespace Umbraco.Cms.Core.PropertyEditors continue; } - var sourcePath = _mediaFileSystem.GetRelativePath(str); - var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); - notification.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); + var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(str); + var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath); + notification.Copy.SetValue(property.Alias, _mediaFileManager.FileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } } @@ -165,7 +165,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); - _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + _mediaFileManager.DeleteMediaFiles(filePathsToDelete); } public void Handle(MediaSavingNotification notification) @@ -194,7 +194,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (string.IsNullOrWhiteSpace(svalue)) _uploadAutoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); + _uploadAutoFillProperties.Populate(model, autoFillConfig, _mediaFileManager.FileSystem.GetRelativePath(svalue), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs index 3f892dd000..b30a0b971d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -19,12 +19,12 @@ namespace Umbraco.Cms.Core.PropertyEditors /// internal class FileUploadPropertyValueEditor : DataValueEditor { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; public FileUploadPropertyValueEditor( DataEditorAttribute attribute, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.PropertyEditors IJsonSerializer jsonSerializer) : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings)); } @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Core.PropertyEditors { var currentPath = currentValue as string; if (!currentPath.IsNullOrWhiteSpace()) - currentPath = _mediaFileSystem.GetRelativePath(currentPath); + currentPath = _mediaFileManager.FileSystem.GetRelativePath(currentPath); string editorFile = null; if (editorValue.Value != null) @@ -84,7 +84,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // value is unchanged. if (string.IsNullOrWhiteSpace(editorFile) && string.IsNullOrWhiteSpace(currentPath) == false) { - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); return null; // clear } @@ -100,11 +100,11 @@ namespace Umbraco.Cms.Core.PropertyEditors // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); // update json and return if (editorFile == null) return null; - return filepath == null ? string.Empty : _mediaFileSystem.GetUrl(filepath); + return filepath == null ? string.Empty : _mediaFileManager.FileSystem.GetUrl(filepath); } @@ -118,14 +118,14 @@ namespace Umbraco.Cms.Core.PropertyEditors // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileSystem.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { // TODO: Here it would make sense to do the auto-fill properties stuff but the API doesn't allow us to do that right // since we'd need to be able to return values for other properties from these methods - _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! + _mediaFileManager.FileSystem.AddFile(filepath, filestream, true); // must overwrite! } return filepath; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index e138e98618..fe66a31e13 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.PropertyEditors INotificationHandler, INotificationHandler, INotificationHandler { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IDataTypeService _dataTypeService; private readonly IIOHelper _ioHelper; @@ -50,7 +50,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// public ImageCropperPropertyEditor( ILoggerFactory loggerFactory, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IDataTypeService dataTypeService, ILocalizationService localizationService, @@ -62,7 +62,7 @@ namespace Umbraco.Cms.Core.PropertyEditors IContentService contentService) : base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileManager ?? throw new ArgumentNullException(nameof(mediaFileManager)); _contentSettings = contentSettings.Value ?? throw new ArgumentNullException(nameof(contentSettings)); _dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService)); _ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper)); @@ -86,7 +86,7 @@ namespace Umbraco.Cms.Core.PropertyEditors /// Creates the corresponding property value editor. /// /// The corresponding property value editor. - protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, LoggerFactory.CreateLogger(), _mediaFileSystem, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _contentSettings, JsonSerializer); + protected override IDataValueEditor CreateValueEditor() => new ImageCropperPropertyValueEditor(Attribute, LoggerFactory.CreateLogger(), _mediaFileManager, DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, _contentSettings, JsonSerializer); /// /// Creates the corresponding preValue editor. @@ -151,11 +151,11 @@ namespace Umbraco.Cms.Core.PropertyEditors { //check if the published value contains data and return it var src = GetFileSrcFromPropertyValue(propertyValue.PublishedValue, out var _); - if (src != null) yield return _mediaFileSystem.GetRelativePath(src); + if (src != null) yield return _mediaFileManager.FileSystem.GetRelativePath(src); //check if the edited value contains data and return it src = GetFileSrcFromPropertyValue(propertyValue.EditedValue, out var _); - if (src != null) yield return _mediaFileSystem.GetRelativePath(src); + if (src != null) yield return _mediaFileManager.FileSystem.GetRelativePath(src); } } @@ -174,7 +174,7 @@ namespace Umbraco.Cms.Core.PropertyEditors deserializedValue = GetJObject(str, true); if (deserializedValue?["src"] == null) return null; var src = deserializedValue["src"].Value(); - return relative ? _mediaFileSystem.GetRelativePath(src) : src; + return relative ? _mediaFileManager.FileSystem.GetRelativePath(src) : src; } /// @@ -198,9 +198,9 @@ namespace Umbraco.Cms.Core.PropertyEditors { continue; } - var sourcePath = _mediaFileSystem.GetRelativePath(src); - var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath); - jo["src"] = _mediaFileSystem.GetUrl(copyPath); + var sourcePath = _mediaFileManager.FileSystem.GetRelativePath(src); + var copyPath = _mediaFileManager.CopyFile(notification.Copy, property.PropertyType, sourcePath); + jo["src"] = _mediaFileManager.FileSystem.GetUrl(copyPath); notification.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment); isUpdated = true; } @@ -221,7 +221,7 @@ namespace Umbraco.Cms.Core.PropertyEditors private void DeleteContainedFiles(IEnumerable deletedEntities) { var filePathsToDelete = ContainedFilePaths(deletedEntities); - _mediaFileSystem.DeleteMediaFiles(filePathsToDelete); + _mediaFileManager.DeleteMediaFiles(filePathsToDelete); } public void Handle(MediaSavingNotification notification) @@ -282,7 +282,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (src == null) _autoFillProperties.Reset(model, autoFillConfig, pvalue.Culture, pvalue.Segment); else - _autoFillProperties.Populate(model, autoFillConfig, _mediaFileSystem.GetRelativePath(src), pvalue.Culture, pvalue.Segment); + _autoFillProperties.Populate(model, autoFillConfig, _mediaFileManager.FileSystem.GetRelativePath(src), pvalue.Culture, pvalue.Segment); } } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs index 5cbbdf8e31..19fceb21e5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -24,13 +24,13 @@ namespace Umbraco.Cms.Core.PropertyEditors internal class ImageCropperPropertyValueEditor : DataValueEditor // TODO: core vs web? { private readonly ILogger _logger; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; public ImageCropperPropertyValueEditor( DataEditorAttribute attribute, ILogger logger, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileSystem, IDataTypeService dataTypeService, ILocalizationService localizationService, ILocalizedTextService localizedTextService, @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Core.PropertyEditors : base(dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer, attribute) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _contentSettings = contentSettings ?? throw new ArgumentNullException(nameof(contentSettings)); } @@ -97,7 +97,7 @@ namespace Umbraco.Cms.Core.PropertyEditors _logger.LogWarning(ex, "Could not parse current db value to a JObject."); } if (string.IsNullOrWhiteSpace(currentPath) == false) - currentPath = _mediaFileSystem.GetRelativePath(currentPath); + currentPath = _mediaFileManager.FileSystem.GetRelativePath(currentPath); // get the new json and path JObject editorJson = null; @@ -130,7 +130,7 @@ namespace Umbraco.Cms.Core.PropertyEditors // value is unchanged. if (string.IsNullOrWhiteSpace(editorFile) && string.IsNullOrWhiteSpace(currentPath) == false) { - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); return null; // clear } @@ -146,11 +146,11 @@ namespace Umbraco.Cms.Core.PropertyEditors // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) - _mediaFileSystem.DeleteFile(currentPath); + _mediaFileManager.FileSystem.DeleteFile(currentPath); // update json and return if (editorJson == null) return null; - editorJson["src"] = filepath == null ? string.Empty : _mediaFileSystem.GetUrl(filepath); + editorJson["src"] = filepath == null ? string.Empty : _mediaFileManager.FileSystem.GetUrl(filepath); return editorJson.ToString(); } @@ -163,14 +163,14 @@ namespace Umbraco.Cms.Core.PropertyEditors // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileSystem.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { // TODO: Here it would make sense to do the auto-fill properties stuff but the API doesn't allow us to do that right // since we'd need to be able to return values for other properties from these methods - _mediaFileSystem.AddFile(filepath, filestream, true); // must overwrite! + _mediaFileManager.FileSystem.AddFile(filepath, filestream, true); // must overwrite! } return filepath; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index 94140d00f2..f92b2911dd 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -27,21 +27,30 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IHostingEnvironment _hostingEnvironment; private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IShortStringHelper _shortStringHelper; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly IJsonSerializer _serializer; const string TemporaryImageDataAttribute = "data-tmpimg"; - public RichTextEditorPastedImages(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IHostingEnvironment hostingEnvironment, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, IPublishedUrlProvider publishedUrlProvider, IJsonSerializer serializer) + public RichTextEditorPastedImages( + IUmbracoContextAccessor umbracoContextAccessor, + ILogger logger, + IHostingEnvironment hostingEnvironment, + IMediaService mediaService, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + MediaFileManager mediaFileManager, + IShortStringHelper shortStringHelper, + IPublishedUrlProvider publishedUrlProvider, + IJsonSerializer serializer) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _hostingEnvironment = hostingEnvironment; _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _shortStringHelper = shortStringHelper; _publishedUrlProvider = publishedUrlProvider; _serializer = serializer; @@ -98,7 +107,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (fileStream == null) throw new InvalidOperationException("Could not acquire file stream"); using (fileStream) { - mediaFile.SetValue(_mediaFileSystem, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); + mediaFile.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); } _mediaService.Save(mediaFile, userId); diff --git a/src/Umbraco.Infrastructure/Scoping/Scope.cs b/src/Umbraco.Infrastructure/Scoping/Scope.cs index 386bd7c06e..9d58cab2b4 100644 --- a/src/Umbraco.Infrastructure/Scoping/Scope.cs +++ b/src/Umbraco.Infrastructure/Scoping/Scope.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Core.Scoping { private readonly ScopeProvider _scopeProvider; private readonly CoreDebugSettings _coreDebugSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IEventAggregator _eventAggregator; private readonly ILogger _logger; @@ -55,7 +55,7 @@ namespace Umbraco.Cms.Core.Scoping private Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -71,7 +71,7 @@ namespace Umbraco.Cms.Core.Scoping { _scopeProvider = scopeProvider; _coreDebugSettings = coreDebugSettings; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _eventAggregator = eventAggregator; _logger = logger; @@ -163,7 +163,7 @@ namespace Umbraco.Cms.Core.Scoping public Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -175,14 +175,14 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } // initializes a new scope in a nested scopes chain, with its parent public Scope( ScopeProvider scopeProvider, CoreDebugSettings coreDebugSettings, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IEventAggregator eventAggregator, ILogger logger, FileSystems fileSystems, @@ -193,7 +193,7 @@ namespace Umbraco.Cms.Core.Scoping bool? scopeFileSystems = null, bool callContext = false, bool autoComplete = false) - : this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) + : this(scopeProvider, coreDebugSettings, mediaFileManager, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete) { } public Guid InstanceId { get; } = Guid.NewGuid(); @@ -397,7 +397,7 @@ namespace Umbraco.Cms.Core.Scoping return ParentScope.Events; } - return _eventDispatcher ??= new QueuingEventDispatcher(_mediaFileSystem); + return _eventDispatcher ??= new QueuingEventDispatcher(_mediaFileManager); } } diff --git a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs index c9e8b39947..cc365beb9b 100644 --- a/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs +++ b/src/Umbraco.Infrastructure/Scoping/ScopeProvider.cs @@ -29,19 +29,19 @@ namespace Umbraco.Cms.Core.Scoping private readonly IRequestCache _requestCache; private readonly FileSystems _fileSystems; private readonly CoreDebugSettings _coreDebugSettings; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private static readonly AsyncLocal> s_scopeStack = new AsyncLocal>(); private static readonly AsyncLocal> s_scopeContextStack = new AsyncLocal>(); private static readonly string s_scopeItemKey = typeof(Scope).FullName; private static readonly string s_contextItemKey = typeof(ScopeProvider).FullName; private readonly IEventAggregator _eventAggregator; - public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) + public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions coreDebugSettings, MediaFileManager mediaFileManager, ILogger logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator) { DatabaseFactory = databaseFactory; _fileSystems = fileSystems; _coreDebugSettings = coreDebugSettings.Value; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _logger = logger; _loggerFactory = loggerFactory; _requestCache = requestCache; @@ -381,7 +381,7 @@ namespace Umbraco.Cms.Core.Scoping RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified, IEventDispatcher eventDispatcher = null, bool? scopeFileSystems = null) - => new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); + => new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems); /// public void AttachScope(IScope other, bool callContext = false) @@ -460,7 +460,7 @@ namespace Umbraco.Cms.Core.Scoping { IScopeContext ambientContext = AmbientContext; ScopeContext newContext = ambientContext == null ? new ScopeContext() : null; - var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var scope = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); // assign only if scope creation did not throw! PushAmbientScope(scope); if (newContext != null) @@ -470,7 +470,7 @@ namespace Umbraco.Cms.Core.Scoping return scope; } - var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); + var nested = new Scope(this, _coreDebugSettings, _mediaFileManager, _eventAggregator, _loggerFactory.CreateLogger(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete); PushAmbientScope(nested); return nested; } diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 831cbb1b01..3f12d94cf1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -29,16 +29,16 @@ namespace Umbraco.Cms.Core.Services.Implement private readonly IEntityRepository _entityRepository; private readonly IShortStringHelper _shortStringHelper; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; #region Constructors - public MediaService(IScopeProvider provider, IMediaFileSystem mediaFileSystem, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, + public MediaService(IScopeProvider provider, MediaFileManager mediaFileManager, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMediaRepository mediaRepository, IAuditRepository auditRepository, IMediaTypeRepository mediaTypeRepository, IEntityRepository entityRepository, IShortStringHelper shortStringHelper) : base(provider, loggerFactory, eventMessagesFactory) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _mediaRepository = mediaRepository; _auditRepository = auditRepository; _mediaTypeRepository = mediaTypeRepository; @@ -1183,12 +1183,12 @@ namespace Umbraco.Cms.Core.Services.Implement public Stream GetMediaFileContentStream(string filepath) { - if (_mediaFileSystem.FileExists(filepath) == false) + if (_mediaFileManager.FileSystem.FileExists(filepath) == false) return null; try { - return _mediaFileSystem.OpenFile(filepath); + return _mediaFileManager.FileSystem.OpenFile(filepath); } catch { @@ -1198,17 +1198,17 @@ namespace Umbraco.Cms.Core.Services.Implement public void SetMediaFileContent(string filepath, Stream stream) { - _mediaFileSystem.AddFile(filepath, stream, true); + _mediaFileManager.FileSystem.AddFile(filepath, stream, true); } public void DeleteMediaFile(string filepath) { - _mediaFileSystem.DeleteFile(filepath); + _mediaFileManager.FileSystem.DeleteFile(filepath); } public long GetMediaFileSize(string filepath) { - return _mediaFileSystem.GetSize(filepath); + return _mediaFileManager.FileSystem.GetSize(filepath); } #endregion diff --git a/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs b/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs new file mode 100644 index 0000000000..d9d5de1ebd --- /dev/null +++ b/src/Umbraco.Tests.Common/TestHelpers/FileSystemsCreator.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; + +namespace Umbraco.Cms.Tests.Common.TestHelpers +{ + public static class FileSystemsCreator + { + /// + /// Create an instance FileSystems where you can set the individual filesystems. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static FileSystems CreateTestFileSystems( + ILoggerFactory loggerFactory, + IIOHelper ioHelper, + IOptions globalSettings, + IHostingEnvironment hostingEnvironment, + IFileSystem macroPartialFileSystem, + IFileSystem partialViewsFileSystem, + IFileSystem stylesheetFileSystem, + IFileSystem scriptsFileSystem, + IFileSystem mvcViewFileSystem) => + new FileSystems(loggerFactory, ioHelper, globalSettings, hostingEnvironment, macroPartialFileSystem, + partialViewsFileSystem, stylesheetFileSystem, scriptsFileSystem, mvcViewFileSystem); + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs index 358a5fb350..ce361e59b6 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/FileSystemsTests.cs @@ -19,52 +19,35 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO public class FileSystemsTests : UmbracoIntegrationTest { [Test] - public void Can_Get_MediaFileSystem() + public void Can_Get_MediaFileManager() { - IMediaFileSystem fileSystem = GetRequiredService(); + MediaFileManager fileSystem = GetRequiredService(); Assert.NotNull(fileSystem); } [Test] - public void Can_Get_IMediaFileSystem() + public void MediaFileManager_Is_Singleton() { - IMediaFileSystem fileSystem = GetRequiredService(); - Assert.NotNull(fileSystem); - } - - [Test] - public void IMediaFileSystem_Is_Singleton() - { - IMediaFileSystem fileSystem1 = GetRequiredService(); - IMediaFileSystem fileSystem2 = GetRequiredService(); - Assert.AreSame(fileSystem1, fileSystem2); - } - - [Test] - public void Can_Unwrap_MediaFileSystem() - { - IMediaFileSystem fileSystem = GetRequiredService(); - IFileSystem unwrapped = fileSystem.Unwrap(); - Assert.IsNotNull(unwrapped); - var physical = unwrapped as PhysicalFileSystem; - Assert.IsNotNull(physical); + MediaFileManager fileManager1 = GetRequiredService(); + MediaFileManager fileManager2 = GetRequiredService(); + Assert.AreSame(fileManager1, fileManager2); } [Test] public void Can_Delete_MediaFiles() { - IMediaFileSystem fs = GetRequiredService(); - var ms = new MemoryStream(Encoding.UTF8.GetBytes("test")); - string virtPath = fs.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); - fs.AddFile(virtPath, ms); + MediaFileManager mediaFileManager = GetRequiredService(); + var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("test")); + string virtualPath = mediaFileManager.GetMediaPath("file.txt", Guid.NewGuid(), Guid.NewGuid()); + mediaFileManager.FileSystem.AddFile(virtualPath, memoryStream); // ~/media/1234/file.txt exists IHostingEnvironment hostingEnvironment = GetRequiredService(); - string physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtPath)); + string physPath = hostingEnvironment.MapPathWebRoot(Path.Combine("media", virtualPath)); Assert.IsTrue(File.Exists(physPath)); // ~/media/1234/file.txt is gone - fs.DeleteMediaFiles(new[] { virtPath }); + mediaFileManager.DeleteMediaFiles(new[] { virtualPath }); Assert.IsFalse(File.Exists(physPath)); IMediaPathScheme scheme = GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs index 276d7a267e..751430a824 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/IO/ShadowFileSystemTests.cs @@ -35,14 +35,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO public void SetUp() { ClearFiles(HostingEnvironment); - FileSystems.ResetShadowId(); } [TearDown] public void TearDown() { ClearFiles(HostingEnvironment); - FileSystems.ResetShadowId(); } private void ClearFiles(IHostingEnvironment hostingEnvironment) @@ -383,13 +381,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO Assert.IsFalse(File.Exists(path + "/ShadowTests/sub/sub/f2.txt")); } - class FS : FileSystemWrapper - { - public FS(IFileSystem innerFileSystem) - : base(innerFileSystem) - { } - } - [Test] public void ShadowScopeComplete() { @@ -403,11 +394,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), loggerFactory, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem(phy); - var sw = (ShadowWrapper) fs.InnerFileSystem; + var fileSystems = new FileSystems(loggerFactory, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = (ShadowWrapper)fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); @@ -439,8 +429,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var typedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/x")); Assert.IsNotNull(typedDir); dirs = Directory.GetDirectories(typedDir); - var suid = fileSystems.Paths[typeof(FS)]; - var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/" + suid)); // this is where files go + var scopedDir = dirs.FirstOrDefault(x => x.Replace('\\', '/').EndsWith("/" + shadowPath)); // this is where files go Assert.IsNotNull(scopedDir); scope.Dispose(); scopedFileSystems = false; @@ -496,11 +485,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem( phy); - var sw = (ShadowWrapper) fs.InnerFileSystem; + var fileSystems = new FileSystems(NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); @@ -548,11 +536,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.IO var phy = new PhysicalFileSystem(IOHelper, HostingEnvironment, Logger, path, "ignore"); - var container = Mock.Of(); var globalSettings = Options.Create(new GlobalSettings()); - var fileSystems = new FileSystems(container, Mock.Of>(), NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem( phy); - var sw = (ShadowWrapper)fs.InnerFileSystem; + var fileSystems = new FileSystems(NullLoggerFactory.Instance, IOHelper, globalSettings, HostingEnvironment) { IsScoped = () => scopedFileSystems }; + var shadowPath = $"x/{Guid.NewGuid().ToString("N").Substring(0, 6)}"; + var sw = fileSystems.CreateShadowWrapper(phy, shadowPath); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) sw.AddFile("sub/f1.txt", ms); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs index fd27dda811..a72e8c6c55 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos private ContentType _simpleContentType; private ContentType _textpageContentType; - private IFileSystems FileSystems => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); private IUmbracoMapper Mapper => GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index 7857115241..8cf2c8c932 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos private IDataTypeService DataTypeService => GetRequiredService(); - private IFileSystems FileSystems => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); private IConfigurationEditorJsonSerializer ConfigurationEditorJsonSerializer => GetRequiredService(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs index 44947b45cc..b0769f68ed 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/PartialViewRepositoryTests.cs @@ -5,13 +5,15 @@ using System; using System.Collections.Generic; using System.IO; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Options; using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; using Constants = Umbraco.Cms.Core.Constants; @@ -42,8 +44,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos public void PathTests() { // unless noted otherwise, no changes / 7.2.8 - IFileSystems fileSystems = Mock.Of(); - Mock.Get(fileSystems).Setup(x => x.PartialViewsFileSystem).Returns(_fileSystem); + FileSystems fileSystems = FileSystemsCreator.CreateTestFileSystems(LoggerFactory, IOHelper, + GetRequiredService>(), HostingEnvironment, + null, _fileSystem, null, null, null); IScopeProvider provider = ScopeProvider; using (IScope scope = provider.CreateScope()) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs index 35e53905a0..001fd89f03 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/ScriptRepositoryTest.cs @@ -7,7 +7,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Options; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; @@ -16,6 +16,7 @@ using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -27,16 +28,18 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos { private IHostingEnvironment HostingEnvironment => GetRequiredService(); - private IFileSystems _fileSystems; + private FileSystems _fileSystems; private IFileSystem _fileSystem; [SetUp] public void SetUpFileSystem() { - _fileSystems = Mock.Of(); string path = GlobalSettings.UmbracoScriptsPath; _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, LoggerFactory.CreateLogger(), HostingEnvironment.MapPathWebRoot(path), HostingEnvironment.ToAbsolute(path)); - Mock.Get(_fileSystems).Setup(x => x.ScriptsFileSystem).Returns(_fileSystem); + + _fileSystems = FileSystemsCreator.CreateTestFileSystems(LoggerFactory, IOHelper, GetRequiredService>(), + HostingEnvironment, + null, null, null, _fileSystem, null); using (Stream stream = CreateStream("Umbraco.Sys.registerNamespace(\"Umbraco.Utils\");")) { _fileSystem.AddFile("test-script.js", stream); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs index 6f6d6e3e71..9b2acd17c1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/StylesheetRepositoryTest.cs @@ -8,7 +8,7 @@ using System.IO; using System.Linq; using System.Text; using Microsoft.Extensions.Logging; -using Moq; +using Microsoft.Extensions.Options; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; @@ -16,6 +16,7 @@ using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Persistence.Repositories; using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -25,7 +26,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos [UmbracoTest(Database = UmbracoTestOptions.Database.None, Logger = UmbracoTestOptions.Logger.Console)] public class StylesheetRepositoryTest : UmbracoIntegrationTest { - private IFileSystems _fileSystems; + private FileSystems _fileSystems; private IFileSystem _fileSystem; private IHostingEnvironment HostingEnvironment => GetRequiredService(); @@ -33,10 +34,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos [SetUp] public void SetUpFileSystem() { - _fileSystems = Mock.Of(); string path = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoCssPath); _fileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), path, "/css"); - Mock.Get(_fileSystems).Setup(x => x.StylesheetsFileSystem).Returns(_fileSystem); + + _fileSystems = FileSystemsCreator.CreateTestFileSystems(LoggerFactory, IOHelper, GetRequiredService>(), + HostingEnvironment, + null, null, _fileSystem, null, null); + Stream stream = CreateStream("body {background:#EE7600; color:#FFF;}"); _fileSystem.AddFile("styles.css", stream); } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index 7b4150c897..646367c99a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -36,7 +36,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos { private IHostingEnvironment HostingEnvironment => GetRequiredService(); - private IFileSystems FileSystems => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); private ITemplateRepository CreateRepository(IScopeProvider provider) => new TemplateRepository((IScopeAccessor)provider, AppCaches.Disabled, LoggerFactory.CreateLogger(), FileSystems, IOHelper, ShortStringHelper); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs index f96852faeb..4fb20cde98 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/ScopeFileSystemsTests.cs @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping [UmbracoTest(Database = UmbracoTestOptions.Database.NewEmptyPerTest)] public class ScopeFileSystemsTests : UmbracoIntegrationTest { - private IMediaFileSystem MediaFileSystem => GetRequiredService(); + private MediaFileManager MediaFileManager => GetRequiredService(); private IHostingEnvironment HostingEnvironment => GetRequiredService(); @@ -35,7 +35,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping [TearDown] public void Teardown() { - FileSystems.ResetShadowId(); ClearFiles(IOHelper); } @@ -47,12 +46,12 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping } [Test] - public void MediaFileSystem_does_not_write_to_physical_file_system_when_scoped_if_scope_does_not_complete() + public void MediaFileManager_does_not_write_to_physical_file_system_when_scoped_if_scope_does_not_complete() { string rootPath = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoMediaPath); string rootUrl = HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoMediaPath); var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), rootPath, rootUrl); - IMediaFileSystem mediaFileSystem = MediaFileSystem; + MediaFileManager mediaFileManager = MediaFileManager; Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); @@ -60,28 +59,28 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f1.txt", ms); + MediaFileManager.FileSystem.AddFile("f1.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } // After scope is disposed ensure shadow wrapper didn't commit to physical - Assert.IsFalse(MediaFileSystem.FileExists("f1.txt")); + Assert.IsFalse(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } [Test] - public void MediaFileSystem_writes_to_physical_file_system_when_scoped_and_scope_is_completed() + public void MediaFileManager_writes_to_physical_file_system_when_scoped_and_scope_is_completed() { string rootPath = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoMediaPath); string rootUrl = HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoMediaPath); var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), rootPath, rootUrl); - IMediaFileSystem mediaFileSystem = MediaFileSystem; + MediaFileManager mediaFileManager = MediaFileManager; Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); @@ -89,20 +88,20 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f1.txt", ms); + mediaFileManager.FileSystem.AddFile("f1.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); scope.Complete(); - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); } // After scope is disposed ensure shadow wrapper writes to physical file system - Assert.IsTrue(MediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f1.txt")); } @@ -112,7 +111,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping string rootPath = HostingEnvironment.MapPathWebRoot(GlobalSettings.UmbracoMediaPath); string rootUrl = HostingEnvironment.ToAbsolute(GlobalSettings.UmbracoMediaPath); var physMediaFileSystem = new PhysicalFileSystem(IOHelper, HostingEnvironment, GetRequiredService>(), rootPath, rootUrl); - IMediaFileSystem mediaFileSystem = MediaFileSystem; + MediaFileManager mediaFileManager = MediaFileManager; var taskHelper = new TaskHelper(Mock.Of>()); IScopeProvider scopeProvider = ScopeProvider; @@ -120,23 +119,23 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f1.txt", ms); + mediaFileManager.FileSystem.AddFile("f1.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f1.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f1.txt")); Assert.IsFalse(physMediaFileSystem.FileExists("f1.txt")); // execute on another disconnected thread (execution context will not flow) Task t = taskHelper.ExecuteBackgroundTask(() => { - Assert.IsFalse(mediaFileSystem.FileExists("f1.txt")); + Assert.IsFalse(mediaFileManager.FileSystem.FileExists("f1.txt")); using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) { - mediaFileSystem.AddFile("f2.txt", ms); + mediaFileManager.FileSystem.AddFile("f2.txt", ms); } - Assert.IsTrue(mediaFileSystem.FileExists("f2.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f2.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f2.txt")); return Task.CompletedTask; @@ -144,7 +143,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping Task.WaitAll(t); - Assert.IsTrue(mediaFileSystem.FileExists("f2.txt")); + Assert.IsTrue(mediaFileManager.FileSystem.FileExists("f2.txt")); Assert.IsTrue(physMediaFileSystem.FileExists("f2.txt")); } } @@ -170,7 +169,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping // not ok to create a 'scoped filesystems' other scope // we will get a "Already shadowing." exception. Assert.Throws(() => - { + { using IScope other = scopeProvider.CreateScope(scopeFileSystems: true); }); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index e1a65d1541..44eabbac6f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -22,6 +22,7 @@ using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations.Install; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.Mappers; @@ -48,11 +49,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components var connectionStrings = new ConnectionStrings(); var f = new UmbracoDatabaseFactory(loggerFactory.CreateLogger(), loggerFactory, Options.Create(globalSettings), Options.Create(connectionStrings), new Lazy(() => new MapperCollection(Enumerable.Empty())), TestHelper.DbProviderFactoryCreator, new DatabaseSchemaCreatorFactory(loggerFactory.CreateLogger(), loggerFactory, new UmbracoVersion(), Mock.Of())); - var fs = new FileSystems(mock.Object, loggerFactory.CreateLogger(), loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); + var fs = new FileSystems(loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); var coreDebug = new CoreDebugSettings(); - IMediaFileSystem mediaFileSystem = Mock.Of(); + MediaFileManager mediaFileManager = new MediaFileManager(Mock.Of(), + Mock.Of(), Mock.Of>(), Mock.Of()); IEventAggregator eventAggregator = Mock.Of(); - var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); + var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(logger); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(loggerFactory.CreateLogger); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs index 43d14677fa..9820bef8c2 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Tests.Common.Builders; @@ -73,18 +74,19 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping private ScopeProvider GetScopeProvider(NullLoggerFactory instance) { var fileSystems = new FileSystems( - Mock.Of(), - Mock.Of>(), instance, Mock.Of(), Options.Create(new GlobalSettings()), Mock.Of()); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + instance.CreateLogger(), Mock.Of()); + return new ScopeProvider( Mock.Of(), fileSystems, Options.Create(new CoreDebugSettings()), - Mock.Of(), + mediaFileManager, Mock.Of>(), instance, Mock.Of(), diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs index 8bfced3b61..6995be88b7 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs @@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; @@ -30,7 +31,10 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping private ScopeProvider GetScopeProvider(out Mock syntaxProviderMock) { var loggerFactory = NullLoggerFactory.Instance; - var fileSystem = new FileSystems(Mock.Of(), loggerFactory.CreateLogger(), loggerFactory, Mock.Of(), Mock.Of>(), Mock.Of()); + var fileSystems = new FileSystems(loggerFactory, + Mock.Of(), Mock.Of>(), Mock.Of()); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); var databaseFactory = new Mock(); var database = new Mock(); var sqlContext = new Mock(); @@ -47,9 +51,9 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping return new ScopeProvider( databaseFactory.Object, - fileSystem, + fileSystems, Options.Create(new CoreDebugSettings()), - Mock.Of(), + mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, Mock.Of(), diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index bb26b2e70e..fbcfdb91ed 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -3,6 +3,7 @@ using System.Xml.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; @@ -35,8 +36,9 @@ namespace Umbraco.Tests.Models var scheme = Mock.Of(); var contentSettings = new ContentSettings(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, loggerFactory.CreateLogger(), ShortStringHelper); - var ignored = new FileUploadPropertyEditor(loggerFactory, mediaFileSystem, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService); + var mediaFileManager = new MediaFileManager(Mock.Of(), scheme, + loggerFactory.CreateLogger(), ShortStringHelper); + var ignored = new FileUploadPropertyEditor(loggerFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService); var media = MockedMedia.CreateMediaImage(mediaType, -1); media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index c285deee65..53b8666889 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Templates; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Serialization; @@ -50,7 +51,9 @@ namespace Umbraco.Tests.PublishedContent var serializer = new ConfigurationEditorJsonSerializer(); var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, Mock.Of(), Mock.Of(), Mock.Of(), ShortStringHelper, publishedUrlProvider, serializer); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); + var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, Mock.Of(), Mock.Of(), mediaFileManager, ShortStringHelper, publishedUrlProvider, serializer); var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new RichTextPropertyEditor( diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 1179887e32..1cb238b0c2 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -51,13 +51,14 @@ namespace Umbraco.Tests.PublishedContent var loggerFactory = NullLoggerFactory.Instance; var mediaService = Mock.Of(); - var mediaFileService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); var backOfficeSecurityAccessor = Mock.Of(); var publishedUrlProvider = Mock.Of(); var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); var serializer = new ConfigurationEditorJsonSerializer(); + var mediaFileService = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper, publishedUrlProvider, serializer); var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); var localizationService = Mock.Of(); diff --git a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs index 7149938b39..d6b7b58c15 100644 --- a/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs +++ b/src/Umbraco.Tests/Routing/MediaUrlProviderTests.cs @@ -13,6 +13,7 @@ using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.Common; using Umbraco.Cms.Tests.Common.Testing; @@ -33,13 +34,14 @@ namespace Umbraco.Tests.Routing base.SetUp(); var loggerFactory = NullLoggerFactory.Instance; - var mediaFileSystemMock = Mock.Of(); + var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), + loggerFactory.CreateLogger(), Mock.Of()); var contentSettings = new ContentSettings(); var dataTypeService = Mock.Of(); var propertyEditors = new MediaUrlGeneratorCollection(new IMediaUrlGenerator[] { - new FileUploadPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService), - new ImageCropperPropertyEditor(loggerFactory, mediaFileSystemMock, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService, UploadAutoFillProperties, JsonNetSerializer, ContentService), + new FileUploadPropertyEditor(loggerFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, LocalizedTextService, ShortStringHelper, UploadAutoFillProperties, JsonNetSerializer, ContentService), + new ImageCropperPropertyEditor(loggerFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), dataTypeService, LocalizationService, IOHelper, ShortStringHelper, LocalizedTextService, UploadAutoFillProperties, JsonNetSerializer, ContentService), }); _mediaUrlProvider = new DefaultMediaUrlProvider(propertyEditors, UriUtility); } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs index 94b4f5500b..1f32e73916 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Linq.Expressions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Moq; using Umbraco.Cms.Core; @@ -18,6 +19,7 @@ using Umbraco.Cms.Core.Web; using Umbraco.Cms.Infrastructure.Persistence; using Umbraco.Cms.Persistence.SqlCe; using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.TestHelpers; using Umbraco.Extensions; using Umbraco.Web; @@ -142,25 +144,23 @@ namespace Umbraco.Tests.TestHelpers { return new GlobalSettings(); } - public IFileSystems GetFileSystemsMock() + public FileSystems GetFileSystemsMock() { - var fileSystems = Mock.Of(); - - MockFs(fileSystems, x => x.MacroPartialsFileSystem); - MockFs(fileSystems, x => x.MvcViewsFileSystem); - MockFs(fileSystems, x => x.PartialViewsFileSystem); - MockFs(fileSystems, x => x.ScriptsFileSystem); - MockFs(fileSystems, x => x.StylesheetsFileSystem); + var fileSystems = FileSystemsCreator.CreateTestFileSystems( + NullLoggerFactory.Instance, + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of() + ); return fileSystems; } - private void MockFs(IFileSystems fileSystems, Expression> fileSystem) - { - var fs = Mock.Of(); - Mock.Get(fileSystems).Setup(fileSystem).Returns(fs); - } - #region Inner classes private class MockDbConnection : DbConnection diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index 524644874c..6f7fcfe2dd 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -84,11 +84,11 @@ namespace Umbraco.Tests.TestHelpers new DatabaseSchemaCreatorFactory(Mock.Of>(),loggerFactory, new UmbracoVersion(), Mock.Of())); } - fileSystems ??= new FileSystems(Current.Factory, loggerFactory.CreateLogger(), loggerFactory, TestHelper.IOHelper, globalSettings, TestHelper.GetHostingEnvironment()); + fileSystems ??= new FileSystems(loggerFactory, TestHelper.IOHelper, globalSettings, TestHelper.GetHostingEnvironment()); var coreDebug = TestHelper.CoreDebugSettings; - var mediaFileSystem = Mock.Of(); + var mediaFileManager = Mock.Of(); var eventAggregator = Mock.Of(); - return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileSystem, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); + return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); } } diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index a33b3f6c10..243e92ad53 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using Moq; using NUnit.Framework; using Serilog; @@ -462,8 +463,8 @@ namespace Umbraco.Tests.Testing var scheme = Mock.Of(); - var mediaFileSystem = new MediaFileSystem(Mock.Of(), scheme, _loggerFactory.CreateLogger(), TestHelper.ShortStringHelper); - Builder.Services.AddUnique(factory => mediaFileSystem); + var mediaFileManager = new MediaFileManager(Mock.Of(), scheme, Mock.Of>(), Mock.Of()); + Builder.Services.AddUnique(factory => mediaFileManager); // no factory (noop) Builder.Services.AddUnique(); diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs index 710fb5aeb8..822f5a4911 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public class CodeFileController : BackOfficeNotificationsController { private readonly IHostingEnvironment _hostingEnvironment; - private readonly IFileSystems _fileSystems; + private readonly FileSystems _fileSystems; private readonly IFileService _fileService; private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public CodeFileController( IHostingEnvironment hostingEnvironment, - IFileSystems fileSystems, + FileSystems fileSystems, IFileService fileService, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ILocalizedTextService localizedTextService, diff --git a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs index 080f84009a..49bff529bd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CurrentUserController.cs @@ -39,7 +39,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class CurrentUserController : UmbracoAuthorizedJsonController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IHostingEnvironment _hostingEnvironment; private readonly IImageUrlGenerator _imageUrlGenerator; @@ -54,7 +54,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly IPasswordChanger _passwordChanger; public CurrentUserController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IShortStringHelper shortStringHelper, IPasswordChanger passwordChanger) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _contentSettings = contentSettings.Value; _hostingEnvironment = hostingEnvironment; _imageUrlGenerator = imageUrlGenerator; @@ -210,7 +210,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IActionResult PostSetAvatar(IList file) { //borrow the logic from the user controller - return UsersController.PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); + return UsersController.PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileManager, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, _backofficeSecurityAccessor.BackOfficeSecurity.GetUserId().ResultOr(0)); } /// diff --git a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs index 8b9ab5652c..24135bcbe6 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ImagesController.cs @@ -16,14 +16,14 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class ImagesController : UmbracoAuthorizedApiController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IImageUrlGenerator _imageUrlGenerator; public ImagesController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IImageUrlGenerator imageUrlGenerator) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _imageUrlGenerator = imageUrlGenerator; } @@ -63,7 +63,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers DateTimeOffset? imageLastModified = null; try { - imageLastModified = _mediaFileSystem.GetLastModified(imagePath); + imageLastModified = _mediaFileManager.FileSystem.GetLastModified(imagePath); } catch (Exception) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs index 2d8f2fa82b..3c3eaa4eff 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogController.cs @@ -24,7 +24,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [PluginController(Constants.Web.Mvc.BackOfficeApiArea)] public class LogController : UmbracoAuthorizedJsonController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IImageUrlGenerator _imageUrlGenerator; private readonly IAuditService _auditService; private readonly IUmbracoMapper _umbracoMapper; @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly ISqlContext _sqlContext; public LogController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileSystem, IImageUrlGenerator imageUrlGenerator, IAuditService auditService, IUmbracoMapper umbracoMapper, @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers AppCaches appCaches, ISqlContext sqlContext) { - _mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); + _mediaFileManager = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem)); _imageUrlGenerator = imageUrlGenerator ?? throw new ArgumentNullException(nameof(imageUrlGenerator)); _auditService = auditService ?? throw new ArgumentNullException(nameof(auditService)); _umbracoMapper = umbracoMapper ?? throw new ArgumentNullException(nameof(umbracoMapper)); @@ -111,7 +111,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { var mappedItems = items.ToList(); var userIds = mappedItems.Select(x => x.UserId).ToArray(); - var userAvatars = Enumerable.ToDictionary(_userService.GetUsersById(userIds), x => x.Id, x => x.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator)); + var userAvatars = Enumerable.ToDictionary(_userService.GetUsersById(userIds), x => x.Id, x => x.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator)); var userNames = Enumerable.ToDictionary(_userService.GetUsersById(userIds), x => x.Id, x => x.Name); foreach (var item in mappedItems) { diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 9d176277ec..88114f673c 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -89,7 +89,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IRelationService relationService, PropertyEditorCollection propertyEditors, - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, IJsonSerializer serializer, @@ -110,7 +110,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _relationService = relationService; _propertyEditors = propertyEditors; - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _hostingEnvironment = hostingEnvironment; _logger = loggerFactory.CreateLogger(); _imageUrlGenerator = imageUrlGenerator; @@ -288,7 +288,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private int[] _userStartNodes; private readonly PropertyEditorCollection _propertyEditors; - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly IHostingEnvironment _hostingEnvironment; @@ -806,7 +806,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers await using (var stream = formFile.OpenReadStream()) { - f.SetValue(_mediaFileSystem,_shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File,fileName, stream); + f.SetValue(_mediaFileManager,_shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File,fileName, stream); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs index 6e7e5f2ed1..2d7418b6bd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UsersController.cs @@ -52,7 +52,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [IsCurrentUserModelFilter] public class UsersController : UmbracoAuthorizedJsonController { - private readonly IMediaFileSystem _mediaFileSystem; + private readonly MediaFileManager _mediaFileManager; private readonly ContentSettings _contentSettings; private readonly IHostingEnvironment _hostingEnvironment; private readonly ISqlContext _sqlContext; @@ -75,7 +75,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly ILogger _logger; public UsersController( - IMediaFileSystem mediaFileSystem, + MediaFileManager mediaFileManager, IOptions contentSettings, IHostingEnvironment hostingEnvironment, ISqlContext sqlContext, @@ -96,7 +96,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers UserEditorAuthorizationHelper userEditorAuthorizationHelper, IPasswordChanger passwordChanger) { - _mediaFileSystem = mediaFileSystem; + _mediaFileManager = mediaFileManager; _contentSettings = contentSettings.Value; _hostingEnvironment = hostingEnvironment; _sqlContext = sqlContext; @@ -125,7 +125,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// public ActionResult GetCurrentUserAvatarUrls() { - var urls = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + var urls = _backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); if (urls == null) return new ValidationErrorResult("Could not access Gravatar endpoint"); @@ -136,10 +136,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [Authorize(Policy = AuthorizationPolicies.AdminUserEditsRequireAdmin)] public IActionResult PostSetAvatar(int id, IList file) { - return PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileSystem, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, id); + return PostSetAvatarInternal(file, _userService, _appCaches.RuntimeCache, _mediaFileManager, _shortStringHelper, _contentSettings, _hostingEnvironment, _imageUrlGenerator, id); } - internal static IActionResult PostSetAvatarInternal(IList files, IUserService userService, IAppCache cache, IMediaFileSystem mediaFileSystem, IShortStringHelper shortStringHelper, ContentSettings contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, int id) + internal static IActionResult PostSetAvatarInternal(IList files, IUserService userService, IAppCache cache, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, ContentSettings contentSettings, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, int id) { if (files is null) { @@ -176,13 +176,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers using (var fs = file.OpenReadStream()) { - mediaFileSystem.AddFile(user.Avatar, fs, true); + mediaFileManager.FileSystem.AddFile(user.Avatar, fs, true); } userService.Save(user); } - return new OkObjectResult(user.GetUserAvatarUrls(cache, mediaFileSystem, imageUrlGenerator)); + return new OkObjectResult(user.GetUserAvatarUrls(cache, mediaFileManager, imageUrlGenerator)); } [AppendUserModifiedHeader("id")] @@ -212,11 +212,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (filePath.IsNullOrWhiteSpace() == false) { - if (_mediaFileSystem.FileExists(filePath)) - _mediaFileSystem.DeleteFile(filePath); + if (_mediaFileManager.FileSystem.FileExists(filePath)) + _mediaFileManager.FileSystem.DeleteFile(filePath); } - return found.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileSystem, _imageUrlGenerator); + return found.GetUserAvatarUrls(_appCaches.RuntimeCache, _mediaFileManager, _imageUrlGenerator); } /// diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs index 6403ec009d..96d9004635 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewMacrosTreeController.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, fileSystems, eventAggregator) { diff --git a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs index ad8b9f16b4..c79175dba8 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PartialViewsTreeController.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, eventAggregator) { diff --git a/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs index 56f1b0ac1d..89e767c124 100644 --- a/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/ScriptsTreeController.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, eventAggregator) { diff --git a/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs index 18d1be6a67..08dd8f2cde 100644 --- a/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/StylesheetsTreeController.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IMenuItemCollectionFactory menuItemCollectionFactory, - IFileSystems fileSystems, + FileSystems fileSystems, IEventAggregator eventAggregator) : base(localizedTextService, umbracoApiControllerTypeCollection, menuItemCollectionFactory, eventAggregator) { diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index 0863d843fa..6240ba27bb 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -144,7 +144,7 @@ namespace Umbraco.Web.Composing // proxy Core for convenience - public static IMediaFileSystem MediaFileSystem => Factory.GetRequiredService(); + public static MediaFileManager MediaFileManager => Factory.GetRequiredService(); public static UmbracoMapper Mapper => Factory.GetRequiredService();