using System; using System.Linq; using System.Text; using System.Web.Mvc; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Services; using Umbraco.Web.Macros; using Umbraco.Web.Mvc; using umbraco; using umbraco.cms.businesslogic.macro; using System.Collections.Generic; using Umbraco.Core; using Template = umbraco.cms.businesslogic.template.Template; namespace Umbraco.Web.WebServices { /// /// A REST controller used to save files such as templates, partial views, macro files, etc... /// /// /// This isn't fully implemented yet but we should migrate all of the logic in the umbraco.presentation.webservices.codeEditorSave /// over to this controller. /// public class SaveFileController : UmbracoAuthorizedController { /// /// Saves a partial view for a partial view macro /// /// /// /// /// [HttpPost] public JsonResult SavePartialView(string filename, string oldName, string contents) { var folderPath = SystemDirectories.MvcViews.EnsureEndsWith('/');// +"/Partials/"; // validate file IOHelper.ValidateEditPath(IOHelper.MapPath(folderPath + filename), folderPath); // validate extension IOHelper.ValidateFileExtension(IOHelper.MapPath(folderPath + filename), new[] { "cshtml" }.ToList()); //TODO: Validate using the macro engine var engine = MacroEngineFactory.GetEngine(PartialViewMacroEngine.EngineName); //engine.Validate(...) var val = contents; var saveOldPath = oldName.StartsWith("~/") ? IOHelper.MapPath(oldName) : IOHelper.MapPath(folderPath + oldName); var savePath = filename.StartsWith("~/") ? IOHelper.MapPath(filename) : IOHelper.MapPath(folderPath + filename); //Directory check.. only allow files in script dir and below to be edited if (!savePath.StartsWith(IOHelper.MapPath(folderPath))) { return Failed( ui.Text("speechBubbles", "partialViewErrorText"), ui.Text("speechBubbles", "partialViewErrorHeader"), //pass in a new exception ... this will also append the the message new ArgumentException("Illegal path: " + savePath)); } if (Saving.IsRaisedEventCancelled(new SaveEventArgs(savePath), this)) { return Failed( ui.Text("speechBubbles", "partialViewErrorText"), ui.Text("speechBubbles", "partialViewErrorHeader"), //pass in a new exception ... this will also append the the message new ArgumentException("Save was cancelled by an event handler " + savePath)); } //deletes the old file if (savePath != saveOldPath) { if (System.IO.File.Exists(saveOldPath)) System.IO.File.Delete(saveOldPath); } //NOTE: I've left the below here just for informational purposes. If we save a file this way, then the UTF8 // BOM mucks everything up, strangely, if we use WriteAllText everything is ok! // http://issues.umbraco.org/issue/U4-2118 //using (var sw = System.IO.File.CreateText(savePath)) //{ // sw.Write(val); //} System.IO.File.WriteAllText(savePath, val, Encoding.UTF8); Saved.RaiseEvent(new SaveEventArgs(savePath, false), this); return Success(ui.Text("speechBubbles", "partialViewSavedText"), ui.Text("speechBubbles", "partialViewSavedHeader")); } /// /// Saves a template /// /// /// /// /// /// /// [HttpPost] public JsonResult SaveTemplate(string templateName, string templateAlias, string templateContents, int templateId, int masterTemplateId) { Template t; bool pathChanged = false; try { t = new Template(templateId) { Text = templateName, Alias = templateAlias, Design = templateContents }; //check if the master page has changed if (t.MasterTemplate != masterTemplateId) { pathChanged = true; t.MasterTemplate = masterTemplateId; } } catch (ArgumentException ex) { //the template does not exist return Failed("Template does not exist", ui.Text("speechBubbles", "templateErrorHeader"), ex); } try { t.Save(); //ensure the correct path is synced as the parent might have been changed // http://issues.umbraco.org/issue/U4-2300 if (pathChanged) { //need to re-look it up t = new Template(templateId); } var syncPath = "-1,init," + t.Path.Replace("-1,", ""); return Success(ui.Text("speechBubbles", "templateSavedText"), ui.Text("speechBubbles", "templateSavedHeader"), new { path = syncPath }); } catch (Exception ex) { return Failed(ui.Text("speechBubbles", "templateErrorText"), ui.Text("speechBubbles", "templateErrorHeader"), ex); } } /// /// Returns a successful message /// /// The message to display in the speach bubble /// The header to display in the speach bubble /// /// private JsonResult Success(string message, string header, object additionalVals = null) { var d = additionalVals == null ? new Dictionary() : additionalVals.ToDictionary(); d["success"] = true; d["message"] = message; d["header"] = header; return Json(d); } /// /// Returns a failed message /// /// The message to display in the speach bubble /// The header to display in the speach bubble /// The exception if there was one /// private JsonResult Failed(string message, string header, Exception exception = null) { if (exception != null) LogHelper.Error("An error occurred saving a file. " + message, exception); return Json(new { success = false, header = header, message = message + (exception == null ? "" : (exception.Message + ". Check log for details.")) }); } /// /// Occurs before Create /// internal static event TypedEventHandler> Saving; /// /// Occurs after Create /// internal static event TypedEventHandler> Saved; } }