diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index b71a4cf0c7..baa8c1d6d4 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -39,6 +39,11 @@ namespace Umbraco.Core.IO public static long GetSize(this IFileSystem fs, string path) { + // unwrap, eg MediaFileSystem is wrapping an IFileSystem + FileSystemWrapper w; + while ((w = fs as FileSystemWrapper) != null) + fs = w.Wrapped; + // no idea why GetSize is not part of IFileSystem, but // for physical file system we have way better & faster ways // to get the size, than to read the entire thing in memory! diff --git a/src/Umbraco.Core/IO/FileSystemWrapper.cs b/src/Umbraco.Core/IO/FileSystemWrapper.cs index 676626d069..db4ab115f6 100644 --- a/src/Umbraco.Core/IO/FileSystemWrapper.cs +++ b/src/Umbraco.Core/IO/FileSystemWrapper.cs @@ -23,6 +23,8 @@ namespace Umbraco.Core.IO _wrapped = wrapped; } + internal IFileSystem Wrapped { get { return _wrapped; } } + public IEnumerable GetDirectories(string path) { return _wrapped.GetDirectories(path); diff --git a/src/Umbraco.Core/IO/IFileSystem.cs b/src/Umbraco.Core/IO/IFileSystem.cs index b4e173b256..3e1c1e3527 100644 --- a/src/Umbraco.Core/IO/IFileSystem.cs +++ b/src/Umbraco.Core/IO/IFileSystem.cs @@ -16,10 +16,10 @@ namespace Umbraco.Core.IO void DeleteDirectory(string path, bool recursive); bool DirectoryExists(string path); - + void AddFile(string path, Stream stream); - void AddFile(string path, Stream stream, bool overrideExisting); + void AddFile(string path, Stream stream, bool overrideIfExists); IEnumerable GetFiles(string path); diff --git a/src/Umbraco.Core/IO/MediaFileSystem.cs b/src/Umbraco.Core/IO/MediaFileSystem.cs index face234970..b6ef6f7bf5 100644 --- a/src/Umbraco.Core/IO/MediaFileSystem.cs +++ b/src/Umbraco.Core/IO/MediaFileSystem.cs @@ -51,6 +51,7 @@ namespace Umbraco.Core.IO // MediaFileSystem is not just IFileSystem // etc + [Obsolete("", true)] public IEnumerable GetThumbnails(string path) { var parentDirectory = Path.GetDirectoryName(path); @@ -61,7 +62,8 @@ namespace Umbraco.Core.IO .ToList(); } - public void DeleteFile(string path, bool deleteThumbnails) + [Obsolete("", true)] + public void DeleteFile(string path, bool deleteThumbnails) { DeleteFile(path); @@ -71,13 +73,15 @@ namespace Umbraco.Core.IO DeleteThumbnails(path); } - public void DeleteThumbnails(string path) + [Obsolete("", true)] + public void DeleteThumbnails(string path) { GetThumbnails(path) .ForEach(DeleteFile); } - public void CopyThumbnails(string sourcePath, string targetPath) + [Obsolete("", true)] + public void CopyThumbnails(string sourcePath, string targetPath) { var targetPathBase = Path.GetDirectoryName(targetPath) ?? ""; foreach (var sourceThumbPath in GetThumbnails(sourcePath)) diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index 7456fb29a4..6b07ff21f8 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -12,27 +12,26 @@ namespace Umbraco.Core.IO // eg "c:" or "c:\path\to\site" or "\\server\path" private readonly string _rootPath; - // _rootPath, with separators replaced by forward-slashes. + // _rootPath, but with separators replaced by forward-slashes + // eg "c:" or "c:/path/to/site" or "//server/path" + // (is used in GetRelativePath) private readonly string _rootPathFwd; - // the ??? url, using url separator chars, NOT ending with a separator - // eg "" (?) or "/Scripts" or ??? + // the relative url, using url separator chars, NOT ending with a separator + // eg "" or "/Views" or "/Media" or "//Media" in case of a virtual path private readonly string _rootUrl; + // virtualRoot should be "~/path/to/root" eg "~/Views" + // the "~/" is mandatory. public PhysicalFileSystem(string virtualRoot) { if (virtualRoot == null) throw new ArgumentNullException("virtualRoot"); if (virtualRoot.StartsWith("~/") == false) throw new ArgumentException("The virtualRoot argument must be a virtual path and start with '~/'"); - _rootPath = IOHelper.MapPath(virtualRoot); - _rootPath = EnsureDirectorySeparatorChar(_rootPath); - _rootPath = _rootPath.TrimEnd(Path.DirectorySeparatorChar); + _rootPath = EnsureDirectorySeparatorChar(IOHelper.MapPath(virtualRoot)).TrimEnd(Path.DirectorySeparatorChar); _rootPathFwd = EnsureUrlSeparatorChar(_rootPath); - - _rootUrl = IOHelper.ResolveUrl(virtualRoot); - _rootUrl = EnsureUrlSeparatorChar(_rootUrl); - _rootUrl = _rootUrl.TrimEnd('/'); + _rootUrl = EnsureUrlSeparatorChar(IOHelper.ResolveUrl(virtualRoot)).TrimEnd('/'); } public PhysicalFileSystem(string rootPath, string rootUrl) @@ -47,19 +46,16 @@ namespace Umbraco.Core.IO throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'"); // rootPath should be... rooted, as in, it's a root path! - // but the test suite App.config cannot really "root" anything so we'll have to do it here - - //var localRoot = AppDomain.CurrentDomain.BaseDirectory; - var localRoot = IOHelper.GetRootDirectorySafe(); if (Path.IsPathRooted(rootPath) == false) + { + // but the test suite App.config cannot really "root" anything so we have to do it here + var localRoot = IOHelper.GetRootDirectorySafe(); rootPath = Path.Combine(localRoot, rootPath); + } - rootPath = EnsureDirectorySeparatorChar(rootPath); - _rootPath = rootPath.TrimEnd(Path.DirectorySeparatorChar); + _rootPath = EnsureDirectorySeparatorChar(rootPath).TrimEnd(Path.DirectorySeparatorChar); _rootPathFwd = EnsureUrlSeparatorChar(_rootPath); - - rootUrl = EnsureUrlSeparatorChar(rootUrl); - _rootUrl = rootUrl.TrimEnd('/'); + _rootUrl = EnsureUrlSeparatorChar(rootUrl).TrimEnd('/'); } /// @@ -152,7 +148,7 @@ namespace Umbraco.Core.IO { var fullPath = GetFullPath(path); var exists = File.Exists(fullPath); - if (exists && overrideExisting == false) + if (exists && overrideExisting == false) throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path)); var directory = Path.GetDirectoryName(fullPath); @@ -261,13 +257,15 @@ namespace Umbraco.Core.IO // test url var path = fullPathOrUrl.Replace('\\', '/'); // ensure url separator char - if (IOHelper.PathStartsWith(path, _rootUrl, '/')) // if it starts with the root url... - return path.Substring(_rootUrl.Length) // strip it - .TrimStart('/'); // it's relative + // if it starts with the root url, strip it and trim the starting slash to make it relative + // eg "/Media/1234/img.jpg" => "1234/img.jpg" + if (IOHelper.PathStartsWith(path, _rootUrl, '/')) + return path.Substring(_rootUrl.Length).TrimStart('/'); - if (IOHelper.PathStartsWith(path, _rootPathFwd, '/')) // if it starts with the root url... - return path.Substring(_rootPathFwd.Length) // strip it - .TrimStart('/'); // it's relative + // if it starts with the root path, strip it and trim the starting slash to make it relative + // eg "c:/websites/test/root/Media/1234/img.jpg" => "1234/img.jpg" + if (IOHelper.PathStartsWith(path, _rootPathFwd, '/')) + return path.Substring(_rootPathFwd.Length).TrimStart('/'); // unchanged - what else? return path; diff --git a/src/Umbraco.Core/IO/UmbracoMediaFile.cs b/src/Umbraco.Core/IO/UmbracoMediaFile.cs index 17162bcfa8..27699baef4 100644 --- a/src/Umbraco.Core/IO/UmbracoMediaFile.cs +++ b/src/Umbraco.Core/IO/UmbracoMediaFile.cs @@ -111,7 +111,7 @@ namespace Umbraco.Core.IO { get { - if (_length != null) return _length.Value; + if (_length.HasValue) return _length.Value; _length = Exists ? _fs.GetSize(Path) : -1; return _length.Value; } @@ -129,7 +129,7 @@ namespace Umbraco.Core.IO public Size GetDimensions() { - if (_size != null) return _size.Value; + if (_size.HasValue) return _size.Value; if (_fs.FileExists(Path)) { @@ -157,12 +157,12 @@ namespace Umbraco.Core.IO return _fs.GetUrl(filepath); } - public string Resize(int maxWidthHeight, string filenameAddition) + public string Resize(int maxWidthHeight, string fileNameAddition) { if (Exists == false) return string.Empty; EnsureFileSupportsResizing(); - var filepath = Resize(-1, -1, maxWidthHeight, filenameAddition); + var filepath = Resize(-1, -1, maxWidthHeight, fileNameAddition); return _fs.GetUrl(filepath); } diff --git a/src/Umbraco.Core/Media/ImageHelper.cs b/src/Umbraco.Core/Media/ImageHelper.cs index 281a229d49..a9775321e8 100644 --- a/src/Umbraco.Core/Media/ImageHelper.cs +++ b/src/Umbraco.Core/Media/ImageHelper.cs @@ -89,6 +89,47 @@ namespace Umbraco.Core.Media return codec.MimeType; } + #region Manage thumbnails + + public static IEnumerable GetThumbnails(IFileSystem fs, string path) + { + var parentDirectory = Path.GetDirectoryName(path); + var extension = Path.GetExtension(path); + + return fs.GetFiles(parentDirectory) + .Where(x => x.StartsWith(path.TrimEnd(extension) + "_thumb") || x.StartsWith(path.TrimEnd(extension) + "_big-thumb")) + .ToList(); + } + + public static void DeleteFile(IFileSystem fs, string path, bool deleteThumbnails) + { + fs.DeleteFile(path); + + if (deleteThumbnails == false) + return; + + DeleteThumbnails(fs, path); + } + + public static void DeleteThumbnails(IFileSystem fs, string path) + { + GetThumbnails(fs, path) + .ForEach(fs.DeleteFile); + } + + public static void CopyThumbnails(IFileSystem fs, string sourcePath, string targetPath) + { + var targetPathBase = Path.GetDirectoryName(targetPath) ?? ""; + foreach (var sourceThumbPath in GetThumbnails(fs, sourcePath)) + { + var sourceThumbFilename = Path.GetFileName(sourceThumbPath) ?? ""; + var targetThumbPath = Path.Combine(targetPathBase, sourceThumbFilename); + fs.CopyFile(sourceThumbPath, targetThumbPath); + } + } + + #endregion + #region GenerateThumbnails public static IEnumerable GenerateThumbnails( diff --git a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs index 7addacdd87..cf5c883dc0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/TemplateRepository.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories _viewsFileSystem = viewFileSystem; _templateConfig = templateConfig; _viewHelper = new ViewHelper(_viewsFileSystem); - _masterPageHelper = new MasterPageHelper(_masterpagesFileSystem); + _masterPageHelper = new MasterPageHelper(_masterpagesFileSystem); } @@ -80,7 +80,7 @@ namespace Umbraco.Core.Persistence.Repositories if (dtos.Count == 0) return Enumerable.Empty(); - //look up the simple template definitions that have a master template assigned, this is used + //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties var childIds = (ids.Any() ? GetAxisDefinitions(dtos.ToArray()) @@ -90,7 +90,7 @@ namespace Umbraco.Core.Persistence.Repositories ParentId = x.NodeDto.ParentId, Name = x.Alias })).ToArray(); - + return dtos.Select(d => MapFromDto(d, childIds)); } @@ -104,7 +104,7 @@ namespace Umbraco.Core.Persistence.Repositories if (dtos.Count == 0) return Enumerable.Empty(); - //look up the simple template definitions that have a master template assigned, this is used + //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties var childIds = GetAxisDefinitions(dtos.ToArray()).ToArray(); @@ -311,14 +311,14 @@ namespace Umbraco.Core.Persistence.Repositories { var masterpageName = string.Concat(entity.Alias, ".master"); _masterpagesFileSystem.DeleteFile(masterpageName); - } + } } #endregion private IEnumerable GetAxisDefinitions(params TemplateDto[] templates) { - //look up the simple template definitions that have a master template assigned, this is used + //look up the simple template definitions that have a master template assigned, this is used // later to populate the template item's properties var childIdsSql = new Sql() .Select("nodeId,alias,parentID") @@ -327,7 +327,7 @@ namespace Umbraco.Core.Persistence.Repositories .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId) //lookup axis's .Where("umbracoNode." + SqlSyntax.GetQuotedColumnName("id") + " IN (@parentIds) OR umbracoNode.parentID IN (@childIds)", - new {parentIds = templates.Select(x => x.NodeDto.ParentId), childIds = templates.Select(x => x.NodeId)}); + new {parentIds = templates.Select(x => x.NodeDto.ParentId), childIds = templates.Select(x => x.NodeId)}); var childIds = Database.Fetch(childIdsSql) .Select(x => new UmbracoEntity @@ -542,7 +542,7 @@ namespace Umbraco.Core.Persistence.Repositories //return the list - it will be naturally ordered by level return descendants; } - + public IEnumerable GetDescendants(string alias) { var all = base.GetAll().ToArray(); @@ -586,7 +586,7 @@ namespace Umbraco.Core.Persistence.Repositories /// [Obsolete("Use GetDescendants instead")] public TemplateNode GetTemplateNode(string alias) - { + { //first get all template objects var allTemplates = base.GetAll().ToArray(); @@ -595,7 +595,7 @@ namespace Umbraco.Core.Persistence.Repositories { return null; } - + var top = selfTemplate; while (top.MasterTemplateAlias.IsNullOrWhiteSpace() == false) { @@ -646,16 +646,16 @@ namespace Umbraco.Core.Persistence.Repositories } /// - /// This checks what the default rendering engine is set in config but then also ensures that there isn't already - /// a template that exists in the opposite rendering engine's template folder, then returns the appropriate + /// This checks what the default rendering engine is set in config but then also ensures that there isn't already + /// a template that exists in the opposite rendering engine's template folder, then returns the appropriate /// rendering engine to use. - /// + /// /// /// /// The reason this is required is because for example, if you have a master page file already existing under ~/masterpages/Blah.aspx - /// and then you go to create a template in the tree called Blah and the default rendering engine is MVC, it will create a Blah.cshtml - /// empty template in ~/Views. This means every page that is using Blah will go to MVC and render an empty page. - /// This is mostly related to installing packages since packages install file templates to the file system and then create the + /// and then you go to create a template in the tree called Blah and the default rendering engine is MVC, it will create a Blah.cshtml + /// empty template in ~/Views. This means every page that is using Blah will go to MVC and render an empty page. + /// This is mostly related to installing packages since packages install file templates to the file system and then create the /// templates in business logic. Without this, it could cause the wrong rendering engine to be used for a package. /// public RenderingEngine DetermineTemplateRenderingEngine(ITemplate template) @@ -763,7 +763,7 @@ namespace Umbraco.Core.Persistence.Repositories /// private void EnsureValidAlias(ITemplate template) { - //ensure unique alias + //ensure unique alias template.Alias = template.Alias.ToCleanString(CleanStringType.UnderscoreAlias); if (template.Alias.Length > 100) diff --git a/src/Umbraco.Web/Editors/ImagesController.cs b/src/Umbraco.Web/Editors/ImagesController.cs index b732dbd79e..eae620e745 100644 --- a/src/Umbraco.Web/Editors/ImagesController.cs +++ b/src/Umbraco.Web/Editors/ImagesController.cs @@ -112,8 +112,8 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.NotFound); //redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file - var response = Request.CreateResponse( HttpStatusCode.Found ); - var imageLastModified = fs.GetLastModified( imagePath ); + var response = Request.CreateResponse(HttpStatusCode.Found); + var imageLastModified = fs.GetLastModified(imagePath); response.Headers.Location = new Uri( string.Format( "{0}?rnd={1}&width={2}", imagePath, string.Format( "{0:yyyyMMddHHmmss}", imageLastModified ), width ), UriKind.Relative ); return response; }