๐Ÿ”ท Core

contentbox-cfml-module-development

Use this skill when building ContentBox modules, including ModuleConfig conventions, admin integration, interceptors, routes, migrations, ORM entities, dependency registration, and module lifecycle hooks.

$ npx skills add coldbox/skills/contentbox-cfml/module-development
$ coldbox ai skills install coldbox/skills/contentbox-cfml/module-development
๐Ÿ”— https://skills.boxlang.io/skills/raw/coldbox/skills/contentbox-cfml~module-development

ContentBox Module Development (CFML)

Build custom modules for ContentBox CMS using CFML. Modules are ColdBox modules that extend ContentBox functionality โ€” adding new admin panels, API endpoints, widgets, interceptors, or content features.

Module Architecture

ContentBox uses a layered module architecture:

LayerPathPurpose
Coremodules/contentbox/ContentBox engine (do not modify)
Sub-modulesmodules/contentbox/modules/Admin, API, UI, deps
Custommodules_app/contentbox-custom/User-owned customizations
Standalonemodules/Independent ColdBox modules

Module Locations for Custom Code

TypeLocation
Custom modulesmodules_app/contentbox-custom/_modules/
Custom widgetsmodules_app/contentbox-custom/_widgets/
Custom themesmodules_app/contentbox-custom/_themes/
Custom contentmodules_app/contentbox-custom/_content/
Standalone modulesmodules/{moduleName}/

ModuleConfig.cfc

Every module requires a ModuleConfig.cfc at its root:

<!--- modules_app/contentbox-custom/_modules/myModule/ModuleConfig.cfc --->
<cfcomponent>

	<!--- Module Properties --->
	<cfset this.title              = "My Module">
	<cfset this.author             = "Your Name">
	<cfset this.webURL             = "https://example.com">
	<cfset this.version            = "1.0.0">
	<cfset this.description        = "Description of my module">
	<cfset this.viewParentLookup   = true>
	<cfset this.layoutParentLookup = true>
	<cfset this.entryPoint         = "mymodule">
	<cfset this.modelNamespace     = "mymodule">
	<cfset this.cfmapping          = "mymodule">
	<cfset this.dependencies       = [ "contentbox" ]>

	<cffunction name="configure" access="public" returntype="void">
		<!--- Module Settings --->
		<cfset settings = {
			mySetting : "defaultValue"
		}>

		<!--- Layout Settings --->
		<cfset layoutSettings = {
			defaultLayout : "layout.cfm"
		}>

		<!--- Custom Interception Points --->
		<cfset interceptorSettings = {
			customInterceptionPoints : [
				"onMyModuleEvent"
			]
		}>

		<!--- Interceptors --->
		<cfset interceptors = [
			{
				class : "mymodule.interceptors.MyInterceptor",
				name  : "MyInterceptor@mymodule"
			}
		]>

		<!--- Routes --->
		<cfset routes = [
			{ pattern = "/mymodule/:action", handler = "home" }
		]>

		<!--- i18n --->
		<cfset cbi18n = {
			resourceBundles : {
				"mymodule" : "#moduleMapping#/i18n/mymodule"
			}
		}>
	</cffunction>

	<cffunction name="onLoad" access="public" returntype="void">
		<!--- Post-load initialization --->
		<!--- Register services, widgets, etc. --->
	</cffunction>

	<cffunction name="onUnload" access="public" returntype="void">
		<!--- Cleanup on module unload --->
	</cffunction>

</cfcomponent>

ModuleConfig Properties

PropertyDescription
titleModule display name
authorAuthor name
webURLAuthor/project website
versionModule version
descriptionModule description
viewParentLookupInherit views from parent modules (true/false)
layoutParentLookupInherit layouts from parent modules (true/false)
entryPointSES URL prefix for module routes
modelNamespaceWireBox DI namespace (e.g., @mymodule)
cfmappingColdFusion mapping for the module
dependenciesArray of required module names

ModuleConfig Methods

MethodWhen CalledPurpose
configure()Module registrationDefine settings, routes, interceptors
onLoad()After module loadedPost-load initialization
onUnload()Module unloadedCleanup resources
onMissingMethod()Missing method callsDynamic method handling

Module Directory Structure

myModule/
โ”œโ”€โ”€ ModuleConfig.cfc       โ† Module configuration
โ”œโ”€โ”€ box.json               โ† ForgeBox/CommandBox metadata
โ”œโ”€โ”€ handlers/              โ† ColdBox event handlers
โ”‚   โ””โ”€โ”€ Home.cfc
โ”œโ”€โ”€ models/                โ† Business logic and entities
โ”‚   โ”œโ”€โ”€ services/
โ”‚   โ”‚   โ””โ”€โ”€ MyService.cfc
โ”‚   โ””โ”€โ”€ entities/
โ”‚       โ””โ”€โ”€ MyEntity.cfc
โ”œโ”€โ”€ views/                 โ† View templates
โ”‚   โ””โ”€โ”€ home/
โ”‚       โ””โ”€โ”€ index.cfm
โ”œโ”€โ”€ layouts/               โ† Layout templates
โ”‚   โ””โ”€โ”€ layout.cfm
โ”œโ”€โ”€ interceptors/          โ† ColdBox interceptors
โ”‚   โ””โ”€โ”€ MyInterceptor.cfc
โ”œโ”€โ”€ widgets/               โ† ContentBox widgets
โ”‚   โ””โ”€โ”€ MyWidget.cfc
โ”œโ”€โ”€ modules/               โ† Nested sub-modules
โ”‚   โ””โ”€โ”€ subModule/
โ”œโ”€โ”€ config/                โ† Configuration files
โ”‚   โ””โ”€โ”€ Router.cfc
โ”œโ”€โ”€ i18n/                  โ† Resource bundles
โ”‚   โ””โ”€โ”€ mymodule.properties
โ”œโ”€โ”€ migrations/            โ† Database migrations
โ”‚   โ””โ”€โ”€ 001_create_my_table.cfm
โ””โ”€โ”€ includes/              โ† Static assets, help files
    โ”œโ”€โ”€ css/
    โ”œโ”€โ”€ js/
    โ””โ”€โ”€ help/

Creating an Admin Module

To add functionality to the ContentBox admin:

<!--- ModuleConfig.cfc with admin integration --->
<cfcomponent>

	<cfset this.title          = "My Admin Module">
	<cfset this.entryPoint     = "cbadmin/myModule">
	<cfset this.modelNamespace = "myModule">
	<cfset this.dependencies   = [ "contentbox", "contentbox-admin" ]>

	<cffunction name="configure">
		<cfset settings = {}>

		<cfset interceptorSettings = {
			customInterceptionPoints : []
		}>

		<cfset routes = [
			{ pattern = "/cbadmin/myModule/:action", handler = "myModule" }
		]>
	</cffunction>

	<cffunction name="onLoad">
		<!--- Register with admin menu via interception --->
	</cffunction>

</cfcomponent>

Admin Handler Pattern

<!--- handlers/MyModule.cfc --->
<cfcomponent extends="contentbox.modules.contentbox-admin.handlers.baseHandler" singleton>

	<cffunction name="index" access="public" returntype="void">
		<cfset prc.pageTitle = "My Module">
		<cfset setView( "myModule/index" )>
	</cffunction>

</cfcomponent>

Registering Admin Menu Items

Listen to cbadmin_onAdminMenuLoad to add menu items:

<!--- interceptors/AdminMenu.cfc --->
<cfcomponent>

	<cffunction name="configure">
	</cffunction>

	<cffunction name="onAdminMenuLoad" access="public" returntype="void">
		<cfargument name="event" type="any">
		<cfargument name="data" type="struct">
		<cfargument name="buffer" type="any">
		<cfargument name="rc" type="struct">
		<cfargument name="prc" type="struct">

		<!--- Add menu item to data.menuItems --->
		<cfset arrayAppend( data.menuItems, {
			name     : "My Module",
			link     : event.buildLink( "cbadmin/myModule" ),
			icon     : "star",
			permission : "MYMODULE_ACCESS"
		} )>
	</cffunction>

</cfcomponent>

Registering 2FA Providers

Register custom two-factor authentication providers in onLoad():

<cffunction name="onLoad">
	<cfset var twoFactorService = wirebox.getInstance( "twoFactorService@contentbox" )>
	<cfset twoFactorService.registerProvider(
		wirebox.getInstance( "MyTwoFactorProvider@myModule" )
	)>
</cffunction>

Registering Content Helpers

Inject mixins into all content objects:

<!--- In core ModuleConfig.cfc configure() --->
<cfset settings.contentHelpers = [
	"myModule.models.mixins.MyContentMixin"
]>

Database Migrations

Use cfmigrations for schema changes:

<!--- migrations/001_create_my_table.cfm --->
<cfscript>
	function up( schema, qb ){
		schema.create( "my_table", function( table ){
			table.increments( "id" ).primary();
			table.string( "name" ).nullable( false );
			table.timestamps();
		} );
	}

	function down( schema, qb ){
		schema.drop( "my_table" );
	}
</cfscript>

Run migrations:

box run-script contentbox:migrate
box run-script contentbox:migrate:up

ORM Entities

Create persistent entities with ContentBox conventions:

<!--- models/entities/MyEntity.cfc --->
<cfcomponent persistent="true" entityname="cbMyEntity" table="cb_my_table" extends="contentbox.models.BaseEntity">

	<cfproperty name="myEntityID" fieldtype="id" generator="uuid" ormtype="string">
	<cfproperty name="name" ormtype="string" length="255" notnull="true">
	<cfproperty name="description" ormtype="text">

</cfcomponent>

Module Dependencies

Declare dependencies in ModuleConfig.cfc:

<cfset this.dependencies = [
	"contentbox",           // Core ContentBox
	"contentbox-admin",     // Admin module
	"contentbox-api",       // API module
	"cbsecurity",           // Security module
	"cborm"                 // ORM module
]>

Accessing ContentBox Services

<!--- Inject ContentBox services --->
<cfproperty name="entryService" inject="entryService@contentbox">
<cfproperty name="pageService" inject="pageService@contentbox">
<cfproperty name="authorService" inject="authorService@contentbox">
<cfproperty name="categoryService" inject="categoryService@contentbox">
<cfproperty name="menuService" inject="menuService@contentbox">
<cfproperty name="settingService" inject="settingService@contentbox">
<cfproperty name="securityService" inject="securityService@contentbox">
<cfproperty name="widgetService" inject="widgetService@contentbox">
<cfproperty name="themeService" inject="themeService@contentbox">
<cfproperty name="cb" inject="CBHelper@contentbox">

Interception Points

Core ContentBox Interception Points

PointWhen Fired
cb_onContentRenderingBefore content is rendered
cb_onContentStoreRenderingBefore ContentStore content is rendered

Admin Interception Points

See the admin extension skill for the full list of cbadmin_* interception points.

Best Practices

  1. Use modules_app/contentbox-custom/_modules/ for user-owned modules
  2. Declare dependencies in this.dependencies
  3. Use namespace injection โ€” @moduleName for DI
  4. Follow ColdBox conventions โ€” handlers, models, views, layouts
  5. Use migrations for database changes โ€” never modify schema directly
  6. Extend base classes โ€” BaseEntity, baseHandler, BaseWidget
  7. Register interceptors for admin menu, content lifecycle, etc.
  8. Use provider: injection to avoid circular dependencies
  9. Include box.json for ForgeBox distribution
  10. Test with all three engines โ€” Lucee, Adobe CF, BoxLang

Engine Compatibility

This skill targets CFML engines (Lucee 5+, Adobe ColdFusion 2018+). For BoxLang-specific syntax and features, see the BoxLang variant of this skill.