If you have been developing with Episerver for any extended amount of time, you are probably familiar with the SEO.Sitemaps plugin offered by Geta Digital. Since Episerver doesn’t offer the functionality we need out-of-the-box, this plugin allows you to create individual sitemaps for each site in your Episerver instance, mobile pages, as well as language-specific sitemaps. Sitemaps are literal maps of a website and essential to telling search engines how to crawl and interpret pages on a site. By having several specific sitemaps, sites can gain some strong SEO equity. This plugin offers a number of other features you can learn more about here.
We use the SEO.Sitemaps plugin on several different projects, and, throughout those projects, we have added some custom enhancements to fit the needs of our clients. Some of these enhancements include:
- Excluding pages from the sitemap when they have an override property set for a canonical URL. The canonical URL property is a custom property for pages we utilize on some of the sites we write.
- Excluding certain types of pages by default from the sitemap without the content editors having to set an exclusion property of any sort.
- Excluding pages that content editors have indicated should not be included in the sitemap when they edit the pages within Episerver.
- Under each site’s sitemap in a multi-site instance, and those sites are tied one-to-one with an Episerver Commerce catalog, including only that site’s catalog of items in the sitemap. By default, when sitemaps are generated, they include all the item URLs from all of the catalogs in Episerver Commerce.
- Excluding Episerver Commerce variants from the sitemap.
- Excluding pages that are shortcuts, external links, inactive, or that fetch data from other pages. Only pages with a LinkType of PageShortcutType.Normal should be included in the sitemap.
Before any of these enhancements can be made, we first have to override the plugin’s default behavior. The key class to override and replace is the SitemapXmlGeneratorFactory. This is because this class is what tells the plugin what code to use when generating the sitemap. We want to override this factory to tell it to use our custom code when generating the sitemap.
The SitemapXmlGeneratorFactory is registered by the plugin with StructureMap, Episerver’s dependency injection tool. With this in mind, we know that not only do we need to override the plugin’s default behavior, but we need to intercept this factory when it is referenced throughout the code. It is extremely easy to intercept classes/interfaces registered with StructureMap so that custom code is used instead. This typically needs to be done in an InitializationModule.
You can learn more about StructureMap and intercepting existing services here.
Here’s what an InitializationModule intercepting the SitemapXmlGeneratorFactory would look like:
<p> CODE: https://gist.github.com/thec2group-blog/9c454bbe05d519fde4a9b20f9b5745ca.js</p>
You can see how easy this is in the above example. It’s important to note that to intercept an existing service and return a custom service, it must inherit from the type you are intercepting. In this case, the class CustomSitemapFactory inherits from SitemapXmlGeneratorFactory.
After you’ve done this, you need to setup the CustomSitemapFactory class:
<p> CODE: https://gist.github.com/thec2group-blog/fbb4d665f144adba0be4761c59345183.js</p>
This is an extremely simple class, but it enables us to return the custom class that is needed to enhance the behavior of the plugin during sitemap generation. The only method that is in this class is the GetSitemapXmlGenerator(), and all we do here is return the custom class, ICustomSitemapXmlGenerator, during sitemap generation. ICustomSitemapXmlGenerator is registered in an InitializationModule through StructureMap, and that’s how we’re able to use the Injected property to retrieve it.
Now we’re able to write the service that will enhance the plugin’s behavior. There are a few interfaces used by the plugin for generating sitemaps. You could write services to change the behavior for all of them, or just one of them. In our case, we’re changing the behavior of ICommerceAndStandardSitemapXmlGenerator, and this is what ICustomSitemapXmlGenerator will inherit from. The other interfaces available for customization are IStandardSitemapXmlGenerator, ICommerceSitemapXmlGenerator, and IMobileSitemapXmlGenerator.
Here’s an example of our code for ICustomSitemapXmlGenerator:
<p> CODE: https://gist.github.com/thec2group-blog/3a632ad55d54ffc12b1a52d80e9ceed5.js</p>
There is a lot going on here. Because this class inherits from CommerceAndStandardSitemapXmlGenerator, all the code for the constructor is necessary, but that’s not what is important here. What’s important is the overridden method AddFilteredContentElement().
Every piece of content to be indexed goes through this method and then gets added to the collection of XElements called “xmlElements”. Before we let the base class go through its normal logic, we can do checks against the content to be processed and whether or not it should be included in the sitemap. If it should not be included in the sitemap, we do a quick exit using a return statement, and then the content is not added to the list of XElements.
Now, you’re able to see the capabilities of using a custom class for generating the sitemap. While AddFilteredContentElement() is the only method shown in the example, there are a number of other methods that a developer could override to further enhance the plugin. You can look at the base class SitemapXmlGenerator to see what else you can do here.