Multi custom links with Sitecore 10 using ItemUrlBuilder
A common need for multisite implementation
Problem
In the past, a multisite project used to create a few custom link provider to support dynamic URL with wildcard item in Sitecore solution.
Unfortunately, when I try to migrating from an older Sitecore solution into Sitecore 10, I noticed that the LinkProvider
is obsolete in Sitecore 10, this is no longer the way to approach custom URL.
However, we can extend Sitecore.Links.UrlBuilders.ItemUrlBuilder
and override the main method Build if the solution is a single site project.
With multisite project, we cannot have multiple ItemUrlBuilder because we can only replace the core Sitecore.Links.UrlBuilders.ItemUrlBuilder
once.
Example
- Site A using CustomLinkProviderSiteA
- Site B using CustomLinkProviderSiteB
Solutions
There are 2 solutions here can resolve the above issue.
- Create a foundation
UrlBuilder
with custom pipeline - Extend from
LocalizableLinkProvider
If you are using SXA. The defaultLocalizableLinkProvider
is actually allow us to extend but have to overrideGetItemUrl(Item, ItemUrlBuilderOptions)
instead
Here, I will only provide detailed solution for foundation UrlBuilder
with custom pipeline.
Foundation ItemUrlBuilder with custom pipeline
Since we can only replace the core Sitecore.Links.UrlBuilders.ItemUrlBuilder
once. I decided to create a foundational ItemUrlBuilder
that kicks off a custom pipeline to generate the URL
Create a pipeline argument class ItemUrlBuilderExtensionsArgs
using Sitecore.Data.Items;
using Sitecore.Links.UrlBuilders;
using Sitecore.Pipelines;
namespace Sitecore.Foundation.SitecoreExtensions.Pipeline
{
public class ItemUrlBuilderExtensionsArgs : PipelineArgs
{
public ItemUrlBuilderOptions ItemUrlBuilderOptions { get; set; }
public Item Item { get; set; }
public string ItemUrl { get; set; }
}
}
Create foundation ItemUrlBuilder
using Sitecore.Foundation.SitecoreExtensions.Pipeline;
using Sitecore.Data.Items;
using Sitecore.Links.UrlBuilders;
using Sitecore.Pipelines;
namespace Sitecore.Foundation.SitecoreExtensions.Links
{
public class ItemUrlBuilder : Sitecore.Links.UrlBuilders.ItemUrlBuilder
{
public ItemUrlBuilder(DefaultItemUrlBuilderOptions defaultOptions) : base(defaultOptions)
{
}
public override string Build(Item item, ItemUrlBuilderOptions options)
{
options.LanguageEmbedding = Sitecore.Links.LanguageEmbedding.Never;
options.LowercaseUrls = true;
var args = new ItemUrlBuilderExtensionsArgs
{
ItemUrlBuilderOptions = options,
Item = item
};
CorePipeline.Run("itemUrlBuilderExtensions", args);
if (!string.IsNullOrEmpty(args.ItemUrl))
{
return args.ItemUrl;
}
return base.Build(item, options);
}
}
}
Patch Sitecore Config
Create a config file to patch <itemUrlBuilder>
and introduce new <itemUrlBuilderExtensions>
in pipelines
Config filename: Sitecore.Foundation.SitecoreExtensions.UrlBuilder.config
<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<links>
<itemUrlBuilder>
<patch:attribute name="type">Sitecore.Foundation.SitecoreExtensions.Links.ItemUrlBuilder, Sitecore.Foundation.SitecoreExtensions</patch:attribute>
</itemUrlBuilder>
</links>
<pipelines>
<itemUrlBuilderExtensions>
</itemUrlBuilderExtensions>
</pipelines>
</sitecore>
</configuration>
Now, the custom itemUrlBuilder is ready and we can introduce new processor in pipeline to generate custom URL based on the project needs
Here is an example of the custom code used for processor:
using Microsoft.Extensions.DependencyInjection;
using Sitecore.DependencyInjection;
using Sitecore.XA.Foundation.Multisite;
using Sitecore.Data;
using Sitecore.Foundation.SitecoreExtensions.Pipeline;
namespace Sitecore.Feature.Article.ItemUrlBuilder
{
public class ArticleUrlBuilder
{
public void Process(ItemUrlBuilderExtensionsArgs args)
{
var options = args.ItemUrlBuilderOptions;
var item = args.Item;
var setting = ServiceLocator.ServiceProvider.GetService<IMultisiteContext>().GetSettingsItem(item);
if (item.DescendsFrom(Templates.Article.ID))
{
//return custom url to args.ItemUrl
var articleWildCard = Context.Database.GetItem(setting.GetField("Article Wildcard Page"));
if (articleWildCard != null && articleWildCard.Parent != null)
{
var baseUrl = Links.LinkManager.GetItemUrl(articleWildCard.Parent, options);
args.ItemUrl = $"{baseUrl}/{MainUtil.EncodePath(item.Name, '/').ToLower()}";
}
}
}
}
}
Finally, register the processor in the custom <itemUrlBuilderExtensions>
pipeline .
<itemUrlBuilderExtensions>
<processor type="Sitecore.Feature.Article.ItemUrlBuilder.ArticleUrlBuilder, Sitecore.Feature.Article" />
</itemUrlBuilderExtensions>
With the custom <itemUrlBuilderExtensions>
pipeline, other developers can add new processor to handle their custom URL without override the one and only one ItemUrlBuilder.