Merge origin/dev-v7-deploy into dev-v8-zbwip (builds)
This commit is contained in:
@@ -8,29 +8,32 @@ using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
public class PhysicalFileSystem : IFileSystem
|
||||
public class PhysicalFileSystem : IFileSystem2
|
||||
{
|
||||
// the rooted, filesystem path, using directory separator chars, NOT ending with a separator
|
||||
// eg "c:" or "c:\path\to\site" or "\\server\path"
|
||||
private readonly string _rootPath;
|
||||
|
||||
// the ??? url, using url separator chars, NOT ending with a separator
|
||||
// eg "" (?) or "/Scripts" or ???
|
||||
// _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 relative url, using url separator chars, NOT ending with a separator
|
||||
// eg "" or "/Views" or "/Media" or "/<vpath>/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);
|
||||
|
||||
_rootUrl = IOHelper.ResolveUrl(virtualRoot);
|
||||
_rootUrl = EnsureUrlSeparatorChar(_rootUrl);
|
||||
_rootUrl = _rootUrl.TrimEnd('/');
|
||||
_rootPath = EnsureDirectorySeparatorChar(IOHelper.MapPath(virtualRoot)).TrimEnd(Path.DirectorySeparatorChar);
|
||||
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
|
||||
_rootUrl = EnsureUrlSeparatorChar(IOHelper.ResolveUrl(virtualRoot)).TrimEnd('/');
|
||||
}
|
||||
|
||||
public PhysicalFileSystem(string rootPath, string rootUrl)
|
||||
@@ -40,22 +43,24 @@ namespace Umbraco.Core.IO
|
||||
if (rootPath.StartsWith("~/")) 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);
|
||||
rootUrl = EnsureUrlSeparatorChar(rootUrl);
|
||||
|
||||
_rootPath = rootPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||
_rootUrl = rootUrl.TrimEnd('/');
|
||||
_rootPath = EnsureDirectorySeparatorChar(rootPath).TrimEnd(Path.DirectorySeparatorChar);
|
||||
_rootPathFwd = EnsureUrlSeparatorChar(_rootPath);
|
||||
_rootUrl = EnsureUrlSeparatorChar(rootUrl).TrimEnd('/');
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets directories in a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the directory.</param>
|
||||
/// <returns>The filesystem-relative path to the directories in the directory.</returns>
|
||||
/// <remarks>Filesystem-relative paths use forward-slashes as directory separators.</remarks>
|
||||
public IEnumerable<string> GetDirectories(string path)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
@@ -77,11 +82,20 @@ namespace Umbraco.Core.IO
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the directory.</param>
|
||||
public void DeleteDirectory(string path)
|
||||
{
|
||||
DeleteDirectory(path, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the directory.</param>
|
||||
/// <param name="recursive">A value indicating whether to recursively delete sub-directories.</param>
|
||||
public void DeleteDirectory(string path, bool recursive)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
@@ -98,38 +112,71 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a directory exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the directory.</param>
|
||||
/// <returns>A value indicating whether a directory exists.</returns>
|
||||
public bool DirectoryExists(string path)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
return Directory.Exists(fullPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the file.</param>
|
||||
/// <param name="stream">A stream containing the file data.</param>
|
||||
/// <remarks>Overrides the existing file, if any.</remarks>
|
||||
public void AddFile(string path, Stream stream)
|
||||
{
|
||||
AddFile(path, stream, true);
|
||||
}
|
||||
|
||||
public void AddFile(string path, Stream stream, bool overrideIfExists)
|
||||
/// <summary>
|
||||
/// Saves a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the file.</param>
|
||||
/// <param name="stream">A stream containing the file data.</param>
|
||||
/// <param name="overrideExisting">A value indicating whether to override the existing file, if any.</param>
|
||||
/// <remarks>If a file exists and <paramref name="overrideExisting"/> is false, an exception is thrown.</remarks>
|
||||
public void AddFile(string path, Stream stream, bool overrideExisting)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
var exists = File.Exists(fullPath);
|
||||
if (exists && overrideIfExists == false)
|
||||
if (exists && overrideExisting == false)
|
||||
throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path));
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); // ensure it exists
|
||||
var directory = Path.GetDirectoryName(fullPath);
|
||||
if (directory == null) throw new InvalidOperationException("Could not get directory.");
|
||||
Directory.CreateDirectory(directory); // ensure it exists
|
||||
|
||||
if (stream.CanSeek)
|
||||
if (stream.CanSeek) // fixme - what else?
|
||||
stream.Seek(0, 0);
|
||||
|
||||
using (var destination = (Stream)File.Create(fullPath))
|
||||
using (var destination = (Stream) File.Create(fullPath))
|
||||
stream.CopyTo(destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets files in a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the directory.</param>
|
||||
/// <returns>The filesystem-relative path to the files in the directory.</returns>
|
||||
/// <remarks>Filesystem-relative paths use forward-slashes as directory separators.</remarks>
|
||||
public IEnumerable<string> GetFiles(string path)
|
||||
{
|
||||
return GetFiles(path, "*.*");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets files in a directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path of the directory.</param>
|
||||
/// <param name="filter">A filter.</param>
|
||||
/// <returns>The filesystem-relative path to the matching files in the directory.</returns>
|
||||
/// <remarks>Filesystem-relative paths use forward-slashes as directory separators.</remarks>
|
||||
public IEnumerable<string> GetFiles(string path, string filter)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
@@ -151,12 +198,21 @@ namespace Umbraco.Core.IO
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the file.</param>
|
||||
/// <returns></returns>
|
||||
public Stream OpenFile(string path)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
return File.OpenRead(fullPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the file.</param>
|
||||
public void DeleteFile(string path)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
@@ -173,52 +229,53 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a file exists.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the file.</param>
|
||||
/// <returns>A value indicating whether the file exists.</returns>
|
||||
public bool FileExists(string path)
|
||||
{
|
||||
var fullpath = GetFullPath(path);
|
||||
return File.Exists(fullpath);
|
||||
}
|
||||
|
||||
// beware, many things depend on how the GetRelative/AbsolutePath methods work!
|
||||
|
||||
/// <summary>
|
||||
/// Gets the relative path.
|
||||
/// Gets the filesystem-relative path of a full path or of an url.
|
||||
/// </summary>
|
||||
/// <param name="fullPathOrUrl">The full path or url.</param>
|
||||
/// <returns>The path, relative to this filesystem's root.</returns>
|
||||
/// <remarks>
|
||||
/// <para>The relative path is relative to this filesystem's root, not starting with any
|
||||
/// directory separator. If input was recognized as a url (path), then output uses url (path) separator
|
||||
/// chars.</para>
|
||||
/// directory separator. All separators are forward-slashes.</para>
|
||||
/// </remarks>
|
||||
public string GetRelativePath(string fullPathOrUrl)
|
||||
{
|
||||
// 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('/');
|
||||
|
||||
// test path
|
||||
path = EnsureDirectorySeparatorChar(fullPathOrUrl);
|
||||
// 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('/');
|
||||
|
||||
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar)) // if it starts with the root path
|
||||
return path.Substring(_rootPath.Length) // strip it
|
||||
.TrimStart(Path.DirectorySeparatorChar); // it's relative
|
||||
|
||||
// unchanged - including separators
|
||||
return fullPathOrUrl;
|
||||
// unchanged - what else?
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full path.
|
||||
/// </summary>
|
||||
/// <param name="path">The full or relative path.</param>
|
||||
/// <param name="path">The full or filesystem-relative path.</param>
|
||||
/// <returns>The full path.</returns>
|
||||
/// <remarks>
|
||||
/// <para>On the physical filesystem, the full path is the rooted (ie non-relative), safe (ie within this
|
||||
/// filesystem's root) path. All separators are converted to Path.DirectorySeparatorChar.</para>
|
||||
/// filesystem's root) path. All separators are Path.DirectorySeparatorChar.</para>
|
||||
/// </remarks>
|
||||
public string GetFullPath(string path)
|
||||
{
|
||||
@@ -226,49 +283,82 @@ namespace Umbraco.Core.IO
|
||||
var opath = path;
|
||||
path = EnsureDirectorySeparatorChar(path);
|
||||
|
||||
// fixme - this part should go!
|
||||
// not sure what we are doing here - so if input starts with a (back) slash,
|
||||
// we assume it's not a FS relative path and we try to convert it... but it
|
||||
// really makes little sense?
|
||||
if (path.StartsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
path = GetRelativePath(path);
|
||||
|
||||
// if already a full path, return
|
||||
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
|
||||
return path;
|
||||
// if not already rooted, combine with the root path
|
||||
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar) == false)
|
||||
path = Path.Combine(_rootPath, path);
|
||||
|
||||
// else combine and sanitize, ie GetFullPath will take care of any relative
|
||||
// sanitize - GetFullPath will take care of any relative
|
||||
// segments in path, eg '../../foo.tmp' - it may throw a SecurityException
|
||||
// if the combined path reaches illegal parts of the filesystem
|
||||
var fpath = Path.Combine(_rootPath, path);
|
||||
fpath = Path.GetFullPath(fpath);
|
||||
path = Path.GetFullPath(path);
|
||||
|
||||
// at that point, path is within legal parts of the filesystem, ie we have
|
||||
// permissions to reach that path, but it may nevertheless be outside of
|
||||
// our root path, due to relative segments, so better check
|
||||
if (IOHelper.PathStartsWith(fpath, _rootPath, Path.DirectorySeparatorChar))
|
||||
return fpath;
|
||||
if (IOHelper.PathStartsWith(path, _rootPath, Path.DirectorySeparatorChar))
|
||||
return path;
|
||||
|
||||
// nothing prevents us to reach the file, security-wise, yet it is outside
|
||||
// this filesystem's root - throw
|
||||
throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path.</param>
|
||||
/// <returns>The url.</returns>
|
||||
/// <remarks>All separators are forward-slashes.</remarks>
|
||||
public string GetUrl(string path)
|
||||
{
|
||||
path = EnsureUrlSeparatorChar(path).Trim('/');
|
||||
return _rootUrl + "/" + path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last-modified date of a directory or file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the directory or the file.</param>
|
||||
/// <returns>The last modified date of the directory or the file.</returns>
|
||||
public DateTimeOffset GetLastModified(string path)
|
||||
{
|
||||
return DirectoryExists(path)
|
||||
? new DirectoryInfo(GetFullPath(path)).LastWriteTimeUtc
|
||||
: new FileInfo(GetFullPath(path)).LastWriteTimeUtc;
|
||||
var fullpath = GetFullPath(path);
|
||||
return DirectoryExists(fullpath)
|
||||
? new DirectoryInfo(fullpath).LastWriteTimeUtc
|
||||
: new FileInfo(fullpath).LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the created date of a directory or file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the directory or the file.</param>
|
||||
/// <returns>The created date of the directory or the file.</returns>
|
||||
public DateTimeOffset GetCreated(string path)
|
||||
{
|
||||
return DirectoryExists(path)
|
||||
? Directory.GetCreationTimeUtc(GetFullPath(path))
|
||||
: File.GetCreationTimeUtc(GetFullPath(path));
|
||||
var fullpath = GetFullPath(path);
|
||||
return DirectoryExists(fullpath)
|
||||
? Directory.GetCreationTimeUtc(fullpath)
|
||||
: File.GetCreationTimeUtc(fullpath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size of a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The filesystem-relative path to the file.</param>
|
||||
/// <returns>The file of the size, in bytes.</returns>
|
||||
/// <remarks>If the file does not exist, returns -1.</remarks>
|
||||
public long GetSize(string path)
|
||||
{
|
||||
var fullPath = GetFullPath(path);
|
||||
var file = new FileInfo(fullPath);
|
||||
return file.Exists ? file.Length : -1;
|
||||
}
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
Reference in New Issue
Block a user