using AutoMapper; using System.Net; using System.Net.Http; using System.Web.Http; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; using Umbraco.Web.Trees; using Umbraco.Core.IO; namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] [PrefixlessBodyModelValidator] [UmbracoApplicationAuthorizeAttribute(Core.Constants.Applications.Settings)] public class CodeFileController : BackOfficeNotificationsController { /// /// Used to create a brand new file /// /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' /// /// Will return a simple 200 if file creation succeeds [ValidationFilter] public HttpResponseMessage PostCreate(string type, CodeFileDisplay display) { switch (type) { case Core.Constants.Trees.PartialViews: var view = new PartialView(display.VirtualPath); var result = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id); return result.Success == true ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); case Core.Constants.Trees.PartialViewMacros: var viewMacro = new PartialView(display.VirtualPath); var resultMacro = Services.FileService.CreatePartialViewMacro(viewMacro, display.Snippet, Security.CurrentUser.Id); return resultMacro.Success == true ? Request.CreateResponse(HttpStatusCode.OK) : Request.CreateNotificationValidationErrorResponse(resultMacro.Exception.Message); case Core.Constants.Trees.Scripts: var script = new Script(display.VirtualPath); Services.FileService.SaveScript(script, Security.CurrentUser.Id); return Request.CreateResponse(HttpStatusCode.OK); default: return Request.CreateResponse(HttpStatusCode.NotFound); } } /// /// Used to get a specific file from disk via the FileService /// /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' /// The filename or urlencoded path of the file to open /// The file and its contents from the virtualPath public CodeFileDisplay GetByPath(string type, string virtualPath) { if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(virtualPath)) { throw new HttpResponseException(HttpStatusCode.NotFound); } virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath); switch (type) { case Core.Constants.Trees.PartialViews: var view = Services.FileService.GetPartialView(virtualPath); if (view != null) { var display = Mapper.Map(view); display.FileType = Core.Constants.Trees.PartialViews; display.Path = Url.GetTreePathFromFilePath(view.Path); return display; } return null; case Core.Constants.Trees.PartialViewMacros: var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath); if (viewMacro != null) { var display = Mapper.Map(viewMacro); display.FileType = Core.Constants.Trees.PartialViewMacros; display.Path = Url.GetTreePathFromFilePath(viewMacro.Path); return display; } return null; case Core.Constants.Trees.Scripts: var script = Services.FileService.GetScriptByName(virtualPath); if (script != null) { var display = Mapper.Map(script); display.FileType = Core.Constants.Trees.Scripts; display.Path = Url.GetTreePathFromFilePath(script.Path); return display; } return null; } throw new HttpResponseException(HttpStatusCode.NotFound); } /// /// Used to delete a specific file from disk via the FileService /// /// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' /// The filename or urlencoded path of the file to delete /// Will return a simple 200 if file deletion succeeds [HttpDelete] [HttpPost] public HttpResponseMessage Delete(string type, string virtualPath) { if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false) { switch (type) { case Core.Constants.Trees.PartialViews: 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"); case Core.Constants.Trees.PartialViewMacros: 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"); case Core.Constants.Trees.Scripts: 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"); default: return Request.CreateResponse(HttpStatusCode.NotFound); } } throw new HttpResponseException(HttpStatusCode.NotFound); } /// /// Used to save/update an existing file after its initial creation /// /// /// The updated CodeFileDisplay model public CodeFileDisplay PostSave(CodeFileDisplay display) { if (ModelState.IsValid == false) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } if (display == null || string.IsNullOrWhiteSpace(display.FileType)) { throw new HttpResponseException(HttpStatusCode.NotFound); } switch (display.FileType) { case Core.Constants.Trees.PartialViews: var view = Services.FileService.GetPartialView(display.VirtualPath); if (view != null) { // might need to find the path var orgPath = view.OriginalPath.Substring(0, view.OriginalPath.IndexOf(view.Name)); view.Path = orgPath + display.Name; view.Content = display.Content; //Save the file and update the response to reflect any name and path changes var result = Services.FileService.SavePartialView(view, Security.CurrentUser.Id); if (result.Success == true) { display = Mapper.Map(result.Result, display); display.Path = Url.GetTreePathFromFilePath(view.Path); return display; } display.AddErrorNotification( Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), Services.TextService.Localize("speechBubbles/partialViewErrorText")); } else { throw new HttpResponseException(HttpStatusCode.NotFound); } break; case Core.Constants.Trees.PartialViewMacros: var viewMacro = Services.FileService.GetPartialViewMacro(display.VirtualPath); if (viewMacro != null) { viewMacro.Content = display.Content; viewMacro.Path = display.Name; //save the file and update the display to reflect any path and name changes var result = Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id); if (result.Success == true) { display = Mapper.Map(result.Result, display); display.Path = Url.GetTreePathFromFilePath(result.Result.Path); return display; } display.AddErrorNotification( Services.TextService.Localize("speechBubbles/partialViewErrorHeader"), Services.TextService.Localize("speechBubbles/partialViewErrorText")); } else { throw new HttpResponseException(HttpStatusCode.NotFound); } break; case Core.Constants.Trees.Scripts: var script = Services.FileService.GetScriptByName(display.VirtualPath); if (script != null) { script.Content = display.Content; script.Path = display.Name; Services.FileService.SaveScript(script, Security.CurrentUser.Id); display = Mapper.Map(script, display); display.Path = Url.GetTreePathFromFilePath(script.Path); return display; } else { throw new HttpResponseException(HttpStatusCode.NotFound); } break; default: throw new HttpResponseException(HttpStatusCode.NotFound); } return display; } } }