How to add custom modules to Kentico CMS

   —   
The customization series continues. Today, we will go through the creation of your own modules and what is available for you in this case.
Hi there,

I am keeping a list of customization topics I would like to go through within this series of closely subsequent posts and it seems like whenever I cover one, two new topics are added to my list. Yes, there is really lot of options how you can customize Kentico CMS and in time it increases and new and better options are coming with every version.

Today's topic are the modules in general.

What is the module?

You can think of modules in many ways, it can be set of web parts, part of the page with specific functionality, and I guess someone could call a module even a customized string. When I hear module, I see some specific set of features, components, functionality and data which can be managed independently on other modules. So when I say module, I mean something that could be attached to the core of the system and removed, without affecting anything else in the system. Because when something is modular, you can dismount it to several pieces without destroying its integrity, right? And this post is exactly about this. It should help you to create modules in the exact same term, so you can ship them for example on our Market place.

The basics

Registering the module in the UI is quite simple. We usually put the modules into the CMSDesk -> Tools section menu to separate them from any other UI.

Just quick note: Part of the upcoming version 4.1 is the availability for the custom installation, where you can install your project with just some of the default modules which you need to make the web project more efficient (having less files). We have done a very large step in the module separability.

When you go to the SiteManager -> Development -> Modules, you can see that our modules are also registered in the Tools section.

So try to create a new module:

Display name: Google module
Code name: Google
Show in: Tools
Resource URL: http://www.google.com

You also need to go to the Sites tab and assign the module to particular sites to be able to display them. Now when you go to the Tools section of CMSDesk, you can display the new module.

You can see it's icon is just some default icon (blue cube), you may change it by creating the icon files ~/App_Themes/Default/Images/CMSModules/google.gif (16x16) and google_big.gif (24x24)

So that is how you register your own application as a module. If you want to create some module files (so the module is a part of your application), you can do that anywhere, but I suggest you the folder ~/CMSGlobalFiles so your module goes to the export package.

This is also covered here http://devnet.kentico.com/docs/devguide/custom_modules.htm I just wanted to make sure that you have the basics and you know that something like that is possible.

Real world module

So we create a very simple module. It's purpose will be to give an instant message from admin to all site visitors, so I created a page ~/CMSGlobalFiles/AdminIM/default.aspx with a text area for the message, time selectors for the time from which to which the message should be displayed and the button to save it. And also a static class with the properties to carry that data in ~/App_Code/Global/MyModules/AdminIM.cs

We register the module within the modules withthe path ~/CMSGlobalFiles/AdminIM/default.aspx and assign it to the site and we are able to edit the values within our "module".

 

Now we can display the module values on live site using the module class properties in the ASPX code or code behind like <%= MyModules.AdminIM.GetCurrentMessage() %>

That was the way how to access the module from the code, but what if someone wants to just use some macro for that? In such case, you may register the module within Kentico CMS API and provide it's context to the macro resolver.

Integrating with Kentico CMS API

You may register the module with Kentico CMS API and provide it's context to the macro resolver. See http://devnet.kentico.com/docs/devguide/appendix_a___macro_expressions.htm for details on macros.

We will want to provide macro {%AdminIMContext.Data.CurrentMessage%} (I will explain later why it has to have the "Data" part) so you can use it in some static text or any other web part property.

To be able to register the module, we have to have a DLL for that, with the module class, so I will need to change the module a little, I will use different namespace for it (MyDLLModules) and different page (DefaultWithContext.aspx) so you can try both at once.

So you move over the static class from App_Code to the library and create a ModuleEntry for the module. It will need the reference to our CMS.SettingsProvider library, I will call the class file AdminIMModule.cs. It just needs to define the constructor with ModuleInfo parameter.

Then you create the context class, in AdminIMContext.cs. It will inherit the AbstractContext class. You need to define some basic code (the "Context" region), see the class file for that, and the most important part for you is:

/// <summary>
/// Gets the context property
/// </summary>
/// <param name="name">Property name</param>
public override object GetProperty(string name)
{
  switch (name.ToLower())
  {
    case "data":
      return AdminIMInfo.CurrentInfo;
  }

  return null;
}


Where you define which properties are available from the context object. Please note that the properties are structured objects implementing IDataContainer interface, so it must be done through the internal object and it is also the reason why our macro is little longer. Here is how the class with the info looks like:

/// <summary>
/// Data container
/// </summary>
public class AdminIMInfo : IDataContainer
{
  /// <summary>
  /// Current information object
  /// </summary>
  public static AdminIMInfo CurrentInfo = new AdminIMInfo();

  #region IDataContainer Members

  /// <summary>
  /// Column names
  /// </summary>
  public string[] ColumnNames
  {
    get
    {
      return new string[] { "CurrentMessage" };
    }
  }

  /// <summary>
  /// Returns true if the container contains specific column
  /// </summary>
  /// <param name="columnName">Column name</param>
  public bool ContainsColumn(string columnName)
  {
    return (columnName.ToLower() == "currentmessage");
  }


  /// <summary>
  /// Gets the value of specific column
  /// </summary>
  /// <param name="columnName">Column name</param>
  public object GetValue(string columnName)
  {
    if (columnName.ToLower() == "currentmessage")
    {
      return AdminIMContext.CurrentMessage;
    }
    else
    {
      return null;
    }
  }

  /// <summary>
  /// Sets the value of specific column
  /// </summary>
  /// <param name="columnName">Column name</param>
  /// <param name="value">New value</param>
  public void SetValue(string columnName, object value)
  {
    // Do nothing
  }

  #endregion
}


Then you need to publish the context through the module using this in the module class (AdminIMModule.cs):

/// <summary>
/// Returns the hashtable of the module contexts. [name.ToLower()] -> [AbstractContext]
/// </summary>
public override Hashtable GetContexts()
{
  Hashtable contexts = new Hashtable();
  contexts["adminimcontext"] = AdminIMContext.ContextObject;

  return contexts;
}


This is the way how you provide contexts from modules. The important thing here that the context name must end with "context" so the Macro resolver knows to query the modules for that.

So our module is ready, we just need to register the module somehow. We want to register it only once, so we do that in the ~/App_Code/Global/CMS/CMSApplication.cs as I described as a connection point in my previous post, here: Blogs/Martin-Hejtmanek/June-2009/How-to-effectively-customize-Kentico-CMS-UI.aspx

You need to just call single line of code:

/// <summary>
/// Fires after the application start event
/// </summary>
public static void AfterApplicationStart(object sender, EventArgs e)
{
  // Register the module
  CMS.SettingsProvider.ModuleEntry.RegisterModule("AdminIM", "AdminIM", "MyDLLModules.AdminModule", false);
}


First comes the module name (any), then the DLL name, then the fully qualified class name and the last one is if the Init method of the module should be called right now (you can override it to load anything necessary for the module). We don't have one so we don't need to call it obviously.

And that's it! Now you can use the module and macro {%AdminIMContext.Data.CurrentMessage%} in your web parts, so just place a static text web part somewhere on your template and put it to its text.

How complicated was that?

Here is the code of for the web project: CMS.zip
And here is the library code: AdminIM.zip

I am sure you can do the rest (add an egg or register the module within Site manager by yourselves ;-) )

See you at my next post
Share this article on   LinkedIn Google+

Martin Hejtmanek

Hi, I am the CTO of Kentico and I will be constantly providing you the information about current development process and other interesting technical things you might want to know about Kentico.