using System;
using AutoMapper;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ClientDependency.Core;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Strings.Css;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Web.Trees;
using Stylesheet = Umbraco.Core.Models.Stylesheet;
using StylesheetRule = Umbraco.Web.Models.ContentEditing.StylesheetRule;
namespace Umbraco.Web.Editors
{
// TODO: Put some exception filters in our webapi to return 404 instead of 500 when we throw ArgumentNullException
// ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
[PluginController("UmbracoApi")]
[PrefixlessBodyModelValidator]
[UmbracoApplicationAuthorize(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)
{
if (display == null) throw new ArgumentNullException("display");
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
switch (type)
{
case Core.Constants.Trees.PartialViews:
var view = new PartialView(PartialViewType.PartialView, display.VirtualPath);
view.Content = display.Content;
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(PartialViewType.PartialViewMacro, display.VirtualPath);
viewMacro.Content = display.Content;
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 create a container/folder in 'partialViews', 'partialViewMacros', 'scripts' or 'stylesheets'
///
/// 'partialViews', 'partialViewMacros' or 'scripts'
/// The virtual path of the parent.
/// The name of the container/folder
///
[HttpPost]
public CodeFileDisplay PostCreateContainer(string type, string parentId, string name)
{
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(parentId)) throw new ArgumentException("Value cannot be null or whitespace.", "parentId");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
// if the parentId is root (-1) then we just need an empty string as we are
// creating the path below and we don't want -1 in the path
if (parentId == Core.Constants.System.Root.ToInvariantString())
{
parentId = string.Empty;
}
name = System.Web.HttpUtility.UrlDecode(name);
if (parentId.IsNullOrWhiteSpace() == false)
{
parentId = System.Web.HttpUtility.UrlDecode(parentId);
name = parentId.EnsureEndsWith("/") + name;
}
var virtualPath = string.Empty;
switch (type)
{
case Core.Constants.Trees.PartialViews:
virtualPath = NormalizeVirtualPath(name, SystemDirectories.PartialViews);
Services.FileService.CreatePartialViewFolder(virtualPath);
break;
case Core.Constants.Trees.PartialViewMacros:
virtualPath = NormalizeVirtualPath(name, SystemDirectories.MacroPartials);
Services.FileService.CreatePartialViewMacroFolder(virtualPath);
break;
case Core.Constants.Trees.Scripts:
virtualPath = NormalizeVirtualPath(name, SystemDirectories.Scripts);
Services.FileService.CreateScriptFolder(virtualPath);
break;
case Core.Constants.Trees.Stylesheets:
virtualPath = NormalizeVirtualPath(name, SystemDirectories.Css);
Services.FileService.CreateStyleSheetFolder(virtualPath);
break;
}
return new CodeFileDisplay
{
VirtualPath = virtualPath,
Path = Url.GetTreePathFromFilePath(virtualPath)
};
}
///
/// Used to get a specific file from disk via the FileService
///
/// This is a string but will be 'scripts' 'partialViews', 'partialViewMacros' or 'stylesheets'
/// 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)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(virtualPath)) throw new ArgumentException("Value cannot be null or whitespace.", "virtualPath");
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);
display.Id = System.Web.HttpUtility.UrlEncode(view.Path);
return display;
}
throw new HttpResponseException(HttpStatusCode.NotFound);
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);
display.Id = System.Web.HttpUtility.UrlEncode(viewMacro.Path);
return display;
}
throw new HttpResponseException(HttpStatusCode.NotFound);
case Core.Constants.Trees.Scripts:
var script = Services.FileService.GetScriptByName(virtualPath);
if (script != null)
{
var display = Mapper.Map