From 1805857eea20a52dc77c59fc8dde612ddea6ee09 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 19 Aug 2014 12:51:07 +0200 Subject: [PATCH] Refactor PartialView.IsValid with logic found in SaveFileController Refactor FileService.CreatePartialView to use Attempt so that it gets rid of PartialView.SaveSucceeded Refactor SaveFileController to move all saving logic into the FileService Added SavingPartialView and SavedPartialView events to the FileService --- src/Umbraco.Core/Models/PartialView.cs | 14 +++- src/Umbraco.Core/Services/FileService.cs | 75 ++++++++++++++++--- .../WebServices/SaveFileController.cs | 67 ++++------------- .../umbraco/create/PartialViewTasksBase.cs | 6 +- 4 files changed, 90 insertions(+), 72 deletions(-) diff --git a/src/Umbraco.Core/Models/PartialView.cs b/src/Umbraco.Core/Models/PartialView.cs index 4ec7cf567d..f963ff0e1a 100644 --- a/src/Umbraco.Core/Models/PartialView.cs +++ b/src/Umbraco.Core/Models/PartialView.cs @@ -28,10 +28,18 @@ namespace Umbraco.Core.Models /// True if file is valid, otherwise false public override bool IsValid() { - //Validate extension - return IOHelper.VerifyFileExtension(Path, new List { "cshtml" }); + //TODO: Validate using the macro engine + //var engine = MacroEngineFactory.GetEngine(PartialViewMacroEngine.EngineName); + //engine.Validate(...) + + var validatePath = IOHelper.ValidateEditPath(IOHelper.MapPath(Path), BasePath); + var verifyFileExtension = IOHelper.VerifyFileExtension(Path, new List { "cshtml" }); + + return validatePath && verifyFileExtension; } + public string OldFileName { get; set; } + public string FileName { get; set; } public string SnippetName { get; set; } @@ -48,8 +56,6 @@ namespace Umbraco.Core.Models public string ReturnUrl { get; set; } - public bool SaveSucceeded { get; set; } - internal Regex HeaderMatch { get { return _headerMatch; } diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index 40389b865e..90a7c7061c 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.Remoting.Messaging; using System.Text; using System.Web; using Umbraco.Core.Auditing; @@ -367,24 +368,25 @@ namespace Umbraco.Core.Services return template.IsValid(); } - internal PartialView CreatePartialView(PartialView partialView) + internal Attempt CreatePartialView(PartialView partialView) { var partialViewsFileSystem = new PhysicalFileSystem(partialView.BasePath); var relativeFilePath = partialView.ParentFolderName.EnsureEndsWith('/') + partialViewsFileSystem.GetRelativePath(partialView.FileName); + partialView.ReturnUrl = string.Format(partialView.EditViewFile + "?file={0}", HttpUtility.UrlEncode(relativeFilePath)); //return the link to edit the file if it already exists if (partialViewsFileSystem.FileExists(partialView.Path)) - { - partialView.ReturnUrl = string.Format(partialView.EditViewFile + "?file={0}", HttpUtility.UrlEncode(relativeFilePath)); - partialView.SaveSucceeded = true; - return partialView; - } + return Attempt.Succeed(partialView); if (CreatingPartialView.IsRaisedEventCancelled(new NewEventArgs(partialView, true, partialView.Alias, -1), this)) { - LogHelper.Info(string.Format("Creating Partial View {0} was cancelled by an event handler.", partialViewsFileSystem.GetFullPath(partialView.FileName))); - partialView.SaveSucceeded = false; - return partialView; + // We have nowhere to return to, clear ReturnUrl + partialView.ReturnUrl = string.Empty; + + var failureMessage = string.Format("Creating Partial View {0} was cancelled by an event handler.", partialViewsFileSystem.GetFullPath(partialView.FileName)); + LogHelper.Info(failureMessage); + + return Attempt.Fail(partialView, new ArgumentException(failureMessage)); } //create the file @@ -411,12 +413,10 @@ namespace Umbraco.Core.Services if (partialView.CreateMacro) CreatePartialViewMacro(partialView); - - partialView.ReturnUrl = string.Format(partialView.EditViewFile + "?file={0}", HttpUtility.UrlEncode(relativeFilePath)); CreatedPartialView.RaiseEvent(new NewEventArgs(partialView, false, partialView.Alias, -1), this); - return partialView; + return Attempt.Succeed(partialView); } internal static void CreatePartialViewMacro(PartialView partialView) @@ -459,6 +459,46 @@ namespace Umbraco.Core.Services return true; } + internal Attempt SavePartialView(PartialView partialView, int userId = 0) + { + if (SavingPartialView.IsRaisedEventCancelled(new SaveEventArgs(partialView, true), this)) + { + return Attempt.Fail(new ArgumentException("Save was cancelled by an event handler " + partialView.FileName)); + } + + //Directory check.. only allow files in script dir and below to be edited + if (partialView.IsValid() == false) + { + return Attempt.Fail( + new ArgumentException(string.Format("Illegal path: {0} or illegal file extension {1}", + partialView.Path, + partialView.FileName.Substring(partialView.FileName.LastIndexOf(".", StringComparison.Ordinal))))); + } + + //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(partialView.Path, partialView.Content, Encoding.UTF8); + + //deletes the old file + if (partialView.FileName != partialView.OldFileName) + { + // Create a new PartialView class so that we can set the FileName of the file that needs deleting + var deletePartial = partialView; + deletePartial.FileName = partialView.OldFileName; + DeletePartialView(deletePartial, userId); + } + + SavedPartialView.RaiseEvent(new SaveEventArgs(partialView), this); + + return Attempt.Succeed(partialView); + } + //TODO Method to change name and/or alias of view/masterpage template #region Event Handlers @@ -522,6 +562,16 @@ namespace Umbraco.Core.Services /// public static event TypedEventHandler> SavedStylesheet; + /// + /// Occurs before Save + /// + internal static event TypedEventHandler> SavingPartialView; + + /// + /// Occurs after Save + /// + internal static event TypedEventHandler> SavedPartialView; + /// /// Occurs before Create /// @@ -543,5 +593,6 @@ namespace Umbraco.Core.Services internal static event TypedEventHandler> DeletedPartialView; #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Web/WebServices/SaveFileController.cs b/src/Umbraco.Web/WebServices/SaveFileController.cs index 17af82353d..56f52887ce 100644 --- a/src/Umbraco.Web/WebServices/SaveFileController.cs +++ b/src/Umbraco.Web/WebServices/SaveFileController.cs @@ -5,6 +5,7 @@ using System.Web.Mvc; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Macros; using Umbraco.Web.Mvc; @@ -38,57 +39,27 @@ namespace Umbraco.Web.WebServices 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))) + var partialView = new PartialView(savePath) + { + BasePath = folderPath, + OldFileName = oldName, + FileName = filename, + Content = contents, + }; + + var fileService = new FileService(); + var attempt = fileService.SavePartialView(partialView); + + if (attempt.Success == false) { 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)); + attempt.Exception); } - - 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")); } @@ -185,15 +156,5 @@ namespace Umbraco.Web.WebServices 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; } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs index 0039f293c6..fdceb238d3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs @@ -65,11 +65,11 @@ namespace umbraco }; var fileService = new FileService(); - var partialView = fileService.CreatePartialView(model); + var attempt = fileService.CreatePartialView(model); - _returnUrl = partialView.ReturnUrl; + _returnUrl = attempt.Result.ReturnUrl; - return partialView.SaveSucceeded; + return attempt.Success; } public override bool PerformDelete()