CMS On-site editing customization

#Kentico development #Kentico Kontent

SHARE

 
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.

Author

Check other articles

Bitsorchestra
5 5

What our clients say

Bits Orchestra team are outstanding developers​. They listen carefully to our business needs and easily turns our business objectives into a well thought out and executed development effort. Roman is very bright and definitely the most capable developer that has worked on our site. He is not only a Kentico expert but has successfully tackled other complicated development assignments demonstrating expertise in both front and backend development. Roman takes initiative to suggest enhancements that make site maintenance easier while improving the customer experience. The team is very responsive to our work requests and has great follow up. They have also worked very business partners and this has reflected positively on our company. Roman is a true partner for us and a tremendous asset to our organization. We will continue to work with them and would highly recommend Roman and his team for your development needs. He and his team will exceed your expectations!
 Alan Lehmann
Alan Lehmann
President at In energy sector

What our clients say

The Bits Orchestra team does excellent work. They are always available and I appreciate our frequent calls and screen-shares together. Their dedication to the projects and knowledge of Kentico is outstanding. They truly care about the quality of their work, and became a part of our team easily!
Shena Lowe
Shena Lowe
Managing Partner at Consensus Interactive

What our clients say

We hired Roman for a Kentico analysis project and have been very satisfied. He is very skilled and professional. We are looking to hire him and his team again on future projects.
Sylvain Audet
Sylvain Audet
CEO at MyDevPartner.com

What our clients say

Roman and team have taken over an existing Kentico EMS site for a large US Oil Company. So far, they have handled every single request that we have thrown at them and these were diverse, challenging, often bespoke, usually urgent and almost daily, over the last 11 months. Their work is of an extremely high quality, they are capable, quick and we have great confidence in the support that we are getting.
Jon Hollis
Jon Hollis
Head of Web Development at confidential

What our clients say

Bits Orchestra team was very helpful, they had a good understanding of the brief and deep knowledge of the system. They were always keen to provide advice and recommendations that benefit the project substantially.
Ramon Lapenta
Ramon Lapenta
Senior Front End Developer at Cyber-Duck Ltd