diff --git a/src/Umbraco.Core/Models/PartialView.cs b/src/Umbraco.Core/Models/PartialView.cs new file mode 100644 index 0000000000..f963ff0e1a --- /dev/null +++ b/src/Umbraco.Core/Models/PartialView.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text.RegularExpressions; +using Umbraco.Core.IO; + +namespace Umbraco.Core.Models +{ + /// + /// Represents a Partial View file + /// + [Serializable] + [DataContract(IsReference = true)] + internal class PartialView : File + { + private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled); + + public PartialView(string path) + : base(path) + { + base.Path = path; + } + + /// + /// Boolean indicating whether the file could be validated + /// + /// True if file is valid, otherwise false + public override bool IsValid() + { + //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; } + + public bool CreateMacro { get; set; } + + public string CodeHeader { get; set; } + + public string ParentFolderName { get; set; } + + public string EditViewFile { get; set; } + + public string BasePath { get; set; } + + public string ReturnUrl { get; set; } + + internal Regex HeaderMatch + { + get { return _headerMatch; } + } + + internal Attempt TryGetSnippetPath(string fileName) + { + var partialViewsFileSystem = new PhysicalFileSystem(BasePath); + var snippetPath = IOHelper.MapPath(string.Format("{0}/PartialViewMacros/Templates/{1}", SystemDirectories.Umbraco, fileName)); + + return partialViewsFileSystem.FileExists(snippetPath) + ? Attempt.Succeed(snippetPath) + : Attempt.Fail(); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/FileService.cs b/src/Umbraco.Core/Services/FileService.cs index 848097f6f0..c96365f7af 100644 --- a/src/Umbraco.Core/Services/FileService.cs +++ b/src/Umbraco.Core/Services/FileService.cs @@ -1,7 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Runtime.Remoting.Messaging; +using System.Text; +using System.Web; using Umbraco.Core.Auditing; using Umbraco.Core.Events; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories; @@ -17,19 +23,21 @@ namespace Umbraco.Core.Services private readonly RepositoryFactory _repositoryFactory; private readonly IUnitOfWorkProvider _fileUowProvider; private readonly IDatabaseUnitOfWorkProvider _dataUowProvider; + private readonly IMacroService _macroService; public FileService() : this(new RepositoryFactory()) { } public FileService(RepositoryFactory repositoryFactory) - : this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory) + : this(new FileUnitOfWorkProvider(), new PetaPocoUnitOfWorkProvider(), repositoryFactory, new MacroService()) { } - public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory) + public FileService(IUnitOfWorkProvider fileProvider, IDatabaseUnitOfWorkProvider dataProvider, RepositoryFactory repositoryFactory, IMacroService macroService) { _repositoryFactory = repositoryFactory; + _macroService = macroService; _fileUowProvider = fileProvider; _dataUowProvider = dataProvider; } @@ -362,6 +370,138 @@ namespace Umbraco.Core.Services return template.IsValid(); } + // TODO: Before making this public: How to get feedback in the UI when cancelled + 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)) + return Attempt.Succeed(partialView); + + if (CreatingPartialView.IsRaisedEventCancelled(new NewEventArgs(partialView, true, partialView.Alias, -1), this)) + { + // 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 + var snippetPathAttempt = partialView.TryGetSnippetPath(partialView.SnippetName); + if (snippetPathAttempt.Success == false) + { + throw new InvalidOperationException("Could not load template with name " + partialView.SnippetName); + } + + using (var snippetFile = new StreamReader(partialViewsFileSystem.OpenFile(snippetPathAttempt.Result))) + { + var snippetContent = snippetFile.ReadToEnd().Trim(); + + //strip the @inherits if it's there + snippetContent = partialView.HeaderMatch.Replace(snippetContent, string.Empty); + + var content = string.Format("{0}{1}{2}", partialView.CodeHeader, Environment.NewLine, snippetContent); + + using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content))) + { + partialViewsFileSystem.AddFile(partialView.Path, stream); + } + } + + if (partialView.CreateMacro) + CreatePartialViewMacro(partialView); + + CreatedPartialView.RaiseEvent(new NewEventArgs(partialView, false, partialView.Alias, -1), this); + + return Attempt.Succeed(partialView); + } + + internal void CreatePartialViewMacro(PartialView partialView) + { + var name = partialView.FileName.Substring(0, (partialView.FileName.LastIndexOf('.') + 1)) + .Trim('.') + .SplitPascalCasing() + .ToFirstUpperInvariant(); + + var macro = new Macro(name, name) { ScriptPath = partialView.BasePath + partialView.FileName }; + _macroService.Save(macro); + } + + // TODO: Before making this public: How to get feedback in the UI when cancelled + internal bool DeletePartialView(PartialView partialView, int userId = 0) + { + var partialViewsFileSystem = new PhysicalFileSystem(partialView.BasePath); + + if (DeletingPartialView.IsRaisedEventCancelled(new DeleteEventArgs(partialView), this)) + { + LogHelper.Info(string.Format("Deleting Partial View {0} was cancelled by an event handler.", partialViewsFileSystem.GetFullPath(partialView.FileName))); + return false; + } + + if (partialViewsFileSystem.FileExists(partialView.FileName)) + { + partialViewsFileSystem.DeleteFile(partialView.FileName); + LogHelper.Info(string.Format("Partial View file {0} deleted by user {1}", partialViewsFileSystem.GetFullPath(partialView.FileName), userId)); + } + // TODO: does this ever even happen? I don't think folders show up in the tree currently. + // Leaving this here as it was in the original PartialViewTasks code - SJ + else if (partialViewsFileSystem.DirectoryExists(partialView.FileName)) + { + partialViewsFileSystem.DeleteDirectory(partialView.FileName, true); + LogHelper.Info(string.Format("Partial View directory {0} deleted by user {1}", partialViewsFileSystem.GetFullPath(partialView.FileName), userId)); + } + + DeletedPartialView.RaiseEvent(new DeleteEventArgs(partialView, false), this); + + 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 @@ -425,6 +565,37 @@ 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 + /// + internal static event TypedEventHandler> CreatingPartialView; + + /// + /// Occurs after Create + /// + internal static event TypedEventHandler> CreatedPartialView; + + /// + /// Occurs before Delete + /// + internal static event TypedEventHandler> DeletingPartialView; + + /// + /// Occurs after Delete + /// + internal static event TypedEventHandler> DeletedPartialView; + #endregion + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs index f358e41bf3..375e1ca1ba 100644 --- a/src/Umbraco.Core/Services/ServiceContext.cs +++ b/src/Umbraco.Core/Services/ServiceContext.cs @@ -144,7 +144,7 @@ namespace Umbraco.Core.Services _dataTypeService = new Lazy(() => new DataTypeService(provider, repositoryFactory.Value)); if (_fileService == null) - _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value)); + _fileService = new Lazy(() => new FileService(fileProvider, provider, repositoryFactory.Value, _macroService.Value)); if (_localizationService == null) _localizationService = new Lazy(() => new LocalizationService(provider, repositoryFactory.Value)); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 83f0aadb2b..c317594937 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -364,6 +364,7 @@ + diff --git a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs index 056f17b157..ab017f3653 100644 --- a/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs +++ b/src/Umbraco.Web.UI/umbraco/create/PartialViewMacro.ascx.cs @@ -9,17 +9,17 @@ using umbraco.presentation.create; namespace Umbraco.Web.UI.Umbraco.Create { - public partial class PartialViewMacro : UserControl - { - + public partial class PartialViewMacro : UserControl + { - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - DataBind(); - LoadTemplates(PartialViewTemplate); - } + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + DataBind(); + + LoadTemplates(PartialViewTemplate); + } private static void LoadTemplates(ListControl list) { @@ -45,44 +45,47 @@ namespace Umbraco.Web.UI.Umbraco.Create } } - protected void SubmitButton_Click(object sender, EventArgs e) - { - if (Page.IsValid) - { - //Seriously, need to overhaul create dialogs, this is rediculous: - // http://issues.umbraco.org/issue/U4-1373 + protected void SubmitButton_Click(object sender, EventArgs e) + { + if (Page.IsValid) + { + //Seriously, need to overhaul create dialogs, this is rediculous: + // http://issues.umbraco.org/issue/U4-1373 + + var createMacroVal = 0; + if (CreateMacroCheckBox.Checked) + createMacroVal = 1; - var createMacroVal = 0; - if (CreateMacroCheckBox.Checked) - createMacroVal = 1; - string returnUrl = dialogHandler_temp.Create(Request.GetItemAsString("nodeType"), createMacroVal, //apparently we need to pass this value to 'ParentID'... of course! :P then we'll extract it in PartialViewTasks to create it. PartialViewTemplate.SelectedValue + "|||" + FileName.Text); - - BasePage.Current.ClientTools - .ChangeContentFrameUrl(returnUrl) - .ChildNodeCreated() - .CloseModalWindow(); - } - } - protected void MacroExistsValidator_OnServerValidate(object source, ServerValidateEventArgs args) - { - if (CreateMacroCheckBox.Checked) - { + BasePage.Current.ClientTools + .ChangeContentFrameUrl(returnUrl) + .ChildNodeCreated() + .CloseModalWindow(); + } + } + + protected void MacroExistsValidator_OnServerValidate(object source, ServerValidateEventArgs args) + { + if (CreateMacroCheckBox.Checked) + { //TODO: Shouldn't this use our string functions to create the alias ? - var fileName = FileName.Text + ".cshtml"; - var name = fileName - .Substring(0, (fileName.LastIndexOf('.') + 1)).Trim('.') - .SplitPascalCasing().ToFirstUpperInvariant(); + var fileName = FileName.Text; + + var name = fileName.Contains(".") + ? fileName.Substring(0, (fileName.LastIndexOf('.') + 1)).Trim('.') + : fileName; + + name = name.SplitPascalCasing().ToFirstUpperInvariant(); var macro = ApplicationContext.Current.Services.MacroService.GetByAlias(name); if (macro != null) { args.IsValid = false; - } - } - } - } + } + } + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 39ca165358..3a8a7c3c38 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -488,6 +488,7 @@ ASPXCodeBehind + ASPXCodeBehind diff --git a/src/Umbraco.Web/WebServices/SaveFileController.cs b/src/Umbraco.Web/WebServices/SaveFileController.cs index 7b90da05c1..591b89e1d4 100644 --- a/src/Umbraco.Web/WebServices/SaveFileController.cs +++ b/src/Umbraco.Web/WebServices/SaveFileController.cs @@ -2,8 +2,11 @@ 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.Models; +using Umbraco.Core.Services; using Umbraco.Web.Macros; using Umbraco.Web.Mvc; using umbraco; @@ -16,91 +19,72 @@ 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 - { + /// 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 macr - /// - /// - /// - /// - /// - [HttpPost] - public JsonResult SavePartialView(string filename, string oldName, string contents) - { - var folderPath = SystemDirectories.MvcViews.EnsureEndsWith('/');// +"/Partials/"; + /// + /// 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/"; + var savePath = filename.StartsWith("~/") ? IOHelper.MapPath(filename) : IOHelper.MapPath(folderPath + filename); - // 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 partialView = new PartialView(savePath) + { + BasePath = folderPath, + OldFileName = oldName, + FileName = filename, + Content = contents, + }; - 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); + var fileService = (FileService)ApplicationContext.Current.Services.FileService; + var attempt = fileService.SavePartialView(partialView); - //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 (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 + attempt.Exception); + } - //deletes the old file - if (savePath != saveOldPath) - { - if (System.IO.File.Exists(saveOldPath)) - System.IO.File.Delete(saveOldPath); - } + return Success(ui.Text("speechBubbles", "partialViewSavedText"), ui.Text("speechBubbles", "partialViewSavedHeader")); + } - //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); - - 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 - }; + /// + /// 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) @@ -108,16 +92,16 @@ namespace Umbraco.Web.WebServices pathChanged = true; t.MasterTemplate = masterTemplateId; } - } - catch (ArgumentException ex) - { - //the template does not exist - return Failed("Template does not exist", ui.Text("speechBubbles", "templateErrorHeader"), ex); - } + } + catch (ArgumentException ex) + { + //the template does not exist + return Failed("Template does not exist", ui.Text("speechBubbles", "templateErrorHeader"), ex); + } - try - { - t.Save(); + try + { + t.Save(); //ensure the correct path is synced as the parent might have been changed // http://issues.umbraco.org/issue/U4-2300 @@ -128,14 +112,14 @@ namespace Umbraco.Web.WebServices } 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); - } - } + 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 @@ -151,27 +135,26 @@ namespace Umbraco.Web.WebServices d["message"] = message; d["header"] = header; - return Json(d); - } + 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.")) - }); - } - - } + /// + /// 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.")) + }); + } + } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs index 7bde1e2b9a..36aee27fcc 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewMacrosTasks.cs @@ -1,14 +1,4 @@ -using System; -using System.IO; -using System.Text.RegularExpressions; -using System.Web; -using Umbraco.Core.CodeAnnotations; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web.Mvc; -using Umbraco.Web.UI; -using umbraco.BasePages; -using Umbraco.Core; +using Umbraco.Core.CodeAnnotations; using umbraco.BusinessLogic; namespace umbraco @@ -17,100 +7,21 @@ namespace umbraco /// The UI 'tasks' for the create dialog and delete processes /// [UmbracoWillObsolete("http://issues.umbraco.org/issue/U4-1373", "This will one day be removed when we overhaul the create process")] - public class PartialViewMacroTasks : LegacyDialogTask + public class PartialViewMacroTasks : PartialViewTasksBase { - private const string CodeHeader = "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; - private string _returnUrl = ""; - private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled); - - - protected virtual string EditViewFile + protected override string CodeHeader { - get { return "Settings/Views/EditView.aspx"; } + get { return "@inherits Umbraco.Web.Macros.PartialViewMacroPage"; } } - protected string BasePath - { - get { return SystemDirectories.MvcViews + "/" + ParentFolderName.EnsureEndsWith('/'); } - } - - protected virtual string ParentFolderName + protected override string ParentFolderName { get { return "MacroPartials"; } } - - public override bool PerformSave() - { - var pipesIndex = Alias.IndexOf("|||", System.StringComparison.Ordinal); - var template = Alias.Substring(0, pipesIndex).Trim(); - var fileName = Alias.Substring(pipesIndex + 3, Alias.Length - pipesIndex - 3) + ".cshtml"; - - var fullFilePath = IOHelper.MapPath(BasePath + fileName); - - //return the link to edit the file if it already exists - if (File.Exists(fullFilePath)) - { - _returnUrl = string.Format(EditViewFile + "?file={0}", HttpUtility.UrlEncode(ParentFolderName.EnsureEndsWith('/') + fileName)); - return true; - } - - //create the file - using (var sw = File.CreateText(fullFilePath)) - { - using (var templateFile = File.OpenText(IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/" + template))) - { - var templateContent = templateFile.ReadToEnd().Trim(); - - //strip the @inherits if it's there - templateContent = _headerMatch.Replace(templateContent, string.Empty); - - sw.Write( - "{0}{1}{2}", - CodeHeader, - Environment.NewLine, - templateContent); - } - } - - // Create macro? - if (ParentID == 1) - { - //TODO: Shouldn't this use our string functions to create the alias ? - var name = fileName - .Substring(0, (fileName.LastIndexOf('.') + 1)).Trim('.') - .SplitPascalCasing().ToFirstUpperInvariant(); - var m = cms.businesslogic.macro.Macro.MakeNew(name); - m.ScriptingFile = BasePath + fileName; - m.Save(); - } - - _returnUrl = string.Format(EditViewFile + "?treeType={0}&file={1}", "partialViewMacros", HttpUtility.UrlEncode(ParentFolderName.EnsureEndsWith('/') + fileName)); - return true; - } - - public override string ReturnUrl - { - get { return _returnUrl; } - } - + public override string AssignedApp { get { return DefaultApps.developer.ToString(); } } - - public override bool PerformDelete() - { - var path = IOHelper.MapPath(BasePath + Alias.TrimStart('/')); - - if (File.Exists(path)) - File.Delete(path); - else if (Directory.Exists(path)) - Directory.Delete(path, true); - - LogHelper.Info(string.Format("{0} Deleted by user {1}", Alias, User.Id)); - - return true; - } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs index 5a51055e96..4fda55c201 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasks.cs @@ -1,155 +1,27 @@ -using System; -using System.IO; -using System.Text.RegularExpressions; -using System.Web; -using Umbraco.Core.CodeAnnotations; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Web.Mvc; -using Umbraco.Web.UI; -using umbraco.BasePages; -using Umbraco.Core; +using Umbraco.Core.CodeAnnotations; using umbraco.BusinessLogic; namespace umbraco { - /// - /// The UI 'tasks' for the create dialog and delete processes - /// - [UmbracoWillObsolete("http://issues.umbraco.org/issue/U4-1373", "This will one day be removed when we overhaul the create process")] - public class PartialViewTasks : LegacyDialogTask + /// + /// The UI 'tasks' for the create dialog and delete processes + /// + [UmbracoWillObsolete("http://issues.umbraco.org/issue/U4-1373", "This will one day be removed when we overhaul the create process")] + public class PartialViewTasks : PartialViewTasksBase { - private const string CodeHeader = "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; - - private readonly Regex _headerMatch = new Regex("^@inherits\\s+?.*$", RegexOptions.Multiline | RegexOptions.Compiled); - - - protected virtual string EditViewFile + protected override string CodeHeader { - get { return "Settings/Views/EditView.aspx"; } + get { return "@inherits Umbraco.Web.Mvc.UmbracoTemplatePage"; } } - protected string BasePath - { - get { return SystemDirectories.MvcViews + "/" + ParentFolderName.EnsureEndsWith('/'); } - } - - protected virtual string ParentFolderName + protected override string ParentFolderName { get { return "Partials"; } } - - public override bool PerformSave() - { - var pipesIndex = Alias.IndexOf("|||", System.StringComparison.Ordinal); - var template = Alias.Substring(0, pipesIndex).Trim(); - var fileName = Alias.Substring(pipesIndex + 3, Alias.Length - pipesIndex - 3); - if (!fileName.ToLowerInvariant().EndsWith(".cshtml")) - { - fileName += ".cshtml"; - } - - var fullFilePath = IOHelper.MapPath(BasePath + fileName); - - //return the link to edit the file if it already exists - if (File.Exists(fullFilePath)) - { - _returnUrl = string.Format(EditViewFile + "?file={0}", HttpUtility.UrlEncode(ParentFolderName.EnsureEndsWith('/') + fileName)); - return true; - } - - //create the file - using (var sw = File.CreateText(fullFilePath)) - { - var templatePathAttempt = TryGetTemplatePath(template); - if (templatePathAttempt.Success == false) - { - throw new InvalidOperationException("Could not load template with name " + template); - } - - using (var templateFile = File.OpenText(templatePathAttempt.Result)) - { - var templateContent = templateFile.ReadToEnd().Trim(); - - //strip the @inherits if it's there - templateContent = _headerMatch.Replace(templateContent, string.Empty); - - sw.Write( - "{0}{1}{2}", - CodeHeader, - Environment.NewLine, - templateContent); - } - } - - // Create macro? - if (ParentID == 1) - { - var name = fileName - .Substring(0, (fileName.LastIndexOf('.') + 1)).Trim('.') - .SplitPascalCasing().ToFirstUpperInvariant(); - var m = cms.businesslogic.macro.Macro.MakeNew(name); - m.ScriptingFile = BasePath + fileName; - m.Save(); - } - - _returnUrl = string.Format(EditViewFile + "?file={0}", HttpUtility.UrlEncode(ParentFolderName.EnsureEndsWith('/') + fileName)); - return true; - } - - protected virtual void WriteTemplateHeader(StreamWriter sw) + + public override string AssignedApp { - //write out the template header - sw.Write("@inherits "); - sw.Write(typeof(UmbracoTemplatePage).FullName.TrimEnd("`1")); - } - - public override bool PerformDelete() - { - var path = IOHelper.MapPath(BasePath + Alias.TrimStart('/')); - - if (File.Exists(path)) - File.Delete(path); - else if (Directory.Exists(path)) - Directory.Delete(path, true); - - LogHelper.Info(string.Format("{0} Deleted by user {1}", Alias, UmbracoEnsuredPage.CurrentUser.Id)); - - return true; - } - - - private string _returnUrl = ""; - public override string ReturnUrl - { - get { return _returnUrl; } - } - - public override string AssignedApp - { get { return DefaultApps.settings.ToString(); } - } - - - /// - /// Looks first in the Partial View template folder and then in the macro partials template folder if not found - /// - /// - /// - private Attempt TryGetTemplatePath(string fileName) - { - var templatePath = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViews/Templates/" + fileName); - - if (File.Exists(templatePath)) - { - return Attempt.Succeed(templatePath); - } - - templatePath = IOHelper.MapPath(SystemDirectories.Umbraco + "/PartialViewMacros/Templates/" + fileName); - - return File.Exists(templatePath) - ? Attempt.Succeed(templatePath) - : Attempt.Fail(); } } } diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs new file mode 100644 index 0000000000..313cbc3efd --- /dev/null +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/create/PartialViewTasksBase.cs @@ -0,0 +1,87 @@ +using System; +using Umbraco.Core.CodeAnnotations; +using Umbraco.Core.IO; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Web.UI; +using umbraco.BasePages; +using Umbraco.Core; + +namespace umbraco +{ + /// + /// The base UI 'tasks' for the create dialog and delete processes + /// + [UmbracoWillObsolete("http://issues.umbraco.org/issue/U4-1373", "This will one day be removed when we overhaul the create process")] + public abstract class PartialViewTasksBase : LegacyDialogTask + { + private string _returnUrl = string.Empty; + public override string ReturnUrl + { + get { return _returnUrl; } + } + + protected abstract string CodeHeader { get; } + + protected abstract string ParentFolderName { get; } + + public override string AssignedApp + { + get { return string.Empty; } + } + + protected virtual string EditViewFile + { + get { return "Settings/Views/EditView.aspx"; } + } + + protected string BasePath + { + get { return SystemDirectories.MvcViews + "/" + ParentFolderName.EnsureEndsWith('/'); } + } + + public override bool PerformSave() + { + var pipesIndex = Alias.IndexOf("|||", StringComparison.Ordinal); + var snippetName = Alias.Substring(0, pipesIndex).Trim(); + var fileName = Alias.Substring(pipesIndex + 3, Alias.Length - pipesIndex - 3); + if (fileName.ToLowerInvariant().EndsWith(".cshtml") == false) + { + fileName += ".cshtml"; + } + + var partialViewsFileSystem = new PhysicalFileSystem(BasePath); + var fullFilePath = partialViewsFileSystem.GetFullPath(fileName); + + var model = new PartialView(fullFilePath) + { + FileName = fileName, + SnippetName = snippetName, + CreateMacro = ParentID == 1, + CodeHeader = CodeHeader, + ParentFolderName = ParentFolderName, + EditViewFile = EditViewFile, + BasePath = BasePath + }; + + var fileService = (FileService)ApplicationContext.Current.Services.FileService; + var attempt = fileService.CreatePartialView(model); + + _returnUrl = attempt.Result.ReturnUrl; + + return attempt.Success; + } + + public override bool PerformDelete() + { + var partialViewsFileSystem = new PhysicalFileSystem(BasePath); + var path = Alias.TrimStart('/'); + var fullFilePath = partialViewsFileSystem.GetFullPath(path); + + var model = new PartialView(fullFilePath) { BasePath = BasePath, FileName = path }; + + var fileService = (FileService)ApplicationContext.Current.Services.FileService; + return fileService.DeletePartialView(model, UmbracoEnsuredPage.CurrentUser.Id); + } + } +}