diff --git a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs index 22e0d33456..a240f1c0ce 100644 --- a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs @@ -2,6 +2,7 @@ using System.Configuration; using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.IO.MediaPathSchemes; namespace Umbraco.Core.Composing.Composers { @@ -13,34 +14,45 @@ namespace Umbraco.Core.Composing.Composers * * Create a component and use it to modify the composition by adding something like: * - * composition.Container.RegisterSingleton("media", factory => ...); + * composition.Container.RegisterFileSystem( + * factory => new PhysicalFileSystem("~/somewhere")); * - * where the ... part returns the new underlying filesystem, as an IFileSystem. + * return whatever supporting filesystem you like. * * * HOW TO IMPLEMENT MY OWN FILESYSTEM * ---------------------------------- * - * Declare your filesystem interface: - * - * public interface IMyFileSystem : IFileSystem - * { } - * * Create your filesystem class: * - * [FileSystem("my")] - * public class MyFileSystem : FileSystemWrapper, IFormsFileSystem + * public class MyFileSystem : FileSystemWrapper * { - * public FormsFileSystem(IFileSystem innerFileSystem) + * public MyFileSystem(IFileSystem innerFileSystem) * : base(innerFileSystem) * { } * } * - * Register both the underlying filesystem, and your filesystem, in a component: + * Note that the ctor parameter MUST be named innerFileSystem. fixme oh yea? + * The ctor can have more parameters that will be resolved by the container. * - * composition.Container.RegisterSingleton("my", factory => ...); - * composition.Container.RegisterSingleton(factory => - * factory.GetInstance().GetFileSystem(); + * Register your filesystem, in a component: + * + * composition.Container.RegisterFileSystem( + * factory => new PhysicalFileSystem("~/my")); + * + * And that's it, you can inject MyFileSystem wherever it's needed. + * + * + * You can also declare a filesystem interface: + * + * public interface IMyFileSystem : IFileSystem + * { } + * + * Make the class implement the interface, then + * register your filesystem, in a component: + * + * composition.Container.RegisterFileSystem( + * factory => new PhysicalFileSystem("~/my")); * * And that's it, you can inject IMyFileSystem wherever it's needed. * @@ -53,14 +65,6 @@ namespace Umbraco.Core.Composing.Composers * compared to creating your own physical filesystem, ensures that your filesystem * would participate into such transactions. * - * Also note that in order for things to work correctly, all filesystems should - * be instantiated before shadowing - so if registering a new filesystem in a - * component, it's a good idea to initialize it. This would be enough (in the - * component): - * - * public void Initialize(IMyFileSystem fs) - * { } - * * */ @@ -70,34 +74,19 @@ namespace Umbraco.Core.Composing.Composers // 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. - //container.RegisterSingleton(); container.RegisterSingleton(factory => factory.CreateInstance(new { container} )); // register IFileSystems, which gives access too all filesystems container.RegisterSingleton(factory => factory.GetInstance()); - // register IMediaFileSystem with its actual, underlying filesystem factory - container.RegisterSingleton(factory => factory.GetInstance().GetFileSystem(MediaInnerFileSystemFactory)); + // register the scheme for media paths + container.RegisterSingleton(); + + // register the IMediaFileSystem implementation with a supporting filesystem + container.RegisterFileSystem( + factory => new PhysicalFileSystem("~/media")); return container; } - - private static IFileSystem MediaInnerFileSystemFactory() - { - // for the time being, we still use the FileSystemProvider config file - // but, detect if ppl are trying to use it to change the "provider" - - var virtualRoot = "~/media"; - - var config = (FileSystemProvidersSection)ConfigurationManager.GetSection("umbracoConfiguration/FileSystemProviders"); - var p = config?.Providers["media"]; - if (p == null) return new PhysicalFileSystem(virtualRoot); - - if (!string.IsNullOrWhiteSpace(p.Type) && p.Type != "Umbraco.Core.IO.PhysicalFileSystem, Umbraco.Core") - throw new InvalidOperationException("Setting a provider type in FileSystemProviders.config is not supported anymore, see FileSystemsComposer for help."); - - virtualRoot = p?.Parameters["virtualRoot"]?.Value ?? "~/media"; - return new PhysicalFileSystem(virtualRoot); - } } } diff --git a/src/Umbraco.Core/Composing/ContainerExtensions.cs b/src/Umbraco.Core/Composing/ContainerExtensions.cs index ac1df5dadb..c9b3200b34 100644 --- a/src/Umbraco.Core/Composing/ContainerExtensions.cs +++ b/src/Umbraco.Core/Composing/ContainerExtensions.cs @@ -3,6 +3,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Umbraco.Core.IO; namespace Umbraco.Core.Composing { @@ -167,6 +168,8 @@ namespace Umbraco.Core.Composing // more expensive to build and cache a dynamic method ctor than to simply invoke the ctor, as we do // here - this can be discussed + // TODO: we currently try the ctor with most parameters, but we could want to fall back to others + var ctor = type.GetConstructors(BindingFlags.Instance | BindingFlags.Public).OrderByDescending(x => x.GetParameters().Length).FirstOrDefault(); if (ctor == null) throw new InvalidOperationException($"Could not find a public constructor for type {type.FullName}."); @@ -177,10 +180,50 @@ namespace Umbraco.Core.Composing { // no! IsInstanceOfType is not ok here // ReSharper disable once UseMethodIsInstanceOfType + // fixme so we just ignore the names? var arg = args?.Values.FirstOrDefault(a => parameter.ParameterType.IsAssignableFrom(a.GetType())); ctorArgs[i++] = arg ?? container.GetInstance(parameter.ParameterType); } return ctor.Invoke(ctorArgs); } + + /// + /// Registers a filesystem. + /// + /// The type of the filesystem. + /// The implementing type. + /// The container. + /// A factory method creating the supporting filesystem. + /// The container. + public static IContainer RegisterFileSystem(this IContainer container, Func supportingFileSystemFactory) + where TImplementing : FileSystemWrapper, TFileSystem + { + container.RegisterSingleton(factory => + { + var fileSystems = factory.GetInstance(); + return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + }); + + return container; + } + + /// + /// Registers a filesystem. + /// + /// The type of the filesystem. + /// The container. + /// A factory method creating the supporting filesystem. + /// The container. + public static IContainer RegisterFileSystem(this IContainer container, Func supportingFileSystemFactory) + where TFileSystem : FileSystemWrapper + { + container.RegisterSingleton(factory => + { + var fileSystems = factory.GetInstance(); + return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + }); + + return container; + } } } diff --git a/src/Umbraco.Core/IO/FileSystems.cs b/src/Umbraco.Core/IO/FileSystems.cs index 3a4eea91d6..4962d88a4e 100644 --- a/src/Umbraco.Core/IO/FileSystems.cs +++ b/src/Umbraco.Core/IO/FileSystems.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Threading; using Umbraco.Core.Logging; using Umbraco.Core.Composing; @@ -13,7 +12,7 @@ namespace Umbraco.Core.IO private readonly IContainer _container; private readonly ILogger _logger; - private readonly ConcurrentDictionary> _filesystems = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _filesystems = new ConcurrentDictionary>(); // wrappers for shadow support private ShadowWrapper _macroPartialFileSystem; @@ -170,18 +169,17 @@ namespace Umbraco.Core.IO /// 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(Func innerFileSystemFactory) + public TFileSystem GetFileSystem(IFileSystem supporting) where TFileSystem : FileSystemWrapper { if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems(); - var name = typeof(TFileSystem).FullName; - if (name == null) throw new Exception("panic!"); - - return (TFileSystem) _filesystems.GetOrAdd(name, _ => new Lazy(() => + return (TFileSystem) _filesystems.GetOrAdd(typeof(TFileSystem), _ => new Lazy(() => { - var innerFileSystem = innerFileSystemFactory(); - var shadowWrapper = CreateShadowWrapper(innerFileSystem, "typed/" + name); + var name = typeof(TFileSystem).FullName; + if (name == null) throw new Exception("panic!"); + + var shadowWrapper = CreateShadowWrapper(supporting, "typed/" + name); return _container.CreateInstance(new { innerFileSystem = shadowWrapper }); })).Value; } @@ -194,19 +192,6 @@ namespace Umbraco.Core.IO // shadowing is thread-safe, but entering and exiting shadow mode is not, and there is only one // 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. - // shadow applies to well-known filesystems *only* - at the moment, any other filesystem that would - // be created directly (via ctor) or via GetFileSystem is *not* shadowed. - - // shadow must be enabled in an app event handler before anything else ie before any filesystem - // is actually created and used - after, it is too late - enabling shadow has a negligible perfs - // impact. - // NO! by the time an app event handler is instantiated it is already too late, see note in ctor. - //internal void EnableShadow() - //{ - // if (_mvcViewsFileSystem != null) // test one of the fs... - // throw new InvalidOperationException("Cannot enable shadow once filesystems have been created."); - // _shadowEnabled = true; - //} internal ICompletable Shadow(Guid id) { diff --git a/src/Umbraco.Core/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs index 5cfb43ed77..9a38cdc74f 100644 --- a/src/Umbraco.Core/IO/IMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/IMediaPathScheme.cs @@ -7,35 +7,27 @@ namespace Umbraco.Core.IO /// public interface IMediaPathScheme { - // fixme - // to anyone finding this code: YES the Initialize() method is CompletelyBroken™ (temporal whatever) - // but at the moment, the media filesystem wants a scheme which wants a filesystem, and it's all - // convoluted due to how filesystems are managed in FileSystems - clear that part first! - - /// - /// Initialize. - /// - void Initialize(IFileSystem filesystem); - /// /// Gets a media file path. /// + /// 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(Guid itemGuid, Guid propertyGuid, string filename, string previous = null); + string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); /// /// Gets the directory that can be deleted when the file is deleted. /// + /// The media filesystem. /// The filesystem-relative path of the file. /// The filesystem-relative path of the directory. /// /// The directory, and anything below it, will be deleted. /// Can return null (or empty) when no directory should be deleted. /// - string GetDeleteDirectory(string filepath); + string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath); } } diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index 867fe94c03..2ce1230bcc 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Linq; using System.Threading.Tasks; using Umbraco.Core.Composing; +using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; @@ -17,24 +19,21 @@ namespace Umbraco.Core.IO /// public class MediaFileSystem : FileSystemWrapper, IMediaFileSystem { + private readonly IMediaPathScheme _mediaPathScheme; + private readonly IContentSection _contentConfig; + private readonly ILogger _logger; + /// /// Initializes a new instance of the class. /// - public MediaFileSystem(IFileSystem innerFileSystem) + public MediaFileSystem(IFileSystem innerFileSystem, IContentSection contentConfig, IMediaPathScheme mediaPathScheme, ILogger logger) : base(innerFileSystem) { - ContentConfig = Current.Container.GetInstance(); - Logger = Current.Container.GetInstance(); - MediaPathScheme = Current.Container.GetInstance(); - MediaPathScheme.Initialize(this); + _contentConfig = contentConfig; + _mediaPathScheme = mediaPathScheme; + _logger = logger; } - private IMediaPathScheme MediaPathScheme { get; } - - private IContentSection ContentConfig { get; } - - private ILogger Logger { get; } - /// public void DeleteMediaFiles(IEnumerable files) { @@ -51,13 +50,13 @@ namespace Umbraco.Core.IO if (FileExists(file) == false) return; DeleteFile(file); - var directory = MediaPathScheme.GetDeleteDirectory(file); + var directory = _mediaPathScheme.GetDeleteDirectory(this, file); if (!directory.IsNullOrWhiteSpace()) DeleteDirectory(directory, true); } catch (Exception e) { - Logger.Error(e, "Failed to delete media file '{File}'.", file); + _logger.Error(e, "Failed to delete media file '{File}'.", file); } }); } @@ -71,7 +70,7 @@ namespace Umbraco.Core.IO if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); filename = IOHelper.SafeFileName(filename.ToLowerInvariant()); - return MediaPathScheme.GetFilePath(cuid, puid, filename); + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); } /// @@ -81,7 +80,7 @@ namespace Umbraco.Core.IO if (filename == null) throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); filename = IOHelper.SafeFileName(filename.ToLowerInvariant()); - return MediaPathScheme.GetFilePath(cuid, puid, filename, prevpath); + return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); } #endregion @@ -124,6 +123,6 @@ namespace Umbraco.Core.IO return filepath; } - #endregion + #endregion } } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index ef71aff3bc..2d9d964b90 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -12,11 +12,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes public class CombinedGuidsMediaPathScheme : IMediaPathScheme { /// - public void Initialize(IFileSystem filesystem) - { } - - /// - public string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(IMediaFileSystem fileSystem, 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 @@ -25,7 +21,7 @@ namespace Umbraco.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(string filepath) + public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs index 1ed2ea59ce..8e53e34ee8 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs @@ -17,18 +17,11 @@ namespace Umbraco.Core.IO.MediaPathSchemes public class OriginalMediaPathScheme : IMediaPathScheme { private readonly object _folderCounterLock = new object(); - private IFileSystem _filesystem; private long _folderCounter; private bool _folderCounterInitialized; /// - public void Initialize(IFileSystem filesystem) - { - _filesystem = filesystem; - } - - /// - public string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { string directory; if (previous != null) @@ -41,11 +34,11 @@ namespace Umbraco.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(); + directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileSystem); } else { - directory = GetNextDirectory(); + directory = GetNextDirectory(fileSystem); } if (directory == null) @@ -57,25 +50,25 @@ namespace Umbraco.Core.IO.MediaPathSchemes } /// - public string GetDeleteDirectory(string filepath) + public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } - private string GetNextDirectory() + private string GetNextDirectory(IFileSystem fileSystem) { - EnsureFolderCounterIsInitialized(); + EnsureFolderCounterIsInitialized(fileSystem); return Interlocked.Increment(ref _folderCounter).ToString(CultureInfo.InvariantCulture); } - private void EnsureFolderCounterIsInitialized() + private void EnsureFolderCounterIsInitialized(IFileSystem fileSystem) { lock (_folderCounterLock) { if (_folderCounterInitialized) return; _folderCounter = 1000; // seed - var directories = _filesystem.GetDirectories(""); + var directories = fileSystem.GetDirectories(""); foreach (var directory in directories) { if (long.TryParse(directory, out var folderNumber) && folderNumber > _folderCounter) diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs index 4a6fdfcdbe..3c06e295e6 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs @@ -12,17 +12,13 @@ namespace Umbraco.Core.IO.MediaPathSchemes public class TwoGuidsMediaPathScheme : IMediaPathScheme { /// - public void Initialize(IFileSystem filesystem) - { } - - /// - public string GetFilePath(Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(IMediaFileSystem fileSystem, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) { return Path.Combine(itemGuid.ToString("N"), propertyGuid.ToString("N"), filename).Replace('\\', '/'); } /// - public string GetDeleteDirectory(string filepath) + public string GetDeleteDirectory(IMediaFileSystem fileSystem, string filepath) { return Path.GetDirectoryName(filepath); } diff --git a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs index efd9bde0b5..34fd04d181 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntimeComponent.cs @@ -107,8 +107,6 @@ namespace Umbraco.Core.Runtime // by default, register a noop factory composition.Container.RegisterSingleton(); - - composition.Container.RegisterSingleton(); } internal void Initialize(IEnumerable mapperProfiles) diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 304f7b1efb..50ce85f356 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -398,7 +398,7 @@ namespace Umbraco.Tests.IO var container = Mock.Of(); var fileSystems = new FileSystems(container, logger) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem(() => phy); + var fs = fileSystems.GetFileSystem(phy); var sw = (ShadowWrapper) fs.InnerFileSystem; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) @@ -491,7 +491,7 @@ namespace Umbraco.Tests.IO var container = Mock.Of(); var fileSystems = new FileSystems(container, logger) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem(() => phy); + var fs = fileSystems.GetFileSystem( phy); var sw = (ShadowWrapper) fs.InnerFileSystem; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) @@ -544,7 +544,7 @@ namespace Umbraco.Tests.IO var container = Mock.Of(); var fileSystems = new FileSystems(container, logger) { IsScoped = () => scopedFileSystems }; - var fs = fileSystems.GetFileSystem(() => phy); + var fs = fileSystems.GetFileSystem( phy); var sw = (ShadowWrapper)fs.InnerFileSystem; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo"))) diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index 492803ce17..1a56fac4eb 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -29,7 +29,12 @@ namespace Umbraco.Tests.Models // reference, so static ctor runs, so event handlers register // and then, this will reset the width, height... because the file does not exist, of course ;-( - var ignored = new FileUploadPropertyEditor(Mock.Of(), new MediaFileSystem(Mock.Of()), Mock.Of()); + var logger = Mock.Of(); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); + var ignored = new FileUploadPropertyEditor(Mock.Of(), mediaFileSystem, config); 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/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index cdc3918fc3..7e56ad61a8 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -70,10 +70,11 @@ namespace Umbraco.Tests.PropertyEditors container.RegisterCollectionBuilder(); - Current.Container.RegisterSingleton(f => Mock.Of()); - Current.Container.RegisterSingleton(f => Mock.Of()); - Current.Container.RegisterSingleton(f => Mock.Of()); - var mediaFileSystem = new MediaFileSystem(Mock.Of()); + var logger = Mock.Of(); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new ImageCropperPropertyEditor(Mock.Of(), mediaFileSystem, Mock.Of(), Mock.Of())) { Id = 1 }); diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index efa77d26dc..9f16141c2f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -117,7 +117,10 @@ namespace Umbraco.Tests.TestHelpers if (logger == null) throw new ArgumentNullException(nameof(logger)); if (eventMessagesFactory == null) throw new ArgumentNullException(nameof(eventMessagesFactory)); - var mediaFileSystem = new MediaFileSystem(Mock.Of()); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); var externalLoginService = GetLazyService(container, c => new ExternalLoginService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); var publicAccessService = GetLazyService(container, c => new PublicAccessService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 4084c2ac69..45a4bc5917 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -295,7 +295,13 @@ namespace Umbraco.Tests.Testing // register filesystems Container.RegisterSingleton(factory => TestObjects.GetFileSystemsMock()); - Container.RegisterSingleton(factory => new MediaFileSystem(Mock.Of())); + + var logger = Mock.Of(); + var scheme = Mock.Of(); + var config = Mock.Of(); + + var mediaFileSystem = new MediaFileSystem(Mock.Of(), config, scheme, logger); + Container.RegisterSingleton(factory => mediaFileSystem); // no factory (noop) Container.RegisterSingleton();