V10: Find and persist embedded images in rich text (#14546)

* add method to find and persist embedded base64 (data-uri) images in a html string

* use method to find embedded images in the Umbraco.TinyMce and Umbraco.Grid property editors

* rename method to better reflect what it does

* set allowed upload file types for integration tests

* add test for embedded images in Umbraco.TinyMce

* let old ctor call new ctor

* Apply suggestions from code review

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>

* apply pattern matching

---------

Co-authored-by: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com>
This commit is contained in:
Jacob Overgaard
2023-07-18 07:17:19 +00:00
committed by GitHub
parent c12cce6d8c
commit ab836d2326
5 changed files with 363 additions and 115 deletions

View File

@@ -664,4 +664,87 @@ public class ContentControllerTests : UmbracoTestServerTestBase
Assert.AreEqual(0, display.Notifications.Count(x => x.NotificationType == NotificationStyle.Warning));
});
}
[TestCase(
@"<p><img alt src=""data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7""></p>",
false)]
[TestCase(
@"<p><img src=""data:image/svg+xml;utf8,<svg viewBox=""0 0 70 74"" fill=""none"" xmlns=""http://www.w3.org/2000/svg""><rect width=""100%"" height=""100%"" fill=""black""/></svg>""></p>",
false)]
[TestCase(
@"<p><img alt src=""/some/random/image.jpg""></p><p><img alt src=""data:image/jpg;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7""></p>",
false)]
[TestCase(
@"<p><img alt src=""data:image/notallowedextension;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7""></p>",
true)]
public async Task PostSave_Simple_RichText_With_Base64(string html, bool shouldHaveDataUri)
{
var url = PrepareApiControllerUrl<ContentController>(x => x.PostSave(null));
var dataTypeService = GetRequiredService<IDataTypeService>();
var contentService = GetRequiredService<IContentService>();
var contentTypeService = GetRequiredService<IContentTypeService>();
var dataType = new DataTypeBuilder()
.WithId(0)
.WithoutIdentity()
.WithDatabaseType(ValueStorageType.Ntext)
.AddEditor()
.WithAlias(Constants.PropertyEditors.Aliases.TinyMce)
.Done()
.Build();
dataTypeService.Save(dataType);
var contentType = new ContentTypeBuilder()
.WithId(0)
.AddPropertyType()
.WithDataTypeId(dataType.Id)
.WithAlias("richText")
.WithName("Rich Text")
.Done()
.WithContentVariation(ContentVariation.Nothing)
.Build();
contentTypeService.Save(contentType);
var content = new ContentBuilder()
.WithId(0)
.WithName("Invariant")
.WithContentType(contentType)
.AddPropertyData()
.WithKeyValue("richText", html)
.Done()
.Build();
contentService.SaveAndPublish(content);
var model = new ContentItemSaveBuilder()
.WithContent(content)
.Build();
// Act
var response =
await Client.PostAsync(url, new MultipartFormDataContent {{new StringContent(JsonConvert.SerializeObject(model)), "contentItem"}});
// Assert
var body = await response.Content.ReadAsStringAsync();
body = body.TrimStart(AngularJsonMediaTypeFormatter.XsrfPrefix);
Assert.Multiple(() =>
{
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode, body);
var display = JsonConvert.DeserializeObject<ContentItemDisplay>(body);
var bodyText = display.Variants.FirstOrDefault()?.Tabs.FirstOrDefault()?.Properties
?.FirstOrDefault(x => x.Alias.Equals("richText"))?.Value?.ToString();
Assert.NotNull(bodyText);
var containsDataUri = bodyText.Contains("data:image");
if (shouldHaveDataUri)
{
Assert.True(containsDataUri, $"Data URIs were expected to be found in the body: {bodyText}");
} else {
Assert.False(containsDataUri, $"Data URIs were not expected to be found in the body: {bodyText}");
}
});
}
}

View File

@@ -1,4 +1,5 @@
{
"$schema": "./appsettings-schema.json",
"Logging": {
"LogLevel": {
"Default": "Warning",
@@ -16,5 +17,12 @@
"EmptyDatabasesCount": 2,
"SQLServerMasterConnectionString": ""
}
},
"Umbraco": {
"CMS": {
"Content": {
"AllowedUploadedFileExtensions": ["jpg", "png", "gif", "svg"]
}
}
}
}