2014-05-09 12:23:59 +02:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
2017-05-12 14:49:44 +02:00
|
|
|
|
using System.Web;
|
2020-06-09 07:49:26 +02:00
|
|
|
|
using Microsoft.AspNetCore.Http;
|
2016-03-30 16:07:45 +02:00
|
|
|
|
using Umbraco.Core;
|
2014-05-09 12:23:59 +02:00
|
|
|
|
using Umbraco.Core.IO;
|
2020-02-17 14:58:34 +01:00
|
|
|
|
using Umbraco.Core.Services;
|
2018-10-29 17:27:33 +11:00
|
|
|
|
using Umbraco.Web.Actions;
|
2014-05-09 12:23:59 +02:00
|
|
|
|
using Umbraco.Web.Models.Trees;
|
2020-06-09 07:49:26 +02:00
|
|
|
|
using Umbraco.Web.Trees;
|
|
|
|
|
|
using Umbraco.Web.WebApi;
|
2018-10-29 17:27:33 +11:00
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
namespace Umbraco.Web.BackOffice.Trees
|
2014-05-09 12:23:59 +02:00
|
|
|
|
{
|
|
|
|
|
|
public abstract class FileSystemTreeController : TreeController
|
|
|
|
|
|
{
|
2020-02-17 14:58:34 +01:00
|
|
|
|
protected FileSystemTreeController(
|
2020-06-09 07:49:26 +02:00
|
|
|
|
ILocalizedTextService localizedTextService,
|
|
|
|
|
|
UmbracoApiControllerTypeCollection umbracoApiControllerTypeCollection,
|
|
|
|
|
|
IMenuItemCollectionFactory menuItemCollectionFactory
|
|
|
|
|
|
)
|
|
|
|
|
|
: base(localizedTextService, umbracoApiControllerTypeCollection)
|
2020-02-17 14:58:34 +01:00
|
|
|
|
{
|
2020-02-25 07:57:59 +01:00
|
|
|
|
MenuItemCollectionFactory = menuItemCollectionFactory;
|
2020-02-17 14:58:34 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
protected abstract IFileSystem FileSystem { get; }
|
2020-02-25 07:57:59 +01:00
|
|
|
|
protected IMenuItemCollectionFactory MenuItemCollectionFactory { get; }
|
2017-05-12 14:49:44 +02:00
|
|
|
|
protected abstract string[] Extensions { get; }
|
|
|
|
|
|
protected abstract string FileIcon { get; }
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Inheritors can override this method to modify the file node that is created.
|
|
|
|
|
|
/// </summary>
|
2018-05-01 00:35:49 +10:00
|
|
|
|
/// <param name="treeNode"></param>
|
|
|
|
|
|
protected virtual void OnRenderFileNode(ref TreeNode treeNode) { }
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Inheritors can override this method to modify the folder node that is created.
|
|
|
|
|
|
/// </summary>
|
2018-05-01 00:35:49 +10:00
|
|
|
|
/// <param name="treeNode"></param>
|
2019-01-21 19:03:16 +01:00
|
|
|
|
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) {
|
2019-01-27 01:17:32 -05:00
|
|
|
|
// TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
|
2019-01-21 19:03:16 +01:00
|
|
|
|
treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);";
|
|
|
|
|
|
}
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
protected override TreeNodeCollection GetTreeNodes(string id, FormCollection queryStrings)
|
2014-05-09 12:23:59 +02:00
|
|
|
|
{
|
2019-11-05 13:45:42 +01:00
|
|
|
|
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString
|
2017-05-12 14:49:44 +02:00
|
|
|
|
? HttpUtility.UrlDecode(id).TrimStart("/")
|
|
|
|
|
|
: "";
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var directories = FileSystem.GetDirectories(path);
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
var nodes = new TreeNodeCollection();
|
2017-05-12 14:49:44 +02:00
|
|
|
|
foreach (var directory in directories)
|
2014-05-09 12:23:59 +02:00
|
|
|
|
{
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var hasChildren = FileSystem.GetFiles(directory).Any() || FileSystem.GetDirectories(directory).Any();
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var name = Path.GetFileName(directory);
|
|
|
|
|
|
var node = CreateTreeNode(HttpUtility.UrlEncode(directory), path, queryStrings, name, "icon-folder", hasChildren);
|
|
|
|
|
|
OnRenderFolderNode(ref node);
|
|
|
|
|
|
if (node != null)
|
2016-03-30 16:07:45 +02:00
|
|
|
|
nodes.Add(node);
|
2014-05-09 12:23:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//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
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var files = FileSystem.GetFiles(path).Where(x =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var extension = Path.GetExtension(x);
|
2018-12-05 15:15:45 +00:00
|
|
|
|
|
|
|
|
|
|
if (Extensions.Contains("*"))
|
|
|
|
|
|
return true;
|
2019-11-05 12:54:22 +01:00
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase);
|
|
|
|
|
|
});
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
foreach (var file in files)
|
2014-05-09 12:23:59 +02:00
|
|
|
|
{
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var withoutExt = Path.GetFileNameWithoutExtension(file);
|
2018-05-01 00:35:49 +10:00
|
|
|
|
if (withoutExt.IsNullOrWhiteSpace()) continue;
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2017-05-12 14:49:44 +02:00
|
|
|
|
var name = Path.GetFileName(file);
|
|
|
|
|
|
var node = CreateTreeNode(HttpUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
|
|
|
|
|
|
OnRenderFileNode(ref node);
|
|
|
|
|
|
if (node != null)
|
|
|
|
|
|
nodes.Add(node);
|
2014-05-09 12:23:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-03-30 16:07:45 +02:00
|
|
|
|
return nodes;
|
2018-10-31 13:40:23 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
protected override TreeNode CreateRootNode(FormCollection queryStrings)
|
2018-10-31 13:40:23 +01:00
|
|
|
|
{
|
|
|
|
|
|
var root = base.CreateRootNode(queryStrings);
|
|
|
|
|
|
//check if there are any children
|
2019-11-05 13:45:42 +01:00
|
|
|
|
root.HasChildren = GetTreeNodes(Constants.System.RootString, queryStrings).Any();
|
2018-10-31 13:40:23 +01:00
|
|
|
|
return root;
|
2016-03-30 16:07:45 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
protected virtual MenuItemCollection GetMenuForRootNode(FormCollection queryStrings)
|
2016-03-30 16:07:45 +02:00
|
|
|
|
{
|
2020-02-25 07:57:59 +01:00
|
|
|
|
var menu = MenuItemCollectionFactory.Create();
|
2018-05-01 00:35:49 +10:00
|
|
|
|
|
|
|
|
|
|
//set the default to create
|
2018-10-29 17:27:33 +11:00
|
|
|
|
menu.DefaultMenuAlias = ActionNew.ActionAlias;
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//create action
|
2020-06-09 07:49:26 +02:00
|
|
|
|
menu.Items.Add<ActionNew>(LocalizedTextService, opensDialog: true);
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//refresh action
|
2020-06-09 07:49:26 +02:00
|
|
|
|
menu.Items.Add(new RefreshNode(LocalizedTextService, true));
|
2018-05-01 00:35:49 +10:00
|
|
|
|
|
|
|
|
|
|
return menu;
|
2016-03-30 16:07:45 +02:00
|
|
|
|
}
|
2016-09-01 19:06:08 +02:00
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
protected virtual MenuItemCollection GetMenuForFolder(string path, FormCollection queryStrings)
|
2016-03-30 16:07:45 +02:00
|
|
|
|
{
|
2020-02-25 07:57:59 +01:00
|
|
|
|
var menu = MenuItemCollectionFactory.Create();
|
2016-09-01 19:06:08 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//set the default to create
|
2018-10-29 17:27:33 +11:00
|
|
|
|
menu.DefaultMenuAlias = ActionNew.ActionAlias;
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//create action
|
2020-06-09 07:49:26 +02:00
|
|
|
|
menu.Items.Add<ActionNew>(LocalizedTextService, opensDialog: true);
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
var hasChildren = 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)
|
2018-03-27 16:18:51 +02:00
|
|
|
|
{
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//delete action
|
2020-06-09 07:49:26 +02:00
|
|
|
|
menu.Items.Add<ActionDelete>(LocalizedTextService, true, opensDialog: true);
|
2018-03-27 16:18:51 +02:00
|
|
|
|
}
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//refresh action
|
2020-06-09 07:49:26 +02:00
|
|
|
|
menu.Items.Add(new RefreshNode(LocalizedTextService, true));
|
2014-05-09 12:23:59 +02:00
|
|
|
|
|
2018-03-27 16:18:51 +02:00
|
|
|
|
return menu;
|
|
|
|
|
|
}
|
2016-09-01 19:06:08 +02:00
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
protected virtual MenuItemCollection GetMenuForFile(string path, FormCollection queryStrings)
|
2018-03-27 16:18:51 +02:00
|
|
|
|
{
|
2020-02-25 07:57:59 +01:00
|
|
|
|
var menu = MenuItemCollectionFactory.Create();
|
2017-05-12 14:49:44 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//if it's not a directory then we only allow to delete the item
|
2020-06-09 07:49:26 +02:00
|
|
|
|
menu.Items.Add<ActionDelete>(LocalizedTextService, opensDialog: true);
|
2018-03-27 16:18:51 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
return menu;
|
2018-03-27 16:18:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-06-09 07:49:26 +02:00
|
|
|
|
protected override MenuItemCollection GetMenuForNode(string id, FormCollection queryStrings)
|
2018-03-27 16:18:51 +02:00
|
|
|
|
{
|
2018-05-01 00:35:49 +10:00
|
|
|
|
//if root node no need to visit the filesystem so lets just create the menu and return it
|
2019-11-05 13:45:42 +01:00
|
|
|
|
if (id == Constants.System.RootString)
|
2017-05-12 14:49:44 +02:00
|
|
|
|
{
|
2018-05-01 00:35:49 +10:00
|
|
|
|
return GetMenuForRootNode(queryStrings);
|
2014-05-09 12:23:59 +02:00
|
|
|
|
}
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2020-02-25 07:57:59 +01:00
|
|
|
|
var menu = MenuItemCollectionFactory.Create();
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2019-11-05 13:45:42 +01:00
|
|
|
|
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString
|
2018-05-01 00:35:49 +10:00
|
|
|
|
? HttpUtility.UrlDecode(id).TrimStart("/")
|
|
|
|
|
|
: "";
|
2018-03-27 16:18:51 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
var isFile = FileSystem.FileExists(path);
|
|
|
|
|
|
var isDirectory = FileSystem.DirectoryExists(path);
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
if (isDirectory)
|
|
|
|
|
|
{
|
|
|
|
|
|
return GetMenuForFolder(path, queryStrings);
|
|
|
|
|
|
}
|
2016-03-30 16:07:45 +02:00
|
|
|
|
|
2018-05-01 00:35:49 +10:00
|
|
|
|
return isFile ? GetMenuForFile(path, queryStrings) : menu;
|
|
|
|
|
|
}
|
2014-05-09 12:23:59 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|