Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
XML sitemap for Umbraco 7 (based on Cultiv Search Engine Sitemap package). See http://www.alexlindgren.com/archive/dynamically-generated-xml-sitemaps-with-umbraco-7/
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@using System.Linq;
@{
Layout = null;
Response.ContentType = "text/xml";
}<?xml version='1.0' encoding='UTF-8' ?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
@ListChildNodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1))
</urlset>
@helper ListChildNodes(IPublishedContent startNode)
{
foreach (var node in startNode.Children.Where("hideInXmlSitemap == false"))
{
if (node.TemplateId > 0)
{
<url>
<loc>@GetUrlWithDomainPrefix(node.Url)</loc>
<lastmod>@(string.Format("{0:s}+00:00", node.UpdateDate))</lastmod>
</url>
}
if (node.Level <= 100 && node.Children.Count() > 0)
{
@ListChildNodes(node)
}
}
}
@functions {
private static string GetUrlWithDomainPrefix(string url)
{
if (url.StartsWith("/"))
url = url.Substring(1);
var domainPrefix = string.Format("http://{0}/", HttpContext.Current.Request.ServerVariables["HTTP_HOST"]);
if (url.StartsWith(domainPrefix))
return url;
else
return domainPrefix + url;
}
}
@garpur

This comment has been minimized.

Show comment Hide comment
@garpur

garpur May 6, 2015

I have a site that has more than one "root"
so I put the sitemap in the root and modified the template with the following:

@foreach (var node in Umbraco.ContentAtRoot()) {

@geturlwithdomainprefix(node.Url)
@(string.Format("{0:s}+00:00", node.UpdateDate))

@ListChildNodes(node);

}

instead of @listchildnodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1))

thx, for the blog and code it was just what I needed.

garpur commented May 6, 2015

I have a site that has more than one "root"
so I put the sitemap in the root and modified the template with the following:

@foreach (var node in Umbraco.ContentAtRoot()) {

@geturlwithdomainprefix(node.Url)
@(string.Format("{0:s}+00:00", node.UpdateDate))

@ListChildNodes(node);

}

instead of @listchildnodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1))

thx, for the blog and code it was just what I needed.

@niallmccabe

This comment has been minimized.

Show comment Hide comment
@niallmccabe

niallmccabe Sep 9, 2015

There is also an issue with the urls in the sitemap if your site uses https as opposed to http. This can be solved by amending the GetUrlWithDomainPrefix function as follows:

private static string GetUrlWithDomainPrefix(string url)
{
if (url.StartsWith("/"))
url = url.Substring(1);

    if (url.StartsWith("http://"))
    {
        url = url.Replace("http://", "https://");
    }

    var domainPrefix = string.Format("https://{0}/", HttpContext.Current.Request.ServerVariables["HTTP_HOST"]);

    if (url.StartsWith(domainPrefix))
        return url;
    else
        return domainPrefix + url;
}

There is also an issue with the urls in the sitemap if your site uses https as opposed to http. This can be solved by amending the GetUrlWithDomainPrefix function as follows:

private static string GetUrlWithDomainPrefix(string url)
{
if (url.StartsWith("/"))
url = url.Substring(1);

    if (url.StartsWith("http://"))
    {
        url = url.Replace("http://", "https://");
    }

    var domainPrefix = string.Format("https://{0}/", HttpContext.Current.Request.ServerVariables["HTTP_HOST"]);

    if (url.StartsWith(domainPrefix))
        return url;
    else
        return domainPrefix + url;
}
@BarryFogarty

This comment has been minimized.

Show comment Hide comment
@BarryFogarty

BarryFogarty Sep 22, 2015

Useful script! Thanks for sharing. NB in Umbraco 7 we can use

node.UrlWithDomain()

and thus dispense with the GetUrlWithDomainPrefix() helper function.

In my case, I don't use a flag to exclude nodes from the sitemap, I do it by document type alias, as follows:

var docTypeExclusions = new List<string>()
    {
        "Basket", "Checkout", "Receipt", "etc"
    };
foreach (var node in startNode.Children.Where(x => !x.DocumentTypeAlias.ContainsAny(docTypeExclusions)))
{ ... }

Useful script! Thanks for sharing. NB in Umbraco 7 we can use

node.UrlWithDomain()

and thus dispense with the GetUrlWithDomainPrefix() helper function.

In my case, I don't use a flag to exclude nodes from the sitemap, I do it by document type alias, as follows:

var docTypeExclusions = new List<string>()
    {
        "Basket", "Checkout", "Receipt", "etc"
    };
foreach (var node in startNode.Children.Where(x => !x.DocumentTypeAlias.ContainsAny(docTypeExclusions)))
{ ... }
@biapar

This comment has been minimized.

Show comment Hide comment
@biapar

biapar Mar 22, 2016

In Umbraco 6 there is NiceUrlWithDomain to bypass GetUrlWithDomainPrefix

biapar commented Mar 22, 2016

In Umbraco 6 there is NiceUrlWithDomain to bypass GetUrlWithDomainPrefix

@biapar

This comment has been minimized.

Show comment Hide comment
@biapar

biapar Mar 22, 2016

Frequency: how to setup?

biapar commented Mar 22, 2016

Frequency: how to setup?

@jclementson

This comment has been minimized.

Show comment Hide comment
@jclementson

jclementson Jul 7, 2016

Excellent. Nice and simple and saved a lot of time. Thanks for sharing!

One typo - xsi:schemalocation should be xsi:schemaLocation

Excellent. Nice and simple and saved a lot of time. Thanks for sharing!

One typo - xsi:schemalocation should be xsi:schemaLocation

@alindgren

This comment has been minimized.

Show comment Hide comment
@alindgren

alindgren Jul 25, 2016

@jclementson: thanks - I've updated the Gist with xsi:schemaLocation

Owner

alindgren commented Jul 25, 2016

@jclementson: thanks - I've updated the Gist with xsi:schemaLocation

@ryanology

This comment has been minimized.

Show comment Hide comment
@ryanology

ryanology Jul 29, 2016

I was running into a problem where the view was adding an extra line at the beginning so that the xml declaration was on line 2, and thus invalid (getting the error "error on line 2 at column 6: XML declaration allowed only at the start of the document");

The fix that made it work for me was to write out the xml declaration in the response stream at the beginning of the view page. So the top looked like this instead:

@{
Response.Write("");
Response.ContentType = "text/xml";
Layout = null;
}
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@using System.Linq;
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
@listchildnodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1))
</urlset>

I was running into a problem where the view was adding an extra line at the beginning so that the xml declaration was on line 2, and thus invalid (getting the error "error on line 2 at column 6: XML declaration allowed only at the start of the document");

The fix that made it work for me was to write out the xml declaration in the response stream at the beginning of the view page. So the top looked like this instead:

@{
Response.Write("");
Response.ContentType = "text/xml";
Layout = null;
}
@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@using System.Linq;
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
@listchildnodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1))
</urlset>

@ps2goat

This comment has been minimized.

Show comment Hide comment
@ps2goat

ps2goat May 5, 2017

The suggestion to use node.UrlWithDomain() by @BarryFogarty works well, and cleans up the code a good deal.

The change I made was to add two properties to most document types: ShowInXmlSitemap and ShowChildrenInXmlSitemap. if either of those properties is true, enter the for loop. if the first is true, output the current node. if the latter is true, loop through the child nodes. We defaulted the ShowInXmlSitemap to true for the non-xml sitemap document type you mentioned in your blog post.

This was necessary in our case because we have situations where we have a content page with several pieces of content under it, but they are merely rendered as content on the parent page. that allows us to disable the child content from producing their own sitemap nodes. In another case, we have a blog parent and both that page and all children should generate nodes.

This combination also helps with short circuiting how deeply the node inspection will go.

Thanks @alindgren for offering a great solution.

ps2goat commented May 5, 2017

The suggestion to use node.UrlWithDomain() by @BarryFogarty works well, and cleans up the code a good deal.

The change I made was to add two properties to most document types: ShowInXmlSitemap and ShowChildrenInXmlSitemap. if either of those properties is true, enter the for loop. if the first is true, output the current node. if the latter is true, loop through the child nodes. We defaulted the ShowInXmlSitemap to true for the non-xml sitemap document type you mentioned in your blog post.

This was necessary in our case because we have situations where we have a content page with several pieces of content under it, but they are merely rendered as content on the parent page. that allows us to disable the child content from producing their own sitemap nodes. In another case, we have a blog parent and both that page and all children should generate nodes.

This combination also helps with short circuiting how deeply the node inspection will go.

Thanks @alindgren for offering a great solution.

@pbres

This comment has been minimized.

Show comment Hide comment
@pbres

pbres Jan 20, 2018

shouldn't be there also a record for the root site before
@ListChildNodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1)) ?

right now it's starting from first child of the root, skipping the root itself

pbres commented Jan 20, 2018

shouldn't be there also a record for the root site before
@ListChildNodes(Umbraco.TypedContent(UmbracoContext.Current.PageId).AncestorOrSelf(1)) ?

right now it's starting from first child of the root, skipping the root itself

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