+1-888-365-2779
Try Now
More in this section

Forums / Developing with Sitefinity / CategoriesTree not URL encoded

CategoriesTree not URL encoded

6 posts, 0 answered
  1. Mike
    Mike avatar
    208 posts
    Registered:
    10 Dec 2007
    21 Mar 2011
    Link to this post
    I noticed the CategoriesTree control is HTML encoding and not URL Encoding the category name when it constructs the link.  This causes a category with an ampersand in it's name to be incorrectly read from the querystring.  So, a category named like:

    Beer & Wine

    produces a URL like:

    /beverages.aspx?CatName=Beer & Wine

    which, when parsed by the browser expands the & entity.  This should be URL encoded, so it's preserved in the UR, like:

    /beverages?CatName=Beer %26 Wine

    I would rather not use category ID, because I need the URL to be human readable.  Is there some way to fix this behavior?

    Also, if you fix this in the control, the text in the link should be HTML encoded.  So a complete link looks like:

    <a class="rtIn" href="/Beverages.aspx?CatName=Beer &amp; Wine">Beer & Wine (1)</a>
    but it should actually be:
    <a class="rtIn" href="/Beverages.aspx?CatName=Beer%20%26%20Wine">Beer &amp; Wine (1)</a>


    Thanks,
    Mike Sharp
  2. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    09 Dec 2016
    21 Mar 2011
    Link to this post
    Hello Mike,

    You need to create a custom class ( in App_Code or class library) that inherits from CategoriesTree. You have to override BindCategories() method and decode the ulr using Server.UrlDecode method.

    Below is a sample code

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Telerik.Cms.Engine.WebControls.Categories;
    using System.Collections;
    using Telerik.Cms.Engine;
    using Telerik.Web.UI;
      
    /// <summary>
    /// Summary description for CustomCategoriesTree
    /// </summary>
    public class CategoriesTreeCustom : CategoriesTree
    {
        private ContentManager _manager;
      
        public CategoriesTreeCustom()
        {
        }
      
        public override string LayoutTemplatePath
        {
            get
            {
                return "~/Sitefinity/ControlTemplates/Generic_Content/CategoriesTree.ascx";
            }
        }
      
        protected override void BindCategories()
        {
                 cntManager = new ContentManager("News");
                IList allCategories = new List<ICategory>();
                if (string.IsNullOrEmpty(RootCategory))
                {
                    allCategories = cntManager.GetCategories(0, 0, "CategoryName ASC");
                }
                else
                {
                    ICategory rootCategory = cntManager.GetCategory(RootCategory);
                    allCategories = cntManager.GetCategoriesTree(rootCategory);
                }
       
                
            this.CategoriesTreeView.DataTextField = "CategoryName";
            this.CategoriesTreeView.DataFieldID = "ID";
            this.CategoriesTreeView.DataValueField = "ID";
            this.CategoriesTreeView.DataFieldParentID = "ParentCategoryID";
            this.CategoriesTreeView.DataSource = allCategories;
            this.CategoriesTreeView.NodeDataBound +=new Telerik.Web.UI.RadTreeViewEventHandler(CategoriesTreeView_NodeDataBound);
            this.CategoriesTreeView.DataBind();
       
        }
      
    void CategoriesTreeView_NodeDataBound(object sender, Telerik.Web.UI.RadTreeNodeEventArgs e)
      {
          ICategory nodeCategory = (ICategory)e.Node.DataItem;
          e.Node.Text = GetNodeText(nodeCategory);
          if (e.Node.Text.Contains("(0)"))
          {
              e.Node.Text = nodeCategory.CategoryName;
          }
          var newUrl = HttpContext.Current.Server.UrlEncode(nodeCategory.CategoryName);
          e.Node.NavigateUrl = base.GetNodeUrl(newUrl, nodeCategory.ID.ToString());
          e.Node.ToolTip = nodeCategory.CategoryName;
      }
     
      
      
        private ContentManager cntManager;
      
    }


    Best wishes,
    Ivan Dimitrov
    the Telerik team
  3. Mike
    Mike avatar
    208 posts
    Registered:
    10 Dec 2007
    21 Mar 2011
    Link to this post
    Thanks Ivan, that's a big help.  Now the links are properly formatted like:

    Beverages.aspx?Category=Beer+%26+Wine

    One problem I noticed, though, the content count seems to be zero for each item in the categories tree.  Also, categories which have no content are displayed as well. I used a 'brute force' approach to fix this, but was hoping there was a more efficient way of handling CountentCount.  I think for this particular client, performance won't be an issue, but is there a better way to access the content counts?

    In case anyone else runs into this scenario, my working code is posted below.

    Regards,
    Mike Sharp

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Web;
    using Telerik.Cms.Engine.WebControls.Categories;
    using System.Collections;
    using Telerik.Cms.Engine;
    using Telerik.Web.UI;
      
      
    using CPi.Web.Public;
      
    namespace CPi.Web.Public.Controls
    {
        /// <summary>
        /// Summary description for CustomCategoriesTree
        /// </summary>
        [DefaultProperty("CategoryRoot")]
        public class CategoriesTreeEncoded : CategoriesTree
        {
      
            public CategoriesTreeEncoded()
            {
            }
      
            [Browsable(true),
             Category("Projects Settings"),
             DisplayName("Root Category"),
             Description("Enter the name of the top category for this category tree")]
            public string CategoryRoot
            {
                get { return categoryRoot; }
                set { categoryRoot = value; }
            }
      
            [Telerik.Cms.Web.UI.WebEditor("Telerik.Cms.Web.UI.CmsUrlWebEditor, Telerik.Cms")]
            [Browsable(true),
             Category("Projects Settings"),
             DisplayName("Content URL"),
             Description("Select a page on this site where the project list will display (typically this page).")]
            public override string CategoryContentUrl
            {
                get { return base.CategoryContentUrl; }
                set { base.CategoryContentUrl = value; }
            }
      
            public override string LayoutTemplatePath
            {
                get
                {
                    return "~/Sitefinity/ControlTemplates/Projects/CategoriesTree.ascx";
                }
            }
      
            protected override void OnLoad(EventArgs e)
            {
                // Making things easy for our user...these properties won't change for the projects content type.
                base.OnLoad(e);
                base.RootCategory = this.CategoryRoot;
                base.CategoryKey = "Category";
                base.CategoryKeyType = CategoryKeyTypes.Name;
                base.ShowContentCount = true;
            }
            protected override void BindCategories()
            {
                cntManager = new ContentManager("Projects");
                ICategory rootCategory = null;
                int rootCategoryCount = 0;
                IList allCategories = new List<ICategory>();
                IList filteredCategories = new List<ICategory>();
                if (string.IsNullOrEmpty(CategoryRoot))
                {
                    allCategories = cntManager.GetCategories(0, 0, "CategoryName ASC", true);
                }
                else
                {
                    rootCategory = cntManager.GetCategory(CategoryRoot);
                    allCategories = cntManager.GetCategoriesTree(rootCategory);
                }
      
                foreach (ICategory catItem in allCategories)
                {
                    List<Telerik.Cms.Engine.IMetaSearchInfo> filter = new List<Telerik.Cms.Engine.IMetaSearchInfo>();
                    filter.Add(new Telerik.Cms.Engine.MetaSearchInfo(Telerik.Cms.Engine.MetaValueTypes.ShortText, "Category", catItem.CategoryName));
                    IList listOfContentItems = cntManager.GetContent(filter.ToArray());
                    if (listOfContentItems.Count > 0 || catItem.CategoryName == RootCategory)
                    {
                        rootCategoryCount = rootCategoryCount + listOfContentItems.Count;
                        ICategory item = catItem;
                        item.ContentCount = listOfContentItems.Count;
                        filteredCategories.Add(item);
                    }
      
                }
                ICategory rootItem = (ICategory)filteredCategories[0];
                rootItem.ContentCount = rootCategoryCount;
                this.CategoriesTreeView.DataTextField = "CategoryName";
                this.CategoriesTreeView.DataFieldID = "ID";
                this.CategoriesTreeView.DataValueField = "ID";
                this.CategoriesTreeView.DataFieldParentID = "ParentCategoryID";
                this.CategoriesTreeView.DataSource = filteredCategories;
                this.CategoriesTreeView.NodeDataBound += new Telerik.Web.UI.RadTreeViewEventHandler(CategoriesTreeView_NodeDataBound);
                this.CategoriesTreeView.DataBind();
      
            }
      
            void CategoriesTreeView_NodeDataBound(object sender, Telerik.Web.UI.RadTreeNodeEventArgs e)
            {
                ICategory nodeCategory = (ICategory)e.Node.DataItem;
                if (nodeCategory.ContentCount > 0)
                {
                    e.Node.Text = GetNodeText(nodeCategory);
                    if (e.Node.Text.Contains("(0)"))
                    {
                        e.Node.Text = string.Format("{0} ({1})", nodeCategory.CategoryName, nodeCategory.ContentCount.ToString());
                    }
                    var newUrl = HttpContext.Current.Server.UrlEncode(nodeCategory.CategoryName);
                    e.Node.NavigateUrl = base.GetNodeUrl(newUrl, nodeCategory.ID.ToString());
                    e.Node.ToolTip = nodeCategory.CategoryName;
                }
                else
                {
                    e.Node.Enabled = false;
                }
            }
      
            private ContentManager cntManager;
            private string categoryRoot;
      
        }
    }
  4. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    09 Dec 2016
    21 Mar 2011
    Link to this post
    Hi Mike,

    You should better enable caching. You can use Manager.GenerateCategoryCountCacheKey, so you can get/set some key to the cache instead of making queries to the database.

    Regards,
    Ivan Dimitrov
    the Telerik team
  5. Mike
    Mike avatar
    208 posts
    Registered:
    10 Dec 2007
    22 Mar 2011
    Link to this post
    Thanks Ivan,

    I was thinking about that...but how can I set a cache dependency when the categories are updated, or a new content item is published that is associated with the category?  I've googled GenerateCategoryCountCacheKey
    but only found one post (from you of course!) that mentions it.  This shows cache expiration set to 1 day, but I'd prefer some sort of cache dependency that automatically expires the cache on publication of a content item or a change to the category structure (as long as it doesn't take too much coding).

    Once I hand the site off to the client, I won't really be around to reset the application, and I'm not sure I want to show them how to do it via FTP (since their hosting company will likely not allow an IIS reset--I know I don't on sites I host).  If they have to wait a day to see the counts update, I'm sure I'll hear about it.  :)

    My thinking is that the list of sub categories will never exceed 10, at least from any particular root category.  Is this really much of a performance concern?

    Thanks again,
    Mike Sharp
  6. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    09 Dec 2016
    22 Mar 2011
    Link to this post
    Hello Mike,

    It would be possible to add some header like if-modified-since, so you detect when a new item is created and  load the categories from the database upon a request or you load the items from the cache.

    Regards,
    Ivan Dimitrov
    the Telerik team
Register for webinar
6 posts, 0 answered