From c625561b95dcf49995359d65caaee4165a871625 Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Thu, 19 Jul 2018 13:18:41 +0300 Subject: [PATCH] Resolved #306: Allow to add js/css files to Pages/Views folders on development. --- .../Styles/CssRelativePathAdjuster.cs | 5 +- .../Mvc/UI/Bundling/Styles/StyleBundler.cs | 17 +++- ...lFileSystemApplicationBuilderExtensions.cs | 25 ++++-- .../AspNetCoreContentOptions.cs | 28 +++++++ .../FileProviderSubFolderWrapper.cs | 82 +++++++++++++++++++ .../HybridWebRootFileProvider.cs | 71 +++++++++++++--- .../IHybridWebRootFileProvider.cs | 2 +- .../DictionaryBasedFileProvider.cs | 2 +- .../FileProviderSubFolderWrapper.cs | 39 --------- .../Pages/Identity/Roles/Index.cshtml | 2 +- .../roles => Pages/Identity/Roles}/index.js | 0 .../Pages/Identity/Users/Index.cshtml | 2 +- .../users => Pages/Identity/Users}/index.js | 0 .../Volo.Abp.Identity.Web.csproj | 4 +- 14 files changed, 211 insertions(+), 68 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs create mode 100644 framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/FileProviderSubFolderWrapper.cs delete mode 100644 framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/FileProviderSubFolderWrapper.cs rename modules/identity/src/Volo.Abp.Identity.Web/{wwwroot/pages/identity/roles => Pages/Identity/Roles}/index.js (100%) rename modules/identity/src/Volo.Abp.Identity.Web/{wwwroot/pages/identity/users => Pages/Identity/Users}/index.js (100%) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/CssRelativePathAdjuster.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/CssRelativePathAdjuster.cs index 983d823937..60f4b0e1bc 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/CssRelativePathAdjuster.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/CssRelativePathAdjuster.cs @@ -8,7 +8,10 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles { private static readonly Regex _rxUrl = new Regex(@"url\s*\(\s*([""']?)([^:)]+)\1\s*\)", RegexOptions.IgnoreCase | RegexOptions.Compiled); - public static string Adjust(string cssFileContents, string absoluteInputFilePath, string absoluteOutputPath) + public static string Adjust( + string cssFileContents, + string absoluteInputFilePath, + string absoluteOutputPath) { var matches = _rxUrl.Matches(cssFileContents); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs index 8849a58e75..c7c7d681a9 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/Styles/StyleBundler.cs @@ -1,3 +1,6 @@ +using System; +using System.IO; +using Microsoft.AspNetCore.Hosting; using Volo.Abp.AspNetCore.Mvc.UI.Minification.Styles; using Volo.Abp.AspNetCore.VirtualFileSystem; @@ -5,21 +8,27 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles { public class StyleBundler : BundlerBase, IStyleBundler { + private readonly IHostingEnvironment _hostingEnvironment; public override string FileExtension => "css"; - public StyleBundler(IHybridWebRootFileProvider webRootFileProvider, ICssMinifier minifier) + public StyleBundler(IHybridWebRootFileProvider webRootFileProvider, ICssMinifier minifier, IHostingEnvironment hostingEnvironment) : base(webRootFileProvider, minifier) { - + _hostingEnvironment = hostingEnvironment; } protected override string GetFileContent(IBundlerContext context, string file) { return CssRelativePath.Adjust( base.GetFileContent(context, file), - WebRootFileProvider.GetAbsolutePath(file), - WebRootFileProvider.GetAbsolutePath(context.BundleRelativePath) + GetAbsolutePath(file), + GetAbsolutePath(context.BundleRelativePath) ); } + + public string GetAbsolutePath(string relativePath) + { + return Path.Combine(_hostingEnvironment.ContentRootPath, "wwwroot", relativePath.RemovePreFix("/")); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs index 652b83918a..5ad7bff1d1 100644 --- a/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore/Microsoft/AspNetCore/Builder/VirtualFileSystemApplicationBuilderExtensions.cs @@ -1,5 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.VirtualFileSystem; namespace Microsoft.AspNetCore.Builder @@ -8,15 +11,23 @@ namespace Microsoft.AspNetCore.Builder { public static void UseVirtualFiles(this IApplicationBuilder app) { - IFileProvider fileProvider = new FileProviderSubFolderWrapper( - app.ApplicationServices.GetRequiredService(), - "/wwwroot" - ); - + //var options = app.ApplicationServices.GetRequiredService>().Value; + //var hostingEnvironment = app.ApplicationServices.GetRequiredService(); + + //var fileProvider = new FileProviderSubFolderWrapper( + // new CompositeFileProvider( + // new PhysicalFileProvider(hostingEnvironment.ContentRootPath), + // app.ApplicationServices.GetRequiredService() + // ), + // "/wwwroot", + // options.AllowedExtraWebContentFolders.ToArray(), + // options.AllowedExtraWebContentFileExtensions.ToArray() + //); + app.UseStaticFiles( new StaticFileOptions { - FileProvider = fileProvider + FileProvider = app.ApplicationServices.GetRequiredService() } ); } diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs new file mode 100644 index 0000000000..d5233c7956 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/AspNetCoreContentOptions.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Volo.Abp.AspNetCore.VirtualFileSystem +{ + public class AspNetCoreContentOptions + { + public List AllowedExtraWebContentFolders { get; } + public List AllowedExtraWebContentFileExtensions { get; } + + public AspNetCoreContentOptions() + { + AllowedExtraWebContentFolders = new List + { + "/Pages", + "/Views" + }; + + AllowedExtraWebContentFileExtensions = new List + { + ".js", + ".css", + ".png", + ".jpg", + ".jpeg" + }; + } + } +} diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/FileProviderSubFolderWrapper.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/FileProviderSubFolderWrapper.cs new file mode 100644 index 0000000000..ca5b1fe879 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/FileProviderSubFolderWrapper.cs @@ -0,0 +1,82 @@ +//using System; +//using System.Linq; +//using JetBrains.Annotations; +//using Microsoft.Extensions.FileProviders; +//using Microsoft.Extensions.Primitives; + +//namespace Volo.Abp.VirtualFileSystem +//{ +// //TODO: Ensure that merging such subfolders is secure! (anyone can add /.. to the beginning!)..? + +// public class FileProviderSubFolderWrapper : IFileProvider +// { +// private readonly IFileProvider _fileProvider; +// private readonly string _rootPath; +// private readonly string[] _allowedExtraFolders; +// private readonly string[] _allowedExtraFileExtensions; + +// public FileProviderSubFolderWrapper( +// IFileProvider fileProvider, +// string rootPath, +// string[] allowedExtraFolders = null, +// string[] allowedExtraFileExtensions = null) +// { +// _fileProvider = fileProvider; +// _rootPath = rootPath; +// _allowedExtraFileExtensions = allowedExtraFileExtensions ?? Array.Empty(); +// _allowedExtraFolders = allowedExtraFolders ?? Array.Empty(); +// } + +// public IFileInfo GetFileInfo(string subpath) +// { +// Check.NotNullOrEmpty(subpath, nameof(subpath)); + +// if (ExtraAllowedPath(subpath)) +// { +// var fileInfo = _fileProvider.GetFileInfo(subpath); +// if (fileInfo.Exists) +// { +// return fileInfo; +// } +// } + +// return _fileProvider.GetFileInfo(_rootPath + subpath); +// } + +// public IDirectoryContents GetDirectoryContents([NotNull] string subpath) +// { +// Check.NotNullOrEmpty(subpath, nameof(subpath)); + +// if (ExtraAllowedPath(subpath)) +// { +// var directory = _fileProvider.GetDirectoryContents(subpath); +// if (directory.Exists) +// { +// return directory; +// } +// } + +// return _fileProvider.GetDirectoryContents(_rootPath + subpath); +// } + +// private bool ExtraAllowedPath(string path) +// { +// return ExtraAllowedFolder(path) && ExtraAllowedExtension(path); +// } + +// private bool ExtraAllowedFolder(string path) +// { +// return _allowedExtraFolders.Any(s => path.StartsWith(s, StringComparison.OrdinalIgnoreCase)); +// } + +// private bool ExtraAllowedExtension(string path) +// { +// return _allowedExtraFileExtensions.Any(e => path.EndsWith(e, StringComparison.OrdinalIgnoreCase)); +// } + +// public IChangeToken Watch(string filter) +// { +// return _fileProvider.Watch(filter); //TODO: Why this does not use NormalizePath? +// } +// } +//} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs index 73658c7391..021aeec35b 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/HybridWebRootFileProvider.cs @@ -1,53 +1,102 @@ using System; using System.IO; +using System.Linq; +using JetBrains.Annotations; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Volo.Abp.DependencyInjection; using Volo.Abp.VirtualFileSystem; namespace Volo.Abp.AspNetCore.VirtualFileSystem { + //TODO: How to handle wwwroot naming? public class HybridWebRootFileProvider : IHybridWebRootFileProvider, ISingletonDependency { private readonly IVirtualFileProvider _virtualFileProvider; private readonly IFileProvider _fileProvider; private readonly IHostingEnvironment _hostingEnvironment; + private string _rootPath = "/wwwroot"; - public HybridWebRootFileProvider(IVirtualFileProvider virtualFileProvider, IHostingEnvironment hostingEnvironment) + protected AspNetCoreContentOptions Options { get; } + + public HybridWebRootFileProvider( + IVirtualFileProvider virtualFileProvider, + IHostingEnvironment hostingEnvironment, + IOptions options) { _virtualFileProvider = virtualFileProvider; _hostingEnvironment = hostingEnvironment; + Options = options.Value; _fileProvider = CreateHybridProvider(); } - public virtual IFileInfo GetFileInfo(string subpath) + public IFileInfo GetFileInfo(string subpath) { - return _fileProvider.GetFileInfo("/wwwroot" + subpath); //TODO: Hard-coded "/wwwroot" is not good! + Check.NotNullOrEmpty(subpath, nameof(subpath)); + + if (ExtraAllowedFolder(subpath) && ExtraAllowedExtension(subpath)) + { + var fileInfo = _fileProvider.GetFileInfo(subpath); + if (fileInfo.Exists) + { + return fileInfo; + } + } + + return _fileProvider.GetFileInfo(_rootPath + subpath); } - public virtual IDirectoryContents GetDirectoryContents(string subpath) + public IDirectoryContents GetDirectoryContents([NotNull] string subpath) { - return _fileProvider.GetDirectoryContents("/wwwroot" + subpath); + Check.NotNullOrEmpty(subpath, nameof(subpath)); + + if (ExtraAllowedFolder(subpath)) + { + var directory = _fileProvider.GetDirectoryContents(subpath); + if (directory.Exists) + { + return directory; + } + } + + return _fileProvider.GetDirectoryContents(_rootPath + subpath); } public virtual IChangeToken Watch(string filter) { - return _fileProvider.Watch("/wwwroot" + filter); - } + if (!ExtraAllowedFolder(filter)) + { + return _fileProvider.Watch("/wwwroot" + filter); + } - public string GetAbsolutePath(string relativePath) - { - return Path.Combine(_hostingEnvironment.ContentRootPath, "wwwroot", relativePath.RemovePreFix("/")); + return new CompositeChangeToken( + new[] + { + _fileProvider.Watch("/wwwroot" + filter), + _fileProvider.Watch(filter) + } + ); } protected virtual IFileProvider CreateHybridProvider() { return new CompositeFileProvider( - _hostingEnvironment.ContentRootFileProvider, + new PhysicalFileProvider(_hostingEnvironment.ContentRootPath), _virtualFileProvider ); } + + protected virtual bool ExtraAllowedFolder(string path) + { + return Options.AllowedExtraWebContentFolders.Any(s => path.StartsWith(s, StringComparison.OrdinalIgnoreCase)); + } + + protected virtual bool ExtraAllowedExtension(string path) + { + return Options.AllowedExtraWebContentFileExtensions.Any(e => path.EndsWith(e, StringComparison.OrdinalIgnoreCase)); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs index 47f938cc86..f639d6e499 100644 --- a/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore/Volo/Abp/AspNetCore/VirtualFileSystem/IHybridWebRootFileProvider.cs @@ -4,6 +4,6 @@ namespace Volo.Abp.AspNetCore.VirtualFileSystem { public interface IHybridWebRootFileProvider : IFileProvider { - string GetAbsolutePath(string relativePath); + } } diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/DictionaryBasedFileProvider.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/DictionaryBasedFileProvider.cs index 2cd14e0402..4fc72c07ec 100644 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/DictionaryBasedFileProvider.cs +++ b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/DictionaryBasedFileProvider.cs @@ -31,7 +31,7 @@ namespace Volo.Abp.VirtualFileSystem var directory = GetFileInfo(subpath); if (!directory.IsDirectory) { - return new NotFoundDirectoryContents(); + return NotFoundDirectoryContents.Singleton; } var fileList = new List(); diff --git a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/FileProviderSubFolderWrapper.cs b/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/FileProviderSubFolderWrapper.cs deleted file mode 100644 index 188a0d5b60..0000000000 --- a/framework/src/Volo.Abp.VirtualFileSystem/Volo/Abp/VirtualFileSystem/FileProviderSubFolderWrapper.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Primitives; - -namespace Volo.Abp.VirtualFileSystem -{ - public class FileProviderSubFolderWrapper : IFileProvider - { - private readonly IFileProvider _fileProvider; - private readonly string _contentPath; - - public FileProviderSubFolderWrapper(IFileProvider fileProvider, string contentPath = null) - { - _contentPath = contentPath; - _fileProvider = fileProvider; - } - - public IFileInfo GetFileInfo(string subpath) - { - return _fileProvider.GetFileInfo(NormalizePath(subpath)); - } - - public IDirectoryContents GetDirectoryContents(string subpath) - { - return _fileProvider.GetDirectoryContents(NormalizePath(subpath)); - } - - private string NormalizePath(string subpath) - { - return _contentPath == null - ? subpath - : _contentPath + subpath; - } - - public IChangeToken Watch(string filter) - { - return _fileProvider.Watch(filter); //TODO: Why this does not use NormalizePath? - } - } -} \ No newline at end of file diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml index 88018089d7..4e2ddbc422 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/Index.cshtml @@ -7,7 +7,7 @@ @section scripts { - + } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/wwwroot/pages/identity/roles/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js similarity index 100% rename from modules/identity/src/Volo.Abp.Identity.Web/wwwroot/pages/identity/roles/index.js rename to modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml index 016456829c..99f3abc8cc 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/Index.cshtml @@ -10,7 +10,7 @@ @section scripts { - + } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/wwwroot/pages/identity/users/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js similarity index 100% rename from modules/identity/src/Volo.Abp.Identity.Web/wwwroot/pages/identity/users/index.js rename to modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj index 21276979d2..3735fe6100 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj +++ b/modules/identity/src/Volo.Abp.Identity.Web/Volo.Abp.Identity.Web.csproj @@ -15,18 +15,18 @@ - - + +