New Community Website

Ordinarily, you'd be at the right spot, but we've recently launched a brand new community website... For the community, by the community.

Yay... Take Me to the Community!

The Community Blog is a personal opinion of community members and by no means the official standpoint of DNN Corp or DNN Platform. This is a place to express personal thoughts about DNNPlatform, the community and its ecosystem. Do you have useful information that you would like to share with the DNN Community in a featured article or blog? If so, please contact .

The use of the Community Blog is covered by our Community Blog Guidelines - please read before commenting or posting.

Creating a Sitemap Provider For Your Module

In DotNetNuke Core 5.3 there was some work done around the Core search engine sitemap provider that was referred to as “The sitemap now allows module admins to plugin sitemap logic for individual modules” in the release notes. When I first heard about this in a Core Team meeting, it was explained that modules that have many pieces of content on a single page (Ex. Forum, Blog, Articles, etc.) can be picked up by the search engine sitemap provider. Having just finished my recent blog series on Taxonomy, which also focuses on multiple content items within a single module, I figured this was the next thing I should cover. If you haven’t read my blog series on Taxonomy some of the terms here may not make sense (so take a gander if you haven’t already although it may not be necessary to follow this). One thing worth mentioning before we get started, I know there are third party search engine sitemap providers out there but I am unsure of how this implementation will work with them.

Basic Flow

Before diving into code, lets first take a look at how this works. When someone (or something) calls your DotNetNuke sitemap page ( the request is intercepted by an HttpHandler (this is set in your web.config). This handler will then run its ProcessRequest method. This method will look to see if a sitemap.xml file exists, and if not, will call BuildSiteMap to create one. BuildSiteMap is part of the Core Library project and basically what it does is the following:

  • Checks cache settings configured via the Sitemap module (located under Admin –> Search Engine SiteMap in most installs)
  • Loops through all Sitemap providers one by one to build a collection of URLs (providers are stored in web.config, visible in same sitemap module)
  • Creates the sitemap.xml file (which is stored in Portals/[PORTALID]/Sitemap/ folder)

Something worth mentioning here is that there is a 50,000 page limit per sitemap file (the forum on this site alone would go above that limit) and that multiple files will need to be created if you go above this limit. However, the core will handle this for you and create multiple files if necessary. With the basic flow covered, lets dive into what we need to create one for our own module.

Module Additions

To get started, you should create a new class for your module project. What you call this class and where you place it in your module is completely up to you but my example is a class named Sitemap and it is stored in MyModule\Components\Providers\Sitemap.cs. In my newly created Sitemap class the first thing I need to do is inherit from the SitemapProvider class of the core (located under the DotNetNuke.Services.Sitemap namespace). In doing so, this is going to inform me that I must override a function named GetUrls if I attempt to compile. Next, since we want to be able to compile, we need to override this GetUrls function which returns a collection of SitemapURL.

At this point, we have to decide what should be in our modules sitemap. In some modules, this is simple. For example, a blog module will need a URL entry in our sitemap for every published blog post. In something like a forum, you have more options and it may lead to some confusion. Should it be the list of forums or should it be the list of threads? While you are free to do whatever you feel is best for your situation, I suggest creating URLs for things you equate to Content Items (discussed in Taxonomy series). In the case of a forum, I would have a Content Item for every thread (thus allowing tags to be assigned per thread) and therefore I would want a sitemap URL for each thread. Once we understand what we want in our sitemap, we need to create logic that communicates with the data store to retrieve our collection of content items on a per portal basis because the sitemap is portal wide. Now, lets take a look at my GetUrls function:

   1: using DotNetNuke.Entities.Portals;
   2: using MyModule.Components.Entities;
   3: using System.Collections.Generic;
   4: using MyModule.Components.Controllers;
   5: using DotNetNuke.Services.Sitemap;
   6: using System.Linq;
   8: namespace MyCompany.MyModule.Components.Providers 
   9: {
  11:     public class Sitemap : SitemapProvider
  12:     {
  14:         #region Public Methods
  16:         public override List GetUrls(int portalID, PortalSettings ps, string version) {
  17:             var cntentry = new MyModuleController();
  18:             var colEntries = cntentry.GetPublishedPortalEntries(portalID);
  20:             return colEntries.Select(GetBlogUrl).ToList();
  21:         }
  23:         #endregion
  25:     }
  26: }

In lines 17-18 I communicate with the data store to retrieve all items that are published per portal, which are all the items I will want listed in the site’s sitemap. In line 20 I use some Linq logic to loop through the collection of content items and for each item I call my own private method shown below, which in the end results in a SitemapUrl list usable for sitemap generation.

   1: private static SitemapUrl GetBlogUrl(EntryInfo objEntry) {
   2:     var pageUrl = new SitemapUrl
   3:                       {
   4:                           Url =
   5:                               DotNetNuke.Common.Globals.NavigateURL(objEntry.TabID, "ViewEntry", "mid=" + objEntry.ModuleId,
   6:                                                                     "EntryID=" + objEntry.EntryId),
   7:                           Priority = (float) 0.5,
   8:                           LastModified = objEntry.LastModifiedOnDate,
   9:                           ChangeFrequency = SitemapChangeFrequency.Daily
  10:                       };
  12:     return pageUrl;
  13: }

In line 4 I set the URL property of a new SitemapUrl object. What this URL is will vary from module to module, just keep in mind that you will likely need TabID, ModuleID and whatever your identifier is (in my example above, it is EntryID). If your module is associated with Content Items, you will have ModuleID and TabID available to you via the relation to the ContentItems table even if you didn’t store either in your modules table (therefore I recommend doing a join to that table when retrieving, just like my previous taxonomy examples). In line 7 we set the priority, which I left here at the default value of .5. What this value should be varies and I will leave it up to you to decide. In line 8 we set the last time the content item was updated and finally in line 9 we tell it what the change frequency is (again, I will leave this up to you to decide). That is all we need as far as code goes, the only thing left for us is to add an entry to our web.config so the module provider is available. Towards the bottom of our web.config file, where the coreSitemapProvider is located, we need to add a line to make our new provider visible. This will look something like this:

   1: <add name="MyModuleSitemapProvider" type="MyCompany.MyModule.Components.Providers.Sitemap, 
   2:     MyCompany.MyModule" providerPath="~\DesktopModules\MyCompany\MyModule\Components\Providers\" />

Once you have done the above (compiled your module and added the web.config entry) you can start testing to see your changes. Simple go to the Admin –> Search Engine SiteMap tab for a portal that contains your module populated with content (locally) and clear the cache for the sitemap module (you may need to completely clear the site cache). At this point, you should be able to see your new provider name located in the data grid within the module. Once you see your provider, you should click the Sitemap URL link (also located in the module) to validate your module is exposing its content to the sitemap. If you are not seeing anything, you should debug to verify your provider is getting called and locate the problem by stepping through it. You should also verify that the URLs that are displayed are taking you to valid locations (and where you intended them to take you). Once all that is working, your development is done.

NOTE: Although I don’t want to cover it in great detail here, you can use a “Config” component section in the dnn manifest file (version 5.0) to automate this during install. In doing so, please be very careful otherwise you can easily bring down any site that attempts to install the module and the only way to correct it will be direct web.config access (via RDP to web server or FTP to sites root directory).

Well, that is it. Hopefully you can start taking advantage of this somewhat recent change to the core.


Hooking into the sitemap provider is a breeze, thanks for the article. What I'm looking for is a way to extend the provider so I can create a video sitemap ( Can I add my own attributes to the provider so I can do something like that?

PSantry Tuesday, October 29, 2013 10:45 AM (link)
Noluck The fly
Please supply example code

Noluck The fly Tuesday, November 26, 2013 12:17 PM (link)
Charles Gaefke
Thanks for this! I had problems getting the web.config entry correct. Once I found this URL:

I was able to find the format of the type field:

Note: For site map providers located in the application s App_Code folder, the value of the type attribute is simply the class name. Alternatively, the custom site map provider could have been created in a separate Class Library project with the compiled assembly placed in the web application s /Bin directory. In that case, the type attribute value would be Namespace.ClassName, AssemblyName.

After I got that right, I was good to go.
Charles Gaefke Sunday, November 1, 2015 12:31 PM (link)
Nice articles work perfect for me.

The issue i'm facing now is with the multi-portal section. Sitemapprovider showing link to all portals where we have module configure or not. Like we have where we added sitemap. AS on same dnn instance we have another portal running like, both portal showing links of the siteprovider i added.

Any specific settings we have to ?

Any help really appreicate!!
ajit Wednesday, November 18, 2015 11:12 AM (link)
Rodrigo Ratan
code is broken, I think DNN blogs should be modernized....

the line
public override List GetUrls(int portalID, PortalSettings ps, string version) {
probably is List but probably the text wasnt encoded
It`s no the first blog post that I see this problem
Rodrigo Ratan Friday, September 2, 2016 1:33 AM (link)
Dann See
This post is a few years old, but really helped me understand what was going on here, and saved me a lot of time (I was tasked with creating a dynamic sitemap index for our 100K+ page site) - it was great to find out this feature is built into DNN.

My question, or issue, at this point though, is to wonder if there is any way to alter SitemapProviders default value of 50,000 entries per sitemap file generated. The length of urls in the generate map are rather large, and as such are producing rather large sitemap files. I would love to be able to half the page size if possible.

Does anyone know if it is possible to decrease the number of entries per page from 50,000 to something smaller? I am not seeing it in any docs I have came across as of yet. Thanks!
Dann See Monday, August 13, 2018 10:26 PM (link)

Comment Form

Only registered users may post comments.


Aderson Oliveira (22)
Alec Whittington (11)
Alessandra Daniels (3)
Alex Shirley (10)
Andrew Hoefling (3)
Andrew Nurse (30)
Andy Tryba (1)
Anthony Glenwright (5)
Antonio Chagoury (28)
Ash Prasad (37)
Ben Schmidt (1)
Benjamin Hermann (25)
Benoit Sarton (9)
Beth Firebaugh (12)
Bill Walker (36)
Bob Kruger (5)
Bogdan Litescu (1)
Brian Dukes (2)
Brice Snow (1)
Bruce Chapman (20)
Bryan Andrews (1)
cathal connolly (55)
Charles Nurse (163)
Chris Hammond (213)
Chris Paterra (55)
Clint Patterson (108)
Cuong Dang (21)
Daniel Bartholomew (2)
Daniel Mettler (181)
Daniel Valadas (48)
Dave Buckner (2)
David Poindexter (12)
David Rodriguez (3)
Dennis Shiao (1)
Doug Howell (11)
Erik van Ballegoij (30)
Ernst Peter Tamminga (80)
Francisco Perez Andres (17)
Geoff Barlow (12)
George Alatrash (12)
Gifford Watkins (3)
Gilles Le Pigocher (3)
Ian Robinson (7)
Israel Martinez (17)
Jan Blomquist (2)
Jan Jonas (3)
Jaspreet Bhatia (1)
Jenni Merrifield (6)
Joe Brinkman (274)
John Mitchell (1)
Jon Henning (14)
Jonathan Sheely (4)
Jordan Coopersmith (1)
Joseph Craig (2)
Kan Ma (1)
Keivan Beigi (3)
Kelly Ford (4)
Ken Grierson (10)
Kevin Schreiner (6)
Leigh Pointer (31)
Lorraine Young (60)
Malik Khan (1)
Matt Rutledge (2)
Matthias Schlomann (16)
Mauricio Márquez (5)
Michael Doxsey (7)
Michael Tobisch (3)
Michael Washington (202)
Miguel Gatmaytan (3)
Mike Horton (19)
Mitchel Sellers (40)
Nathan Rover (3)
Navin V Nagiah (14)
Néstor Sánchez (31)
Nik Kalyani (14)
Oliver Hine (1)
Patricio F. Salinas (1)
Patrick Ryan (1)
Peter Donker (54)
Philip Beadle (135)
Philipp Becker (4)
Richard Dumas (22)
Robert J Collins (5)
Roger Selwyn (8)
Ruben Lopez (1)
Ryan Martinez (1)
Sacha Trauwaen (1)
Salar Golestanian (4)
Sanjay Mehrotra (9)
Scott McCulloch (1)
Scott Schlesier (11)
Scott Wilkinson (3)
Scott Willhite (97)
Sebastian Leupold (80)
Shaun Walker (237)
Shawn Mehaffie (17)
Stefan Cullmann (12)
Stefan Kamphuis (12)
Steve Fabian (31)
Steven Fisher (1)
Tony Henrich (3)
Torsten Weggen (3)
Tycho de Waard (4)
Vicenç Masanas (27)
Vincent Nguyen (3)
Vitaly Kozadayev (6)
Will Morgenweck (40)
Will Strohl (180)
William Severance (5)
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out
What is Liquid Content?
Find Out