IÂ did a bit of playing around recently with Sitecore’s SiteProvider
infrastructure. Â Our client wanted to be able to create new websites without needing a developer to set it up for them. Â The nuts and bolts of sharing templates and renderings is already well documented, so I’m not going to go into that. Â I am going to write a bit about how a custom SiteProvider
can be created to avoid the need for developers to add a new <site>
entry in the configuration. I did come across an older blog article from the integration solution team about writing a SiteProvider
, but it’s worth noting that this doesn’t take into account more recent developments and so might not function as expected in some newer installations.
SiteProvider
s are (fairly obviously) responsible for providing Sitecore with the list of Site
objects, representing the websites that it should be serving. Â This list of sites is accessed through the SiteManager
class, and this feeds into the SiteContextManager
, which keeps a lookup table for the purposes of site resolution and appears to be used much more generally throughout Sitecore.
Glossing over FXM for the moment, Sitecore ships with two SiteProvider
implementations: ConfigSiteProvider
and SitecoreSiteProvider
. Â The former is the one we know and love, and is what reads the sites from the <sites>
section of the Sitecore configuration (you may have guessed this from the name).  The latter is much newer (maybe Sitecore 8 or late Sitecore 7?) and acts as an umbrella for any providers that it is told to reference (allowing the application to have multiple SiteProviders
active at any one time). Â The SitecoreSiteProvider
is not especially complex: for the most part it simply delegates calls to all the registered providers and aggregates the result.  This works fine, but can cause some issues that I mention in the list below if you aren’t careful.
Having previously mentioned FXM, it is important to know that it registers its own SiteProvider
with the SitecoreSiteProvider
, and this is necessary to support its use.  If those sites are not present, then it will not function correctly.  For that reason, I would consider it inadvisable to select any provider other than the SitecoreSiteProvider
as the default one. Â Conceivably, it would be possible to support it, but I don’t see any benefit to doing so when the default provider handles it already.
I decided to do a proof-of-concept based on what I’d found out so far. I knew that a custom list of sites could be provided by implementing a custom SiteProvider
, and that this could be to the SitecoreSiteProvider
to ensure it was called alongside the existing providers. Â My proof-of-concept implementation simply looked for items using a custom SiteRoot
template under a particular parent item, and created a blank Site
object with that item’s name and populated the properties from the default “website” site, and then to refresh that collection after a publish:end
event. Â I then created a quick test page to list all the registered sites and their names so that I could check on my progress.
As you can likely see from the <site>
nodes, there are a fair few properties that Sitecore supports in terms of site configuration (plus any custom ones you wish to add).  Even with the best content team in the world, giving editors access to all of these is probably not a great idea.  I went down the route of allowing editors to supply the host name, target host name, and a few other bits and bobs.  Everything else I decided to inherit from a related site in the configuration.
Implementing this lead to a few interesting discoveries:
- The name of the site is pulled from a property called
name
, not thename
parameter that you give theSite
constructor, so make sure you set it explicitly. - If you get the
Properties
collection from aSite
object, you are given a direct reference to that object’s property collection. Â If you pass that collection in to anotherSite
constructor, then the direct reference is saved to that object too. So, changing one changes the other – don’t do it. - Don’t try to get a
Site
while you’re in the constructor of yourSiteProvider
, you’ll cause aStackOverflowException
. - Sitecore Rocks does not seem to pick up sites that come from a custom
SiteProvider
.
After a bit of trial and error, I got the list of sites showing what I was expecting. Â I then tried creating a new site item and publishing it. Â My breakpoints on the refresh logic in my SiteProvider
were hit, and I could see my new site in the internal site collection. Â I refreshed my test page, but was surprised to see that it wasn’t there. Â This is what led me back to the SiteContextManager
: as it holds its own list of sites, I needed to somehow tell it that this list had changed and make it re-fetch the new site list. Â So I called SiteContextManager.Reset()
, which clears the table. Â On the next access, this table gets repopulated. Â After that, I could add (and remove) sites by publishing them.
A second part of my proof-of-concept was to determine whether I could also leverage the CanEnter
 method on the SiteProvider
to provide custom logic controlling whether a user would be able to enter the various sites that I was providing. Â The short answer is: no. Â The SiteProvider
class provides a default (and virtual) implementation of CanEnter
, which checks the site:enter
permission on the site, but the SitecoreSiteProvider
does not override this method to allow delegation to the registered child providers. Â It would be possible if I elected not to use the SitecoreSiteProvider
at all, and registered my one as the only one Sitecore should use, but in that case I would need to surface all the default sites as well as any added by the FXM, which as I previously mentioned is not an attractive option. I could also override the SitecoreSiteProvider
, but that option was not terribly attractive either.
As this was all based on my findings from a proof-of-concept, where you are aiming to (as the name implies) prove certain ideas rather than write production code, parts of the code that do not need proving are often simplified – sometimes drastically so. Â Certainly, if you follow the above then your solution will work, but it might not perform as well as it could and in some cases you may experience a few problems. Â Here are a few extra things that you should probably consider:
- I subscribed to the
publish:end
events, because it’s quick and easy. Â However, only a small fraction of publish events will be changes to the site manifest and so will result in a disproportionate number of re-builds of the site list and the site resolution table. Â In cases where the website experiences high traffic, this may also cause performance problems. Â You might want to look into a solution involving either filtering the events based on the items published, or subscribing to custom events. - I didn’t make any allowance for the
HtmlCacheClearer
. Â As the default implementation requires a list of site names in the configuration, this undermines the “no development” requirement. Â To get around this, you’ll need to provide your own implementation of the clearer, but this is really quite trivial. - Sitecore site resolution will resolve sites based on primacy: the first matching site is the one selected, regardless of whether there is a more specific match later. Â If your sites overlap with the ones in your configuration at all, then you may experience some unusual behaviour that is difficult to track down.
- I also completely ignored the fact that, for sites of any reasonable size, authors will want to be able to preview the sites before actually publishing them; so they’ll likely need to be available on some other URL before they actually become available to the public. Â You may want to add something to allow editors to switch a site from ‘preview’ to ‘active’ or some such.
I know that I haven’t provided much (any) code in this post, but I was reluctant to do this in case someone used it and then had issues because of it.  In my opinion, the information here should be enough to get you over the major hurdles that left me scratching my head for a while.  The meat of your provider will be down to your specific requirements around how the sites should work.  Hopefully people who stumble across this will find it helpful.