diff --git a/src/Umbraco.Core/DynamicWrapper.cs b/src/Umbraco.Core/DynamicWrapper.cs new file mode 100644 index 0000000000..120481b6ac --- /dev/null +++ b/src/Umbraco.Core/DynamicWrapper.cs @@ -0,0 +1,178 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; + +namespace Umbraco.Core +{ + internal class DynamicWrapper + { + public class DynamicWrapperBase + { + internal protected object RealObject; + } + + private static readonly ModuleBuilder _moduleBuilder; + + static DynamicWrapper() + { + var name = Assembly.GetExecutingAssembly().GetName(); + var assembly = Thread.GetDomain().DefineDynamicAssembly(name, AssemblyBuilderAccess.Run); + _moduleBuilder = assembly.DefineDynamicModule("DynamicWrapperModule", false); + } + + private static readonly WrapperDictionary _wrapperDictionary = new WrapperDictionary(); + + public static Type GetWrapper(Type interfaceType, Type realObjectType) + { + Type wrapperType = _wrapperDictionary.GetType(interfaceType, realObjectType); + + if (wrapperType == null) + { + wrapperType = GenerateWrapperType(interfaceType, realObjectType); + _wrapperDictionary.SetType(interfaceType, realObjectType, wrapperType); + } + + return wrapperType; + } + + private static Type GenerateWrapperType(Type InterfaceType, Type RealObjectType) + { + var wrapperName = string.Format("{0}_{1}_Wrapper", InterfaceType.Name, RealObjectType.Name); + + TypeBuilder wrapperBuilder = _moduleBuilder.DefineType( + wrapperName, + TypeAttributes.NotPublic | TypeAttributes.Sealed, + typeof(DynamicWrapperBase), + new[] { InterfaceType }); + + var wrapperMethod = new WrapperMethodBuilder(RealObjectType, wrapperBuilder); + + foreach (MethodInfo method in InterfaceType.AllMethods()) + { + wrapperMethod.Generate(method); + } + + return wrapperBuilder.CreateType(); + } + + public static T CreateWrapper(object realObject) where T : class + { + var dynamicType = GetWrapper(typeof(T), realObject.GetType()); + var dynamicWrapper = (DynamicWrapperBase)Activator.CreateInstance(dynamicType); + + dynamicWrapper.RealObject = realObject; + + return dynamicWrapper as T; + } + } + + internal class WrapperMethodBuilder + { + private readonly Type _realObjectType; + private readonly TypeBuilder _wrapperBuilder; + + public WrapperMethodBuilder(Type realObjectType, TypeBuilder proxyBuilder) + { + _realObjectType = realObjectType; + _wrapperBuilder = proxyBuilder; + } + + public void Generate(MethodInfo newMethod) + { + if (newMethod.IsGenericMethod) + newMethod = newMethod.GetGenericMethodDefinition(); + + FieldInfo srcField = typeof(DynamicWrapper.DynamicWrapperBase).GetField("RealObject", BindingFlags.Instance | BindingFlags.NonPublic); + + var parameters = newMethod.GetParameters(); + var parameterTypes = parameters.Select(parameter => parameter.ParameterType).ToArray(); + + var methodBuilder = _wrapperBuilder.DefineMethod( + newMethod.Name, + MethodAttributes.Public | MethodAttributes.Virtual, + newMethod.ReturnType, + parameterTypes); + + if (newMethod.IsGenericMethod) + { + methodBuilder.DefineGenericParameters( + newMethod.GetGenericArguments().Select(arg => arg.Name).ToArray()); + } + + ILGenerator ilGenerator = methodBuilder.GetILGenerator(); + + LoadRealObject(ilGenerator, srcField); + PushParameters(parameters, ilGenerator); + ExecuteMethod(newMethod, parameterTypes, ilGenerator); + Return(ilGenerator); + } + + private static void Return(ILGenerator ilGenerator) + { + ilGenerator.Emit(OpCodes.Ret); + } + + private void ExecuteMethod(MethodBase newMethod, Type[] parameterTypes, ILGenerator ilGenerator) + { + MethodInfo srcMethod = GetMethod(newMethod, parameterTypes); + + if (srcMethod == null) throw new MissingMethodException("Unable to find method " + newMethod.Name + " in " + _realObjectType.FullName); + + ilGenerator.Emit(OpCodes.Call, srcMethod); + } + + private MethodInfo GetMethod(MethodBase realMethod, Type[] parameterTypes) + { + if (realMethod.IsGenericMethod) + return _realObjectType.GetGenericMethod(realMethod.Name, parameterTypes); + + return _realObjectType.GetMethod(realMethod.Name, parameterTypes); + } + + private static void PushParameters(ICollection parameters, ILGenerator ilGenerator) + { + for (int i = 1; i < parameters.Count + 1; i++) + ilGenerator.Emit(OpCodes.Ldarg, i); + } + + private static void LoadRealObject(ILGenerator ilGenerator, FieldInfo srcField) + { + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Ldfld, srcField); + } + } + + internal class WrapperDictionary + { + private readonly Dictionary _wrapperTypes = new Dictionary(); + + private static string GenerateKey(Type interfaceType, Type realObjectType) + { + return interfaceType.Name + "->" + realObjectType.Name; + } + + public Type GetType(Type interfaceType, Type realObjectType) + { + string key = GenerateKey(interfaceType, realObjectType); + + if (_wrapperTypes.ContainsKey(key)) + return _wrapperTypes[key]; + + return null; + } + + public void SetType(Type interfaceType, Type realObjectType, Type wrapperType) + { + string key = GenerateKey(interfaceType, realObjectType); + + if (_wrapperTypes.ContainsKey(key)) + _wrapperTypes[key] = wrapperType; + else + _wrapperTypes.Add(key, wrapperType); + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/IO/FileSystemProviderAttribute.cs b/src/Umbraco.Core/IO/FileSystemProviderAttribute.cs new file mode 100644 index 0000000000..8b34bcd04c --- /dev/null +++ b/src/Umbraco.Core/IO/FileSystemProviderAttribute.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.IO +{ + [AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal class FileSystemProviderAttribute : Attribute + { + public string Alias { get; set; } + + public FileSystemProviderAttribute(string alias) + { + Alias = alias; + } + } +} diff --git a/src/Umbraco.Core/IO/FileSystemProviderManager.cs b/src/Umbraco.Core/IO/FileSystemProviderManager.cs index da4a1e8dec..de59497555 100644 --- a/src/Umbraco.Core/IO/FileSystemProviderManager.cs +++ b/src/Umbraco.Core/IO/FileSystemProviderManager.cs @@ -57,5 +57,18 @@ namespace Umbraco.Core.IO return (IFileSystem) constructor.Invoke(parameters); } + + public TProviderTypeFilter GetFileSystemProvider() + where TProviderTypeFilter : class, IFileSystem + { + var attr = + (FileSystemProviderAttribute)typeof(TProviderTypeFilter).GetCustomAttributes(typeof(FileSystemProviderAttribute), false). + SingleOrDefault(); + + if (attr == null) + throw new InvalidOperationException(string.Format("The provider type filter '{0}' is missing the required FileSystemProviderAttribute", typeof(FileSystemProviderAttribute).FullName)); + + return GetFileSystemProvider(attr.Alias).As(); + } } } diff --git a/src/Umbraco.Core/IO/IFileSystem.cs b/src/Umbraco.Core/IO/IFileSystem.cs index 0bb11527ea..a2621965ce 100644 --- a/src/Umbraco.Core/IO/IFileSystem.cs +++ b/src/Umbraco.Core/IO/IFileSystem.cs @@ -29,6 +29,7 @@ namespace Umbraco.Core.IO bool FileExists(string path); + string GetRelativePath(string fullPathOrUrl); string GetFullPath(string path); diff --git a/src/Umbraco.Core/IO/IMediaFileSystem.cs b/src/Umbraco.Core/IO/IMediaFileSystem.cs new file mode 100644 index 0000000000..eaa0796301 --- /dev/null +++ b/src/Umbraco.Core/IO/IMediaFileSystem.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.IO +{ + [FileSystemProvider("media")] + internal interface IMediaFileSystem : IFileSystem + { } +} diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 5b2cbd7db5..de21e94622 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -14,10 +14,7 @@ namespace Umbraco.Core.IO public PhysicalFileSystem(string virtualRoot) { - if(HttpContext.Current == null) - throw new InvalidOperationException("The single parameter constructor can only be accessed when there is a valid HttpContext"); - - _rootPath = HttpContext.Current.Server.MapPath(virtualRoot); + _rootPath = System.Web.Hosting.HostingEnvironment.MapPath(virtualRoot); _rootUrl = VirtualPathUtility.ToAbsolute(virtualRoot); } diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs new file mode 100644 index 0000000000..a25894aac2 --- /dev/null +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -0,0 +1,24 @@ +namespace Umbraco.Core +{ + public static class ObjectExtensions + { + internal static T As(this object realObject) where T : class + { + if (realObject is T) + return realObject as T; + + return DynamicWrapper.CreateWrapper(realObject); + } + + internal static T AsReal(this object wrapper) where T : class + { + if (wrapper is T) + return wrapper as T; + + if (wrapper is DynamicWrapper.DynamicWrapperBase) + return (T)(wrapper as DynamicWrapper.DynamicWrapperBase).RealObject; + + return null; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Properties/AssemblyInfo.cs b/src/Umbraco.Core/Properties/AssemblyInfo.cs index 904076028f..0c5ec9f7bb 100644 --- a/src/Umbraco.Core/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Core/Properties/AssemblyInfo.cs @@ -23,5 +23,6 @@ using System.Runtime.InteropServices; [assembly: InternalsVisibleTo("businesslogic")] [assembly: InternalsVisibleTo("umbraco.editorControls")] [assembly: InternalsVisibleTo("Umbraco.Tests")] +[assembly: InternalsVisibleTo("Umbraco.Core")] [assembly: InternalsVisibleTo("Our.Umbraco.AmazonS3FileSystem")] diff --git a/src/Umbraco.Core/TypeExtensions.cs b/src/Umbraco.Core/TypeExtensions.cs new file mode 100644 index 0000000000..928224bc81 --- /dev/null +++ b/src/Umbraco.Core/TypeExtensions.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Umbraco.Core +{ + public static class TypeExtensions + { + internal static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes) + { + var methods = type.GetMethods().Where(method => method.Name == name); + + foreach (var method in methods) + { + if (method.HasParameters(parameterTypes)) + return method; + } + + return null; + } + + internal static bool HasParameters(this MethodInfo method, params Type[] parameterTypes) + { + var methodParameters = method.GetParameters().Select(parameter => parameter.ParameterType).ToArray(); + + if (methodParameters.Length != parameterTypes.Length) + return false; + + for (int i = 0; i < methodParameters.Length; i++) + if (methodParameters[i].ToString() != parameterTypes[i].ToString()) + return false; + + return true; + } + + public static IEnumerable AllInterfaces(this Type target) + { + foreach (var IF in target.GetInterfaces()) + { + yield return IF; + foreach (var childIF in IF.AllInterfaces()) + { + yield return childIF; + } + } + } + + public static IEnumerable AllMethods(this Type target) + { + var allTypes = target.AllInterfaces().ToList(); + allTypes.Add(target); + + return from type in allTypes + from method in type.GetMethods() + select method; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index d5621bf47f..28c28b1b40 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -49,17 +49,22 @@ + + + + + diff --git a/src/Umbraco.Tests/App.config b/src/Umbraco.Tests/App.config new file mode 100644 index 0000000000..c6642b945c --- /dev/null +++ b/src/Umbraco.Tests/App.config @@ -0,0 +1,16 @@ + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs b/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs new file mode 100644 index 0000000000..66efad03b9 --- /dev/null +++ b/src/Umbraco.Tests/IO/FileSystemProviderManagerTests.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Umbraco.Core.IO; + +namespace Umbraco.Tests.IO +{ + [TestFixture] + public class FileSystemProviderManagerTests + { + [Test] + public void Can_Get_File_System() + { + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(FileSystemProvider.Media); + + Assert.NotNull(fs); + } + + [Test] + public void Can_Get_Typed_File_System() + { + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + + Assert.NotNull(fs); + } + } +} diff --git a/src/Umbraco.Tests/Properties/AssemblyInfo.cs b/src/Umbraco.Tests/Properties/AssemblyInfo.cs index b28db73a43..80cbcb1115 100644 --- a/src/Umbraco.Tests/Properties/AssemblyInfo.cs +++ b/src/Umbraco.Tests/Properties/AssemblyInfo.cs @@ -33,4 +33,4 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 7b2b2cb75e..503e57c874 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -49,11 +49,13 @@ + + diff --git a/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs b/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs index 5d117f1040..9e90dfcb33 100644 --- a/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs +++ b/src/Umbraco.Web/Media/ThumbnailProviders/ImageThumbnailProvider.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.Media.ThumbnailProviders try { - var fs = FileSystemProviderManager.Current.GetFileSystemProvider(FileSystemProvider.Media); + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); var relativeThumbPath = fs.GetRelativePath(tmpThumbUrl); if (!fs.FileExists(relativeThumbPath)) return false; diff --git a/src/umbraco.cms/businesslogic/Content.cs b/src/umbraco.cms/businesslogic/Content.cs index fe7d5fdb25..00a17ea0b0 100644 --- a/src/umbraco.cms/businesslogic/Content.cs +++ b/src/umbraco.cms/businesslogic/Content.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Data; using System.Xml; +using Umbraco.Core.IO; using umbraco.cms.businesslogic.property; using umbraco.cms.businesslogic.propertytype; using umbraco.DataLayer; @@ -14,6 +15,7 @@ using umbraco.interfaces; using System.IO; using umbraco.IO; using umbraco.cms.businesslogic.datatype.controls; +using Umbraco.Core; namespace umbraco.cms.businesslogic { @@ -568,8 +570,10 @@ namespace umbraco.cms.businesslogic protected void DeleteAssociatedMediaFiles() { // Remove all files - IDataType uploadField = new Factory().GetNewObject(new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c")); + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); + var uploadField = new Factory().GetNewObject(new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c")); + foreach (Property p in GenericProperties) { var isUploadField = false; @@ -589,14 +593,23 @@ namespace umbraco.cms.businesslogic isUploadField = false; } if (isUploadField) - { - var fi = new FileInfo(IOHelper.MapPath(p.Value.ToString())); + { + var relativeFilePath = fs.GetRelativePath(p.Value.ToString()); + var parentDirectory = System.IO.Path.GetDirectoryName(relativeFilePath); + var extension = System.IO.Path.GetExtension(relativeFilePath); - fi.Directory.GetFiles().ToList().ForEach(x => + // don't want to delete the media folder if not using directories. + if (UmbracoSettings.UploadAllowDirectories && parentDirectory != fs.GetRelativePath("/")) { - x.Delete(); - }); - fi.Directory.Delete(true); + fs.DeleteDirectory(parentDirectory); + } + else + { + fs.GetFiles(parentDirectory) + .Where(x => x.StartsWith(relativeFilePath.TrimEnd(extension))) + .ToList() + .ForEach(fs.DeleteFile); + } } } } diff --git a/src/umbraco.cms/businesslogic/Files/UmbracoFile.cs b/src/umbraco.cms/businesslogic/Files/UmbracoFile.cs index a8f65839a2..70500793f4 100644 --- a/src/umbraco.cms/businesslogic/Files/UmbracoFile.cs +++ b/src/umbraco.cms/businesslogic/Files/UmbracoFile.cs @@ -19,18 +19,18 @@ namespace umbraco.cms.businesslogic.Files private string _url; private long _length; - private IFileSystem _fs; + private IMediaFileSystem _fs; #region Constructors public UmbracoFile() { - _fs = FileSystemProviderManager.Current.GetFileSystemProvider(FileSystemProvider.Media); + _fs = FileSystemProviderManager.Current.GetFileSystemProvider(); } public UmbracoFile(string path) { - _fs = FileSystemProviderManager.Current.GetFileSystemProvider(FileSystemProvider.Media); + _fs = FileSystemProviderManager.Current.GetFileSystemProvider(); _path = path; @@ -55,7 +55,7 @@ namespace umbraco.cms.businesslogic.Files public static UmbracoFile Save(Stream inputStream, string path) { - var fs = FileSystemProviderManager.Current.GetFileSystemProvider(FileSystemProvider.Media); + var fs = FileSystemProviderManager.Current.GetFileSystemProvider(); fs.AddFile(path, inputStream); return new UmbracoFile(path); diff --git a/src/umbraco.editorControls/uploadfield/uploadField.cs b/src/umbraco.editorControls/uploadfield/uploadField.cs index dbd0d6f46e..f917344031 100644 --- a/src/umbraco.editorControls/uploadfield/uploadField.cs +++ b/src/umbraco.editorControls/uploadfield/uploadField.cs @@ -19,11 +19,11 @@ namespace umbraco.editorControls private readonly String _thumbnails; private String _text; - private IFileSystem _fs; + private IMediaFileSystem _fs; public uploadField(IData Data, string ThumbnailSizes) { - _fs = FileSystemProviderManager.Current.GetFileSystemProvider(FileSystemProvider.Media); + _fs = FileSystemProviderManager.Current.GetFileSystemProvider(); _data = (cms.businesslogic.datatype.DefaultData) Data; _thumbnails = ThumbnailSizes; }