11using System ;
22using System . Collections . Generic ;
3+ using System . Globalization ;
34using System . Text ;
5+ using System . Text . RegularExpressions ;
46
57using Umbraco . Cms . Core ;
68using Umbraco . Cms . Core . Models ;
79using Umbraco . Cms . Core . Services ;
8- using Umbraco . Cms . Core . Templates ;
910
1011namespace uSync . Core . Versions . _18 . _0 ;
1112
@@ -18,19 +19,27 @@ public interface ISyncTypedLocalLinkProcessor
1819 public Func < object ? , Func < object ? , bool > , Func < string , string > , bool > Process { get ; }
1920}
2021
21-
22- public class SyncLocalLinkProcessor
22+ /// <summary>
23+ /// Finds and replaces the legacy {localLink:x} syntax within values, mapping ids/UDIs to keys.
24+ /// </summary>
25+ /// <remarks>
26+ /// Umbraco's own <c>HtmlLocalLinkParser.FindLegacyLocalLinkIds</c> (and its <c>LocalLinkTag</c> model)
27+ /// are obsolete and scheduled for removal in Umbraco 18, but uSync still needs to detect these
28+ /// legacy (un-migrated) links when mapping content, so the parsing logic lives here instead.
29+ /// </remarks>
30+ public partial class SyncLocalLinkProcessor
2331{
24- private readonly HtmlLocalLinkParser _localLinkParser ;
32+ // copied from Umbraco.Cms.Core.Templates.HtmlLocalLinkParser.LocalLinkPattern
33+ [ GeneratedRegex ( @"href=['""](?<locallink>\/?(?:\{|\%7B)localLink:(?<guid>[a-zA-Z0-9-://]+)(?:\}|\%7D))" , RegexOptions . IgnoreCase | RegexOptions . Compiled | RegexOptions . IgnorePatternWhitespace , "en-GB" ) ]
34+ private static partial Regex LegacyLocalLinkPattern ( ) ;
35+
2536 private readonly IIdKeyMap _idKeyMap ;
2637 private readonly IEnumerable < ISyncTypedLocalLinkProcessor > _localLinkProcessors ;
2738
2839 public SyncLocalLinkProcessor (
29- HtmlLocalLinkParser localLinkParser ,
3040 IIdKeyMap idKeyMap ,
3141 IEnumerable < ISyncTypedLocalLinkProcessor > localLinkProcessors )
3242 {
33- _localLinkParser = localLinkParser ;
3443 _idKeyMap = idKeyMap ;
3544 _localLinkProcessors = localLinkProcessors ;
3645 }
@@ -49,9 +58,9 @@ public bool ProcessToEditorValue(object? editorValue)
4958 public string ProcessStringValue ( string input )
5059 {
5160 // find all legacy tags
52- var tags = _localLinkParser . FindLegacyLocalLinkIds ( input ) . ToList ( ) ;
61+ var tags = FindLegacyLocalLinkIds ( input ) . ToList ( ) ;
5362
54- foreach ( HtmlLocalLinkParser . LocalLinkTag tag in tags )
63+ foreach ( LocalLinkTag tag in tags )
5564 {
5665 string newTagHref ;
5766 if ( tag . Udi is not null )
@@ -101,4 +110,49 @@ public string ProcessStringValue(string input)
101110 return null ;
102111 }
103112
113+ // copied from Umbraco.Cms.Core.Templates.HtmlLocalLinkParser.FindLegacyLocalLinkIds
114+ private static IEnumerable < LocalLinkTag > FindLegacyLocalLinkIds ( string text )
115+ {
116+ MatchCollection tags = LegacyLocalLinkPattern ( ) . Matches ( text ) ;
117+ foreach ( Match tag in tags )
118+ {
119+ if ( tag . Groups . Count <= 0 )
120+ {
121+ continue ;
122+ }
123+
124+ var id = tag . Groups [ "guid" ] . Value ;
125+
126+ // The id could be an int or a UDI
127+ if ( UdiParser . TryParse ( id , out Udi ? udi ) )
128+ {
129+ if ( udi is GuidUdi guidUdi )
130+ {
131+ yield return new LocalLinkTag ( null , guidUdi , tag . Groups [ "locallink" ] . Value ) ;
132+ }
133+ }
134+
135+ if ( int . TryParse ( id , NumberStyles . Integer , CultureInfo . InvariantCulture , out var intId ) )
136+ {
137+ yield return new LocalLinkTag ( intId , null , tag . Groups [ "locallink" ] . Value ) ;
138+ }
139+ }
140+ }
141+
142+ // copied from Umbraco.Cms.Core.Templates.HtmlLocalLinkParser.LocalLinkTag
143+ private sealed class LocalLinkTag
144+ {
145+ public LocalLinkTag ( int ? intId , GuidUdi ? udi , string tagHref )
146+ {
147+ IntId = intId ;
148+ Udi = udi ;
149+ TagHref = tagHref ;
150+ }
151+
152+ public int ? IntId { get ; }
153+
154+ public GuidUdi ? Udi { get ; }
155+
156+ public string TagHref { get ; }
157+ }
104158}
0 commit comments