Example #1
0
        /// <summary>
        /// Converts internal urls to public ones in a given html fragment
        /// </summary>
        /// <param name="html"></param>
        /// <param name="converters"></param>
        /// <returns></returns>
        internal static string ConvertInternalUrlsToPublic(string html, IEnumerable <IInternalUrlConverter> converters)
        {
            var convertersMap = GetConvertersMap(_ => _);

            if (!convertersMap.Any())
            {
                return(html);
            }

            // Urls, generated in UserControl-s may still have "~/" as a prefix
            foreach (var urlPrefix in convertersMap.Keys)
            {
                string rawUrlPrefix      = "~/" + urlPrefix;
                string resolvedUrlPrefix = ResolvePrefix(urlPrefix);

                html = UrlUtils.ReplaceUrlPrefix(html, rawUrlPrefix, resolvedUrlPrefix);
            }

            StringBuilder result = null;

            var urlsToConvert = new List <UrlToConvert> ();

            foreach (var pair in convertersMap)
            {
                string internalPrefix = ResolvePrefix(pair.Key);
                var    converter      = pair.Value;

                // Bracket encoding fix
                string prefixToSearch = internalPrefix;
                if (prefixToSearch.EndsWith("(", StringComparison.Ordinal))
                {
                    prefixToSearch = prefixToSearch.Substring(0, internalPrefix.Length - 1);
                }

                urlsToConvert.AddRange(UrlUtils.FindUrlsInHtml(html, prefixToSearch).Select(match =>
                                                                                            new UrlToConvert(match, internalPrefix, converter)));
            }

            // Sorting the offsets by descending, so we can replace urls in that order by not affecting offsets of not yet processed urls
            urlsToConvert.Sort((a, b) => - a.Match.Index.CompareTo(b.Match.Index));

            int lastReplacementIndex = int.MaxValue;

            var urlSpace = new UrlSpace();

            var measurements = new Dictionary <string, Measurement>();

            var convertionCache = new Dictionary <string, string>();

            foreach (var urlToConvert in urlsToConvert)
            {
                UrlUtils.UrlMatch urlMatch = urlToConvert.Match;
                if (urlMatch.Index == lastReplacementIndex)
                {
                    continue;
                }

                string internalUrlPrefix = urlToConvert.UrlPrefix;

                string internalUrl = urlMatch.Value;
                string publicUrl;

                if (!convertionCache.TryGetValue(internalUrl, out publicUrl))
                {
                    string decodedInternalUrl = internalUrl.Replace("%28", "(").Replace("%29", ")").Replace("&amp;", "&");

                    if (!decodedInternalUrl.StartsWith(internalUrlPrefix))
                    {
                        continue;
                    }

                    var converter = urlToConvert.Converter;
                    MeasureConvertionPerformance(measurements, converter, () =>
                    {
                        publicUrl = urlToConvert.Converter.ToPublicUrl(decodedInternalUrl, urlSpace);
                    });

                    if (publicUrl == null)
                    {
                        convertionCache.Add(internalUrl, null);
                        continue;
                    }

                    // Encoding xml attribute value
                    publicUrl = publicUrl.Replace("&", "&amp;");

                    convertionCache.Add(internalUrl, publicUrl);
                }
                else
                {
                    if (internalUrl == null)
                    {
                        continue;
                    }
                }

                if (result == null)
                {
                    result = new StringBuilder(html);
                }

                result.Remove(urlMatch.Index, urlMatch.Value.Length);
                result.Insert(urlMatch.Index, publicUrl);

                lastReplacementIndex = urlMatch.Index;
            }

            foreach (var measurement in measurements.Values)
            {
                Profiler.AddSubMeasurement(measurement);
            }

            return(result != null?result.ToString() : html);
        }