From 1c70ffd26c652419b8091f9a7844baf1792967e0 Mon Sep 17 00:00:00 2001 From: Kris Goncalves Date: Thu, 1 Oct 2020 21:26:23 -0400 Subject: [PATCH 1/3] Adding Hyperlink Markup Type Support --- .../main/default/classes/ConnectApiHelper.cls | 33 +++++++++++++------ .../classes/ConnectApiHelper.cls-meta.xml | 2 +- .../default/classes/ConnectApiHelperTest.cls | 32 ++++++++++++++++-- .../classes/ConnectApiHelperTest.cls-meta.xml | 2 +- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/force-app/main/default/classes/ConnectApiHelper.cls b/force-app/main/default/classes/ConnectApiHelper.cls index 81eb5d9..767e307 100644 --- a/force-app/main/default/classes/ConnectApiHelper.cls +++ b/force-app/main/default/classes/ConnectApiHelper.cls @@ -56,7 +56,9 @@ global class ConnectApiHelper { 'p' => ConnectApi.MarkupType.Paragraph, 's' => ConnectApi.MarkupType.Strikethrough, 'u' => ConnectApi.MarkupType.Underline, - 'ul' => ConnectApi.MarkupType.UnorderedList + 'ul' => ConnectApi.MarkupType.UnorderedList, + 'a' => ConnectApi.MarkupType.Hyperlink, + 'a href=' => ConnectApi.MarkupType.Hyperlink }; /** @@ -144,14 +146,16 @@ global class ConnectApiHelper { List messageSegmentInputs = new List(); Integer strPos = 0; // The pattern for matching mentions, markup begin/end tags, and inline images. - // The first group matches a 15 or 18 character ID surrounded by {}: + // The first and second group matches a 15 or 18 character ID surrounded by {}: // (\\{[a-zA-Z0-9]{15}\\}|\\{[a-zA-Z0-9]{18}\\}) - // The second/third groups match beginning/ending HTML tags: (<[a-zA-Z]*>)|() - // The fourth group matches a 15 or 18 character content document ID preceded by "img:", + // The thrid/fourth groups match hyperlink tags + // (()|()) + // The fourth/fifth groups match the other beginning/ending HTML tags: (<[b-zB-Z]*>)|() + // The sixth group matches a 15 or 18 character content document ID preceded by "img:", // optionally followed by a string (not containing '}'), and surrounded by {}: // (\\{img:(069[a-zA-Z0-9]{12,15})(:[\\s\\S]*?)?\\}) - // The fifth group matches a 15 or 18 character record ID preceded by "record:" ex:{record:01t3E000002GCm9QAG} - Pattern globalPattern = Pattern.compile('(\\{[a-zA-Z0-9]{15}\\}|\\{[a-zA-Z0-9]{18}\\})|(<[a-zA-Z]*>)|()|(\\{img:(069[a-zA-Z0-9]{12,15})(:[\\s\\S]*?)?\\})|(\\{record:([a-zA-Z0-9]){15,18}(:[\\s\\S]*?)?\\})'); + // The seventh group matches a 15 or 18 character record ID preceded by "record:" ex:{record:01t3E000002GCm9QAG} + Pattern globalPattern = Pattern.compile('(\\{[a-zA-Z0-9]{15}\\}|\\{[a-zA-Z0-9]{18}\\})|(?i)()|(?i)()|(<[b-zB-Z]*>)|()|(\\{img:(069[a-zA-Z0-9]{12,15})(:[\\s\\S]*?)?\\})|(\\{record:([a-zA-Z0-9]){15,18}(:[\\s\\S]*?)?\\})'); Matcher globalMatcher = globalPattern.matcher(inputText); @@ -197,12 +201,18 @@ global class ConnectApiHelper { // Add a segment for any accumulated text (which includes unsupported HTML tags). addTextSegment(messageSegmentInputs, textSegment); - ConnectApi.MarkupBeginSegmentInput markupBeginSegmentInput = makeMarkupBeginSegmentInput(tag); + ConnectApi.MarkupBeginSegmentInput markupBeginSegmentInput = makeMarkupBeginSegmentInput(tag, null); + messageSegmentInputs.add(markupBeginSegmentInput); + strPos = globalMatcher.end(); + } else if (tag.toLowerCase().contains('href=')){ + //hyperlink tag - get the URL. Assumes format of a href="url"> + String url = tag.substring(tag.indexOf('"') + 1, tag.lastIndexOf('"')); + String htmlTag = tag.substring(0, tag.indexOf('"')); + ConnectApi.MarkupBeginSegmentInput markupBeginSegmentInput = makeMarkupBeginSegmentInput(htmlTag, url); messageSegmentInputs.add(markupBeginSegmentInput); strPos = globalMatcher.end(); } - } - else { // This is an end tag. + } else { // This is an end tag. // Strip off the . String tag = matchingText.substring(2, matchingText.indexOf('>')); if (supportedMarkup.containsKey(tag.toLowerCase())) { @@ -258,9 +268,12 @@ global class ConnectApiHelper { * Create a MarkupBeginSegmentInput corresponding to the tag. Checking whether the tag is * supported markup should happen before calling this method. */ - private static ConnectApi.MarkupBeginSegmentInput makeMarkupBeginSegmentInput(String tag) { + private static ConnectApi.MarkupBeginSegmentInput makeMarkupBeginSegmentInput(String tag, String url) { ConnectApi.MarkupBeginSegmentInput markupBeginSegment = new ConnectApi.MarkupBeginSegmentInput(); markupBeginSegment.markupType = supportedMarkup.get(tag.toLowerCase()); + if(!String.isEmpty(url)){ + markupBeginSegment.url = url; + } return markupBeginSegment; } diff --git a/force-app/main/default/classes/ConnectApiHelper.cls-meta.xml b/force-app/main/default/classes/ConnectApiHelper.cls-meta.xml index 252fbfd..8e4d11f 100755 --- a/force-app/main/default/classes/ConnectApiHelper.cls-meta.xml +++ b/force-app/main/default/classes/ConnectApiHelper.cls-meta.xml @@ -1,5 +1,5 @@ - 47.0 + 49.0 Active diff --git a/force-app/main/default/classes/ConnectApiHelperTest.cls b/force-app/main/default/classes/ConnectApiHelperTest.cls index 9b0a784..a7a1d7b 100644 --- a/force-app/main/default/classes/ConnectApiHelperTest.cls +++ b/force-app/main/default/classes/ConnectApiHelperTest.cls @@ -147,6 +147,20 @@ public class ConnectApiHelperTest { System.assertEquals(mentionId2, mentionSegment2.id); } + @IsTest(SeeAllData=true) + static void testHyperlinkPost() { + String mentionId = '005x0000000URNPzzz'; + String mentionId2 = '0F9x00000000D7m'; + String text = 'Testing Hyperlink'; + List segments = ConnectApiHelper.getMessageSegmentInputs(text); + + System.assertEquals(3, segments.size()); + System.assert(segments.get(0) instanceof ConnectApi.MarkupBeginSegmentInput); + System.assert(segments.get(1) instanceof ConnectApi.TextSegmentInput); + System.assert(segments.get(2) instanceof ConnectApi.MarkupEndSegmentInput); + + } + @IsTest(SeeAllData=true) static void testLinkAndHashtagParsing() { // The test string is: #Yolo: http://salesforce.com, {005} {005x0000000URNPzzz} test. @@ -384,7 +398,7 @@ public class ConnectApiHelperTest { // 2 = text2 // 3 = markup end // 4 = text3 - String text1 = 'a,

b
'; + String text1 = 'a,

b
'; String text2 = 'Does this work?'; String text3 = '

'; String text = text1 + '' + text2 + '' + text3; @@ -440,10 +454,10 @@ public class ConnectApiHelperTest { String text1 = 'foo'; String text2 = 'bar'; String text3 = 'baz'; - String text = '' + text1 + '' + text2 + '
  1. ' + text3 + '
'; + String text = '' + text1 + '' + text2 + '
  1. ' + text3 + '
+ ' + text3 + ''; List segments = ConnectApiHelper.getMessageSegmentInputs(text); - System.assertEquals(11, segments.size()); + System.assertEquals(14, segments.size()); System.assert(segments.get(0) instanceof ConnectApi.MarkupBeginSegmentInput); System.assert(segments.get(1) instanceof ConnectApi.TextSegmentInput); System.assert(segments.get(2) instanceof ConnectApi.MarkupEndSegmentInput); @@ -455,6 +469,9 @@ public class ConnectApiHelperTest { System.assert(segments.get(8) instanceof ConnectApi.TextSegmentInput); System.assert(segments.get(9) instanceof ConnectApi.MarkupEndSegmentInput); System.assert(segments.get(10) instanceof ConnectApi.MarkupEndSegmentInput); + System.assert(segments.get(11) instanceof ConnectApi.MarkupBeginSegmentInput); + System.assert(segments.get(12) instanceof ConnectApi.TextSegmentInput); + System.assert(segments.get(13) instanceof ConnectApi.MarkupEndSegmentInput); ConnectApi.MarkupBeginSegmentInput markupBeginSegment = (ConnectApi.MarkupBeginSegmentInput) segments.get(0); System.assertEquals(ConnectApi.MarkupType.Underline, markupBeginSegment.markupType); @@ -488,6 +505,15 @@ public class ConnectApiHelperTest { markupEndSegment = (ConnectApi.MarkupEndSegmentInput) segments.get(10); System.assertEquals(ConnectApi.MarkupType.OrderedList, markupEndSegment.markupType); + + markupBeginSegment = (ConnectApi.MarkupBeginSegmentInput) segments.get(11); + System.assertEquals(ConnectApi.MarkupType.Hyperlink, markupBeginSegment.markupType); + + textSegment = (ConnectApi.TextSegmentInput) segments.get(12); + System.assertEquals(text3, textSegment.text); + + markupEndSegment = (ConnectApi.MarkupEndSegmentInput) segments.get(13); + System.assertEquals(ConnectApi.MarkupType.Hyperlink, markupEndSegment.markupType); } @IsTest(SeeAllData=true) diff --git a/force-app/main/default/classes/ConnectApiHelperTest.cls-meta.xml b/force-app/main/default/classes/ConnectApiHelperTest.cls-meta.xml index 252fbfd..8e4d11f 100755 --- a/force-app/main/default/classes/ConnectApiHelperTest.cls-meta.xml +++ b/force-app/main/default/classes/ConnectApiHelperTest.cls-meta.xml @@ -1,5 +1,5 @@ - 47.0 + 49.0 Active From 7311514bc97791de20857399c136b30d41e0b2b3 Mon Sep 17 00:00:00 2001 From: Kris Goncalves Date: Fri, 2 Oct 2020 14:34:01 -0400 Subject: [PATCH 2/3] Improving Hyperlink Tag Regex/Logic --- .../main/default/classes/ConnectApiHelper.cls | 26 ++++++++++++------- .../default/classes/ConnectApiHelperTest.cls | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/force-app/main/default/classes/ConnectApiHelper.cls b/force-app/main/default/classes/ConnectApiHelper.cls index 767e307..4ba3ba5 100644 --- a/force-app/main/default/classes/ConnectApiHelper.cls +++ b/force-app/main/default/classes/ConnectApiHelper.cls @@ -57,7 +57,6 @@ global class ConnectApiHelper { 's' => ConnectApi.MarkupType.Strikethrough, 'u' => ConnectApi.MarkupType.Underline, 'ul' => ConnectApi.MarkupType.UnorderedList, - 'a' => ConnectApi.MarkupType.Hyperlink, 'a href=' => ConnectApi.MarkupType.Hyperlink }; @@ -148,14 +147,14 @@ global class ConnectApiHelper { // The pattern for matching mentions, markup begin/end tags, and inline images. // The first and second group matches a 15 or 18 character ID surrounded by {}: // (\\{[a-zA-Z0-9]{15}\\}|\\{[a-zA-Z0-9]{18}\\}) - // The thrid/fourth groups match hyperlink tags + // The thrid group match hyperlink tags with the url and alt text and end tag // (()|()) - // The fourth/fifth groups match the other beginning/ending HTML tags: (<[b-zB-Z]*>)|() + // The fourth/fifth groups match the other beginning/ending HTML tags and text: (<[a-zA-Z]*>)|() // The sixth group matches a 15 or 18 character content document ID preceded by "img:", // optionally followed by a string (not containing '}'), and surrounded by {}: // (\\{img:(069[a-zA-Z0-9]{12,15})(:[\\s\\S]*?)?\\}) // The seventh group matches a 15 or 18 character record ID preceded by "record:" ex:{record:01t3E000002GCm9QAG} - Pattern globalPattern = Pattern.compile('(\\{[a-zA-Z0-9]{15}\\}|\\{[a-zA-Z0-9]{18}\\})|(?i)()|(?i)()|(<[b-zB-Z]*>)|()|(\\{img:(069[a-zA-Z0-9]{12,15})(:[\\s\\S]*?)?\\})|(\\{record:([a-zA-Z0-9]){15,18}(:[\\s\\S]*?)?\\})'); + Pattern globalPattern = Pattern.compile('(\\{[a-zA-Z0-9]{15}\\}|\\{[a-zA-Z0-9]{18}\\})|(<[aA]\\s+[hH][rR][eE][fF]=")(.*?)">(.*?)|(<[a-zA-Z]*>)|()|(\\{img:(069[a-zA-Z0-9]{12,15})(:[\\s\\S]*?)?\\})|(\\{record:([a-zA-Z0-9]){15,18}(:[\\s\\S]*?)?\\})'); Matcher globalMatcher = globalPattern.matcher(inputText); @@ -201,15 +200,21 @@ global class ConnectApiHelper { // Add a segment for any accumulated text (which includes unsupported HTML tags). addTextSegment(messageSegmentInputs, textSegment); - ConnectApi.MarkupBeginSegmentInput markupBeginSegmentInput = makeMarkupBeginSegmentInput(tag, null); + ConnectApi.MarkupBeginSegmentInput markupBeginSegmentInput = makeMarkupBeginSegmentInput(tag, null, null); messageSegmentInputs.add(markupBeginSegmentInput); strPos = globalMatcher.end(); } else if (tag.toLowerCase().contains('href=')){ - //hyperlink tag - get the URL. Assumes format of a href="url"> - String url = tag.substring(tag.indexOf('"') + 1, tag.lastIndexOf('"')); - String htmlTag = tag.substring(0, tag.indexOf('"')); - ConnectApi.MarkupBeginSegmentInput markupBeginSegmentInput = makeMarkupBeginSegmentInput(htmlTag, url); + // Add a segment for any accumulated text (which includes unsupported HTML tags). + addTextSegment(messageSegmentInputs, textSegment); + //hyperlink tag - get the URL. Assumes format of altText + String url = matchingText.substring(matchingText.indexOf('"') + 1, matchingText.lastIndexOf('>') - 1); + String htmlTag = matchingText.substring(1, matchingText.indexOf('"')); + String altText = matchingText.substring(matchingText.indexOf('>') + 1, matchingText.indexOf('' + text1 + '' + text2 + '
  1. ' + text3 + '
+ ' + text3 + ''; + String text = '' + text1 + '' + text2 + '
  1. ' + text3 + '
' + '' + text3 + ''; List segments = ConnectApiHelper.getMessageSegmentInputs(text); System.assertEquals(14, segments.size()); From 0041aff2cc07ca98c2a8f318ae5793620222ca36 Mon Sep 17 00:00:00 2001 From: Kris Goncalves Date: Mon, 7 Feb 2022 13:14:22 -0500 Subject: [PATCH 3/3] Fix parsing url from hyperlink tag Co-authored-by: Lourdes Montero --- force-app/main/default/classes/ConnectApiHelper.cls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/force-app/main/default/classes/ConnectApiHelper.cls b/force-app/main/default/classes/ConnectApiHelper.cls index 4ba3ba5..c2f94a7 100644 --- a/force-app/main/default/classes/ConnectApiHelper.cls +++ b/force-app/main/default/classes/ConnectApiHelper.cls @@ -207,7 +207,7 @@ global class ConnectApiHelper { // Add a segment for any accumulated text (which includes unsupported HTML tags). addTextSegment(messageSegmentInputs, textSegment); //hyperlink tag - get the URL. Assumes format of altText - String url = matchingText.substring(matchingText.indexOf('"') + 1, matchingText.lastIndexOf('>') - 1); + String url = matchingText.substring(matchingText.indexOf('"') + 1, matchingText.indexOf('>') - 1); String htmlTag = matchingText.substring(1, matchingText.indexOf('"')); String altText = matchingText.substring(matchingText.indexOf('>') + 1, matchingText.indexOf('