< Return to Feed
Justin Brown - 04.19.2017

Sitecore Custom Friendly URLs with LinkManager

Perhaps you have shared content between multiple sites, or simply a content tree you don’t want to expose. In either case you want to change the url returned by LinkManager.GetItemUrl(). In order to accomplish this we’ll have to implement a custom LinkProvider and configure Sitecore to use it.

A Simple LinkProvider Implementation

Let’s say we have blog posts and we’ve decided to embed the date of the post into the url by creating custom links rather than restructuring your content tree.

At first glance it appears that we need to subclass Sitecore.Links.LinkProvider, then override the GetItemUrl() method:

using Sitecore;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Links;

namespace Sandbox.Sc.Links
{
    public class SandboxLinkProvider : LinkProvider
    {
        public override string GetItemUrl(Item item, UrlOptions options)
        {
            if (item.TemplateName == "Blog Post")
            {
                DateField dateField = item.Fields["Blog Post Date"];
                var title = item["Blog Post Title"];
                return MainUtil.EncodePath($"/{dateField.DateTime:yyyy-MM-dd}-{title}", '/');
            }

            return base.GetItemUrl(item, options);
        }
    }
}

Configuring Sitecore

Now that we have a LinkProvider implementation we need to configure Sitecore’s LinkManager to use our provider by using an include patch file:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set">
  <sitecore>
    <linkManager set:defaultProvider="sandbox">
      <providers>
        <add name="sandbox" type="Sandbox.Sc.Links.SandboxLinkProvider, Sandbox.Sc"/>
      </providers>
    </linkManager>
  </sitecore>
</configuration>

Testing our LinkProvider

In order to test our LinkProvider we implement a small rendering that passes in UrlOptions:

<ul>
    @foreach (var post in sortedPosts)
    {
        var date = ((DateField)post.Fields["Blog Post Date"]).DateTime;
        var title = post["Blog Post Title"];
        <li>
            <a href="@LinkManager.GetItemUrl(post, new UrlOptions() { AlwaysIncludeServerUrl = true })">@date.ToShortDateString() -- @title</a>
        </li>
    }
</ul>

(Note: While we probably wouldn’t render absolute urls for a blog post listing we would want them in sitemaps, and for SEO metadata such as canonical urls, OpenGraph tags, and microdata)

Then we view the output from our rendering:

        <ul>
                <li>
                    <a href="/2017-04-07-Foo">4/7/2017 -- Foo</a>
                </li>
                <li>
                    <a href="/2017-04-02-Bar">4/2/2017 -- Bar</a>
                </li>
                <li>
                    <a href="/2017-02-01-Baz">2/1/2017 -- Baz</a>
                </li>
        </ul>

Fixing our LinkProvider

Our custom url works but the UrlOptions are being ignored. To correct this we need to subclass LinkProvider.LinkBuilder and override its GetItemPathElement() method:

using Sitecore;
using Sitecore.Data.Fields;
using Sitecore.Data.Items;
using Sitecore.Links;
using Sitecore.Web;

namespace Sandbox.Sc.Links
{
    public class SandboxLinkProvider : LinkProvider
    {
        protected override LinkBuilder CreateLinkBuilder(UrlOptions options)
        {
            return new SandboxLinkBuilder(options);
        }
    }

    public class SandboxLinkBuilder : LinkProvider.LinkBuilder
    {
        public SandboxLinkBuilder(UrlOptions options) : base(options)
        {
        }

        protected override string GetItemPathElement(Item item, SiteInfo site)
        {
            if (item.TemplateName == "Blog Post")
            {
                DateField dateField = item.Fields["Blog Post Date"];
                var title = item["Blog Post Title"];
                return MainUtil.EncodePath($"/{dateField.DateTime:yyyy-MM-dd}-{title}", '/');
            }

            return base.GetItemPathElement(item, site);
        }       
    }
}

Using the LinkBuilder our rendering will now produce the expected results:

        <ul>
                <li>
                    <a href="http://sandbox.local/2017-04-07-Foo">4/7/2017 -- Foo</a>
                </li>
                <li>
                    <a href="http://sandbox.local/2017-04-02-Bar">4/2/2017 -- Bar</a>
                </li>
                <li>
                    <a href="http://sandbox.local/2017-02-01-Baz">2/1/2017 -- Baz</a>
                </li>
        </ul>