Skip to content

Commit 3d8707e

Browse files
authored
V16 RC: HtmlImageSourceParser should not care for order of attributes (#19331)
* fix: split attribute regex into two to be able to ignore the order of attributes * the regex should not care for the ending of the tag * test: adds test cases to test order of attributes * remove image sources using new regex * adds more documentation * adds test cases to test removal of source with and without parameters * test for null value * test: adds more cases for null and reversed parameters
1 parent 930a29f commit 3d8707e

File tree

2 files changed

+68
-21
lines changed

2 files changed

+68
-21
lines changed

src/Umbraco.Core/Templates/HtmlImageSourceParser.cs

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ namespace Umbraco.Cms.Core.Templates;
77
public sealed class HtmlImageSourceParser
88
{
99
private static readonly Regex ResolveImgPattern = new(
10-
@"(<img[^>]*src="")([^""\?]*)((?:\?[^""]*)?""[^>]*data-udi="")([^""]*)(""[^>]*>)",
10+
@"<img[^>]*(data-udi=""([^""]*)"")[^>]*>",
11+
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
12+
13+
private static readonly Regex SrcAttributeRegex = new(
14+
@"src=""([^""\?]*)(\?[^""]*)?""",
1115
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
1216

1317
private static readonly Regex DataUdiAttributeRegex = new(
@@ -61,17 +65,26 @@ public string EnsureImageSources(string text)
6165
return ResolveImgPattern.Replace(text, match =>
6266
{
6367
// match groups:
64-
// - 1 = from the beginning of the image tag until src attribute value begins
65-
// - 2 = the src attribute value excluding the querystring (if present)
66-
// - 3 = anything after group 2 and before the data-udi attribute value begins
67-
// - 4 = the data-udi attribute value
68-
// - 5 = anything after group 4 until the image tag is closed
69-
var udi = match.Groups[4].Value;
68+
// - 1 = the data-udi attribute
69+
// - 2 = the data-udi attribute value
70+
var udi = match.Groups[2].Value;
7071
if (udi.IsNullOrWhiteSpace() || UdiParser.TryParse<GuidUdi>(udi, out GuidUdi? guidUdi) == false)
7172
{
7273
return match.Value;
7374
}
7475

76+
// Find the src attribute
77+
// src match groups:
78+
// - 1 = the src attribute value until the query string
79+
// - 2 = the src attribute query string including the '?'
80+
Match src = SrcAttributeRegex.Match(match.Value);
81+
82+
if (src.Success == false)
83+
{
84+
// the src attribute isn't found, return the original value
85+
return match.Value;
86+
}
87+
7588
var mediaUrl = _getMediaUrl(guidUdi.Guid);
7689
if (mediaUrl == null)
7790
{
@@ -80,7 +93,9 @@ public string EnsureImageSources(string text)
8093
return match.Value;
8194
}
8295

83-
return $"{match.Groups[1].Value}{mediaUrl}{match.Groups[3].Value}{udi}{match.Groups[5].Value}";
96+
var newImgTag = match.Value.Replace(src.Value, $"src=\"{mediaUrl}{src.Groups[2].Value}\"");
97+
98+
return newImgTag;
8499
});
85100
}
86101

@@ -91,6 +106,16 @@ public string EnsureImageSources(string text)
91106
/// <returns></returns>
92107
public string RemoveImageSources(string text)
93108

94-
// see comment in ResolveMediaFromTextString for group reference
95-
=> ResolveImgPattern.Replace(text, "$1$3$4$5");
109+
// find each ResolveImgPattern match in the text, then find each
110+
// SrcAttributeRegex match in the match value, then replace the src
111+
// attribute value with an empty string
112+
// (see comment in ResolveMediaFromTextString for group reference)
113+
=> ResolveImgPattern.Replace(text, match =>
114+
{
115+
// Find the src attribute
116+
Match src = SrcAttributeRegex.Match(match.Value);
117+
118+
return src.Success == false || string.IsNullOrWhiteSpace(src.Groups[1].Value) ?
119+
match.Value : match.Value.Replace(src.Groups[1].Value, string.Empty);
120+
});
96121
}

tests/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,42 @@ public void Returns_Udis_From_Data_Udi_Html_Attributes()
3939
Assert.AreEqual(UdiParser.Parse("umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"), result[1]);
4040
}
4141

42-
[Test]
43-
public void Remove_Image_Sources()
44-
{
45-
var imageSourceParser = new HtmlImageSourceParser(Mock.Of<IPublishedUrlProvider>());
46-
47-
var result = imageSourceParser.RemoveImageSources(@"<p>
42+
[TestCase(
43+
@"<p>
4844
<div>
4945
<img src=""/media/12354/test.jpg"" />
5046
</div></p>
5147
<p>
5248
<div><img src=""/media/987645/test.jpg"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
53-
</p>");
54-
55-
Assert.AreEqual(
56-
@"<p>
49+
</p>",
50+
ExpectedResult = @"<p>
5751
<div>
5852
<img src=""/media/12354/test.jpg"" />
5953
</div></p>
6054
<p>
6155
<div><img src="""" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" /></div>
6256
</p>",
63-
result);
57+
TestName = "Remove image source with data-udi set")]
58+
[TestCase(
59+
@"<img alt title=""Title"" src=""/media/12354/test.jpg?width=400&height=400&hmac=test"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" />",
60+
ExpectedResult = @"<img alt title=""Title"" src=""?width=400&height=400&hmac=test"" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" />",
61+
TestName = "Remove image source but keep querystring")]
62+
[TestCase(
63+
@"<img alt title=""Title"" src="""" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" />",
64+
ExpectedResult = @"<img alt title=""Title"" src="""" data-udi=""umb://media/81BB2036-034F-418B-B61F-C7160D68DCD4"" />",
65+
TestName = "Remove image source with empty src")]
66+
[TestCase(
67+
@"<img src=""/media/12345/test.jpg"" />",
68+
ExpectedResult = @"<img src=""/media/12345/test.jpg"" />",
69+
TestName = "Do not remove image source without data-udi set")]
70+
[Category("Remove image sources")]
71+
public string Remove_Image_Sources(string sourceHtml)
72+
{
73+
var imageSourceParser = new HtmlImageSourceParser(Mock.Of<IPublishedUrlProvider>());
74+
75+
var actual = imageSourceParser.RemoveImageSources(sourceHtml);
76+
77+
return actual;
6478
}
6579

6680
[Test]
@@ -146,6 +160,10 @@ public void Ensure_Image_Sources()
146160
@"<div><img src=""non empty src"" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4""/></div>",
147161
ExpectedResult = @"<div><img src=""/media/1001/image.jpg"" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4""/></div>",
148162
TestName = "Filled source is overwritten with data-udi set")]
163+
[TestCase(
164+
@"<div><img alt title=""Test"" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4"" src=""non empty src"" /></div>",
165+
ExpectedResult = @"<div><img alt title=""Test"" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4"" src=""/media/1001/image.jpg"" /></div>",
166+
TestName = "Order of attributes does not matter")]
149167
[TestCase(
150168
@"<div><img src=""some src"" some-attribute data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4"" another-attribute/></div>",
151169
ExpectedResult = @"<div><img src=""/media/1001/image.jpg"" some-attribute data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4"" another-attribute/></div>",
@@ -158,6 +176,10 @@ public void Ensure_Image_Sources()
158176
@"<div><img src=""?width=100&height=500"" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4""/></div>",
159177
ExpectedResult = @"<div><img src=""/media/1001/image.jpg?width=100&height=500"" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4""/></div>",
160178
TestName = "Parameters are prefixed")]
179+
[TestCase(
180+
@"<div><img data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4"" src=""?width=100&height=500"" /></div>",
181+
ExpectedResult = @"<div><img data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4"" src=""/media/1001/image.jpg?width=100&height=500"" /></div>",
182+
TestName = "Parameters are prefixed (order of attributes reversed)")]
161183
[TestCase(
162184
@"<div>
163185
<img src="""" data-udi=""umb://media/81BB2036034F418BB61FC7160D68DCD4""/>

0 commit comments

Comments
 (0)