How to add a sitemap with Agility.AspNetCore

Follow

The following code of middleware iterates over your Agility instance's Page Tree recursively and determines if the tree node should be listed in the sitemap.xml based on its "Visible on Sitemap" attribute in Agility CMS.

public class SitemapMiddleware
{

	protected readonly RequestDelegate _next;

	public SitemapMiddleware(RequestDelegate next)
	{
		this._next = next;
	}

	public IHostingEnvironment HostingEnvironment { get; set; }

	private const string XMLNSAttribute = "http://www.sitemaps.org/schemas/sitemap/0.9";
	private const string XMLNSImageAttribute = "http://www.google.com/schemas/sitemap-image/1.1";

	public async Task Invoke(HttpContext context, IHostingEnvironment env)
	{
		HostingEnvironment = env;

		string response = await Task.Run<string>(() =>
		{
			return GenerateResponse(context);
		});

		context.Response.ContentType = "text/xml";

		await context.Response.WriteAsync(response);

	}

	private string GenerateResponse(HttpContext context)
	{
		HttpRequest Request = context.Request;
		HttpResponse Response = context.Response;



		using (StringWriter sw = new StringWriter())
		{
			using (XmlTextWriter writer = new XmlTextWriter(sw))
			{
				writer.Formatting = Formatting.Indented;

				writer.WriteRaw("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
				// write out <PromoItem>s
				writer.WriteStartElement("urlset");
				writer.WriteAttributeString("xmlns", XMLNSAttribute);
				writer.WriteAttributeString("xmlns:image", XMLNSImageAttribute);
				// write elements

				BuildSiteMap(context, writer);

				// write out </PromoItems>
				writer.WriteEndElement();

				writer.Close();

				return sw.ToString();
			}
		}
	}

	protected virtual void BuildSiteMap(HttpContext context, XmlTextWriter writer)
	{
		//Loops through All Languages
		string startingLanguage = Agility.Web.AgilityContext.LanguageCode;
		Agility.Web.Objects.Language[] langs = Agility.Web.AgilityContext.Domain.Languages;


		var channel = AgilityContext.CurrentChannel;

		foreach (Agility.Web.Objects.Language l in langs)
		{

			var channelDomain = channel.Domains.FirstOrDefault(d =>
				string.Equals(d.DefaultLanguage, l.LanguageCode, StringComparison.CurrentCultureIgnoreCase)
				&& d.ForceDefaultLanguageToThisDomain);

			Agility.Web.AgilityContext.LanguageCode = l.LanguageCode;

			var sitemap = AgilityContext.AgilitySiteMap;

			foreach (var oNode in sitemap.RootNode.ChildNodes)
			{

				WriteURLNode(context, writer, l.LanguageCode, oNode, channelDomain);
			}
		}

		Agility.Web.AgilityContext.LanguageCode = startingLanguage;
	}


	protected virtual void WriteURLNode(HttpContext context, XmlTextWriter writer, string langCode, AgilitySiteMapNode node, ChannelDomain channelDomain)
	{
		//skip stuff that doesn't belong on the sitemap...
		if (!node.SitemapVisible)
		{
			return;
		}

		string host = null;
		if (channelDomain == null)
		{
			//no default domain per language... 
			host = context.Request.GetDisplayUrl();
			host = host.Substring(0, host.IndexOf("/", host.IndexOf("://") + 3));
		}
		else
		{
			host = channelDomain.URL.TrimEnd('/');
		}

		string loc = node.Url.ToLowerInvariant();
		if (loc.EndsWith(".aspx"))
		{
			loc = loc.Substring(0, loc.LastIndexOf(".aspx"));
		}

		DateTime lastMod = DateTime.MinValue;
		string changeFreq = "daily";
		double priority = 0;

		Agility.Web.Objects.AgilitySiteMapNode agilityNode = node as Agility.Web.Objects.AgilitySiteMapNode;
		Agility.Web.Objects.AgilityDynamicSiteMapNode dynamicNode = node as Agility.Web.Objects.AgilityDynamicSiteMapNode;

		if (loc == "~/home" || loc == "~/default")
		{
			//home page
			loc = "/";
			changeFreq = "daily";

			//home page has highest priority
			priority = 1.0;

		}
		else if (dynamicNode != null)
		{
			//get the item that this page represents and use the last mod of it...
			AgilityContentRepository<AgilityContentItem> content = new AgilityContentRepository<AgilityContentItem>(dynamicNode.ReferenceName);
			AgilityContentItem item = content.Item(string.Format("ContentID = {0}", dynamicNode.ContentID));
			lastMod = item.ModifiedDate;
			priority = 0;
		}
		else if (agilityNode != null)
		{
			if (agilityNode.AgilityPage != null)
			{
				foreach (var section in agilityNode.AgilityPage.ContentSections)
				{
					AgilityContentRepository<AgilityContentItem> content = new AgilityContentRepository<AgilityContentItem>(section.ContentReferenceName);
					var item = content.Items().FirstOrDefault();

					if (item != null && item.ModifiedDate > lastMod) lastMod = item.ModifiedDate;

				}
			}
		}

		do
		{
			if (loc.Contains("javascript:"))
			{
				break;
			}

			//check if the location is absolute and part of a different domain...
			if (loc.StartsWith("http"))
			{
				Uri url = new Uri(loc);
				if (url.Host != host) break;
			}

			writer.WriteStartElement("url");
			if (!string.IsNullOrEmpty(loc))
			{
				writer.WriteStartElement("loc");

				if (loc.StartsWith("http"))
				{
					writer.WriteString(loc);
				}
				else
				{
					writer.WriteString(string.Format("{0}{1}", host, ResolveUrl(loc).ToLowerInvariant()));
				}
				writer.WriteEndElement();
			}

			if (lastMod > DateTime.MinValue)
			{
				writer.WriteStartElement("lastmod");
				writer.WriteString(lastMod.ToUniversalTime().ToString("u").Replace(" ", "T"));
				writer.WriteEndElement();


				if (loc == "/")
				{

				}
				else
				{
					//subtract a tenth for each 

					int weeks = (int)Math.Floor((DateTime.Now - lastMod).TotalDays / 7);

					priority = 1.0 - (weeks / .1);
					if (priority < 0) priority = 0;
				}
			}

			if (priority > 0)
			{
				writer.WriteStartElement("priority");
				writer.WriteString(priority.ToString("F1"));
				writer.WriteEndElement();
			}

			if (!string.IsNullOrEmpty(changeFreq))
			{
				writer.WriteStartElement("changefreq");
				writer.WriteString(changeFreq);
				writer.WriteEndElement();
			}


			writer.WriteEndElement();
		} while (false);

		var children = AgilityContext.AgilitySiteMap.GetChildNodes(node);


		//loop all the child elements..
		foreach (var child in children)
		{
			WriteURLNode(context, writer, langCode, child, channelDomain);
		}
	}

	protected string ResolveUrl(string relativeUrl)
	{
		//replace the ~/
		if (relativeUrl.StartsWith("~/"))
		{
			string appPath = "/";
			if (appPath.EndsWith("/")) return string.Format("{0}{1}", appPath, relativeUrl.Substring(2));
			return string.Format("{0}{1}", appPath, relativeUrl.Substring(1));

		}

		return relativeUrl;
	}
}

public static class SitemapExtensions
{
	public static IApplicationBuilder UseSitemapHandler(this IApplicationBuilder builder)
	{
		return builder.UseMiddleware<SitemapMiddleware>();
	}
}

You can then register this middleware to kick in when the request path ends with "sitemap.xml" within your Startup.cs Configure method with following code.

app.MapWhen(context => context.Request.Path.Value.EndsWith("sitemap.xml", true, null),
		appBranch => { appBranch.UseSitemapHandler(); });
0 out of 0 found this helpful

Comments

0 comments

Please sign in to leave a comment.