diff --git a/src/Umbraco.Web/Editors/CodeFileController.cs b/src/Umbraco.Web/Editors/CodeFileController.cs index 6775f84a61..c197f4eef0 100644 --- a/src/Umbraco.Web/Editors/CodeFileController.cs +++ b/src/Umbraco.Web/Editors/CodeFileController.cs @@ -1,5 +1,6 @@ using AutoMapper; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net; using System.Net.Http; @@ -266,29 +267,46 @@ namespace Umbraco.Web.Editors { if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false) { + virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath); + switch (type) { case Core.Constants.Trees.PartialViews: + if (IsDirectory(virtualPath, SystemDirectories.PartialViews)) + { + Services.FileService.DeletePartialViewFolder(virtualPath); + return Request.CreateResponse(HttpStatusCode.OK); + } if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id)) { return Request.CreateResponse(HttpStatusCode.OK); } - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View found with the specified path"); + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View or folder found with the specified path"); case Core.Constants.Trees.PartialViewMacros: + if (IsDirectory(virtualPath, SystemDirectories.MacroPartials)) + { + Services.FileService.DeletePartialViewMacroFolder(virtualPath); + return Request.CreateResponse(HttpStatusCode.OK); + } if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id)) { return Request.CreateResponse(HttpStatusCode.OK); } - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro found with the specified path"); + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path"); case Core.Constants.Trees.Scripts: + if (IsDirectory(virtualPath, SystemDirectories.Scripts)) + { + Services.FileService.DeleteScriptFolder(virtualPath); + return Request.CreateResponse(HttpStatusCode.OK); + } if (Services.FileService.GetScriptByName(virtualPath) != null) { Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id); return Request.CreateResponse(HttpStatusCode.OK); } - return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script found with the specified path"); + return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path"); default: return Request.CreateResponse(HttpStatusCode.NotFound); @@ -442,5 +460,12 @@ namespace Umbraco.Web.Editors return value; } + + private bool IsDirectory(string virtualPath, string systemDirectory) + { + var path = IOHelper.MapPath(systemDirectory + "/" + virtualPath); + var dirInfo = new DirectoryInfo(path); + return dirInfo.Attributes == FileAttributes.Directory; + } } } diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index 61ee8c4ff9..88f322971d 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -1,10 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Net.Http.Formatting; +using umbraco.BusinessLogic.Actions; +using Umbraco.Core; using Umbraco.Core.IO; +using Umbraco.Core.Services; using Umbraco.Web.Models.Trees; namespace Umbraco.Web.Trees @@ -27,7 +27,7 @@ namespace Umbraco.Web.Trees /// protected virtual void OnRenderFolderNode(ref TreeNode treeNode) { } - protected override Models.Trees.TreeNodeCollection GetTreeNodes(string id, System.Net.Http.Formatting.FormDataCollection queryStrings) + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { string orgPath = ""; string path = ""; @@ -90,6 +90,64 @@ namespace Umbraco.Web.Trees } return nodes; - } + } + + protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) + { + var menu = new MenuItemCollection(); + + //if root node no need to visit the filesystem so lets just create the menu and return it + if (id == Constants.System.Root.ToInvariantString()) + { + //set the default to create + menu.DefaultMenuAlias = ActionNew.Instance.Alias; + //create action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + //refresh action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + + return menu; + } + + string path; + if (string.IsNullOrEmpty(id) == false) + { + var orgPath = System.Web.HttpUtility.UrlDecode(id); + path = IOHelper.MapPath(FilePath + "/" + orgPath); + } + else + { + path = IOHelper.MapPath(FilePath); + } + + var dirInfo = new DirectoryInfo(path); + //check if it's a directory + if (dirInfo.Attributes == FileAttributes.Directory) + { + //set the default to create + menu.DefaultMenuAlias = ActionNew.Instance.Alias; + //create action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); + + var hasChildren = dirInfo.GetFiles().Length > 0 || dirInfo.GetDirectories().Length > 0; + //We can only delete folders if it doesn't have any children (folders or files) + if (hasChildren == false) + { + //delete action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)), true); + } + + //refresh action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); + } + //if it's not a directory then we only allow to delete the item + else + { + //delete action + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + } + + return menu; + } } } diff --git a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs index 61d9c17cef..9643823e7b 100644 --- a/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewMacrosTreeController.cs @@ -1,16 +1,13 @@ using Umbraco.Core; using Umbraco.Core.IO; -using umbraco.BusinessLogic.Actions; using Umbraco.Web.Models.Trees; -using System.Net.Http.Formatting; -using Umbraco.Core.Services; namespace Umbraco.Web.Trees { - /// - /// Tree for displaying partial view macros in the developer app - /// - [Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)] + /// + /// Tree for displaying partial view macros in the developer app + /// + [Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)] public class PartialViewMacrosTreeController : FileSystemTreeController { protected override string FilePath @@ -28,37 +25,6 @@ namespace Umbraco.Web.Trees get { return "icon-article"; } } - protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) - { - var menu = new MenuItemCollection(); - - if (id == Constants.System.Root.ToInvariantString()) - { - //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; - //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - - return menu; - } - - if (id.EndsWith(FileSearchPattern.TrimStart("*")) == false) - { - //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; - //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - } - - // TODO: Wire up new delete dialog - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); - return menu; - } - protected override void OnRenderFileNode(ref TreeNode treeNode) { base.OnRenderFileNode(ref treeNode); diff --git a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs index 96da9a2e33..07a0a557af 100644 --- a/src/Umbraco.Web/Trees/PartialViewsTreeController.cs +++ b/src/Umbraco.Web/Trees/PartialViewsTreeController.cs @@ -1,9 +1,6 @@ using Umbraco.Core; using Umbraco.Core.IO; -using umbraco.BusinessLogic.Actions; using Umbraco.Web.Models.Trees; -using System.Net.Http.Formatting; -using Umbraco.Core.Services; namespace Umbraco.Web.Trees { @@ -27,37 +24,6 @@ namespace Umbraco.Web.Trees get { return "icon-article"; } } - protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) - { - var menu = new MenuItemCollection(); - - if (id == Constants.System.Root.ToInvariantString()) - { - //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; - //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - - return menu; - } - - if (id.EndsWith(FileSearchPattern.TrimStart("*")) == false) - { - //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; - //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - } - - // TODO: Wire up new delete dialog - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); - return menu; - } - protected override void OnRenderFileNode(ref TreeNode treeNode) { base.OnRenderFileNode(ref treeNode); diff --git a/src/Umbraco.Web/Trees/ScriptTreeController.cs b/src/Umbraco.Web/Trees/ScriptTreeController.cs index c7d4e728aa..f491989228 100644 --- a/src/Umbraco.Web/Trees/ScriptTreeController.cs +++ b/src/Umbraco.Web/Trees/ScriptTreeController.cs @@ -1,10 +1,6 @@ -using System.Linq; -using Umbraco.Core; +using Umbraco.Core; using Umbraco.Core.IO; -using umbraco.BusinessLogic.Actions; using Umbraco.Web.Models.Trees; -using System.Net.Http.Formatting; -using Umbraco.Core.Services; namespace Umbraco.Web.Trees { @@ -25,37 +21,6 @@ namespace Umbraco.Web.Trees get { return "icon-script"; } } - protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) - { - var menu = new MenuItemCollection(); - - if (id == Constants.System.Root.ToInvariantString()) - { - //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; - //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - - return menu; - } - - if (id.EndsWith(FileSearchPattern.TrimStart("*")) == false) - { - //set the default to create - menu.DefaultMenuAlias = ActionNew.Instance.Alias; - //create action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias))); - //refresh action - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); - } - - // TODO: Wire up new delete dialog - menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); - return menu; - } - protected override 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.