Dynamische Sitemaps

Eine XML-Sitemap ist eine klassische Datei, welche nahezu jede Seite besitzen soll. Da wir unter der Haube ein modernes CMS haben, wollen wir hierbei natürlich auch eine automatisch generierte Sitemap-XML haben. Unsere Sitemap soll jedoch nicht blind, alle Content-Items in ihren Index packen, sondern auch je nach Fall entscheiden können, diese draussen zu lassen.

Damit dies in Sitecore möglich ist, erstellen wir hierfür einen gewöhnlichen ASPX-Handler, welcher sich gegen aussen als www.mywebsite.ch/sitemap.xml outet.

  1. Wir erzeugen also in unserer Solution einen Handler (Add new item…) und geben ihm den Namen sitemapxml_handler.ashx
  2. Wir erstellen eine für uns optimale XML-Sitemap. Hier ein mögliches Beispiel:
    // --------------------------------------------------------------------------------------------------------------------
    // <copyright file="SitemapXmlHandler.cs" company="Namics AG">
    //   (c) Namics AG
    // </copyright>
    // <summary>
    //   The sitemap xml handler.
    // </summary>
    // --------------------------------------------------------------------------------------------------------------------

    namespace Namics.Samples.WebApp.Services
    {
        using System;
        using System.Web;
        using System.Xml;
        using Namics.Framework.Sitecore.Core.Utils;

        using Sitecore.Configuration;
        using Sitecore.Data;
        using Sitecore.Data.Items;
        using Sitecore.Diagnostics;
        using Sitecore.Links;

        /// <summary>
        /// The sitemap xml handler.
        /// </summary>
        public class SitemapXmlHandler : IHttpHandler
        {
            #region Public Properties

            /// <summary>
            /// Gets a value indicating whether IsReusable.
            /// </summary>
            public bool IsReusable
            {
                get
                {
                    return true;
                }
            }

            #endregion

            #region Public Methods and Operators

            /// <summary>
            /// The process request.
            /// </summary>
            /// <param name="pContext">
            /// The context.
            /// </param>
            public void ProcessRequest(HttpContext pContext)
            {
                try
                {
                    pContext.Response.ContentType = "text/xml";
                    pContext.Response.StatusCode = 200;
                   
                    XmlDocument xmlDoc = new XmlDocument();
                    XmlDeclaration xmlDeclaration = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);

                    XmlElement rootNode = xmlDoc.CreateElement("urlset");
                    rootNode.SetAttribute("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9");
                    xmlDoc.InsertBefore(xmlDeclaration, xmlDoc.DocumentElement);
                    xmlDoc.AppendChild(rootNode);

                    string databaseName = Settings.GetSetting("Sitemap.Database");
                    string startItemPath = Settings.GetSetting("Sitemap.StartItemPath");
                    Database database = Factory.GetDatabase(databaseName);
                    UrlOptions urlOptions = UrlOptions.DefaultOptions;
                    urlOptions.AlwaysIncludeServerUrl = true;

                    Item item = database.GetItem(startItemPath);
                    AddUrlEntry(item, xmlDoc, rootNode, urlOptions);
                   
                    pContext.Response.Write(xmlDoc.OuterXml);
                }
                catch (Exception ex)
                {
                    Log.Error("Error at sitemap xml handler.", ex, this);
                    pContext.Response.StatusCode = 500;
                }
            }

            #endregion

            #region Methods

            /// <summary>
            /// The add url entry.
            /// </summary>
            /// <param name="pItem">
            /// The item.
            /// </param>
            /// <param name="pXmlDoc">
            /// The xml doc.
            /// </param>
            /// <param name="pRootNode">
            /// The root node.
            /// </param>
            /// <param name="pUrlOptions">
            /// The url options.
            /// </param>
            private void AddUrlEntry(
                Item pItem, XmlDocument pXmlDoc, XmlElement pRootNode, UrlOptions pUrlOptions)
            {
                if (pItem != null && pItem.Visualization != null && pItem.Visualization.Layout != null && pItem.Name != "*")
                {
                    if (!pItem.Fields.Contains(FieldNames.GetFieldId(FieldNames.EXCLUDEFROMSITEMAP))
                        || pItem.Fields[FieldNames.EXCLUDEFROMSITEMAP].Value != "1")
                    {
                        string url = LinkManager.GetItemUrl(pItem, pUrlOptions);
                        GenerateUrlEntry(pXmlDoc, pRootNode, url);
                    }
                }

                if (pItem != null)
                {
                    foreach (Item childItem in pItem.Children)
                    {
                        AddUrlEntry(childItem, pXmlDoc, pRootNode, pUrlOptions);
                    }
                }
            }

            /// <summary>
            /// The generate url entry.
            /// </summary>
            /// <param name="pXmlDoc">
            /// The xml doc.
            /// </param>
            /// <param name="pRootNode">
            /// The root node.
            /// </param>
            /// <param name="pUrl">
            /// The url.
            /// </param>
            private void GenerateUrlEntry(XmlDocument pXmlDoc, XmlElement pRootNode, string pUrl)
            {
                XmlElement sitemapNode = pXmlDoc.CreateElement("url");
                pRootNode.AppendChild(sitemapNode);

                XmlElement locationNode = pXmlDoc.CreateElement("loc");
                locationNode.AppendChild(pXmlDoc.CreateTextNode(pUrl));
                sitemapNode.AppendChild(locationNode);

                XmlElement changeFrequencyNode = pXmlDoc.CreateElement("changefreq");
                changeFrequencyNode.AppendChild(pXmlDoc.CreateTextNode("weekly"));
                sitemapNode.AppendChild(changeFrequencyNode);
            }

            #endregion
        }
    }
  3. Wir registrieren unseren Handler in der Web.Config unter der Rubrik Handlers:
    <add verb="*" path="sitemapxml_handler.ashx" type="Namics.Samples.WebApp.Services.SitemapXmlHandler, Namics.Samples.WebApp" name="Namics.SitemapXmlHandler" />
  4. Wir registrieren unseren Handler in Sitecore (ebenfalls Web.Config) unter der Rubrik „CustomHandlers“ und geben ihm unseren gewünschten Namen:
    <handler trigger="/sitemap.xml" handler="sitemapxml_handler.ashx" />
  5. Wie im C-Sharp Code ersichtlich, prüfen wir noch, ob ein Item aus der Sitemap excluded werden soll. Hierfür erstellen wir auf dem entsprechenden Page-Template ein Checkbox-Field, welches „Excluded from Sitemap“ lautet und demzufolge für die Steuerung zuständig ist.

Als Resultat haben wir eine vollfunktionsfähige sitemap.xml auf unserer WebPage.