Dynamic sitemap.xml generator

14 May 2016

Sitefinity provides a very good platform for Content Management System and also exposes many APIs through which developers could access the data, customize and render it according to their requirement.

If you have a smaller website(pages less than 1000), then you can use the following code to generate the sitemap XML and cache it for particular duration.

This code utilizes Sitefinity Page API (I know SitemapBase is cached and better with performance), but this page api gives you better control the pages from the multiple languages with the permission checking included. This code also takes care of situation where sites are hosted using Sitefinity MSM feature and generates the pages based on current MSM site.

protected void Application_BeginRequest(object sender, EventArgs e)
{
string strUrl = Request.Path.ToLowerInvariant();
//Code block to redirect the generic request to the correct sitemap index and sitemap XML
if (new Regex(@"^/sitemap\.xml", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant).IsMatch(Request.Path))
HttpContext.Current.RewritePath("/sitemap.ashx");
}
view raw Global.asax hosted with ❤ by GitHub
<%@ WebHandler Language="C#" Class="Sitemap" %>
using System;
using System.Text;
using System.Web;
using System.Xml;
using System.Linq;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Modules.Pages;
using Telerik.Sitefinity.Multisite;
using Telerik.Sitefinity.Services;
using System.Globalization;
using System.Collections.Generic;
using System.IO;
using System.Web.Caching;
public class AltInfo
{
public string LangName { get; set; }
public string Link { get; set; }
}
public class Sitemap : IHttpHandler
{
// global properties
protected string host;
private const string sitemapKey = "customsitemap";
/// <summary>
/// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler" /> interface.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpContext" /> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
public void ProcessRequest(HttpContext context)
{
try
{
// prepare response type
var response = context.Response;
response.ContentType = "text/xml";
var multisiteContext = SystemManager.CurrentContext as MultisiteContext;
string currrentSitemapKey = sitemapKey + multisiteContext.CurrentSite.Id;
string sSitemap = string.Empty;
if (HttpRuntime.Cache[currrentSitemapKey] == null)
{
lock (currrentSitemapKey)
{
if (HttpRuntime.Cache[currrentSitemapKey] == null)
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (var writer = new XmlTextWriter(memoryStream, Encoding.UTF8){ Formatting = Formatting.Indented })// begin xml response
{
writer.WriteStartDocument();
writer.WriteStartElement("urlset");
writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteAttributeString("xsi:schemaLocation", "http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd");
writer.WriteAttributeString("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9");
writer.WriteAttributeString("xmlns:xhtml", "http://www.w3.org/1999/xhtml");
var vars = HttpContext.Current.Request.ServerVariables;
string port;
// parse content
using (var api = App.WorkWith())
{
string defaultCultureName = multisiteContext.CurrentSite.DefaultCulture;
CultureInfo defCulture = new CultureInfo(defaultCultureName);
var allCultures = multisiteContext.CurrentSite.PublicContentCultures.Where(c => c.Name != defaultCultureName).ToList();
allCultures.Insert(0, new CultureInfo(defaultCultureName));
var rId = multisiteContext.CurrentSite.SiteMapRootNodeId;
// append pages
var pages = api.Pages().ThatArePublished().Where(p => p.ShowInNavigation == true && p.Crawlable && !p.IsBackend && p.NodeType == Telerik.Sitefinity.Pages.Model.NodeType.Standard && p.RootNodeId == rId).Get();
foreach (var page in pages)
{
string defaultUrl = null;
DateTime lastMod = DateTime.Now;
List<AltInfo> altInfo = new List<AltInfo>();
{
var pData = page.GetPageData(defCulture);
if (pData != null)
{
// build host
var protocol = pData.NavigationNode.RequireSsl ? "https://" : "http://";
// append port
port = pData.NavigationNode.RequireSsl ? "443" : vars["SERVER_PORT"];
if (port == "80" || port == "443")
port = string.Empty;
else
port = string.Concat(":", port);
defaultUrl = string.Concat(protocol, vars["SERVER_NAME"], port, VirtualPathUtility.ToAbsolute(page.GetFullUrl()));
lastMod = pData.LastModified;
}
}
foreach (var culture in allCultures)
{
if (page.AvailableCultures.Contains(culture))
{
var pData = page.GetPageData(culture);
if (pData != null)
{
// build host
var protocol = pData.NavigationNode.RequireSsl ? "https://" : "http://";
// append port
port = pData.NavigationNode.RequireSsl ? "443" : vars["SERVER_PORT"];
if (port == "80" || port == "443")
port = string.Empty;
else
port = string.Concat(":", port);
var url = string.Concat(protocol, vars["SERVER_NAME"], port, VirtualPathUtility.ToAbsolute(page.GetFullUrl(culture, false)));
altInfo.Add(new AltInfo() { LangName = culture.Name, Link = url });
}
}
}
// append page to sitemap
this.AppendUrl(writer, defaultUrl, lastMod, altInfo);
}
}
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
}
sSitemap = Encoding.UTF8.GetString(memoryStream.ToArray());
HttpRuntime.Cache.Add(currrentSitemapKey, sSitemap, null, DateTime.Now.AddHours(12), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}
}
else
sSitemap = HttpRuntime.Cache[currrentSitemapKey] + "";
}
}
else
sSitemap = HttpRuntime.Cache[currrentSitemapKey] + "";
response.Write(sSitemap);
response.Flush();
}
catch (Exception ex)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(ex);
}
}
private void AppendUrl(XmlTextWriter writer, string fullUrl, DateTime lastModified, List<AltInfo> altUrls)
{
// calculate change frequency
string changeFreq = "monthly";
var changeInterval = (DateTime.Now - lastModified).Days;
if (changeInterval <= 1)
changeFreq = "daily";
else if (changeInterval <= 7 & changeInterval > 1)
changeFreq = "daily";
else if (changeInterval <= 30 & changeInterval > 7)
changeFreq = "weekly";
else if (changeInterval <= 30 & changeInterval > 365)
changeFreq = "weekly";
// append to sitemap
writer.WriteStartElement("url");
writer.WriteElementString("loc", fullUrl);
writer.WriteElementString("lastmod", lastModified.ToString("yyyy-MM-ddThh:mm:sszzzz"));
writer.WriteElementString("changefreq", changeFreq);
writer.WriteElementString("priority", "1");
foreach (var xhtml in altUrls)
{
writer.WriteStartElement("xhtml:link");
writer.WriteAttributeString("rel", "alternate");
writer.WriteAttributeString("hreflang", xhtml.LangName);
writer.WriteAttributeString("ref", xhtml.Link);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
public bool IsReusable
{
get { return true; }
}
}
view raw services.ashx hosted with ❤ by GitHub