Skip to content

Commit c8acab4

Browse files
authored
FIX: Correct "Automatic Links" (#1875)
* FIX: Rework automatic links Regex and logic; add unit tests
1 parent 0e703e6 commit c8acab4

6 files changed

Lines changed: 459 additions & 20 deletions

File tree

Dnn.CommunityForums/class/ForumsConfig.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,6 +1023,39 @@ internal void RemoveLegacyAvatarsFolder_090700()
10231023

10241024
internal void RelocateAttachments_090700()
10251025
{
1026+
foreach (DotNetNuke.Abstractions.Portals.IPortalInfo portal in DotNetNuke.Entities.Portals.PortalController.Instance.GetPortals())
1027+
{
1028+
foreach (ModuleInfo module in DotNetNuke.Entities.Modules.ModuleController.Instance.GetModules(portal.PortalId))
1029+
{
1030+
if (module.DesktopModule.ModuleName.Trim().Equals(Globals.ModuleName.Trim(), StringComparison.InvariantCultureIgnoreCase))
1031+
{
1032+
try
1033+
{
1034+
if (!DotNetNuke.Services.FileSystem.FolderManager.Instance.FolderExists(portal.PortalId, Globals.ContentFolderNameBase))
1035+
{
1036+
DotNetNuke.Services.FileSystem.FolderManager.Instance.AddFolder(portal.PortalId, Globals.ContentFolderNameBase);
1037+
}
1038+
}
1039+
catch (Exception ex)
1040+
{
1041+
DotNetNuke.Modules.ActiveForums.Exceptions.LogException(ex);
1042+
}
1043+
1044+
try
1045+
{
1046+
if (!DotNetNuke.Services.FileSystem.FolderManager.Instance.FolderExists(portal.PortalId, Globals.AttachmentUploadsFolderName))
1047+
{
1048+
DotNetNuke.Services.FileSystem.FolderManager.Instance.AddFolder(portal.PortalId, Globals.AttachmentUploadsFolderName);
1049+
}
1050+
}
1051+
catch (Exception ex)
1052+
{
1053+
DotNetNuke.Modules.ActiveForums.Exceptions.LogException(ex);
1054+
}
1055+
}
1056+
}
1057+
}
1058+
10261059
var attachmentController = new DotNetNuke.Modules.ActiveForums.Controllers.AttachmentController();
10271060
foreach (var attach in attachmentController.Get().ToList())
10281061
{

Dnn.CommunityForums/class/Utilities.cs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace DotNetNuke.Modules.ActiveForums
2828
using System.Linq;
2929
using System.Reflection;
3030
using System.Security.Cryptography;
31+
using System.Security.Policy;
3132
using System.Security.Principal;
3233
using System.Text;
3334
using System.Text.RegularExpressions;
@@ -47,6 +48,7 @@ namespace DotNetNuke.Modules.ActiveForums
4748
using DotNetNuke.Modules.ActiveForums.Extensions;
4849
using DotNetNuke.Modules.ActiveForums.Helpers;
4950
using DotNetNuke.Security.Permissions;
51+
using DotNetNuke.Web.UI.WebControls;
5052

5153
public abstract partial class Utilities
5254
{
@@ -339,12 +341,13 @@ internal static string PrepareForEdit(int portalId, int moduleId, string themePa
339341
return text;
340342
}
341343

342-
private static string ReplaceLink(Match match, string currentSite, string text)
344+
internal static string ReplaceLink(Match match, string currentSite, string text)
343345
{
344346
const int maxLengthAutoLinkLabel = 47;
345347
const string outSite = "<a href=\"{0}\" target=\"_blank\" rel=\"nofollow\">{1}</a>";
346348
const string inSite = "<a href=\"{0}\">{1}</a>";
347349
var url = match.Value;
350+
348351
if (url.ToLowerInvariant().Contains("jpg") || url.ToLowerInvariant().Contains("gif") || url.ToLowerInvariant().Contains("png") || url.ToLowerInvariant().Contains("jpeg"))
349352
{
350353
return url;
@@ -372,34 +375,48 @@ private static string ReplaceLink(Match match, string currentSite, string text)
372375
return url;
373376
}
374377

375-
var urlText = match.Value;
378+
var urlText = url;
376379
if (urlText.Length > maxLengthAutoLinkLabel)
377380
{
378-
urlText = string.Concat(match.Value.Substring(0, maxLengthAutoLinkLabel - 22), "...", match.Value.Substring(match.Value.Length - 20));
381+
urlText = string.Concat(url.Substring(0, maxLengthAutoLinkLabel - 22), "...", url.Substring(url.Length - 20));
379382
}
380383

381-
return url.ToLowerInvariant().Contains(currentSite.ToLowerInvariant()) ? string.Format(inSite, url, urlText) : string.Format(outSite, url, urlText);
384+
return url.ToLowerInvariant().Contains(currentSite.ToLowerInvariant()) ? string.Format(inSite, new Uri(url).AbsoluteUri, urlText) : string.Format(outSite, new Uri(url).AbsoluteUri, urlText);
382385
}
383386

384387
public static string AutoLinks(string text, string currentSite)
385388
{
386-
var original = text;
387389
if (!string.IsNullOrEmpty(text))
388390
{
389-
const string encodedHref = "&lt;a.*?href=[\"'](?<url>.*?)[\"'].*?&gt;(http[s]?.*?)&lt;/a&gt;"; // Encoded href regex
391+
var original = text;
390392

391393
// Replace encoded url with decoded url
392-
foreach (Match m in DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(encodedHref, RegexOptions.IgnoreCase).Matches(text))
394+
const string EncodedHrefPattern = "&lt;a.*?href=[\"'](?<url>.*?)[\"'].*?&gt;(http[s]?.*?)&lt;/a&gt;";
395+
foreach (Match m in DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(EncodedHrefPattern, RegexOptions.IgnoreCase).Matches(text))
393396
{
394397
text = text.Replace(m.Value, System.Net.WebUtility.HtmlDecode(m.Value));
395398
}
396399

397-
const string regHref = "<a.*?href=[\"'](?<url>.*?)[\"'].*?>(?<http>http[s]?.*?)</a>";
400+
const string HRefPattern = @"<a.*?href=[\""'](?<url>.*?)[\""'].*?>(?<inner>[http[s]?.*?.*?|.*?])</a>";
398401

399402
// Remove all exiting <A> anchors, so they will be treated by the ReplaceLink function. (adding target=_blank & nofollow)
400-
foreach (Match m in DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(regHref, RegexOptions.IgnoreCase).Matches(text))
403+
foreach (Match m in DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(HRefPattern, RegexOptions.IgnoreCase).Matches(text))
401404
{
402-
text = text.Replace(m.Value, m.Groups["http"].Value.Contains("...") ? m.Groups["url"].Value : m.Groups["http"].Value);
405+
//-- if content between the tags contains href or src or =
406+
if (m.Groups["inner"]?.Success == true)
407+
{
408+
var innerValue = m.Groups["inner"].Value.ToLowerInvariant();
409+
if (innerValue.Contains("href") || innerValue.Contains("src") || innerValue.Contains("="))
410+
{
411+
continue;
412+
}
413+
414+
var innerGroupValue = m.Groups["inner"].Value;
415+
if (innerGroupValue.Contains("...") || innerGroupValue.ToLowerInvariant().StartsWith("http"))
416+
{
417+
text = text.Replace(m.Value, m.Groups["url"].Value);
418+
}
419+
}
403420
}
404421

405422
// Handle Empty string
@@ -408,12 +425,11 @@ public static string AutoLinks(string text, string currentSite)
408425
return original;
409426
}
410427

411-
// Look for http(s) URLs that are not perceded by a quote or <a>.
412-
String strRegexUrl = @"(?<!['""]+|<a.*?>\s*)http[s]?://([\w+?\.\w+])+([a-zA-Z0-9\~\!\@\\#\$\%\^\&amp;\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?";
428+
// Look for http(s) URLs that are not perceded by a quote or <a>.
429+
const string UrlPattern = @"(?<!['""]+|<a.*?>\s*)\bhttps?://[-A-Z0-9+&@#/%?=~_|$!:,.;]*[A-Z0-9+&@#/%=~_|$]";
413430

414431
// Create auto link
415-
text = DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(strRegexUrl, RegexOptions.IgnoreCase).Replace(text, m => ReplaceLink(m, currentSite, text));
416-
432+
text = DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(UrlPattern, RegexOptions.IgnoreCase).Replace(text, m => ReplaceLink(m, currentSite, text));
417433
if (string.IsNullOrEmpty(text))
418434
{
419435
return original;
@@ -1212,6 +1228,7 @@ public static TimeSpan GetTimeZoneOffsetForUser(int portalId, int userId)
12121228
return GetTimeZoneOffsetForUser(new DotNetNuke.Entities.Users.UserController().GetUser(portalId, userId));
12131229
}
12141230

1231+
[Obsolete("Deprecated in Community Forums. Removed in 10.00.00. Not Used")]
12151232
public static DateTime GetUserFormattedDate(DateTime displayDate, int mid, TimeSpan offset)
12161233
{
12171234
return displayDate.AddMinutes(offset.TotalMinutes);
@@ -1273,6 +1290,7 @@ public static string ParseSpacer(string template)
12731290
return DotNetNuke.Common.Utilities.RegexUtils.GetCachedRegex(expression, RegexOptions.IgnoreCase).Replace(template, spacerTemplate);
12741291
}
12751292

1293+
[Obsolete("Deprecated in Community Forums. Removed in 10.00.00. Not Used")]
12761294
internal static string GetSqlString(string sqlFile)
12771295
{
12781296
var resourceLocation = sqlFile;
@@ -1468,6 +1486,7 @@ public static string GetSharedResource(string key, bool isAdmin = false)
14681486
return string.IsNullOrEmpty(sValue) ? key : sValue;
14691487
}
14701488

1489+
[Obsolete("Deprecated in Community Forums. Removed in 10.00.00. Not Used")]
14711490
public static string FormatFileSize(int fileSize)
14721491
{
14731492
try

Dnn.CommunityForumsTests/DnnCommunityForumsTests.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,4 @@
5454
<HintPath>ReferencedAssemblies\DotNetNuke.Tests.Utilities.dll</HintPath>
5555
</Reference>
5656
</ItemGroup>
57-
5857
</Project>

Dnn.CommunityForumsTests/Entities/ForumInfoTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ public void IsPublicForum_WithNoPublicRoles_ReturnsFalse()
182182
TotalTopics = 0,
183183
Security = new DotNetNuke.Modules.ActiveForums.Entities.PermissionInfo()
184184
{
185-
Read = DotNetNuke.Tests.Utilities.Constants.RoleID_Administrators.ToString(),
186185
View = DotNetNuke.Tests.Utilities.Constants.RoleID_Administrators.ToString(),
186+
Read = DotNetNuke.Tests.Utilities.Constants.RoleID_Administrators.ToString(),
187187
},
188188
ForumGroup = new DotNetNuke.Modules.ActiveForums.Entities.ForumGroupInfo
189189
{

Dnn.CommunityForumsTests/TestBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ public class TestBase
5858
private Mock<IRoleController> roleController;
5959
private Mock<IHostController> mockHostController;
6060

61-
internal string DefaultPortalAlias = "localhost/en-us";
61+
internal string DefaultPortalAlias = "example.com/en-us";
62+
internal string DefaultSite = "https://example.com";
6263

6364
internal Mock<DotNetNuke.Entities.Modules.ModuleInfo> MockModule;
6465

0 commit comments

Comments
 (0)