As you’re developing ContentBox modules, you will certainly come to a point where you’ll want to be able to extend the Page/Entry editor. Whether you need to add a custom section to the options sidebar, or add some functionality beneath the main content editing area, ContentBox makes it incredibly easy to add your own custom functionality/views via interceptors.

In what follows, I’ll share an example of a module I recently developed and show how simple it is to add your own custom functionality to the ContentBox page/entry editor.

Some Context

Before we begin, let me describe what we’re building. My module is called “Super Menu”, and allows users to not only create custom menus from page/post content (as well as custom links), but also allows users to apply these custom menus to individual pages and entries. Because of this, my module needs to have a “section” in the page/entry editor that allows users to choose from a drop-down of menu names. For my module, I decided to place it in the “Page Details” sidebar. At the end, it should look something like this (notice the custom “Super Menu” section at the bottom):

And, of course, when the form itself is submitted, I’ll want to be able to capture any form fields and values that were included in the custom content…

Interceptors

So now that we know what we’re shooting for, let’s talk about how to achieve it. Ultimately, we’re going to have to rely upon a custom interceptor to hook into the rendering AND submission of the form. If you’ve never used interceptors in ColdBox before, I’d suggest checking out the guide first.

Create the Interceptor Shell

Alright, so first let’s create out interceptor. To do this:

  • In your ContentBox custom module folder, add an interceptors folder (mine is: cboxroot/modules/contentbox/modules/SuperMenu/interceptors)
  • Add a new component, and call it whatever you want (mine is SuperMenu.cfc)
    • Make sure that your interceptor extends coldbox.system.Interceptor

Determine the Interception Point

Ok, good enough. Now that we’ve created our interceptor, we can start hooking into the page/entry editor. At this point, though, we need to decide exactly where we want to hook into. In both the page and entry editors, there are a number of interception points that might work, such as:

  • cbadmin_pageEditorSidebar
  • cbadmin_pageEditorSidebarAccordion
  • cbadmin_pageEditorSidebarFooter
  • cbadmin_pageEditorFooter
  • cbadmin_pageEditorInBody

The best way to determine which to use is to actually see where these interception points live within the ContentBox view where they are announced. These views, for pages and entries, respectively, can be found here:

  • cboxroot/modules/contentbox-admin/views/pages/editor.cfm
  • cboxroot/modules/contentbox-admin/views/entries/editor.cfm

When exploring these views, you’ll be looking for something like:

<!--- Event --->
#announceInterception("cbadmin_pageEditorSidebarAccordion")#

This indicates that an interception is being announced, and you can hook into that announcement and do whatever you need to do.

Hook into the Interception Point

Since I want to add a mini-section to the “Page Details” sidebar, I’m going to leverage the cbadmin_pageEditorSidebarAccordion interception point. To make use of this in the interceptor that I created earlier, I’ll start by adding the following:

/**
* Add content page editor side-bar
*/
public void function cbadmin_pageEditorSidebarAccordion( required Any event ) {
   // do some processing here...
}

Nothing complicated here. In my interceptor, I simply create a method that has the same name as the interception point that I want to hook into. Now that I’ve got a hook into the page editor, I can do whatever I need to do. In my case, I want to add some html, including some form fields, that will be submitted along with the rest of the form. While I could build out some nasty strings in the interceptor itself and append those to the buffer, a MUCH EASIER way is to simply create a new view that contains the HTML I want to render, and then render the view itself to the buffer.

Here’s how to do that. First, in your module folder, add a new file to your views folder. Mine is called menuselect.cfm. Fill it up with whatever HTML and CFML you need. Next, in your custom interceptor, render your view and use the appendToBuffer() method to add your view’s content to the page editor. Here’s an example:

/**
* Add content page editor side-bar
*/
public void function cbadmin_pageEditorSidebarAccordion( required Any event ) {
   appendToBuffer( 
      renderView( 
         view='menu/menuselect', 
         module="SuperMenu", 
         args=SuperMenuService.getAccordionContent( event.getCollection() ) 
      ) 
   );
}

So as you can see, I simply render my custom view, passing along some custom data from my SuperMenuService. The rendered view is then appended to the buffer, which is really just a fancy way of saying that whatever content is contained in my rendered view is added to the rendered content of the page editor AT THE POINT of interception. In other words, since I am doing all of this at the cbadmin_pageEditorSidebarAccordion interception point, my custom view’s content will be added to the end of the sidebar accordion content…e.g., will create a new panel within the sidebar accordion menu.

If we now open up the page editor, we should see our new accordion panel in the Page Details menu on the right. Woohoo!

Intercepting Form Submission

Even though we’ve successfully added a custom section to the page editor, we’re only halfway done. After all, it doesn’t do us much good to add an interface if ContentBox still doesn’t know about what to do with the content of that interface. So the last step in our process is to define an interceptor for the save event of the page so that we specify custom rules for managing our injected content.

Before we do this, though, it’s important to understand that there are a number of “page-saving” related interception points to which we have access, including:

  • cbadmin_prePageSave / cbadmin_preEntrySave
  • cbadmin_postPageSave / cbadmin_postEntrySave
  • cbadmin_prePageRemove / cbadmin_preEntryRemove
  • cbadmin_postPageRemove / cbadmin_postEntryRemove
  • cbadmin_onPageStatusUpdate / cbadmin_onEntryStatusUpdate

As with determining where to place your custom editor content, it’s important to hook into the correct interception points when dealing with the submitted data. For this example, I’m going to use cbadmin_postPageSave.

Like before, to intercept the cbadmin_postPageSave, I simply need to create a method with the same name in my custom interceptor component. Mine looks like:

/**
* Make any additions/update/deletions to page links based on changes in page state
*/
public Any function cbadmin_postPageSave( 
   required Any event, 
   required struct interceptData 
) {
   SuperMenuService.manageContentMenuLinks( 
      collection=event.getCollection(), 
      content=arguments.interceptData.page 
   );
}

In this case, the interceptor has two arguments: the event (which includes the request collection) and interceptData. Within my interceptor, I simply pass off the request collection and the “page” entity within the interceptData to my custom service.

What is important to see, though, is that with the event and interceptData, I have full access to all the information about the submitted data that I could possibly want, and can do whatever I need to with that information either here in my interceptor or some other service.

Wrapping Up

As you can see, adding custom functionality to the ContentBox page (and entry) editor is as simple as determining the correct interception points to leverage in your code. With these simple steps, you are minutes away from unleashing your modules to do some truly awesome stuff, all without ever having to modify the core of ContentBox whatsoever.

I hope this short walkthrough is helpful, and be sure to check out the full code for Super Menu.