Multiple custom sitemap.xml generators with GETA SEO Sitemaps for Episerver

At my current client’s, we’re building a common platform code base supporting 5 Episerver multisite installations. These all have different needs when it comes to xml sitemaps.

Here is an easy way of adding custom sitemap generation and have the GETA SEO Sitemap functionality use it.

Overriding the generator factory

The first thing we need to do is come up with a way of recognizing our custom generators. This may be done with an interface like the one below:

public interface IMyCustomXmlGenerator : ISitemapXmlGenerator
{
  bool UseGeneratorFor(SitemapData sitemapData);
}

The UseGeneratorFor method is used to determine if the custom generator should be used for the supplied sitemapData or not.

Then we override GETA’s SitemapXmlGeneratorFactory with one of our own and use it as our point of injection. Here we have StructureMap inject all implementations of our interface.

public class MySitemapXmlGeneratorFactory : SitemapXmlGeneratorFactory
{
  private readonly IMyCustomXmlGenerator[] _myGenerators;

  public MySitemapXmlGeneratorFactory(IMyCustomXmlGenerator[] myGenerators)
  {
    _myGenerators = myGenerators ?? new IMyCustomXmlGenerator[0];
  }

  public override ISitemapXmlGenerator GetSitemapXmlGenerator(SitemapData sitemapData)
  {
    foreach(IMyCustomXmlGenerator generator in _myGenerators)
    {
      if (generator.UseGeneratorFor(sitemapData))
      {
        return generator;
      }
    }
    return base.GetSitemapXmlGenerator(sitemapData);
  }
}

GETA uses the GetSitemapXmlGenerator method in order to retrieve the proper generator, so intercepting the call to this method will allow us to test if we would like to use our own generator instead (with the mentioned UseGeneratorFor method).

public class MyGetaSitemapRegistry : Registry
{
  public MyGetaSitemapRegistry()
  {
    For<SitemapXmlGeneratorFactory>().ClearAll().Use<MySitemapXmlGeneratorFactory>();

    For<IMyCustomXmlGenerator>().Add<MyCustomXmlGenerator>();
  }
}

Clear the StructureMap container from GETA’s own generator factory and have it use our own instead, also you may want to register your custom generators.

A custom sitemap xml generator

The implementation of our IMyCustomXmlGenerator interface will also need to extend GETA’s SitemapXmlGenerator class. I have omitted some code to reduce noise.

Our own method (UseGeneratorFor) will need to be implemented here. In this example it will return true if you have called a sitemap my-custom-sitemap.xml in the tool interface in Episerver’s admin mode.

public class MyCustomSitemapXmlGenerator : SitemapXmlGenerator, IMyCustomXmlGenerator
{
  // ...

  public MyCustomSitemapXmlGenerator(
    ISitemapRepository SitemapRepository,
    IContentRepository ContentRepository,
    UrlResolver UrlResolver,
    ISiteDefinitionRepository SiteDefinitionRepository,
    ILanguageBranchRepository LanguageBranchRepository,
    IContentFilter ContentFilter
    )
    : base(
      SitemapRepository,
      ContentRepository,
      UrlResolver,
      SiteDefinitionRepository,
      LanguageBranchRepository,
      ContentFilter)
  {
  }

  public bool UseGeneratorFor(SitemapData sitemapData)
  {
    return string.Compare(sitemapData.Host, "my-custom-sitemap.xml", StringComparison.InvariantCultureIgnoreCase) == 0;
  }

  public override bool Generate(SitemapData sitemapData, bool persistData, out int entryCount)
  {
    // GETA won't handle the 50k limit anyway, so no need to persist between different usages of the same generator.
    base.UrlSet.Clear();
    return base.Generate(sitemapData, persistData, out entryCount);
  }

  protected override IEnumerable<XElement> GetSitemapXmlElements()
  {
    IEnumerable<XElement> xmlElements = CreateElements();
    return xmlElements;
  }

There are a few methods that may be overridden here, in this example we use the GetSitemapXmlElements method to inject our own elements.