Removed Type attribute from parsed local links (#16780)

* Removed type attribute from processed local links

improved code readabilty by using multi return type to private class

* Removed type attribute from processed locallinks in delivery api

* Removed type attribute from expected output regarding locallink parsing

* Cleanup

* Fixed spacing bug

* Added 2 more edge test cases

---------

Co-authored-by: Elitsa <elm@umbraco.dk>
This commit is contained in:
Sven Geusens
2024-07-23 10:25:54 +02:00
committed by GitHub
parent 94ee50c819
commit 9e0fb54ea5
4 changed files with 67 additions and 29 deletions

View File

@@ -35,11 +35,11 @@ public sealed class HtmlLocalLinkParser
public IEnumerable<Udi?> FindUdisFromLocalLinks(string text)
{
foreach ((var intId, GuidUdi? udi, var tagValue) in FindLocalLinkIds(text))
foreach (LocalLinkTag tagData in FindLocalLinkIds(text))
{
if (udi is not null)
if (tagData.Udi is not null)
{
yield return udi; // In v8, we only care abuot UDIs
yield return tagData.Udi; // In v8, we only care about UDIs
}
}
}
@@ -80,38 +80,41 @@ public sealed class HtmlLocalLinkParser
throw new InvalidOperationException("Could not parse internal links, there is no current UmbracoContext");
}
foreach ((var intId, GuidUdi? udi, var tagValue) in FindLocalLinkIds(text))
foreach (LocalLinkTag tagData in FindLocalLinkIds(text))
{
if (udi is not null)
if (tagData.Udi is not null)
{
var newLink = "#";
if (udi?.EntityType == Constants.UdiEntityType.Document)
if (tagData.Udi?.EntityType == Constants.UdiEntityType.Document)
{
newLink = _publishedUrlProvider.GetUrl(udi.Guid);
newLink = _publishedUrlProvider.GetUrl(tagData.Udi.Guid);
}
else if (udi?.EntityType == Constants.UdiEntityType.Media)
else if (tagData.Udi?.EntityType == Constants.UdiEntityType.Media)
{
newLink = _publishedUrlProvider.GetMediaUrl(udi.Guid);
newLink = _publishedUrlProvider.GetMediaUrl(tagData.Udi.Guid);
}
if (newLink == null)
{
newLink = "#";
}
text = text.Replace(tagValue, "href=\"" + newLink);
text = StripTypeAttributeFromTag(text, tagData.Udi!.EntityType);
text = text.Replace(tagData.TagHref, "href=\"" + newLink);
}
else if (intId.HasValue)
else if (tagData.IntId.HasValue)
{
var newLink = _publishedUrlProvider.GetUrl(intId.Value);
text = text.Replace(tagValue, "href=\"" + newLink);
var newLink = _publishedUrlProvider.GetUrl(tagData.IntId.Value);
text = text.Replace(tagData.TagHref, "href=\"" + newLink);
}
}
return text;
}
private IEnumerable<(int? intId, GuidUdi? udi, string tagValue)> FindLocalLinkIds(string text)
// under normal circumstances, the type attribute is preceded by a space
// to cover the rare occasion where it isn't, we first replace with a a space and then without.
private string StripTypeAttributeFromTag(string tag, string type) =>
tag.Replace($" type=\"{type}\"", string.Empty)
.Replace($"type=\"{type}\"", string.Empty);
private IEnumerable<LocalLinkTag> FindLocalLinkIds(string text)
{
MatchCollection localLinkTagMatches = LocalLinkTagPattern.Matches(text);
foreach (Match linkTag in localLinkTagMatches)
@@ -126,18 +129,22 @@ public sealed class HtmlLocalLinkParser
continue;
}
yield return (null, new GuidUdi(linkTag.Groups["type"].Value, guid), linkTag.Groups["locallink"].Value);
yield return new LocalLinkTag(
null,
new GuidUdi(linkTag.Groups["type"].Value, guid),
linkTag.Groups["locallink"].Value,
linkTag.Value);
}
// also return legacy results for values that have not been migrated
foreach ((int? intId, GuidUdi? udi, string tagValue) legacyResult in FindLegacyLocalLinkIds(text))
foreach (LocalLinkTag legacyResult in FindLegacyLocalLinkIds(text))
{
yield return legacyResult;
}
}
// todo remove at some point?
private IEnumerable<(int? intId, GuidUdi? udi, string tagValue)> FindLegacyLocalLinkIds(string text)
private IEnumerable<LocalLinkTag> FindLegacyLocalLinkIds(string text)
{
// Parse internal links
MatchCollection tags = LocalLinkPattern.Matches(text);
@@ -153,15 +160,41 @@ public sealed class HtmlLocalLinkParser
var guidUdi = udi as GuidUdi;
if (guidUdi is not null)
{
yield return (null, guidUdi, tag.Value);
yield return new LocalLinkTag(null, guidUdi, tag.Value, null);
}
}
if (int.TryParse(id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intId))
{
yield return (intId, null, tag.Value);
yield return new LocalLinkTag (intId, null, tag.Value, null);
}
}
}
}
private class LocalLinkTag
{
public LocalLinkTag(int? intId, GuidUdi? udi, string tagHref)
{
IntId = intId;
Udi = udi;
TagHref = tagHref;
}
public LocalLinkTag(int? intId, GuidUdi? udi, string tagHref, string? fullTag)
{
IntId = intId;
Udi = udi;
TagHref = tagHref;
FullTag = fullTag;
}
public int? IntId { get; }
public GuidUdi? Udi { get; }
public string TagHref { get; }
public string? FullTag { get; }
}
}

View File

@@ -60,6 +60,7 @@ internal sealed class ApiRichTextMarkupParser : ApiRichTextParserBase, IApiRichT
link.SetAttributeValue("href", route.Path);
link.SetAttributeValue("data-start-item-path", route.StartItem.Path);
link.SetAttributeValue("data-start-item-id", route.StartItem.Id.ToString("D"));
link.Attributes["type"]?.Remove();
},
url => link.SetAttributeValue("href", url),
() => link.Attributes.Remove("href"));

View File

@@ -1,7 +1,6 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Linq;
using Microsoft.Extensions.Options;
using Moq;
using NUnit.Framework;
@@ -13,7 +12,6 @@ using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Templates;
using Umbraco.Cms.Tests.Common;
using Umbraco.Cms.Tests.UnitTests.TestHelpers.Objects;
using Umbraco.Extensions;
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Templates;
@@ -111,10 +109,16 @@ public class HtmlLocalLinkParserTests
// current
[TestCase(
"<a type=\"document\" href=\"/{localLink:9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" title=\"world\">world</a>",
"<a type=\"document\" href=\"/my-test-url\" title=\"world\">world</a>")]
"<a href=\"/my-test-url\" title=\"world\">world</a>")]
[TestCase(
"<a type=\"media\" href=\"/{localLink:9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" title=\"world\">world</a>",
"<a type=\"media\" href=\"/media/1001/my-image.jpg\" title=\"world\">world</a>")]
"<a href=\"/media/1001/my-image.jpg\" title=\"world\">world</a>")]
[TestCase(
"<a href=\"/{localLink:9931BDE0-AAC3-4BAB-B838-909A7B47570E}\"type=\"document\" title=\"world\">world</a>",
"<a href=\"/my-test-url\" title=\"world\">world</a>")]
[TestCase(
"<a href=\"/{localLink:9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" title=\"world\"type=\"media\">world</a>",
"<a href=\"/media/1001/my-image.jpg\" title=\"world\">world</a>")]
// legacy
[TestCase(
"hello href=\"{localLink:1234}\" world ",

View File

@@ -72,8 +72,8 @@ public class ApiRichTextMarkupParserTests
<p>and to the <a type=""document"" href=""/{localLink:cc143afe-4cbf-46e5-b399-c9f451384373}"" title=""other page"">other page</a></p>";
var expectedOutput =
@"<p>Rich text outside of the blocks with a link to <a type=""document"" href=""/self/"" title=""itself"" data-start-item-path=""self"" data-start-item-id=""eed5fc6b-96fd-45a5-a0f1-b1adfb483c2f"">itself</a><br><br></p>
<p>and to the <a type=""document"" href=""/other/"" title=""other page"" data-start-item-path=""other"" data-start-item-id=""cc143afe-4cbf-46e5-b399-c9f451384373"">other page</a></p>";
@"<p>Rich text outside of the blocks with a link to <a href=""/self/"" title=""itself"" data-start-item-path=""self"" data-start-item-id=""eed5fc6b-96fd-45a5-a0f1-b1adfb483c2f"">itself</a><br><br></p>
<p>and to the <a href=""/other/"" title=""other page"" data-start-item-path=""other"" data-start-item-id=""cc143afe-4cbf-46e5-b399-c9f451384373"">other page</a></p>";
var parsedHtml = parser.Parse(html);