To fix the issue we ended up implementing a custom UrlBuilder and patching it in place of Sitecore.Links.UrlBuilders.ItemUrlBuilder, Sitecore.Kernel.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Sitecore.Data;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Foundation.SitecoreExtensions.Extensions;
using Sitecore.Globalization;
using Sitecore.IO;
using Sitecore.Links;
using Sitecore.Links.UrlBuilders;
using Sitecore.Sites;
using Sitecore.Web;
namespace Sitecore.Foundation.SitecoreExtensions
{
public class CustomItemUrlBuilder : ItemUrlBuilder
{
private ItemUrlBuilderOptions Options;
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="CustomItemUrlBuilder"/> class.
/// </summary>
/// <param name="defaultOptions">The defaultOptions<see cref="DefaultItemUrlBuilderOptions"/>.</param>
public CustomItemUrlBuilder(DefaultItemUrlBuilderOptions defaultOptions) : base(defaultOptions)
{
}
#endregion
#region Methods
/// <summary>
/// The Build.
/// </summary>
/// <param name="item">The item<see cref="Item"/>.</param>
/// <param name="options">The options<see cref="ItemUrlBuilderOptions"/>.</param>
/// <returns>The <see cref="string"/>.</returns>
public override string Build(Item item, ItemUrlBuilderOptions options)
{
if (Context.Site == null || Context.Site.Name == "shell")
{
return base.Build(item, options);
}
Options = options;
var url = BuildItemUrl(item);
if (Options.LowercaseUrls != null && Options.LowercaseUrls.Value)
url = url.ToLowerInvariant();
return url;
}
#endregion
public string BuildItemUrl(Item item)
{
Assert.ArgumentNotNull((object)item, nameof(item));
SiteInfo siteInfo = item.GetSite();
string itemPathElement = GetItemPathElement(item, siteInfo);
if (itemPathElement.Length == 0)
return string.Empty;
string serverUrlElement = GetServerUrlElement(siteInfo);
if (siteInfo != null)
return BuildItemUrl(serverUrlElement, itemPathElement, siteInfo.VirtualFolder);
return BuildItemUrl(serverUrlElement, itemPathElement);
}
public string BuildItemUrl(
string serverUrl,
string itemPath,
string virtualFolder)
{
Assert.ArgumentNotNull((object)serverUrl, nameof(serverUrl));
Assert.ArgumentNotNull((object)itemPath, nameof(itemPath));
Assert.ArgumentNotNull((object)virtualFolder, nameof(virtualFolder));
string part1 = string.Empty;
if (!serverUrl.EndsWith("/", StringComparison.InvariantCulture))
part1 += "/";
string path = FileUtil.MakePath(part1, itemPath, '/');
if (path.Length > 1)
path = StringUtil.RemovePostfix('/', path);
if (Options.EncodeNames != null && Options.EncodeNames.Value)
path = MainUtil.EncodePath(path, '/');
return serverUrl + path;
}
public string BuildItemUrl(string serverUrl, string itemPath)
{
Assert.ArgumentNotNull((object)serverUrl, nameof(serverUrl));
Assert.ArgumentNotNull((object)itemPath, nameof(itemPath));
return BuildItemUrl(serverUrl, itemPath, "/");
}
public bool IsDescendantOrSelfOf(string itemPath, string rootPath)
{
Assert.ArgumentNotNull((object)itemPath, nameof(itemPath));
Assert.ArgumentNotNull((object)rootPath, nameof(rootPath));
return !string.IsNullOrEmpty(rootPath) && itemPath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase) && (string.Compare(itemPath, rootPath, StringComparison.OrdinalIgnoreCase) == 0 || itemPath.StartsWith(rootPath + "/", StringComparison.OrdinalIgnoreCase));
}
public string GetRootPath(
SiteInfo site,
Language language,
Database database,
bool useStartItem = false)
{
return useStartItem ? FileUtil.MakePath(site.RootPath, site.StartItem) : site.RootPath;
}
public string GetItemPathElement(Item item, SiteInfo site)
{
ItemPathType type = ItemPathType.Name;
string str = item.Paths.GetPath(type);
if (site == null)
return str;
string rootPath1 = GetRootPath(site, item.Language, item.Database, true).Trim().TrimEnd('/');
if (IsDescendantOrSelfOf(str, rootPath1))
{
str = str.Substring(rootPath1.Length);
}
else
{
string rootPath2 = GetRootPath(site, item.Language, item.Database, true).Trim().TrimEnd('/');
if (IsDescendantOrSelfOf(str, rootPath2))
str = str.Substring(rootPath2.Length);
}
string virtualFolder = site.VirtualFolder;
if (virtualFolder.Length > 0 && !str.StartsWith(virtualFolder, StringComparison.OrdinalIgnoreCase))
str = FileUtil.MakePath(virtualFolder, str);
return str;
}
public string GetServerUrlElement(SiteInfo siteInfo, bool alwaysIncludeServerUrl = false)
{
SiteContext site = Context.Site;
string str1 = site != null ? site.Name : string.Empty;
string hostName = Sitecore.Web.WebUtil.GetHostName();
string str2 = alwaysIncludeServerUrl ? WebUtil.GetServerUrl() : string.Empty;
if (siteInfo == null)
return str2;
string str3;
if (string.IsNullOrEmpty(siteInfo.HostName) || string.IsNullOrEmpty(hostName) || !siteInfo.Matches(hostName))
str3 = StringUtil.GetString(new string[2]
{
siteInfo.TargetHostName, siteInfo.HostName
});
else
str3 = hostName;
string str4 = str3;
string str5 = StringUtil.GetString(new string[2]
{
siteInfo.Scheme,
WebUtil.GetScheme()
});
int defaultValue = MainUtil.GetInt(siteInfo.Port, WebUtil.GetPort());
int port = WebUtil.GetPort();
int num = MainUtil.GetInt(siteInfo.ExternalPort, defaultValue);
if (num != defaultValue)
{
if (alwaysIncludeServerUrl)
str2 = num == 80 ? string.Format("{0}://{1}", (object)str5, hostName) : string.Format("{0}://{1}:{2}", (object)str5, hostName, (object)num);
defaultValue = num;
}
if (alwaysIncludeServerUrl && siteInfo.Name.Equals(str1, StringComparison.OrdinalIgnoreCase) && (string.IsNullOrEmpty(hostName) || hostName.Equals(str4, StringComparison.OrdinalIgnoreCase)) || (string.IsNullOrEmpty(str4) || str4.IndexOf('*') >= 0))
return str2;
string scheme = WebUtil.GetScheme();
StringComparison comparisonType = StringComparison.OrdinalIgnoreCase;
if (str4.Equals(hostName, comparisonType) && defaultValue == port && str5.Equals(scheme, comparisonType))
return str2;
string str6 = str5 + "://" + str4;
if (defaultValue > 0 && defaultValue != 80)
str6 = str6 + ":" + (object)defaultValue;
return str6;
}
}
}
Patch config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">
<sitecore>
<links>
<itemUrlBuilder type="Sitecore.Links.UrlBuilders.ItemUrlBuilder, Sitecore.Kernel">
<patch:delete />
</itemUrlBuilder>
<itemUrlBuilder type="Sitecore.Foundation.SitecoreExtensions.CustomItemUrlBuilder, Sitecore.Foundation.SitecoreExtensions">
<param desc="defaultOptions" type="Sitecore.Links.UrlBuilders.DefaultItemUrlBuilderOptions, Sitecore.Kernel">
<alwaysIncludeServerUrl>true</alwaysIncludeServerUrl>
<languageEmbedding>always</languageEmbedding>
<languageLocation>filePath</languageLocation>
<lowercaseUrls>true</lowercaseUrls>
<encodeNames>true</encodeNames>
<useDisplayName>false</useDisplayName>
<addAspxExtension>false</addAspxExtension>
<siteResolving>true</siteResolving>
<shortenUrls>true</shortenUrls>
</param>
</itemUrlBuilder>
</links>
</sitecore>
</configuration>