There is no out of the box way to make custom link manager configurations scope to a particular site, however you could always include that logic in the custom link manager itself. You can make your link manager resolve the site that a link is for. Then, if the resolved site is Site A then use the custom link manager logic; otherwise, if the resolved site is Site B then use the native link manager logic; and so on. This is not an original idea, but rather one that I took from JammyKam's SwitchingLinkProvider implementation (credit to @MarkCassidy for finding the link before I did).
SwitchingLinkProvider
Core Concepts
The following are the core concepts on which the SwitchingLinkProvider
implementation is based:
- Sitecore supports a
LinkProviderCollection
allowing more than one LinkProvider
to be specified
- One
LinkProvider
must be the default
LinkProvider
can resolve the context site
- Custom config attributes can be added to
<site>
definitions
Implementation at a High-Level
Implementing the SwitchingLinkProvider
, the first thing that you need to do is create a default LinkProvider
that does nothing except choose which site-specific LinkProvider
's logic will be used. This LinkProvider
is the switcher. This provider should be patched into the list of providers and set as the defaultProvider
.
The next thing that you need to do is create and patch in your site-specific LinkProvider
types into the list, (conventionally) giving them a name that will help you recognize which site(s) they belong to.
Lastly, you will need to add custom linkProvider
attributes to each of your <site>
definitions for which you wish to use a custom, site-specific LinkProvider
. The linkProvider
attribute value will be set to the name of the LinkProvider
configuration that you wish to use. In your SwitchingLinkProvider
class, you will resolve the context site, retrieve the configuration from its <site>
definition, and then read the value of the linkProvider
attribute to decide which LinkProvider
configuration you will use.
Code and Patches
The SwitchingLinkProvider
The following is the class for the SwitchingLinkProvider
, as copied from JammyKam's blog:
public class SwitchingLinkProvider : Sitecore.Links.LinkProvider
{
private const String FALLBACK_ATTRIBUTE_KEY = "fallback";
private const String LINKPROVIDER_ATTRIBUTE_KEY = "linkProvider";
private string Fallback { get; set; }
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
this.Fallback = config[FALLBACK_ATTRIBUTE_KEY];
Assert.IsNotNullOrEmpty(this.Fallback, "fallback");
}
private Sitecore.Links.LinkProvider SiteLinkProvider
{
get
{
var siteLinkProvider = (Sitecore.Context.Site != null)
? Sitecore.Context.Site.Properties[LINKPROVIDER_ATTRIBUTE_KEY] : String.Empty;
if (String.IsNullOrEmpty(siteLinkProvider))
siteLinkProvider = this.Fallback;
return LinkManager.Providers[siteLinkProvider]
?? LinkManager.Providers[this.Fallback];
}
}
...
}
Note that JammyKam includes support for LinkProvider
fallback, which I highly recommend that you use, but will not go into in this post as it is extra, optional functionality.
Note also that the site-resolving logic that you are looking for occurs in the SiteLinkProvider
property's getter method. This is where the context site is resolved and the LinkProvider
to be used is resolved.
The following is the patch-config for the SwitchingLinkProvider
, which JammyKam aptly named "switcher":
<linkManager>
<patch:attribute name="defaultProvider" value="switcher" />
<providers>
<add name="switcher" fallback="sitecore" type="Sitecore.Custom.Links.SwitchingLinkProvider, Sitecore.Custom" />
</providers>
</linkManager>
Note that the defaultProvider
is set to switcher
so that the SwitchingLinkProvider
will be used first, giving it the ability to choose which LinkProvider
's logic should actually be used.
Site-Specific Providers
The following is the patch configuration for two sample site-specific providers that JammyKam included in his post. He uses the naming convention "provider-[sitename]" which is totally valid and descriptive, but you can change it, if you like. Remember that the name is not really functional, but more for description and reference purposes:
<linkManager>
<providers>
<add name="provider-site1" type="Sitecore.Links.LinkProvider, Sitecore.Kernel" addAspxExtension="false" languageEmbedding="never" useDisplayName="true" ... />
<add name="provider-site2" type="Sitecore.Custom.Links.SpecificLinkProvider, Sitecore.Custom" alwaysIncludeServerUrl="true" languageEmbedding="always" lowercaseUrls="true" ... />
</providers>
</linkManager>
Lastly, the following is the patch-config for the custom linkProvider
attributes added to the <site>
definitions, in order to tie each site-specific LinkProvider
to its respective site(s):
<sites>
<site name="site1">
<patch:attribute name="linkProvider" value="provider-site1" />
</site>
<site name="site2">
<patch:attribute name="linkProvider" value="provider-site2" />
</site>
<site name="site3">
...
</site>
...
</sites>
Adapting to Your Use-Case
In order to use this solution, you will need to implement the SwitchingLinkProvider
, add in the necessary patch configuration for it, and patch the <site>
definition for the site that you wish to use the "siteSpecificLinkManager" to include a linkProvider="siteSpecificLinkManager"
attribute.
The patches should end up looking something like the following:
<linkManager>
<patch:attribute name="defaultProvider" value="switcher" />
<providers>
<add name="switcher" fallback="sitecore" type="Sitecore.Custom.Links.SwitchingLinkProvider, Sitecore.Custom" />
<add name="siteSpecificLinkManager" fallback="sitecore" type="..." />
</providers>
</linkManager>
<sites>
<site name="mySiteWithCustomLinkProvider">
<patch:attribute name="linkProvider" value="siteSpecificLinkManager" />
</site>
</sites>
Important Note for Those Using Sitecore 8.x...
In the comments, @jwsadler points out that the LinkManager.Providers
property has been marked obsolete in 8.x, which means that this solution will result in compiler warnings. The property has been marked with the ObsoleteAttribute
and given the comment "Use methods from
LinkManagerinstead."
The property looks as follows:
[Obsolete("Use methods from LinkManager instead.")]
public static LinkProviderCollection Providers
{
get
{
return LinkManager.ProviderHelper.Value.Providers;
}
}
Unfortunately, Sitecore has not provided any methods for getting the providers on the LinkManager
, so the technique above is really the only way of doing. However, if your goal is to simply get rid of the compiler warnings (for code analysis, etc.) then what you can do is instantiate your own ProviderHelper
and get the values from there, using the same logic as the LinkManager.Providers
property. Note that the logic would be exactly the same as the obsolete logic, but the warnings would not show up.
Please note that I do NOT recommend the below implementation, as it circumvents good compiler warnings for a property that may be removed and replaced in an upcoming release. Instead, I recommend the same implementation as was described earlier in this post, using the LinkManager.Providers
property. However, if you are still set on eliminating the compiler warnings then you can change your SwitchingLinkProvider
to be as follows:
public class SwitchingLinkProvider : Sitecore.Links.LinkProvider
{
private const String FALLBACK_ATTRIBUTE_KEY = "fallback";
private const String LINKPROVIDER_ATTRIBUTE_KEY = "linkProvider";
private string Fallback { get; set; }
private static readonly LazyResetable<Sitecore.Configuration.ProviderHelper<LinkProvider, LinkProviderCollection>> ProviderHelper = ServiceLocator.GetRequiredResetableService<Sitecore.Configuration.ProviderHelper<LinkProvider, LinkProviderCollection>>();
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
this.Fallback = config[FALLBACK_ATTRIBUTE_KEY];
Assert.IsNotNullOrEmpty(this.Fallback, "fallback");
}
private Sitecore.Links.LinkProvider SiteLinkProvider
{
get
{
var siteLinkProvider = (Sitecore.Context.Site != null)
? Sitecore.Context.Site.Properties[LINKPROVIDER_ATTRIBUTE_KEY] : String.Empty;
if (String.IsNullOrEmpty(siteLinkProvider))
siteLinkProvider = this.Fallback;
var providers = ProviderHelper.Value.Providers;
return providers[siteLinkProvider]
?? providers[this.Fallback];
}
}
...
}
To summarize, if you're using Sitecore 8.x, my recommendation would be to use the same technique as described for earlier versions, including the use of the LinkManager.Providers
property, and make an exception for the resulting compiler warnings in your CI/CD processes, code analysis, etc. If and when Sitecore adds a new technique for getting the providers, this answer will be updated. Until then, the LinkManager.Providers
property still works and is the way to go.