Adding an index xml-file with GETA SEO Sitemaps for Episerver

Sometimes you need a sitemap index file for easing access to multiple sitemaps. Here is a way of creating one using a custom generator for GETA SEO Sitemaps, described in Multiple custom sitemap.xml generators with GETA SEO Sitemaps for Episerver.

The UseGeneratorFor method will expect the index file to be named sitemap.xml, so be sure to create one of those in your Episerver admin mode tool.

Cluttering code has been omitted.

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

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

  public override bool Generate(SitemapData sitemapData, bool persistData, out int entryCount)
  {
    base.UrlSet.Clear();
    return base.Generate(sitemapData, persistData, out entryCount);
  }

The root of the index file will be sitemapindex, so we need to override the GenerateRootElement method to make it so.

protected override XElement GenerateRootElement()
{
  var rootElement = new XElement(SitemapXmlNamespace + "sitemapindex");
  if (SitemapData.IncludeAlternateLanguagePages)
  {
    rootElement.Add(new XAttribute(XNamespace.Xmlns + "xhtml", SitemapXhtmlNamespace));
  }
  return rootElement;
}

Also the GetSitemapXmlElements will need to be overridden in order to create our index.

First of all we use GETA’s sitemap repository in order to get all the available sitemaps. Since the index file itself should not be included in the sitemap index, this is the first thing we make sure to exclude.

After that, we exclude all sitemaps that are not located below the sitemap index file’s root in the Episerver page tree, as we do not want to include those of different structures or even other websites.

protected override IEnumerable<XElement> GetSitemapXmlElements()
{
  IList<XElement> xmlElements = new List<XElement>();
  CreateSitemapRoot(xmlElements);
  return xmlElements;
}

internal void CreateSitemapRoot(IList<XElement> xmlElements)
{
  foreach (SitemapData sitemapData in _sitemapRepository.GetAllSitemapData())
  {
    if (string.Compare(sitemapData.Host, "sitemap.xml", StringComparison.InvariantCultureIgnoreCase) == 0)
    {
      continue;
    }
                
    if (sitemapData.RootPageId != SitemapData.RootPageId && !AncestorIdsFor(sitemapData.RootPageId) .Contains(SitemapData.RootPageId))
    {
      // Current and looped sitemaps do not share root id, and looped one is not below the current one.
      // i.e. it belongs to another part of the site, or another website.
      continue;
    }

    string url = _sitemapRepository.GetSitemapUrl(sitemapData);

    url = GetAbsoluteUrl(url);
    Uri fullContentUrl = new Uri(url);

    if (UrlSet.Contains(fullContentUrl.ToString()) || UrlFilter.IsUrlFiltered(fullContentUrl.AbsolutePath, sitemapData))
    {
      continue;
    }

    XElement contentElement = GenerateSitemapIndexElement(fullContentUrl.ToString());

    if (contentElement == null)
    {
      continue;
    }

    xmlElements.Add(contentElement);
    UrlSet.Add(fullContentUrl.ToString());
  }
}

After excluding unwanted sitemaps, we proceed with retrieving the sitemap URLs, generating their xml element objects and adding them to the result collection as seen above.

Creation of the sitemap index elements may be done as follows.

internal XElement GenerateSitemapIndexElement(string url)
{
  return string.IsNullOrWhiteSpace(url)
    ? null
    : new XElement(
      SitemapXmlNamespace + "sitemap",
      new XElement(SitemapXmlNamespace + "loc", url),
      new XElement(SitemapXmlNamespace + "lastmod", DateTime.UtcNow.ToString(DateTimeFormat, CultureInfo.InvariantCulture))
  );
}

private int[] AncestorIdsFor(int pageId)
{
  return _contentRepository
    .GetAncestors(new PageReference(pageId))
    .Select(c => c.ContentLink.ToReferenceWithoutVersion() .ID).ToArray();
}

Here is also the AncestorIdsFor helper method.