Loading

CMS On-site editing customization

Kentico allows to work with the content of websites through an on-site editing interface, which make it easy for editors to work with page content and manage documents directly while viewing the website from the live site perspective. In this post we will extend this functionality to be able to work with custom modules using the on-site editing interface.
 

Sometimes for convenience you may want to be able to edit information of some custom module through on-site editing interface - like you have ability to edit properties of current page in modal dialog. It is possible, even without modification of Kentico built-in controls and pages.

The first step is to identify what custom modules you want to be available through Kentico on-site editing interface. Then group selected modules under single UI element that can be placed under on-site editing UI element. Then we will need to create custom control that will display all necessary modules in drop-down menu and load this control dynamically using global events handling. So, let's go down to business and review all required steps in details.

First of all create a new module with code name, for example OnSiteEditCustom. Then on User interface tab, under CMS On-site editing UI element, we should create custom UI element (OnSiteCustomModules - code name) where we will place elements (names of modules) that should be available for on-site editing. As an example, for setting relations between regular custom module UI elements (those that are used in Kentico administration module) and those which are used for on-site editing, we will give them the same code name.

Then we should create control, to display list of modules available for on-site editing. As it is recommended by Kentico, we need to place this control to folder /CMSModules/OnSiteEditCustom/. Examples of .ascx file and code behind logic you can find below. For module's menu we will use existing UniMenu control that will be dynamically populated in code behind with set items in created module based on agreed relations. Modal dialog is used to display module's editing interface.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="CustomModulesMenu.ascx.cs" Inherits="CMSModules_OnSiteEditingCustom_CustomModulesMenu" %>
<%@ Register Src="/CMSAdminControls/UI/UniMenu/UniMenuButtons.ascx" TagName="UniMenuButtons"
    TagPrefix="cms" %>
<div class="ActionButtons">
    <cms:UniMenuButtons ID="otherMenu" ShortID="cmm" runat="server" EnableViewState="false" />
</div>
 
using System;
using System.Data;
using System.Web.UI.WebControls;
using CMS.Helpers;
using CMS.Modules;
using CMS.PortalEngine;
using CMS.UIControls;
using MenuItem = CMS.UIControls.UniMenuConfig.Item;
using SubMenuItem = CMS.UIControls.UniMenuConfig.SubItem;

public partial class CMSModules_OnSiteEditingCustom_CustomModulesMenu : CMSUserControl
{
    private const string ModalDialogScript = "modalDialog({0}, '{1}', '90%', '90%');";
    private const string OnsiteCustomModuleName = "OnSiteEditCustom";
    private const string OnsiteCustomElementName = "OnSiteCustomModules";

    protected void Page_Load(object sender, EventArgs e)
    {
        var cui = CurrentUser;

        if (cui != null)
        {
            UIElementInfo uiInfo = UIElementInfoProvider.GetUIElementInfo(OnsiteCustomModuleName, OnsiteCustomElementName);

            if (uiInfo != null && cui.IsAuthorizedPerUIElement(OnsiteCustomModuleName, OnsiteCustomElementName))
            {
                MenuItem customModuleItem = new MenuItem();
                customModuleItem.CssClass = "BigButton";
                customModuleItem.ImageAlign = ImageAlign.Top;
                customModuleItem.IconClass = "icon-app-modules";
                customModuleItem.Text = string.IsNullOrEmpty(uiInfo.ElementCaption) ? uiInfo.ElementDisplayName : uiInfo.ElementCaption;
                customModuleItem.Tooltip = uiInfo.ElementDescription;
                customModuleItem.ImageAltText = uiInfo.ElementCaption;

                otherMenu.Buttons.Add(customModuleItem);

                DataSet ds = UIElementInfoProvider.GetChildUIElements(OnsiteCustomModuleName, OnsiteCustomElementName);
                if (!DataHelper.DataSourceIsEmpty(ds))
                {
                    foreach (DataRow dr in ds.Tables[0].Rows)
                    {
                        string moduleUrl = GetRelatedElementUrl(ValidationHelper.GetString(dr["ElementName"], ""), ValidationHelper.GetInteger(dr["ElementResourceID"] , 0));
                        if(!string.IsNullOrEmpty(moduleUrl))
                        {
                            SubMenuItem subItem = new SubMenuItem();
                            subItem.Text = ResHelper.LocalizeString(ValidationHelper.GetString(dr["ElementCaption"], ""));
                            subItem.OnClientClick += GetModalDialogScript(moduleUrl, ValidationHelper.GetString(dr["ElementName"], ""));
                            customModuleItem.SubItems.Add(subItem);
                        }
                    }
                }
            }
        }

        ScriptHelper.RegisterDialogScript(Page);
    }

    /// <summary>
    /// Returns Url of related custom module UI element
    /// </summary>
    private string GetRelatedElementUrl(string elementName, int moduleId)
    {
        string moduleUrl = "";
        DataSet elements = UIElementInfoProvider.GetUIElements().WhereEquals("ElementName", elementName).And().WhereNotEquals("ElementResourceID", moduleId).Result;
        if (!DataHelper.DataSourceIsEmpty(elements))
        {
            DataRow elem = elements.Tables[0].Rows[0];
            ResourceInfo module = ResourceInfoProvider.GetResourceInfo(ValidationHelper.GetInteger(elem["ElementResourceID"], 0));
            if (module != null && ResourceSiteInfoProvider.IsResourceOnSite(module.ResourceName, CurrentSite.SiteName))
            {
                moduleUrl = UIContextHelper.GetElementDialogUrl(module.ResourceName, ValidationHelper.GetString(elem["ElementName"], ""));
            }
        }

        return moduleUrl;
    }

    /// <summary>
    /// Returns script string for displaying modal dialog.
    /// </summary>
    private string GetModalDialogScript(string url, string dialogName, bool useQuotesForUrl = true)
    {
        if (useQuotesForUrl)
        {
            url = "'" + url + "'";
        }

        return String.Format(ModalDialogScript, url, dialogName);
    }
}
 

When control is created, the next step is to display it on on-site editing toolbar. Global event handling is very useful here. Create class file in folder /App_Code/CMSModules/OnSiteEditCustom/. In the class we load created drop-down menu control using available event handlers and properties for UIToolbar control. Code sample of class is placed below. For more information on global event handling in modules, see: Initializing modules to run custom code.

 
using CMS;
using CMS.DataEngine;
using CMS.UIControls;
using CMS.UIControls.UniMenuConfig;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

[assembly: RegisterModule(typeof(OnSiteEditCustomModule))]
public class OnSiteEditCustomModule:Module
{
    public OnSiteEditCustomModule() : base("OnSiteEditCustom") { }

    /// <summary>
    /// Initializes the module. Called when the application starts.
    /// </summary>
    protected override void OnInit()
    {
        base.OnInit();

        UIToolbar.OnBeforeUserControlLoad += UIToolbar_OnBeforeUserControlLoad;
    }

    private bool UIToolbar_OnBeforeUserControlLoad(object sender, EventArgs e)
    {
        UIToolbar toolbar = sender as UIToolbar;
        if (toolbar != null)
        {
            toolbar.OnGroupsCreated += toolbar_OnGroupsCreated;
        }

        return true;
    }

    private void toolbar_OnGroupsCreated(object sender, List<Group> groups)
    {
        Group customModule = groups.Find(g => g.CssClass.Contains("OnSiteCustomModule"));
        if (customModule != null)
        {
            customModule.ControlPath = "/CMSModules/OnSiteEditCustom/CustomModulesMenu.ascx";
        }

    }
}
 

If you found this post useful please leave your comments below. Your feedback is very important for us.