SitemapGenerator: Ecommerce products are not included in the sitemap

15 May 2016

Sitefinity sitemap generator is cool, but you get frustrated when it won't do what it was build for ... And you have to vote for the bug and wait for it to be fixed in the next release or next to that next, so on., You can see & vote the bug logged in their feedback tool.

Well., that is not the end of the story. Even though I blame them for such obvious reasons, I love their Sitefinity product. It is such extensible and provides/exposes various events for most built in modules through Event Hub. You can subscribe to this events and implement your requirements in a Jiff.

So for Sitemap Generator, I could hook to ISitemapGeneratorBeforeWriting and fix the bug on my own till the official fix is released for this version OR may be the next version(Forced to upgrade for a fix).

Following code in global.ascx, will help you add the missing dynamic pages to sitemap

protected void Application_Start(object sender, EventArgs e) {
    Telerik.Sitefinity.Abstractions.Bootstrapper.Initialized += new EventHandler < Telerik.Sitefinity.Data.ExecutedEventArgs > (this.Bootstrapper_Initialized);

private void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.ExecutedEventArgs e) {
    if (e.CommandName == "Bootstrapped") {
        EventHub.Subscribe < ISitemapGeneratorBeforeWriting > (Before_Writing);
private void Before_Writing(ISitemapGeneratorBeforeWriting evt) {
    try {
        // sets the collection of entries to modified collection
        evt.Entries = GenerateProductNodes(evt);
    } catch (Exception ex) {
        Elmah.ErrorLog.GetDefault(null).Log(new Elmah.Error(ex));

public static IEnumerable < SitemapEntry > GenerateProductNodes(ISitemapGeneratorBeforeWriting evt) {
    // gets the entries that are about to be written in the sitemap
    var entries = evt.Entries.ToList();

    var siteId = evt.SiteId;

    ISite currentSiteContext = new MultisiteContext().GetSiteById(siteId);
    string scheme = currentSiteContext.RequiresSsl ? "https" : "http";
    string sSiteUrl = string.Format("{0}://{1}", scheme, currentSiteContext.LiveUrl);

    var catalogProvider = currentSiteContext.GetDefaultProvider("Catalog");
    string catalogProviderName = (catalogProvider != null) ? catalogProvider.ProviderName : CatalogManager.GetDefaultProviderName();

    var catalogManager = CatalogManager.GetManager(catalogProviderName);
    var products = catalogManager.GetProducts()
        .Where(p => p.Status == ContentLifecycleStatus.Live).ToList();

    CultureInfo defaultCulture = new CultureInfo(currentSiteContext.DefaultCulture);

    var siteCultures = currentSiteContext.PublicContentCultures;

    var clService = SystemManager.GetContentLocationService();
    IContentItemLocation location;

    foreach(var product in products) {
        if (product.AvailableCultures.Length <= 0) continue;

        SitemapEntry productEntry = new SitemapEntry();
        bool bAvailableInDefault = product.AvailableCultures.Contains(defaultCulture);

        CultureInfo defaultEntryCulture = bAvailableInDefault ? defaultCulture : product.AvailableCultures[0];

        location = clService.GetItemDefaultLocation(product, defaultEntryCulture);
        productEntry.Location = string.Format("{0}{1}", sSiteUrl, new Uri(location.ItemAbsoluteUrl).PathAndQuery);
        productEntry.Priority = 1;
        productEntry.LastModified = product.LastModified;

        string type = product.GetType().Name;

        Dictionary < string, string > altLink = new Dictionary < string, string > ();

        foreach(var culture in product.AvailableCultures) {
            if (string.IsNullOrEmpty(culture.Name))

            //gets the item default location of a given item by itemId provided
            location = clService.GetItemDefaultLocation(typeof(Product), catalogProviderName, product.Id, culture);

            //gets the absolute url for the location
            if (location != null)
                altLink.Add(culture.Name, string.Format("{0}{1}", sSiteUrl, new Uri(location.ItemAbsoluteUrl).PathAndQuery));
        productEntry.Rels = altLink;

    return entries;