2012-12-30 03:11:21 +03:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
2013-04-19 10:18:46 -02:00
|
|
|
|
using System.Text;
|
2012-12-30 03:11:21 +03:00
|
|
|
|
using System.Web.Mvc;
|
2014-08-14 17:20:25 +02:00
|
|
|
|
using Umbraco.Core.Events;
|
2012-12-30 03:11:21 +03:00
|
|
|
|
using Umbraco.Core.IO;
|
|
|
|
|
|
using Umbraco.Core.Logging;
|
2014-08-19 12:51:07 +02:00
|
|
|
|
using Umbraco.Core.Models;
|
2014-08-19 10:39:52 +02:00
|
|
|
|
using Umbraco.Core.Services;
|
2013-01-01 00:42:20 +03:00
|
|
|
|
using Umbraco.Web.Macros;
|
2012-12-30 03:11:21 +03:00
|
|
|
|
using Umbraco.Web.Mvc;
|
|
|
|
|
|
using umbraco;
|
2013-01-01 00:42:20 +03:00
|
|
|
|
using umbraco.cms.businesslogic.macro;
|
2013-08-30 14:15:51 +10:00
|
|
|
|
using System.Collections.Generic;
|
2015-09-03 15:12:37 +02:00
|
|
|
|
using umbraco.cms.helpers;
|
2012-12-31 02:51:30 +03:00
|
|
|
|
using Umbraco.Core;
|
2015-09-03 15:12:37 +02:00
|
|
|
|
using Umbraco.Core.Configuration;
|
2013-02-11 03:55:58 +06:00
|
|
|
|
using Template = umbraco.cms.businesslogic.template.Template;
|
2012-12-30 03:11:21 +03:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Web.WebServices
|
|
|
|
|
|
{
|
2013-02-09 04:05:01 +06:00
|
|
|
|
/// <summary>
|
2014-08-14 17:20:25 +02:00
|
|
|
|
/// A REST controller used to save files such as templates, partial views, macro files, etc...
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <remarks>
|
|
|
|
|
|
/// This isn't fully implemented yet but we should migrate all of the logic in the umbraco.presentation.webservices.codeEditorSave
|
|
|
|
|
|
/// over to this controller.
|
|
|
|
|
|
/// </remarks>
|
2015-11-25 19:39:24 +01:00
|
|
|
|
[ValidateMvcAngularAntiForgeryToken]
|
2014-08-14 17:20:25 +02:00
|
|
|
|
public class SaveFileController : UmbracoAuthorizedController
|
|
|
|
|
|
{
|
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Saves a partial view macro
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="filename"></param>
|
|
|
|
|
|
/// <param name="oldName"></param>
|
|
|
|
|
|
/// <param name="contents"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
[HttpPost]
|
|
|
|
|
|
public JsonResult SavePartialViewMacro(string filename, string oldName, string contents)
|
|
|
|
|
|
{
|
2015-09-08 17:48:26 +02:00
|
|
|
|
var svce = (FileService) Services.FileService;
|
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
|
|
|
|
|
2015-09-08 17:48:26 +02:00
|
|
|
|
return SavePartialView(svce,
|
|
|
|
|
|
filename, oldName, contents,
|
|
|
|
|
|
"MacroPartials/",
|
|
|
|
|
|
(s, n) => s.GetPartialViewMacro(n),
|
|
|
|
|
|
(s, v) => s.ValidatePartialViewMacro((PartialView) v),
|
|
|
|
|
|
(s, v) => s.SavePartialViewMacro(v));
|
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
|
|
|
|
}
|
2014-08-14 17:20:25 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
|
|
|
|
/// Saves a partial view
|
2014-08-14 17:20:25 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="filename"></param>
|
|
|
|
|
|
/// <param name="oldName"></param>
|
|
|
|
|
|
/// <param name="contents"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
[HttpPost]
|
|
|
|
|
|
public JsonResult SavePartialView(string filename, string oldName, string contents)
|
|
|
|
|
|
{
|
2015-09-08 17:48:26 +02:00
|
|
|
|
var svce = (FileService) Services.FileService;
|
|
|
|
|
|
|
|
|
|
|
|
return SavePartialView(svce,
|
|
|
|
|
|
filename, oldName, contents,
|
|
|
|
|
|
"Partials/",
|
|
|
|
|
|
(s, n) => s.GetPartialView(n),
|
|
|
|
|
|
(s, v) => s.ValidatePartialView((PartialView) v),
|
|
|
|
|
|
(s, v) => s.SavePartialView(v));
|
|
|
|
|
|
}
|
2014-08-14 17:20:25 +02:00
|
|
|
|
|
2015-09-08 17:48:26 +02:00
|
|
|
|
private JsonResult SavePartialView(IFileService svce,
|
|
|
|
|
|
string filename, string oldname, string contents,
|
|
|
|
|
|
string pathPrefix,
|
|
|
|
|
|
Func<IFileService, string, IPartialView> get,
|
|
|
|
|
|
Func<IFileService, IPartialView, bool> validate,
|
|
|
|
|
|
Func<IFileService, IPartialView, Attempt<IPartialView>> save)
|
|
|
|
|
|
{
|
2015-09-10 14:10:45 +02:00
|
|
|
|
// sanitize input - partial view names have an extension
|
|
|
|
|
|
filename = filename
|
|
|
|
|
|
.Replace('\\', '/')
|
|
|
|
|
|
.TrimStart('/');
|
|
|
|
|
|
|
2015-09-08 17:48:26 +02:00
|
|
|
|
// sharing the editor with partial views & partial view macros,
|
|
|
|
|
|
// using path prefix to differenciate,
|
|
|
|
|
|
// but the file service manages different filesystems,
|
|
|
|
|
|
// and we need to come back to filesystem-relative paths
|
|
|
|
|
|
|
|
|
|
|
|
// not sure why we still need this but not going to change it now
|
|
|
|
|
|
|
|
|
|
|
|
if (filename.InvariantStartsWith(pathPrefix))
|
|
|
|
|
|
filename = filename.TrimStart(pathPrefix);
|
|
|
|
|
|
|
2015-09-10 14:10:45 +02:00
|
|
|
|
if (oldname != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
oldname = oldname.TrimStart('/', '\\');
|
|
|
|
|
|
if (oldname.InvariantStartsWith(pathPrefix))
|
|
|
|
|
|
oldname = oldname.TrimStart(pathPrefix);
|
|
|
|
|
|
}
|
2015-09-08 17:48:26 +02:00
|
|
|
|
|
2015-10-22 15:40:12 +02:00
|
|
|
|
var currentView = oldname.IsNullOrWhiteSpace()
|
|
|
|
|
|
? get(svce, filename)
|
|
|
|
|
|
: get(svce, oldname);
|
|
|
|
|
|
|
|
|
|
|
|
if (currentView == null)
|
|
|
|
|
|
currentView = new PartialView(filename);
|
2015-09-08 17:48:26 +02:00
|
|
|
|
else
|
2015-10-22 15:40:12 +02:00
|
|
|
|
currentView.Path = filename;
|
|
|
|
|
|
currentView.Content = contents;
|
2015-09-08 17:48:26 +02:00
|
|
|
|
|
|
|
|
|
|
Attempt<IPartialView> attempt;
|
|
|
|
|
|
try
|
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
|
|
|
|
{
|
2015-10-22 15:40:12 +02:00
|
|
|
|
var partialView = currentView as PartialView;
|
2015-09-08 17:48:26 +02:00
|
|
|
|
if (partialView != null && validate != null && validate(svce, partialView) == false)
|
|
|
|
|
|
return Failed(ui.Text("speechBubbles", "partialViewErrorText"), ui.Text("speechBubbles", "partialViewErrorHeader"),
|
2015-10-22 15:40:12 +02:00
|
|
|
|
new FileSecurityException("File '" + currentView.Path + "' is not a valid partial view file."));
|
2015-09-08 17:48:26 +02:00
|
|
|
|
|
2015-10-22 15:40:12 +02:00
|
|
|
|
attempt = save(svce, currentView);
|
Updates PartialView & PartialViewMacros models/services/repositories, streamlines their operations, fixes up other underlying problems with the FileRepository, fixes tree syncing for partial views, partial view macros and scripts, fixes scripts being created in folders, allows partial views and partial view macros to be managed and created in folders, fixes FileUnitOfWork to use a queue, publicizes some internal test classes, fixes tree syncing when dealing with invariant case, adds correct validation to the create dialogs of scripts and partial views (and partial view macros)
2014-10-22 16:44:45 +10:00
|
|
|
|
}
|
2015-09-08 17:48:26 +02:00
|
|
|
|
catch (Exception e)
|
2014-08-14 17:20:25 +02:00
|
|
|
|
{
|
2015-09-08 17:48:26 +02:00
|
|
|
|
return Failed(ui.Text("speechBubbles", "partialViewErrorText"), ui.Text("speechBubbles", "partialViewErrorHeader"), e);
|
|
|
|
|
|
}
|
2014-08-14 17:20:25 +02:00
|
|
|
|
|
2014-08-19 12:51:07 +02:00
|
|
|
|
if (attempt.Success == false)
|
2014-08-14 17:20:25 +02:00
|
|
|
|
{
|
2015-09-08 17:48:26 +02:00
|
|
|
|
return Failed(ui.Text("speechBubbles", "partialViewErrorText"), ui.Text("speechBubbles", "partialViewErrorHeader"),
|
|
|
|
|
|
attempt.Exception);
|
2014-08-14 17:20:25 +02:00
|
|
|
|
}
|
2013-04-18 23:00:57 +06:00
|
|
|
|
|
2014-08-14 17:20:25 +02:00
|
|
|
|
return Success(ui.Text("speechBubbles", "partialViewSavedText"), ui.Text("speechBubbles", "partialViewSavedHeader"));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Saves a template
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="templateName"></param>
|
|
|
|
|
|
/// <param name="templateAlias"></param>
|
|
|
|
|
|
/// <param name="templateContents"></param>
|
|
|
|
|
|
/// <param name="templateId"></param>
|
|
|
|
|
|
/// <param name="masterTemplateId"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
[HttpPost]
|
|
|
|
|
|
public JsonResult SaveTemplate(string templateName, string templateAlias, string templateContents, int templateId, int masterTemplateId)
|
|
|
|
|
|
{
|
2014-12-05 17:29:47 +11:00
|
|
|
|
//TODO: Change this over to use the new API - Also this will be migrated to a TemplateEditor or ViewEditor when it's all moved to angular
|
|
|
|
|
|
|
2014-08-14 17:20:25 +02:00
|
|
|
|
Template t;
|
|
|
|
|
|
bool pathChanged = false;
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
t = new Template(templateId)
|
|
|
|
|
|
{
|
2016-01-05 11:42:02 +01:00
|
|
|
|
Text = templateName.CleanForXss('[', ']', '(', ')', ':'),
|
|
|
|
|
|
Alias = templateAlias.CleanForXss('[', ']', '(', ')', ':'),
|
2014-08-14 17:20:25 +02:00
|
|
|
|
Design = templateContents
|
|
|
|
|
|
};
|
2013-08-30 14:15:51 +10:00
|
|
|
|
|
2014-12-05 17:29:47 +11:00
|
|
|
|
//check if the master page has changed - we need to normalize both - if it's 0 or -1, then make it 0... this is easy
|
|
|
|
|
|
// to do with Math.Max
|
|
|
|
|
|
if (Math.Max(t.MasterTemplate, 0) != Math.Max(masterTemplateId, 0))
|
2013-08-30 14:15:51 +10:00
|
|
|
|
{
|
2014-12-05 17:29:47 +11:00
|
|
|
|
t.MasterTemplate = Math.Max(masterTemplateId, 0);
|
|
|
|
|
|
pathChanged = true;
|
2013-08-30 14:15:51 +10:00
|
|
|
|
}
|
2014-08-14 17:20:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (ArgumentException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
//the template does not exist
|
|
|
|
|
|
return Failed("Template does not exist", ui.Text("speechBubbles", "templateErrorHeader"), ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
t.Save();
|
2012-12-30 03:11:21 +03:00
|
|
|
|
|
2013-08-30 14:15:51 +10:00
|
|
|
|
//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,", "");
|
|
|
|
|
|
|
2014-08-14 17:20:25 +02:00
|
|
|
|
return Success(ui.Text("speechBubbles", "templateSavedText"), ui.Text("speechBubbles", "templateSavedHeader"),
|
2015-01-09 17:01:38 +11:00
|
|
|
|
new
|
|
|
|
|
|
{
|
|
|
|
|
|
path = syncPath,
|
|
|
|
|
|
contents = t.Design
|
|
|
|
|
|
});
|
2014-08-14 17:20:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Failed(ui.Text("speechBubbles", "templateErrorText"), ui.Text("speechBubbles", "templateErrorHeader"), ex);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-12-30 03:11:21 +03:00
|
|
|
|
|
2015-09-03 15:12:37 +02:00
|
|
|
|
[HttpPost]
|
|
|
|
|
|
public JsonResult SaveScript(string filename, string oldName, string contents)
|
|
|
|
|
|
{
|
2015-09-10 14:10:45 +02:00
|
|
|
|
// sanitize input - script names have an extension
|
|
|
|
|
|
filename = filename
|
|
|
|
|
|
.Replace('\\', '/')
|
|
|
|
|
|
.TrimStart('/');
|
2015-09-03 15:12:37 +02:00
|
|
|
|
|
|
|
|
|
|
var svce = (FileService) Services.FileService;
|
|
|
|
|
|
var script = svce.GetScriptByName(oldName);
|
|
|
|
|
|
if (script == null)
|
|
|
|
|
|
script = new Script(filename);
|
|
|
|
|
|
else
|
|
|
|
|
|
script.Path = filename;
|
|
|
|
|
|
script.Content = contents;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (svce.ValidateScript(script) == false)
|
|
|
|
|
|
return Failed(ui.Text("speechBubbles", "scriptErrorText"), ui.Text("speechBubbles", "scriptErrorHeader"),
|
|
|
|
|
|
new FileSecurityException("File '" + filename + "' is not a valid script file."));
|
|
|
|
|
|
|
|
|
|
|
|
svce.SaveScript(script);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Failed(ui.Text("speechBubbles", "scriptErrorText"), ui.Text("speechBubbles", "scriptErrorHeader"), e);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Success(ui.Text("speechBubbles", "scriptSavedText"), ui.Text("speechBubbles", "scriptSavedHeader"),
|
|
|
|
|
|
new
|
|
|
|
|
|
{
|
|
|
|
|
|
path = DeepLink.GetTreePathFromFilePath(script.Path),
|
|
|
|
|
|
name = script.Path,
|
|
|
|
|
|
url = script.VirtualPath,
|
|
|
|
|
|
contents = script.Content
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-09-09 17:44:59 +02:00
|
|
|
|
[HttpPost]
|
|
|
|
|
|
public JsonResult SaveStylesheet(string filename, string oldName, string contents)
|
|
|
|
|
|
{
|
2015-09-10 14:10:45 +02:00
|
|
|
|
// sanitize input - stylesheet names have no extension
|
|
|
|
|
|
filename = filename
|
|
|
|
|
|
.Replace('\\', '/')
|
|
|
|
|
|
.TrimStart('/')
|
|
|
|
|
|
.EnsureEndsWith(".css");
|
2015-09-09 17:44:59 +02:00
|
|
|
|
|
|
|
|
|
|
var svce = (FileService) Services.FileService;
|
|
|
|
|
|
var stylesheet = svce.GetStylesheetByName(oldName);
|
|
|
|
|
|
if (stylesheet == null)
|
|
|
|
|
|
stylesheet = new Stylesheet(filename);
|
|
|
|
|
|
else
|
|
|
|
|
|
stylesheet.Path = filename;
|
|
|
|
|
|
stylesheet.Content = contents;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (svce.ValidateStylesheet(stylesheet) == false)
|
|
|
|
|
|
return Failed(ui.Text("speechBubbles", "cssErrorText"), ui.Text("speechBubbles", "cssErrorHeader"),
|
|
|
|
|
|
new FileSecurityException("File '" + filename + "' is not a valid stylesheet file."));
|
|
|
|
|
|
|
|
|
|
|
|
svce.SaveStylesheet(stylesheet);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
return Failed(ui.Text("speechBubbles", "cssErrorText"), ui.Text("speechBubbles", "cssErrorHeader"), e);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Success(ui.Text("speechBubbles", "cssSavedText"), ui.Text("speechBubbles", "cssSavedHeader"),
|
|
|
|
|
|
new
|
|
|
|
|
|
{
|
|
|
|
|
|
path = DeepLink.GetTreePathFromFilePath(stylesheet.Path),
|
|
|
|
|
|
name = stylesheet.Path,
|
|
|
|
|
|
url = stylesheet.VirtualPath,
|
|
|
|
|
|
contents = stylesheet.Content
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-08-30 14:15:51 +10:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns a successful message
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="message">The message to display in the speach bubble</param>
|
|
|
|
|
|
/// <param name="header">The header to display in the speach bubble</param>
|
|
|
|
|
|
/// <param name="additionalVals"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private JsonResult Success(string message, string header, object additionalVals = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var d = additionalVals == null ? new Dictionary<string, object>() : additionalVals.ToDictionary<object>();
|
|
|
|
|
|
d["success"] = true;
|
|
|
|
|
|
d["message"] = message;
|
|
|
|
|
|
d["header"] = header;
|
|
|
|
|
|
|
2014-08-14 17:20:25 +02:00
|
|
|
|
return Json(d);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns a failed message
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="message">The message to display in the speach bubble</param>
|
|
|
|
|
|
/// <param name="header">The header to display in the speach bubble</param>
|
|
|
|
|
|
/// <param name="exception">The exception if there was one</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private JsonResult Failed(string message, string header, Exception exception = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (exception != null)
|
|
|
|
|
|
LogHelper.Error<SaveFileController>("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."))
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2012-12-30 03:11:21 +03:00
|
|
|
|
}
|