diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 46636483b6..f1e2bf20d6 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Web.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Web; namespace Umbraco.Tests.PublishedContent { @@ -39,7 +40,7 @@ namespace Umbraco.Tests.PublishedContent var converters = Factory.GetInstance(); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new RichTextPropertyEditor(Mock.Of(), Mock.Of(), Mock.Of())) { Id = 1 }); + new DataType(new RichTextPropertyEditor(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of())) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index e98e28c3dd..6ef632bf90 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -44,11 +44,12 @@ namespace Umbraco.Tests.PublishedContent var logger = Mock.Of(); var mediaService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); + var umbracoContextAccessor = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(logger)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 }, - new DataType(new RichTextPropertyEditor(logger, mediaService, contentTypeBaseServiceProvider)) { Id = 1002 }, + new DataType(new RichTextPropertyEditor(logger, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor)) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 }, new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 }); diff --git a/src/Umbraco.Web/Editors/TinyMceController.cs b/src/Umbraco.Web/Editors/TinyMceController.cs index 7ed71f0268..5bed080f7f 100644 --- a/src/Umbraco.Web/Editors/TinyMceController.cs +++ b/src/Umbraco.Web/Editors/TinyMceController.cs @@ -1,26 +1,34 @@ -using System.Net; -using System.Net.Http; -using System.Web.Http; -using Umbraco.Core.Services; -using Umbraco.Web.WebApi; -using Umbraco.Core; -using Umbraco.Web.Mvc; -using Umbraco.Core.IO; +using System; using System.IO; -using System.Threading.Tasks; -using Umbraco.Web.Composing; -using Umbraco.Core.Configuration.UmbracoSettings; using System.Linq; -using System; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web.Http; +using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Services; +using Umbraco.Web.Composing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Editors { [PluginController("UmbracoApi")] + [UmbracoApplicationAuthorize( + Constants.Applications.Content, + Constants.Applications.Media, + Constants.Applications.Members)] public class TinyMceController : UmbracoAuthorizedApiController { private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + public TinyMceController(IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) { _mediaService = mediaService; @@ -91,9 +99,28 @@ namespace Umbraco.Web.Editors } catch (Exception ex) { - // Could be a file permission ex - throw; - } + // IOException, PathTooLong, DirectoryNotFound, UnathorizedAccess + Logger.Error(ex, "Error when trying to move {CurrentFilePath} to {NewFilePath}", currentFile, newFilePath); + return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, $"Error when trying to move {currentFile} to {newFilePath}", ex); + } + + // Now remove all old files so that the temp folder(s) never grow + // Anything older than one day gets deleted + var tempFiles = Directory.GetFiles(SystemDirectories.TempFileUploads, "*", SearchOption.AllDirectories); + foreach (var tempFile in tempFiles) + { + if (DateTime.UtcNow - File.GetLastWriteTimeUtc(tempFile) > TimeSpan.FromDays(1)) + { + try + { + File.Delete(tempFile); + } + catch (Exception ex) + { + Logger.Error(ex, "Could not delete temp file {FileName}", tempFile); + } + } + } return Request.CreateResponse(HttpStatusCode.OK, new { tmpLocation = relativeNewFilePath }); } diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 710c68eeb3..48273a1651 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -1,13 +1,12 @@ -using System.Linq; -using Umbraco.Core.Logging; +using Newtonsoft.Json; +using System.Linq; using Umbraco.Core; -using Umbraco.Core.PropertyEditors; -using Newtonsoft.Json; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; -using Umbraco.Web.Templates; -using Umbraco.Web.Composing; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Web.Templates; namespace Umbraco.Web.PropertyEditors { @@ -26,12 +25,16 @@ namespace Umbraco.Web.PropertyEditors { private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private IUmbracoContextAccessor _umbracoContextAccessor; + private ILogger _logger; - public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor) : base(logger) { _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _umbracoContextAccessor = umbracoContextAccessor; + _logger = logger; } public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory(); @@ -40,7 +43,7 @@ namespace Umbraco.Web.PropertyEditors /// Overridden to ensure that the value is validated /// /// - protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider); + protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger); protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor(); @@ -48,12 +51,16 @@ namespace Umbraco.Web.PropertyEditors { private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private IUmbracoContextAccessor _umbracoContextAccessor; + private ILogger _logger; - public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger) : base(attribute) { _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _umbracoContextAccessor = umbracoContextAccessor; + _logger = logger; } /// @@ -81,11 +88,11 @@ namespace Umbraco.Web.PropertyEditors { // Parse the HTML var html = rte.Value?.ToString(); - - var userId = Current.UmbracoContext.Security.CurrentUser.Id; + + var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? -1; // TODO: In future task(get the parent folder from this config) to save the media into - var parsedHtml = TemplateUtilities.FindAndPersistPastedTempImages(html, Constants.System.Root, userId, _mediaService, _contentTypeBaseServiceProvider); + var parsedHtml = TemplateUtilities.FindAndPersistPastedTempImages(html, Constants.System.Root, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); rte.Value = parsedHtml; } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 8cab94a4be..ec18ff203f 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -1,15 +1,11 @@ -using HtmlAgilityPack; -using System; +using System; using System.Collections.Generic; -using System.IO; using Umbraco.Core; -using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Examine; -using Umbraco.Web.Composing; using Umbraco.Web.Macros; using Umbraco.Web.Templates; @@ -30,21 +26,25 @@ namespace Umbraco.Web.PropertyEditors { private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private IUmbracoContextAccessor _umbracoContextAccessor; + private ILogger _logger; /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RichTextPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) : base(logger) + public RichTextPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor) : base(logger) { _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _umbracoContextAccessor = umbracoContextAccessor; + _logger = logger; } /// /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider); + protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); @@ -57,12 +57,16 @@ namespace Umbraco.Web.PropertyEditors { private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private IUmbracoContextAccessor _umbracoContextAccessor; + private ILogger _logger; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + public RichTextPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger) : base(attribute) { _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _umbracoContextAccessor = umbracoContextAccessor; + _logger = logger; } /// @@ -113,10 +117,10 @@ namespace Umbraco.Web.PropertyEditors var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(editorValue.Value.ToString()); var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved); - var userId = Current.UmbracoContext.Security.CurrentUser.Id; + var userId = _umbracoContextAccessor.UmbracoContext?.Security.CurrentUser.Id ?? -1; // TODO: In future task(get the parent folder from this config) to save the media into - parsed = TemplateUtilities.FindAndPersistPastedTempImages(parsed, Constants.System.Root, userId, _mediaService, _contentTypeBaseServiceProvider); + parsed = TemplateUtilities.FindAndPersistPastedTempImages(parsed, Constants.System.Root, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); return parsed; } } diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 96ea2659b1..60a57b500e 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -3,8 +3,8 @@ using System; using System.IO; using System.Text.RegularExpressions; using Umbraco.Core; -using Umbraco.Core.Configuration; using Umbraco.Core.IO; +using Umbraco.Core.Logging; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.PublishedCache; @@ -189,7 +189,7 @@ namespace Umbraco.Web.Templates // see comment in ResolveMediaFromTextString for group reference => ResolveImgPattern.Replace(text, "$1$3$4$5"); - internal static string FindAndPersistPastedTempImages(string html, int mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + internal static string FindAndPersistPastedTempImages(string html, int mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger) { // Find all img's that has data-tmpimg attribute // Use HTML Agility Pack - https://html-agility-pack.net @@ -236,6 +236,21 @@ namespace Umbraco.Web.Templates // Remove the data attribute (so we do not re-process this) img.Attributes.Remove(TemporaryImageDataAttribute); + + // Delete folder & image now its saved in media + // The folder should contain one image - as a unique guid folder created + // for each image uploaded from TinyMceController + var folderName = Path.GetDirectoryName(absoluteTempImagePath); + try + { + File.Delete(absoluteTempImagePath); + Directory.Delete(folderName); + } + catch (Exception ex) + { + logger.Error(typeof(TemplateUtilities), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); + } + } return htmlDoc.DocumentNode.OuterHtml;