Merge branch 'v10/dev' into v10/contrib
# Conflicts: # src/Umbraco.Core/Actions/ActionAssignDomain.cs # src/Umbraco.Core/Actions/ActionCopy.cs # src/Umbraco.Core/Actions/ActionCreateBlueprintFromContent.cs # src/Umbraco.Core/Actions/ActionDelete.cs # src/Umbraco.Core/Actions/ActionMove.cs # src/Umbraco.Core/Actions/ActionNew.cs # src/Umbraco.Core/Actions/ActionNotify.cs # src/Umbraco.Core/Actions/ActionProtect.cs # src/Umbraco.Core/Actions/ActionRestore.cs # src/Umbraco.Core/Actions/ActionRights.cs # src/Umbraco.Core/Actions/ActionRollback.cs # src/Umbraco.Core/Actions/ActionSort.cs # src/Umbraco.Core/Actions/ActionToPublish.cs # src/Umbraco.Core/Actions/ActionUnpublish.cs # src/Umbraco.Core/Actions/ActionUpdate.cs # src/Umbraco.Core/Constants-Telemetry.cs # src/Umbraco.Core/DependencyInjection/ServiceCollectionExtensions.cs # src/Umbraco.Core/DependencyInjection/UmbracoBuilder.CollectionBuilders.cs # src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs # src/Umbraco.Core/Models/Trees/ActionMenuItem.cs # src/Umbraco.Core/Models/Trees/CreateChildEntity.cs # src/Umbraco.Core/Models/Trees/ExportMember.cs # src/Umbraco.Core/Models/Trees/MenuItem.cs # src/Umbraco.Core/Models/Trees/RefreshNode.cs # src/Umbraco.Core/Persistence/Repositories/IRedirectUrlRepository.cs # src/Umbraco.Core/PropertyEditors/NestedContentConfiguration.cs # src/Umbraco.Core/Routing/ContentFinderByRedirectUrl.cs # src/Umbraco.Core/Services/IFileService.cs # src/Umbraco.Core/Services/IRedirectUrlService.cs # src/Umbraco.Core/Services/RedirectUrlService.cs # src/Umbraco.Core/Trees/MenuItemList.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs # src/Umbraco.Infrastructure/Persistence/Repositories/Implement/RedirectUrlRepository.cs # src/Umbraco.Infrastructure/Telemetry/Providers/SystemInformationTelemetryProvider.cs # src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs # src/Umbraco.Web.BackOffice/Controllers/ContentController.cs # src/Umbraco.Web.BackOffice/Controllers/DictionaryController.cs # src/Umbraco.Web.BackOffice/Controllers/EntityController.cs # src/Umbraco.Web.BackOffice/Controllers/PreviewController.cs # src/Umbraco.Web.BackOffice/Trees/ContentBlueprintTreeController.cs # src/Umbraco.Web.BackOffice/Trees/ContentTreeController.cs # src/Umbraco.Web.BackOffice/Trees/ContentTreeControllerBase.cs # src/Umbraco.Web.BackOffice/Trees/ContentTypeTreeController.cs # src/Umbraco.Web.BackOffice/Trees/DataTypeTreeController.cs # src/Umbraco.Web.BackOffice/Trees/DictionaryTreeController.cs # src/Umbraco.Web.BackOffice/Trees/FileSystemTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MacrosTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MediaTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MediaTypeTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MemberGroupTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MemberTreeController.cs # src/Umbraco.Web.BackOffice/Trees/MemberTypeAndGroupTreeControllerBase.cs # src/Umbraco.Web.BackOffice/Trees/RelationTypeTreeController.cs # src/Umbraco.Web.BackOffice/Trees/TemplatesTreeController.cs # src/Umbraco.Web.UI.Client/src/less/navs.less # templates/UmbracoProject/.gitignore # tests/Umbraco.Tests.Integration/Umbraco.Core/Telemetry/TelemetryServiceTests.cs # tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/UserDataServiceTests.cs # tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/SystemInformationTelemetryProviderTests.cs
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@@ -12,205 +9,209 @@ using Umbraco.Cms.Core.Models.Trees;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Trees;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Web.BackOffice.Trees
|
||||
namespace Umbraco.Cms.Web.BackOffice.Trees;
|
||||
|
||||
public abstract class FileSystemTreeController : TreeController
|
||||
{
|
||||
public abstract class FileSystemTreeController : TreeController
|
||||
protected FileSystemTreeController(
|
||||
ILocalizedTextService localizedTextService,
|
||||
UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection,
|
||||
IMenuItemCollectionFactory menuItemCollectionFactory,
|
||||
IEventAggregator eventAggregator
|
||||
)
|
||||
: base(localizedTextService, umbracoApiControllerTypeCollection, eventAggregator) =>
|
||||
MenuItemCollectionFactory = menuItemCollectionFactory;
|
||||
|
||||
protected abstract IFileSystem? FileSystem { get; }
|
||||
protected IMenuItemCollectionFactory MenuItemCollectionFactory { get; }
|
||||
protected abstract string[] Extensions { get; }
|
||||
protected abstract string FileIcon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Inheritors can override this method to modify the file node that is created.
|
||||
/// </summary>
|
||||
/// <param name="treeNode"></param>
|
||||
protected virtual void OnRenderFileNode(ref TreeNode treeNode) { }
|
||||
|
||||
/// <summary>
|
||||
/// Inheritors can override this method to modify the folder node that is created.
|
||||
/// </summary>
|
||||
/// <param name="treeNode"></param>
|
||||
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) =>
|
||||
// TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
|
||||
treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);";
|
||||
|
||||
protected override ActionResult<TreeNodeCollection> GetTreeNodes(string id, FormCollection queryStrings)
|
||||
{
|
||||
protected FileSystemTreeController(
|
||||
ILocalizedTextService localizedTextService,
|
||||
UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection,
|
||||
IMenuItemCollectionFactory menuItemCollectionFactory,
|
||||
IEventAggregator eventAggregator
|
||||
)
|
||||
: base(localizedTextService, umbracoApiControllerTypeCollection, eventAggregator)
|
||||
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString
|
||||
? WebUtility.UrlDecode(id).TrimStart("/")
|
||||
: "";
|
||||
|
||||
IEnumerable<string>? directories = FileSystem?.GetDirectories(path);
|
||||
|
||||
var nodes = new TreeNodeCollection();
|
||||
if (directories is not null)
|
||||
{
|
||||
MenuItemCollectionFactory = menuItemCollectionFactory;
|
||||
}
|
||||
|
||||
protected abstract IFileSystem? FileSystem { get; }
|
||||
protected IMenuItemCollectionFactory MenuItemCollectionFactory { get; }
|
||||
protected abstract string[] Extensions { get; }
|
||||
protected abstract string FileIcon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Inheritors can override this method to modify the file node that is created.
|
||||
/// </summary>
|
||||
/// <param name="treeNode"></param>
|
||||
protected virtual void OnRenderFileNode(ref TreeNode treeNode) { }
|
||||
|
||||
/// <summary>
|
||||
/// Inheritors can override this method to modify the folder node that is created.
|
||||
/// </summary>
|
||||
/// <param name="treeNode"></param>
|
||||
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) {
|
||||
// TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
|
||||
treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);";
|
||||
}
|
||||
|
||||
protected override ActionResult<TreeNodeCollection> GetTreeNodes(string id, FormCollection queryStrings)
|
||||
{
|
||||
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString
|
||||
? WebUtility.UrlDecode(id).TrimStart("/")
|
||||
: "";
|
||||
|
||||
var directories = FileSystem?.GetDirectories(path);
|
||||
|
||||
var nodes = new TreeNodeCollection();
|
||||
if (directories is not null)
|
||||
foreach (var directory in directories)
|
||||
{
|
||||
foreach (var directory in directories)
|
||||
var hasChildren = FileSystem is not null &&
|
||||
(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);
|
||||
|
||||
OnRenderFolderNode(ref node);
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
var hasChildren = FileSystem is not null && (FileSystem.GetFiles(directory).Any() || FileSystem.GetDirectories(directory).Any());
|
||||
|
||||
var name = Path.GetFileName(directory);
|
||||
var node = CreateTreeNode(WebUtility.UrlEncode(directory), path, queryStrings, name, Constants.Icons.Folder, hasChildren);
|
||||
|
||||
OnRenderFolderNode(ref node);
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
nodes.Add(node);
|
||||
}
|
||||
nodes.Add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//this is a hack to enable file system tree to support multiple file extension look-up
|
||||
//so the pattern both support *.* *.xml and xml,js,vb for lookups
|
||||
var files = FileSystem?.GetFiles(path).Where(x =>
|
||||
//this is a hack to enable file system tree to support multiple file extension look-up
|
||||
//so the pattern both support *.* *.xml and xml,js,vb for lookups
|
||||
IEnumerable<string>? files = FileSystem?.GetFiles(path).Where(x =>
|
||||
{
|
||||
var extension = Path.GetExtension(x);
|
||||
|
||||
if (Extensions.Contains("*"))
|
||||
{
|
||||
var extension = Path.GetExtension(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Extensions.Contains("*"))
|
||||
return true;
|
||||
return extension != null && Extensions.Contains(extension.Trim(Constants.CharArrays.Period),
|
||||
StringComparer.InvariantCultureIgnoreCase);
|
||||
});
|
||||
|
||||
return extension != null && Extensions.Contains(extension.Trim(Constants.CharArrays.Period), StringComparer.InvariantCultureIgnoreCase);
|
||||
});
|
||||
|
||||
if (files is not null)
|
||||
if (files is not null)
|
||||
{
|
||||
foreach (var file in files)
|
||||
{
|
||||
foreach (var file in files)
|
||||
var withoutExt = Path.GetFileNameWithoutExtension(file);
|
||||
if (withoutExt.IsNullOrWhiteSpace())
|
||||
{
|
||||
var withoutExt = Path.GetFileNameWithoutExtension(file);
|
||||
if (withoutExt.IsNullOrWhiteSpace()) continue;
|
||||
continue;
|
||||
}
|
||||
|
||||
var name = Path.GetFileName(file);
|
||||
var node = CreateTreeNode(WebUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
|
||||
var name = Path.GetFileName(file);
|
||||
TreeNode? node = CreateTreeNode(WebUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
|
||||
|
||||
OnRenderFileNode(ref node);
|
||||
OnRenderFileNode(ref node);
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
nodes.Add(node);
|
||||
}
|
||||
if (node != null)
|
||||
{
|
||||
nodes.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
protected override ActionResult<TreeNode?> CreateRootNode(FormCollection queryStrings)
|
||||
return nodes;
|
||||
}
|
||||
|
||||
protected override ActionResult<TreeNode?> CreateRootNode(FormCollection queryStrings)
|
||||
{
|
||||
ActionResult<TreeNode?> rootResult = base.CreateRootNode(queryStrings);
|
||||
if (!(rootResult.Result is null))
|
||||
{
|
||||
var rootResult = base.CreateRootNode(queryStrings);
|
||||
if (!(rootResult.Result is null))
|
||||
{
|
||||
return rootResult;
|
||||
}
|
||||
var root = rootResult.Value;
|
||||
|
||||
// check if there are any children
|
||||
var treeNodesResult = GetTreeNodes(Constants.System.RootString, queryStrings);
|
||||
|
||||
if (!(treeNodesResult.Result is null))
|
||||
{
|
||||
return treeNodesResult.Result;
|
||||
}
|
||||
|
||||
if (root is not null)
|
||||
{
|
||||
root.HasChildren = treeNodesResult.Value?.Any() ?? false;
|
||||
|
||||
}
|
||||
|
||||
return root;
|
||||
return rootResult;
|
||||
}
|
||||
|
||||
protected virtual MenuItemCollection GetMenuForRootNode(FormCollection queryStrings)
|
||||
TreeNode? root = rootResult.Value;
|
||||
|
||||
//check if there are any children
|
||||
ActionResult<TreeNodeCollection> treeNodesResult = GetTreeNodes(Constants.System.RootString, queryStrings);
|
||||
|
||||
if (!(treeNodesResult.Result is null))
|
||||
{
|
||||
var menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
// set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.ActionAlias;
|
||||
|
||||
// create action
|
||||
menu.Items.Add<ActionNew>(LocalizedTextService, opensDialog: true, useLegacyIcon: false);
|
||||
|
||||
// refresh action
|
||||
menu.Items.Add(new RefreshNode(LocalizedTextService, true));
|
||||
|
||||
return menu;
|
||||
return treeNodesResult.Result;
|
||||
}
|
||||
|
||||
protected virtual MenuItemCollection GetMenuForFolder(string path, FormCollection queryStrings)
|
||||
if (root is not null)
|
||||
{
|
||||
var menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
// set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.ActionAlias;
|
||||
|
||||
// create action
|
||||
menu.Items.Add<ActionNew>(LocalizedTextService, opensDialog: true, useLegacyIcon: false);
|
||||
|
||||
var hasChildren = FileSystem is not null && (FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any());
|
||||
|
||||
// We can only delete folders if it doesn't have any children (folders or files)
|
||||
if (hasChildren == false)
|
||||
{
|
||||
// delete action
|
||||
menu.Items.Add<ActionDelete>(LocalizedTextService, true, opensDialog: true, useLegacyIcon: false);
|
||||
}
|
||||
|
||||
// refresh action
|
||||
menu.Items.Add(new RefreshNode(LocalizedTextService, true));
|
||||
|
||||
return menu;
|
||||
root.HasChildren = treeNodesResult.Value?.Any() ?? false;
|
||||
}
|
||||
|
||||
protected virtual MenuItemCollection GetMenuForFile(string path, FormCollection queryStrings)
|
||||
return root;
|
||||
}
|
||||
|
||||
protected virtual MenuItemCollection GetMenuForRootNode(FormCollection queryStrings)
|
||||
{
|
||||
MenuItemCollection menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.ActionAlias;
|
||||
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(LocalizedTextService, opensDialog: true, useLegacyIcon: false);
|
||||
|
||||
//refresh action
|
||||
menu.Items.Add(new RefreshNode(LocalizedTextService, separatorBefore: true));
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
protected virtual MenuItemCollection GetMenuForFolder(string path, FormCollection queryStrings)
|
||||
{
|
||||
MenuItemCollection menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
//set the default to create
|
||||
menu.DefaultMenuAlias = ActionNew.ActionAlias;
|
||||
|
||||
//create action
|
||||
menu.Items.Add<ActionNew>(LocalizedTextService, opensDialog: true, useLegacyIcon: false);
|
||||
|
||||
var hasChildren = FileSystem is not null &&
|
||||
(FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any());
|
||||
|
||||
//We can only delete folders if it doesn't have any children (folders or files)
|
||||
if (hasChildren == false)
|
||||
{
|
||||
var menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
// if it's not a directory then we only allow to delete the item
|
||||
menu.Items.Add<ActionDelete>(LocalizedTextService, opensDialog: true, useLegacyIcon: false);
|
||||
|
||||
return menu;
|
||||
//delete action
|
||||
menu.Items.Add<ActionDelete>(LocalizedTextService, hasSeparator: true, opensDialog: true, useLegacyIcon: false);
|
||||
}
|
||||
|
||||
protected override ActionResult<MenuItemCollection> GetMenuForNode(string id, FormCollection queryStrings)
|
||||
//refresh action
|
||||
menu.Items.Add(new RefreshNode(LocalizedTextService, separatorBefore: true));
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
protected virtual MenuItemCollection GetMenuForFile(string path, FormCollection queryStrings)
|
||||
{
|
||||
MenuItemCollection menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
//if it's not a directory then we only allow to delete the item
|
||||
menu.Items.Add<ActionDelete>(LocalizedTextService, opensDialog: true, useLegacyIcon: false);
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
protected override ActionResult<MenuItemCollection> GetMenuForNode(string id, FormCollection queryStrings)
|
||||
{
|
||||
//if root node no need to visit the filesystem so lets just create the menu and return it
|
||||
if (id == Constants.System.RootString)
|
||||
{
|
||||
//if root node no need to visit the filesystem so lets just create the menu and return it
|
||||
if (id == Constants.System.RootString)
|
||||
{
|
||||
return GetMenuForRootNode(queryStrings);
|
||||
}
|
||||
|
||||
var menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString
|
||||
? WebUtility.UrlDecode(id).TrimStart("/")
|
||||
: "";
|
||||
|
||||
var isFile = FileSystem?.FileExists(path) ?? false;
|
||||
var isDirectory = FileSystem?.DirectoryExists(path) ?? false;
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
return GetMenuForFolder(path, queryStrings);
|
||||
}
|
||||
|
||||
return isFile ? GetMenuForFile(path, queryStrings) : menu;
|
||||
return GetMenuForRootNode(queryStrings);
|
||||
}
|
||||
|
||||
MenuItemCollection menu = MenuItemCollectionFactory.Create();
|
||||
|
||||
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString
|
||||
? WebUtility.UrlDecode(id).TrimStart("/")
|
||||
: "";
|
||||
|
||||
var isFile = FileSystem?.FileExists(path) ?? false;
|
||||
var isDirectory = FileSystem?.DirectoryExists(path) ?? false;
|
||||
|
||||
if (isDirectory)
|
||||
{
|
||||
return GetMenuForFolder(path, queryStrings);
|
||||
}
|
||||
|
||||
return isFile ? GetMenuForFile(path, queryStrings) : menu;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user