Skip to content

Instantly share code, notes, and snippets.

@davidsekar
Created April 22, 2016 18:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidsekar/c82e8be66b371929ba1b094a8f1cf7bd to your computer and use it in GitHub Desktop.
Save davidsekar/c82e8be66b371929ba1b094a8f1cf7bd to your computer and use it in GitHub Desktop.
Sitefinity – Create dynamic sitemap XML
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");
}
<%@ 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; }
}
}
@nkante
Copy link

nkante commented Jun 3, 2016

Hi,
Thanks for this, I have 2 additions to code:

  1. Remove as MultisiteContext in var multisiteContext = SystemManager.CurrentContext as MultisiteContext;
    Method can be then used even in singlesite configuration.
  2. Convert last modified time to local time before converting them to string. Exception is thrown if you use zzzz on UTC dates.
    writer.WriteElementString("lastmod", lastModified**.ToLocalTime()**.ToString("yyyy-MM-ddThh:mm:sszzzz"));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment