Anders G. Nordby

Lead Consultant at Itera

Tag Archives: jQuery

Improving the Category UI for CMS 6 R2

Even though the new EPiServer 7 is out, some of us are still working with older versions. The Categories tab in EPiServer CMS 6 R2 is not very good looking, and things get really ugly if you have a huge number of categories to select from. That’s why I wanted to improve a little on the useability here. I’m sure there’s a way to hack into the real Categories tab, but my solution is to create a custom property, add this to the site’s basepage (making it available throughout the site), and hide the real Categories tab.

In other words, I’m changing this

into this

First, I searched around for a suitable JavaScript tree control, and found the jsTree, which seemed to offer everything I wanted (plus a lot more that I didn’t need). The next step was making my property update the PageCategory property during save: Dõda Vinkeln and Anders Hattestad showed me how to do this. Then Mari Jørgensen showed my how to hide the Categories tab by adding the following code in the Global.asax.cs file:

EPiServer.UI.Edit.EditPanel.LoadedPage += (sender1, e1) =>
	{
		var pageCategory = e1.Page.Property["PageCategory"];
		if (pageCategory != null)
			pageCategory.DisplayEditUI = false;
	};

OK, then it was just a matter of putting it all together. The code can be downloaded here (EPiCategoriesUI.zip), or simply copy & paste from the code snippets below:

using System;
using System.Linq;
using System.Web.UI;
using System.Web.UI.WebControls;
using EPiServer.Core;
using EPiServer.DataAbstraction;

namespace MakingWaves.CMS6R2.Internet.Web.CustomProperties
{
   public partial class PropertyCategoryTreeControlDialog : UserControl
    {
        protected string CurrentCategoryTree { get; private set; }

        public CategoryList CurrentCats { get; set; }

       public TextBox TextCategories
       {
           get { return txtSelectedValues; }
       }

       protected void Page_Load(object sender, EventArgs e)
       {
           ScriptManager.RegisterClientScriptInclude(Page, typeof (Page), "treecontrol-jq", "/CustomProperties/jsTree/_lib/jquery.js");
           ScriptManager.RegisterClientScriptInclude(Page, typeof (Page), "treecontrol-jqc", "/CustomProperties/jsTree/_lib/jquery.cookie.js");
           ScriptManager.RegisterClientScriptInclude(Page, typeof (Page), "treecontrol-jqh", "/CustomProperties/jsTree/_lib/jquery.hotkeys.js");
           ScriptManager.RegisterClientScriptInclude(Page, typeof (Page), "treecontrol-jqt", "/CustomProperties/jsTree/jquery.jstree.js");
           ScriptManager.RegisterClientScriptInclude(Page, typeof (Page), "treecontrol-ctrl", "/CustomProperties/jsTreeControl.js");

           CurrentCategoryTree = RenderCategories(Category.GetRoot().Categories);
       }

       private string RenderCategory(Category catRoot)
       {
const string ITEMFORMAT = "<ul>	<li id="\&quot;cat-{0}\&quot;" data-name="\&quot;{0}\&quot;{3}"><a href="\&quot;#\&quot;">{1}</a>{2}</li></ul>";

           var children = catRoot.Categories;
           var classChecked = CurrentCats.Contains(catRoot.ID) ? " class=\"jstree-checked\"" : string.Empty;

           return children.Count < 1
                       ? string.Format(ITEMFORMAT, catRoot.ID, catRoot.Name, string.Empty, classChecked)
                       : string.Format(ITEMFORMAT, catRoot.ID, catRoot.Name, RenderCategories(children), classChecked);
         }

         private string RenderCategories(CategoryCollection children)
         {
             string retval = children.Cast()
                 .Aggregate(string.Empty, (current, child) => current + RenderCategory(child));

             return string.Format("<ul>{0}</ul>", retval);
        }
    }
}

 

using System.Web;
using System.Web.UI;
using EPiServer.PlugIn;
using EPiServer.UI.Edit;

namespace MakingWaves.CMS6R2.Internet.Web.CustomProperties
{
    ///
    /// Ensures that edit mode in EPiServer has a script manager
    /// http://tedgustaf.com/en/blog/2010/8/use-updatepanel-inside-an-episerver-custom-property/
    ///

    [GuiPlugIn(Area = PlugInArea.EditPanel)]
    public class ScriptManagerPlugin : ICustomPlugInLoader
    {
        public PlugInDescriptor[] List()
        {
            var editPanel = HttpContext.Current.Handler as EditPanel;

            if (editPanel != null)
            {
                editPanel.Page.Init += delegate
                {
                    // Check if a ScriptManager is already added
                    var scriptManager = ScriptManager.GetCurrent(editPanel.Page);

                    if (scriptManager == null) // No ScriptManager present
                    {
                        scriptManager = new ScriptManager
                        {
                            ID = "scriptManager",
                            EnablePartialRendering = true
                        };

                        editPanel.Page.Form.Controls.AddAt(0, scriptManager);
                    }
                };
            }

            return new PlugInDescriptor[] { };
        }
    }
}

 

$(function () {
    var selectedValues = [];

    var divtextid = $('.textboxid');
    var textboxid = divtextid.text();
    var textbox = $('#' + textboxid);

    divtextid.hide();
    textbox.hide();

    $("#categoryTree")

	.jstree({
	    "plugins": ["themes", "html_data", "ui", "checkbox"],
	    "themes": { "icons": false },
	    "checkbox": {
	        "two_state": true,
	        "real_checkboxes": true
	    }
	})

	.bind("loaded.jstree", function (event, data) {

	    $("#categoryTree").jstree("get_checked", null, true).each
        (function () {
            selectedValues.push($(this).data('name'));
        });

	    textbox.val(selectedValues.join(','));
	})

    .bind("change_state.jstree", function (event, data) {
        var result = $(data.rslt).data('name');
        if (!data.args[1]) {
            // add the item
            selectedValues.push(result);
        } else {
            // remove the item
            selectedValues = jQuery.grep(selectedValues, function (value) {
                return value != result;
            });
        }

        textbox.val(selectedValues.join(','));
    });
});

 

using System;
using EPiServer.Core;
using EPiServer.PlugIn;
namespace MakingWaves.CMS6R2.Internet.Web.CustomProperties
{
    [Serializable]
    [PageDefinitionTypePlugIn]
    public class PropertyCategoryTree : PropertyString
    {
        public override IPropertyControl CreatePropertyControl()
        {
            return new PropertyCategoryTreeControl();
        }
    }
}

 

using System.Collections.Generic;
using System.Linq;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.PlugIn;

namespace MakingWaves.CMS6R2.Internet.Web.CustomProperties
{
    public class PropertyCategoryTreeAttachEvents : PlugInAttribute
    {
        public static void Start()
        {
            DataFactory.Instance.CreatingPage += InstanceSavingPage;
            DataFactory.Instance.SavingPage += InstanceSavingPage;
        }

        private static void InstanceSavingPage(object sender, PageEventArgs e)
        {
            var newSave = new CategoryList();

            foreach (var prop in e.Page.Property)
            {
                if (!(prop is PropertyCategoryTree))
                    continue;

                foreach (int catID in GetIntsFromString(prop.Value.ToString()))
                {
                    var cat = Category.Find(catID);
                    if (cat != null)
                        newSave.Add(catID);
                }
            }

            e.Page.Property["PageCategory"].Value = newSave;
        }

        private static IEnumerable GetIntsFromString(string input)
        {
            int i = 0;
            return (from item in input.Split(',') where int.TryParse(item, out i) select i);
        }
    }
}

 

using EPiServer.Web.PropertyControls;

namespace MakingWaves.CMS6R2.Internet.Web.CustomProperties
{
    public class PropertyCategoryTreeControl : PropertyDataControl
    {
        private PropertyCategoryTreeControlDialog _dialog;

        public override bool SupportsOnPageEdit
        {
            get { return false; }
        }

        public override void ApplyEditChanges()
        {
            SetValue(_dialog.TextCategories.Text);
        }

        public override void CreateEditControls()
        {
            _dialog = Page.LoadControl(@"~/CustomProperties/PropertyCategoryTreeControlDialog.ascx") as PropertyCategoryTreeControlDialog;
            if (_dialog == null)
                return;

            _dialog.CurrentCats = CurrentPage.Category;
            Controls.Add(_dialog);
        }
    }
}

 

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="PropertyCategoryTreeControlDialog.ascx.cs" Inherits="MakingWaves.CMS6R2.Internet.Web.CustomProperties.PropertyCategoryTreeControlDialog" %>
<div id="categoryTree"><%= CurrentCategoryTree %></div>
<asp:TextBox runat="server" ID="txtSelectedValues" CssClass="selectedvalues"></asp:TextBox>
<div class="textboxid"><%= txtSelectedValues.ClientID %></div>
Advertisements