From 4a54215fa1280232e1efaed7c8a5280bf8ba0c65 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:36:32 +0100 Subject: [PATCH] V13: Add rcl files to static files tree (#17671) * Refactor to use IWebhostEnvironment for wwwroot files. * Obsolete ctor * Add ActivatorUtiiliesConstructor * Refactor logic to include folders * Fix so we can see files in wwwroot/App_Plugins * Cosmetic: Use invariant comparison for the "umbraco" folder". --------- Co-authored-by: kjac --- .../Trees/StaticFilesTreeController.cs | 156 ++++++++++++++---- 1 file changed, 122 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Trees/StaticFilesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/StaticFilesTreeController.cs index 7e2b21f9c6..92931f14ca 100644 --- a/src/Umbraco.Web.BackOffice/Trees/StaticFilesTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/StaticFilesTreeController.cs @@ -1,7 +1,11 @@ using System.Net; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Services; @@ -17,61 +21,57 @@ public class StaticFilesTreeController : TreeController private const string Webroot = "wwwroot"; private readonly IFileSystem _fileSystem; private readonly IMenuItemCollectionFactory _menuItemCollectionFactory; + private readonly IWebHostEnvironment _webHostEnvironment; + [ActivatorUtilitiesConstructor] + public StaticFilesTreeController( + ILocalizedTextService localizedTextService, + UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, + IEventAggregator eventAggregator, + IPhysicalFileSystem fileSystem, + IMenuItemCollectionFactory menuItemCollectionFactory, + IWebHostEnvironment webHostEnvironment) + : base(localizedTextService, umbracoApiControllerTypeCollection, eventAggregator) + { + _fileSystem = fileSystem; + _menuItemCollectionFactory = menuItemCollectionFactory; + _webHostEnvironment = webHostEnvironment; + } + + [Obsolete("Obsolete, use ctor that takes an IWebHostEnvironment, will be removed in future versions.")] public StaticFilesTreeController( ILocalizedTextService localizedTextService, UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection, IEventAggregator eventAggregator, IPhysicalFileSystem fileSystem, IMenuItemCollectionFactory menuItemCollectionFactory) - : base(localizedTextService, umbracoApiControllerTypeCollection, eventAggregator) + : this(localizedTextService, umbracoApiControllerTypeCollection, eventAggregator, fileSystem, menuItemCollectionFactory, StaticServiceProvider.Instance.GetRequiredService()) { - _fileSystem = fileSystem; - _menuItemCollectionFactory = menuItemCollectionFactory; } protected override ActionResult GetTreeNodes(string id, FormCollection queryStrings) { var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString ? WebUtility.UrlDecode(id).TrimStart("/") - : ""; + : string.Empty; var nodes = new TreeNodeCollection(); - IEnumerable directories = _fileSystem.GetDirectories(path); - foreach (var directory in directories) + // Add App_Plugins && wwwroot folder if path is empty, as we are only returning root folders. + if (path == string.Empty) { - // We don't want any other directories under the root node other than the ones serving static files - App_Plugins and wwwroot - if (id == Constants.System.RootString && directory != AppPlugins && directory != Webroot) - { - continue; - } - - var hasChildren = _fileSystem.GetFiles(directory).Any() || _fileSystem.GetDirectories(directory).Any(); - - var name = Path.GetFileName(directory); - TreeNode? node = CreateTreeNode(WebUtility.UrlEncode(directory), path, queryStrings, name, - Constants.Icons.Folder, hasChildren); - - if (node != null) - { - nodes.Add(node); - } + AddRootFolder(AppPlugins, queryStrings, nodes); + AddRootFolder(Webroot, queryStrings, nodes); } - - // Only get the files inside App_Plugins and wwwroot - IEnumerable files = _fileSystem.GetFiles(path) - .Where(x => x.StartsWith(AppPlugins) || x.StartsWith(Webroot)); - - foreach (var file in files) + else { - var name = Path.GetFileName(file); - TreeNode? node = CreateTreeNode(WebUtility.UrlEncode(file), path, queryStrings, name, - Constants.Icons.DefaultIcon, false); - - if (node != null) + if (path.StartsWith(Webroot, StringComparison.OrdinalIgnoreCase)) { - nodes.Add(node); + AddWebRootFiles(path, queryStrings, nodes); + } + else if (path.StartsWith(AppPlugins, StringComparison.OrdinalIgnoreCase)) + { + AddPhysicalFiles(path, queryStrings, nodes); } } @@ -81,4 +81,92 @@ public class StaticFilesTreeController : TreeController // We don't have any menu item options (such as create/delete/reload) & only use the root node to load a custom UI protected override ActionResult GetMenuForNode(string id, FormCollection queryStrings) => _menuItemCollectionFactory.Create(); + + private void AddRootFolder(string directory, FormCollection queryStrings, TreeNodeCollection nodes) + { + if (_fileSystem.DirectoryExists(directory) is false) + { + return; + } + + var hasChildren = _fileSystem.GetFiles(directory).Any() || _fileSystem.GetDirectories(directory).Any(); + + var name = Path.GetFileName(directory); + TreeNode node = CreateTreeNode(WebUtility.UrlEncode(directory), "", queryStrings, name, Constants.Icons.Folder, hasChildren); + nodes.Add(node); + } + + private void AddPhysicalFiles(string path, FormCollection queryStrings, TreeNodeCollection nodes) + { + IEnumerable files = _fileSystem.GetFiles(path) + .Where(x => x.StartsWith(AppPlugins) || x.StartsWith(Webroot)); + + foreach (var file in files) + { + var name = Path.GetFileName(file); + TreeNode node = CreateTreeNode(WebUtility.UrlEncode(file), path, queryStrings, name, Constants.Icons.DefaultIcon, false); + nodes.Add(node); + } + + IEnumerable directories = _fileSystem.GetDirectories(path); + + foreach (var directory in directories) + { + var hasChildren = _fileSystem.GetFiles(directory).Any() || _fileSystem.GetDirectories(directory).Any(); + var name = Path.GetFileName(directory); + TreeNode node = CreateTreeNode(WebUtility.UrlEncode(directory), path, queryStrings, name, Constants.Icons.Folder, hasChildren); + nodes.Add(node); + } + } + + private void AddWebRootFiles(string path, FormCollection queryStrings, TreeNodeCollection nodes) + { + var calculatedPath = path.TrimStart(Webroot); + IDirectoryContents files = _webHostEnvironment.WebRootFileProvider.GetDirectoryContents(calculatedPath); + foreach (IFileInfo file in files.OrderByDescending(x => x.IsDirectory)) + { + // This logic looks a little wierd, but because the WebrootFileProvider can actually detect + // our files from App_Plugins, we have to manually exclude them, luckily they are always physical files + // so we can just check if the files are in there, and then exclude them. + bool isPhysicalFile = file.PhysicalPath is not null; + if (isPhysicalFile) + { + if (file.PhysicalPath!.StartsWith(_webHostEnvironment.ContentRootPath + $"\\{AppPlugins}", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + } + + TreeNode? node; + if (file.IsDirectory) + { + // We don't want to include the umbraco folder, so exclude it. + if (calculatedPath == string.Empty && "umbraco".InvariantEquals(file.Name)) + { + continue; + } + + bool hasChildren; + + if (isPhysicalFile) + { + var calculatedFilePaths = _webHostEnvironment.WebRootPath + calculatedPath; + hasChildren = _fileSystem.GetFiles(calculatedFilePaths).Any() || _fileSystem.GetDirectories(calculatedFilePaths).Any(); + } + else + { + IDirectoryContents childFiles = _webHostEnvironment.WebRootFileProvider.GetDirectoryContents(calculatedPath + $"/{file.Name}"); + hasChildren = childFiles.Any(); + } + + node = CreateTreeNode(WebUtility.UrlEncode(string.Join("/", path, file.Name)), path, queryStrings, file.Name, Constants.Icons.Folder, hasChildren); + } + else + { + node = CreateTreeNode(WebUtility.UrlEncode(string.Join("/", path, file.Name)), path, queryStrings, file.Name, Constants.Icons.DefaultIcon, false); + } + + nodes.Add(node); + } + } }