More in this section

Forums / Developing with Sitefinity / Sitemap and CMSSitemapProvider

Sitemap and CMSSitemapProvider

11 posts, 0 answered
  1. Clyde
    Clyde avatar
    5 posts
    Registered:
    06 Nov 2008
    22 Jan 2009
    Link to this post
    Hi,

    I have a website where not all the pages are being shown in menu (hence the show in navigation is switched off)

    I would like to create a sitemap page, using the Sitemap control, and the CmsSitemapProvider.

    Is there anyway to override the check for "show in navigation" so I'm able to get all the pages, even if set NOT to show in navigation?

    I've seen a thread where the CmsSitemapProvider was inherited and the IsAccessibleToUser (from .NET's SitemapProvider) was overridden.

    I would like to override something similar, but to show all the content!

    Any help is kindly appreciated!

    Thanks,

    Clyde.
  2. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    12 Sep 2017
    22 Jan 2009
    Link to this post
    Hi Clyde,

    You need to create your custom SiteMapProvider. I prepared an example for you. You can extend it and add additional logic to it. The provider will show all pages - navigable and non navigable.

    Create a class CustomSiteMap in App_Code folder.

    using System; 
    using System.Collections; 
    using System.Collections.Specialized; 
    using System.Web; 
    using Telerik.Cms; 
     
    /// <summary> 
    /// Summary description for CustomSiteMap 
    /// </summary> 
    ///  
    namespace Telerik.Samples 
        public class CustomSiteMap : StaticSiteMapProvider 
        { 
            /// <summary>Initializes a new instance of the <see cref="T:Telerik.Web.UI.Cms.CmsSiteMapProvider"></see> class. </summary> 
            public override void Initialize(string name, NameValueCollection attributes) 
            { 
                base.Initialize(name, attributes); 
            } 
     
            protected override void Clear() 
            { 
                lock (this
                { 
                    base.Clear(); 
                    this.root = null
                } 
            } 
     
            public override SiteMapNode BuildSiteMap() 
            { 
                if (this.root != null
                    return this.root; 
     
                lock (this
                { 
                    this.root = new SiteMapNode(this"00000000-0000-0000-0000-000000000000", String.Empty, "[My Site Map]"); 
                    CmsManager cmsManager = new CmsManager(); 
                    IList pages = cmsManager.GetPages(Guid.Empty); 
     
                    foreach (ICmsPage page in pages) 
                    { 
                        SiteMapNode node = new SiteMapNode(this, page.ID.ToString(), page.DefaultUrl.Url, page.MenuName); 
                        base.AddNode(node, root); 
                        this.LoadPagesRecursive(node, page); 
                    } 
     
                    return root; 
                } 
            } 
     
            protected override SiteMapNode GetRootNodeCore() 
            { 
                return this.BuildSiteMap(); 
            } 
     
            private void LoadPagesRecursive(SiteMapNode parentNode, ICmsPage parentPage) 
            { 
                if (parentPage.Pages.Count > 0) 
                { 
                    foreach (ICmsPage page in parentPage.Pages) 
                    { 
                        SiteMapNode node = new SiteMapNode(this, page.ID.ToString(), page.DefaultUrl.Url, page.MenuName); 
                        base.AddNode(node, parentNode); 
                        this.LoadPagesRecursive(node, page); 
                    } 
                } 
            } 
     
            private SiteMapNode root; 
        } 
     

    Register the new provider in the web.config and set it as default.

     <siteMap defaultProvider="CustomSiteMapProvider" enabled="true"
          <providers> 
            <clear /> 
            <add name="CmsSiteMapProvider" description="Displays Cms Pages" type="Telerik.Cms.Web.CmsSiteMapProvider" /> 
            <add name="CustomSiteMapProvider" description="CustomSiteMapProvider" type="Telerik.Samples.CustomSiteMap, App_Code" /> 
          </providers> 
        </siteMap> 

    I hope this helps.

    Greetings,
    Ivan Dimitrov
    the Telerik team

    Check out Telerik Trainer, the state of the art learning tool for Telerik products.
  3. Clyde
    Clyde avatar
    5 posts
    Registered:
    06 Nov 2008
    22 Jan 2009
    Link to this post
    Wow, that's exactly what i needed :)

    Your great help is much appreciated. Keep up the good work ;)
  4. michael
    michael avatar
    20 posts
    Registered:
    09 Jan 2009
    20 Feb 2009
    Link to this post
    I notice in your example, that the site map is cached.  In the BuildSiteMap procedure, the site map is build once and never changed.

    This implementation is a bit problematic for me as I'm adding pages frequently and need the site map to be updated when a page is added.

    I can figure out the details of recreating the Site Map, but I need to know how to determine if a new page has been added through the Admin pages.

    I was looking at the CmsManager but there doesn't seem to be a flag indicating that a page was added, or a count of the site's total number of pages.  The only way that I can see is to iterate through all the pages, and if I'm doing that then I might as well recreate the sitemap each call.

    Anyone have any suggestions?
  5. Bob
    Bob avatar
    330 posts
    Registered:
    30 Dec 2016
    23 Feb 2009
    Link to this post
    Hello Michael,

    Please consider the flowing example:

    public class CustomSiteMap : StaticSiteMapProvider    
    {  
        ...  
     
        public override void Initialize(string name, NameValueCollection attributes)  
        {  
            base.Initialize(name, attributes);  
            CmsManager.Executed += new EventHandler<ExecutedEventArgs>(CmsManager_Executed);  
        }    
     
        void CmsManager_Executed(object sender, ExecutedEventArgs e)  
        {  
            switch (e.CommandName)  
            {  
                case "PublishNew":  
                case "DeletePage":  
                    // If pages are often added and removed and   
                    // performance is very important you could add and remove   
                    // individual pages instead of rebuilding the entire tree.  
                    Clear();  
                    break;  
            }  
        }  
     
        ...  
    }  
     

    Another approach is to use cache dependency (SiteMapCacheDependency class) instead of dealing with individual events, but in this particular case I think it will only complicate your code as cache dependencies are intended cache engines.

    Kind regards,
    Bob
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  6. michael
    michael avatar
    20 posts
    Registered:
    09 Jan 2009
    23 Feb 2009
    Link to this post
    Works like a charm.  Thanks!
  7. Diana
    Diana avatar
    16 posts
    Registered:
    26 Nov 2008
    15 May 2009
    Link to this post

    Hi,

    I've been working with Michael on the same project and a new issue came up with this, now that the site is live and in a load balanced environment.  We have 2 load balanced IIS servers, pointing at the same database. 

     

    After the fix that you provided to Michael above, when a user creates a new page or deletes a page, the customsitemap cache is refreshed and the user sees the new page in the controls which use that provider, as expected.

     

    Now the problem is that a different user connected to the site and did not see the new changes until over 24 hours later.  I think that this second user must have connected to the other load balanced server, which could have been looking at the old cache.

    In our web.config, we have <cachedependency mode="InDatabase"
    as suggested in the thread http://www.sitefinity.com/support/forums/support-forum-thread/b1043S-bbtttd.aspx.  So I'm not even sure why the cache refresh is not reflected on both servers at the same time - since they are both pointing to the same database.

    Is there a setting somewhere that controls when this cache expires for a server? 
    Or is there a way to force clearing of the cache for all applications on certain events?  If not, can you suggest a workaround for this problem? 

    Thank you again,
    Diana

     

  8. Vlad
    Vlad avatar
    498 posts
    Registered:
    19 Jun 2017
    16 May 2009
    Link to this post
    Hello Diana,

    In a load balanced environment, in order to take advantage of the InDatabase cacheDependency setting in the web.config, instead of events you should use CacheDependency objects, which are also based on the events, however the events are fired on each server connected to the database.
    Here is an example:
    public class CustomSiteMap : StaticSiteMapProvider 
        ... 
        protected override void Clear() 
        { 
            lock (this
            { 
                base.Clear(); 
                this.root = null
                this.cacheDependency = null
            } 
        } 
     
        public override SiteMapNode BuildSiteMap() 
        { 
            if (this.root != null
                return this.root; 
     
            lock (this
            { 
                this.root = new SiteMapNode(this"00000000-0000-0000-0000-000000000000", String.Empty, "[My Site Map]"); 
                CmsManager cmsManager = new CmsManager(); 
                IList pages = cmsManager.GetPages(Guid.Empty); 
     
                foreach (ICmsPage page in pages) 
                { 
                    SiteMapNode node = new SiteMapNode(this, page.ID.ToString(), page.DefaultUrl.Url, page.MenuName); 
                    base.AddNode(node, root); 
                    this.LoadPagesRecursive(node, page); 
                } 
     
                this.cacheDependency = new AggregateCacheDependency(); 
                this.cacheDependency.Add(new SiteMapCacheDependency(), new DummyCacheDependency(this)); 
     
                return root; 
            } 
        } 
     
        ... 
     
        class DummyCacheDependency : CacheDependency 
        { 
            public DummyCacheDependency(CustomSiteMap siteMap) 
            { 
                this.siteMap = siteMap; 
            } 
     
            protected override void DependencyDispose() 
            { 
                this.siteMap.Clear(); 
            } 
     
            private CustomSiteMap siteMap; 
        } 
        private AggregateCacheDependency cacheDependency = null
     
        ... 

    In the code above, the main work is done by the SiteMapCacheDependency. We need an instance of DummyCacheDependency just to handle when the SiteMapCacheDependency is disposed and call Clear(), because the AggregateCacheDependency is disposed when at least one of the dependency in the collection is disposed.

    Hope this is helpful.
    Let us know if you have additional questions.

    All the best,
    Vlad
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  9. Diana
    Diana avatar
    16 posts
    Registered:
    26 Nov 2008
    19 May 2009
    Link to this post
    Hi Vlad,
    Thanks for your help. I can not seem to find the namespace for SiteMapCacheDependency.  Or is this a class I need to create?

    Thanks again,
    Diana
  10. Diana
    Diana avatar
    16 posts
    Registered:
    26 Nov 2008
    19 May 2009
    Link to this post
    Hello Vlad,
    Using Reflector, I found that SiteMapCacheDependency is a class inside the CmsSiteMapProvider.  Since we are writing our own sitemap provider, I've copied the code for this class into the CustomSiteMap class, and now I can compile. 
            class SiteMapCacheDependency : CacheDependencyBase  
            {  
                public SiteMapCacheDependency(CustomSiteMap siteMap)  
                    : base("CmsSiteMapCommands".GetHashCode(), null)  
                {  
                    this.siteMap = siteMap;  
                }  
                public SiteMapCacheDependency()  
                    : this(null)  
                {  
                }  
     
                static SiteMapCacheDependency()  
                {  
                    CacheDependencyHandler.Current.RegisterCacheDependacy(typeof(ICmsPage), new EventHashDelegate(CustomSiteMap.SiteMapCacheDependency.GetDependancyKey));  
                }  
     
                protected override void DependencyDispose()  
                {  
                    if (this.siteMap != null)  
                    {  
                        this.siteMap.Clear();  
                    }  
                    base.DependencyDispose();  
                }  
     
                private static int GetDependancyKey(object sender, ExecutedEventArgs args)  
                {  
                    if (((!args.CommandName.Equals("CreatePage") && !args.CommandName.Equals("UpdatePage")) && (!args.CommandName.Equals("Publish") && !args.CommandName.Equals("PageOrdinalChanged"))) && (!args.CommandName.Equals("DeletePage") && !args.CommandName.Equals("ParentChanged")))  
                    {  
                        return 0;  
                    }  
                    return "CmsSiteMapCommands".GetHashCode();  
                }  
     
                private CustomSiteMap siteMap;  
     
            } 

    Please let me know if this was the best way to accomplish this - as it seems to have fixed my problems.

    Thank you very much for your help,
    Diana
  11. Vlad
    Vlad avatar
    498 posts
    Registered:
    19 Jun 2017
    20 May 2009
    Link to this post
    Hi Diana,

    Seems you are using Sitefinity 3.5.
    Actually, the provided in my previous post solution is for v3.6, where SiteMapCacheDependency is no longer inside the CmsSiteMapProvider, but now it is in the Telerik.Cms.Caching namespace.
    However, you have implemented the alternative approach for 3.5 and it looks correct.

    Also, I forgot to mention, that after applying this implementation, you can remove the code for subscribing to the CmsManager.Executed event from the Initialize() method of the SiteMapProvider, as the CacheDependency object does the same.

    Greetings,
    Vlad
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
Register for webinar
11 posts, 0 answered