Using multiple link providers in Sitecore

I can set multiple link providers in Sitecore but, how do I use them? How do I use them in c# code and xslt code?

In C# it seems pretty straight forward, all I need is to use the provider directly and call methods on it, instead of on LinkManager.

Sitecore.Links.LinkManager.Providers["myCustomProvider"].GetItemUrl(item,options);


I don’t like it as it is above, mainly because of applying indexing with that string in the middle, but also because there’s no method to which you could pass only an item. I need this fluffed a bit and I could wrap it into a new LinkManager class, called something other than LinkManager of course… create LinkProvider classes for all my different providers. Yeah, I like that better.

    public static class ALinkProvider
    {
        public static string GetItemUrl(string providerName, Sitecore.Data.Items.Item item, Sitecore.Links.UrlOptions options)
        {
            Sitecore.Links.LinkProvider provider = GetProvider(providerName);
            
            return provider.GetItemUrl(item, options);
        }

        public static string GetItemUrl(string providerName, Sitecore.Data.Items.Item item)
        {
            Sitecore.Links.LinkProvider provider = GetProvider(providerName);

            Sitecore.Links.UrlOptions options = Sitecore.Diagnostics.Assert.ResultNotNull<Sitecore.Links.UrlOptions>(provider.GetDefaultUrlOptions());

            return provider.GetItemUrl(item, options);
        }

        public static Sitecore.Links.LinkProvider GetProvider(string providerName)
        {
            return Sitecore.Links.LinkManager.Providers[providerName];
        }
        
    }
    public static class FrenchLinkProvider
    {
        private static string ProviderName = "french";
        
        public static string GetItemUrl(Sitecore.Data.Items.Item item)
        {
            return ALinkProvider.GetItemUrl(ProviderName, item);
        }

        public static string GetItemUrl(Sitecore.Data.Items.Item item, Sitecore.Links.UrlOptions options)
        {
            return ALinkProvider.GetItemUrl(ProviderName, item, options);
        }
    }

Cool, so how do I use this in xslt? I would very much like to do this:

<sc:link field="link" select="." provider="myCustomProvider">
</sc:link>

XslControls are rendered by Sitecore using the RenderField pipeline, sc:link controls are rendered by the pipeline processor called GetLinkFieldValue, which makes use of a new LinkRenderer(). I build a LinkRenderer then.

    public class GetLinkFieldValue : Sitecore.Pipelines.RenderField.GetLinkFieldValue
    {
        override protected Sitecore.Xml.Xsl.LinkRenderer CreateRenderer(Sitecore.Data.Items.Item item)
        {
            return new LinkRenderer(item);
        }
    }
    public class LinkRenderer : Sitecore.Xml.Xsl.LinkRenderer
    {
        public LinkRenderer(Sitecore.Data.Items.Item item) : base(item) { }
        override protected string GetUrl(Sitecore.Data.Fields.XmlField field)
        {
            if (this.Parameters.Keys.Contains("provider"))
            {
                if (field != null)
                    return new LinkUrl().GetUrl(this.Parameters["provider"], field, this.Item.Database);
                return ALinkProvider.GetItemUrl(this.Parameters["provider"], this.Item);
            }
            return base.GetUrl(field);
        }
    }
    public class LinkUrl : Sitecore.Xml.Xsl.LinkUrl
    {
        protected virtual string GetInternalUrl(Sitecore.Data.Database database, string url, string itemID, string anchor, string queryString, string provider)
        {
            Sitecore.Diagnostics.Assert.ArgumentNotNull(database, "database");
            Sitecore.Diagnostics.Assert.ArgumentNotNull(url, "url");
            Sitecore.Diagnostics.Assert.ArgumentNotNull(itemID, "itemID");
            Sitecore.Diagnostics.Assert.ArgumentNotNull(anchor, "anchor");
            Sitecore.Diagnostics.Assert.ArgumentNotNull(queryString, "queryString");
            
            Sitecore.Data.Items.Item item = database.Items[url] ?? database.Items[itemID];
            
            if (item == null)
                return string.Empty;
            
            if (item.Paths.IsMediaItem)
                return this.GetMediaUrl(database, itemID);

            if(string.IsNullOrEmpty(provider))
                return Sitecore.Links.LinkManager.GetItemUrl(item) + this.GetQueryString(queryString) + anchor;

            return (ALinkProvider.GetItemUrl(provider, item) + this.GetQueryString(queryString) + anchor);
        }
        override protected string GetInternalUrl(Sitecore.Data.Database database, string url, string itemID, string anchor, string queryString)
        {
            return GetInternalUrl(database, url, itemID, anchor, queryString, "");
        }

        public string GetUrl(string providerName, Sitecore.Data.Fields.XmlField field, Sitecore.Data.Database database)
        {
            Sitecore.Diagnostics.Assert.ArgumentNotNull(field, "field");
            Sitecore.Diagnostics.Assert.ArgumentNotNull(database, "database");
            string linkType = field.GetAttribute("linktype");
            string url = field.GetAttribute("url");
            string itemID = field.GetAttribute("id");
            string anchor = field.GetAttribute("anchor");
            string queryString = field.GetAttribute("querystring");

            if (linkType == "internal")
            {
                if (!string.IsNullOrEmpty(anchor))
                    anchor = "#" + anchor;

                return this.GetInternalUrl(database, url, itemID, anchor, queryString, providerName);
            }
            return base.GetUrl(field, database);
        }
    }

Maybe a bit too much code for what it’s trying to achieve and I don’t have a use for it at the moment, I don’t think I ever will, but I’m glad I looked into it. Sitecore is using the default LinkProvider to build all links, even links in the Sitecore admin-interface so if I have to extend and replace the LinkProvider, I think I’ll go with defining a second provider instead.

Untested, non-production code.

One thought on “Using multiple link providers in Sitecore

  1. Pingback: Site Specific Link Provider for Multi-Site Implementation in Sitecore | jammykam

Leave a Reply