/// <summary> /// Process RSDN link tag /// </summary> /// <param name="name">Regexp match with RSDN link tag</param> /// <returns>Formatted link as HtmlAnchor</returns> protected virtual HtmlAnchor ProcessRsdnLinkAsAnchor(Match name) { var link = new HtmlAnchor { Target = "_blank", HRef = $"{GetPathToRoot()}/Forum/Info/{name.Groups[1].Value.EncodeAgainstXSS()}.aspx", InnerText = name.Groups[1].Value }; return AddClass(link, "m"); }
/// <summary> /// Renders HTML for url. /// </summary> protected static string RenderHtmlAnchor(HtmlAnchor htmlAnchor) { var stringBuilder = new StringBuilder(); stringBuilder.Append("<a"); foreach(var attribute in htmlAnchor.Attributes.OrderBy(a => a.Key)) stringBuilder.AppendFormat( " {0}=\"{1}\"", attribute.Key, attribute.Value); var inner = htmlAnchor.InnerHtml; if(string.IsNullOrWhiteSpace(inner)) inner = htmlAnchor.InnerText; if(!string.IsNullOrWhiteSpace(inner)) stringBuilder.AppendFormat(">{0}</a>", inner); else stringBuilder.Append(" />"); return stringBuilder.ToString(); }
/// <summary> /// Format RSDN URLs to hyperlinks. /// Used in both, explicitly & implicitly specified links. /// </summary> /// <param name="urlMatch">Regex match with URL address.</param> /// <param name="link">HtmlLink, initialized by default</param> /// <returns>true - processed by formatter itself, no further processing</returns> protected virtual bool FormatRsdnURLs(Match urlMatch, HtmlAnchor link) { var urlScheme = urlMatch.Groups["scheme"]; var urlHostname = urlMatch.Groups["hostname"]; var originalScheme = urlScheme.Success ? urlScheme.Value : Uri.UriSchemeHttp; Action<String> rsdnHostReplacer = delegate(string urlHost) { var schemeMatchStart = (urlScheme.Success ? urlScheme.Index : urlMatch.Index); link.HRef = (((HttpContext.Current != null) && HttpContext.Current.Request.IsSecureConnection) ? Uri.UriSchemeHttps : originalScheme) + (urlScheme.Success ? null : "://") + link.HRef.Substring(schemeMatchStart - urlMatch.Index + urlScheme.Length, urlHostname.Index - schemeMatchStart - urlScheme.Length) + urlHost + link.HRef.Substring(urlHostname.Index - urlMatch.Index + urlHostname.Length); }; IDictionary<string, ThreadStart> rsdnSchemesProcessing = new Dictionary<string, ThreadStart>(3, StringComparer.OrdinalIgnoreCase); // redirect rsdn svn rsdnSchemesProcessing["svn"] = (() => rsdnHostReplacer("svn.rsdn.ru")); // rebase only http or https links rsdnSchemesProcessing[Uri.UriSchemeHttp] = rsdnSchemesProcessing[Uri.UriSchemeHttps] = (() => rsdnHostReplacer(CanonicalRsdnHostName)); if (rsdnSchemesProcessing.ContainsKey(originalScheme)) rsdnSchemesProcessing[originalScheme](); AddClass(link, "m"); if (_openRsdnLinksInNewWindow) link.Target = "_blank"; return true; }
/// <summary> /// Format URLs to hyperlinks. /// Used in both, explicitly & implicitly specified links. /// </summary> /// <param name="urlMatch">Regex match with URL address.</param> /// <param name="urlAdsress">URL address.</param> /// <param name="urlName">URL name. May be or may be not different from URL address.</param> /// <returns>Formatted link for specified URL.</returns> protected virtual string FormatURLs(Match urlMatch, string urlAdsress, string urlName) { // by default pass url directly (just antiXSS processing) var link = new HtmlAnchor { HRef = urlAdsress.EncodeAgainstXSS(), InnerHtml = urlName.EncodeAgainstXSS() }; var processesedItself = false; // if valid url detected - do additional formatting if (urlMatch.Success) { // if no scheme detected - use default http if (!urlMatch.Groups["scheme"].Success) link.HRef = Uri.UriSchemeHttp + "://" + link.HRef; else { // process custom scheme formatting, if exists ProcessUrl schemeFormatter; if (SchemeFormatting.TryGetValue(urlMatch.Groups["scheme"].Value, out schemeFormatter)) processesedItself = schemeFormatter(urlMatch, link); } if (!processesedItself) { var matchedHostname = urlMatch.Groups["hostname"].Value; ProcessUrl hostFormatter; if (HostFormatting.TryGetValue(matchedHostname, out hostFormatter)) processesedItself = hostFormatter(urlMatch, link); else { foreach (var host in _wellKnownHosts) if (matchedHostname.EndsWith(host.Key)) AddClass(link, host.Value); } } } if (!processesedItself) { AddClass(link, "m"); link.Target = "_blank"; } return RenderHtmlAnchor(link); }
/// <summary> /// Add css class to HtmlAnchor /// </summary> protected static HtmlAnchor AddClass(HtmlAnchor link, string className) { const string @class = "class"; if(!link.Attributes.ContainsKey(@class)) link.Attributes[@class] = ""; var cssClass = link.Attributes[@class]; if (!string.IsNullOrEmpty(cssClass)) cssClass += " "; link.Attributes[@class] = cssClass + className; return link; }
/// <summary> /// Process local MSDN links (ms-help scheme). /// </summary> /// <param name="urlMatch">Input URL match</param> /// <param name="link">Output formatted URL</param> private static bool ProcessMsHelpLink(Match urlMatch, HtmlAnchor link) { var guidMatch = _msdnGuidDetector.Match(urlMatch.Value); if (guidMatch.Success) { link.HRef = string.Format( guidMatch.Groups["section"].Success ? "http://msdn.microsoft.com/library/en-us/{1}/html/{0}.asp" : "http://msdn2.microsoft.com/{0}.aspx", guidMatch.Value, guidMatch.Groups["section"].Value); } return false; }
/// <summary> /// Process RSDN link tag /// </summary> /// <param name="name">Regexp match with RSDN link tag</param> /// <returns>Formatted link as HtmlAnchor</returns> protected virtual HtmlAnchor ProcessRsdnLinkAsAnchor(Match name) { var link = new HtmlAnchor { Target = "_blank", HRef = string.Format( "{0}/Forum/Info/{1}.aspx", GetPathToRoot(), name.Groups[1].Value), InnerText = name.Groups[1].Value }; return AddClass(link, "m"); }
/// <summary> /// Process RSDN partneship links. /// </summary> /// <param name="urlMatch"></param> /// <param name="link"></param> protected static bool ProcessPartnerLink(Match urlMatch, HtmlAnchor link) { var uriBuilder = new UriBuilder(link.HRef); var queryBuilder = new QueryBuilder(uriBuilder.Query); var partnerRecord = _partnresIDs[uriBuilder.Host]; queryBuilder[partnerRecord.QueryParameter] = partnerRecord.PartnerID; uriBuilder.Query = HttpUtility.HtmlEncode(queryBuilder.ToString()); link.HRef = uriBuilder.Uri.AbsoluteUri; return false; }
private static bool ProcessPiterLink(Match urlMatch, HtmlAnchor link) { var uriBuilder = new UriBuilder(link.HRef); var queryBuilder = new QueryBuilder(uriBuilder.Query); // если есть анонимный параметр if (!string.IsNullOrEmpty(queryBuilder[null])) { queryBuilder["id"] = queryBuilder[null]; queryBuilder.Remove(null); uriBuilder.Query = HttpUtility.HtmlEncode(queryBuilder.ToString()); link.HRef = uriBuilder.Uri.AbsoluteUri; } // стандартная обработка return ProcessPartnerLink(urlMatch, link); }
/// <summary> /// Process Amazon.com links /// </summary> private static bool ProcessAmazonLink(Match urlMatch, HtmlAnchor link) { var url = HttpUtility.UrlDecode(link.HRef); if (url == null) return false; var asinMatch = _asinDetector.Match(url); if (asinMatch.Success) link.HRef = string.Format(_directAmazonUrl, asinMatch.Groups["asin"].Value); else { var originalAmazonUri = new Uri(link.HRef); if (originalAmazonUri.PathAndQuery.StartsWith(_amazonPathPrefix)) link.HRef = _amazonUrl + originalAmazonUri.PathAndQuery.Substring(_amazonPathPrefix.Length); else link.HRef = _amazonUrl + HttpUtility.UrlEncode(link.HRef); } return false; }