Skip to content

Creating Custom Modules

Modules are self-contained feature extensions that boot conditionally based on enabled status and dependency availability. The module system was introduced in version 7.5.0.


Located at includes/modules/class-ld-dashboard-module-base.php.

All custom modules must extend this abstract class.

Method Return Description
get_id(): string string Unique module identifier (e.g., my-analytics). Must be a valid slug.
get_name(): string string Human-readable module name for display in settings.
get_description(): string string Short description of what the module does.
boot(): void void Module initialization — register hooks, load files, enqueue assets. Called only when all dependencies pass.

Declare dependencies in the $requires property. The module will not boot if any dependency is missing.

protected $requires = array(
'plugin:woocommerce', // Check if WooCommerce class exists
'class:WooCommerce', // Check for specific class
'function:wc_get_product', // Check for function
'constant:WOOCOMMERCE_VERSION', // Check for constant
);

Dependency types:

Prefix Check Example
plugin: Class lookup for known plugins, then is_plugin_active() plugin:buddypress
class: class_exists() class:BuddyPress
function: function_exists() function:bp_is_active
constant: defined() constant:BP_VERSION

Known plugins with class-based checks: buddypressBuddyPress, bp-user-todo-listBP_User_Todo_List, learndashSFWD_LMS, woocommerceWooCommerce.

The registry calls init() on each module. You cannot override init() — it is declared final.

init() is called by registry
├── is_enabled()? (checks ld_dashboard_module_settings option)
│ └── false → skip (module disabled by user)
├── check_dependencies()? (iterates $requires)
│ └── false → skip (missing plugin/class/function/constant)
└── boot() is called
└── do_action('ld_dashboard_module_loaded', $module)
└── do_action('ld_dashboard_module_{id}_loaded', $module)
Method Default Description
is_enabled(): bool Reads ld_dashboard_module_settings option Override to use a different settings source.
get_settings_config(): array Base config with id, name, description, requires, enabled Override to add module-specific settings fields for the admin UI.
Method Description
get_module_path(): string Returns the module’s directory path with trailing slash.
get_module_url(): string Returns the module’s URL with trailing slash.

Located at includes/modules/class-ld-dashboard-module-registry.php.

The registry is a singleton that manages all module registration and lifecycle.

Register modules inside the ld_dashboard_register_modules action hook, which fires during the registry’s private constructor before built-in modules boot.

add_action( 'ld_dashboard_register_modules', function( $registry ) {
$registry->register( 'my-analytics', 'My_Analytics_Module' );
} );

You must register modules before the registry calls boot(). Registering after boot triggers _doing_it_wrong().

// Get the registry instance
$registry = ld_dashboard_modules();
// Check if a module is active (loaded, deps met, and booted)
if ( ld_dashboard_is_module_active( 'zoom' ) ) {
// Zoom is running.
}
// Get a module instance
$zoom = ld_dashboard_modules()->get( 'zoom' );
// Get all active modules
$active = ld_dashboard_modules()->get_active_modules();
// Check if a module is registered (not necessarily active)
$registered = ld_dashboard_modules()->is_registered( 'my-analytics' );

Modules can be toggled in the admin via Settings → Integrations. The enabled state is stored in the ld_dashboard_module_settings WordPress option as an associative array of module_id => bool.

// Programmatically enable/disable a module
ld_dashboard_modules()->set_module_enabled( 'my-analytics', true );
// Check if a module is enabled (not the same as active — dependency check not run)
$module = ld_dashboard_modules()->get( 'my-analytics' );
if ( $module && $module->is_enabled() ) {
// Enabled in settings.
}

This example creates an “Analytics” module that requires a custom analytics plugin to be active.

<?php
/**
* Analytics Module for LD Dashboard.
*
* @since 1.0.0
*/
class My_Analytics_Module extends LD_Dashboard_Module_Base {
/**
* Module dependencies.
*
* @var array
*/
protected $requires = array(
'plugin:my-analytics-plugin', // Must be active
'class:My_Analytics', // Class must exist
);
/**
* Get module ID.
*
* @since 1.0.0
* @return string
*/
public function get_id(): string {
return 'my-analytics';
}
/**
* Get module name.
*
* @since 1.0.0
* @return string
*/
public function get_name(): string {
return __( 'My Analytics', 'my-plugin' );
}
/**
* Get module description.
*
* @since 1.0.0
* @return string
*/
public function get_description(): string {
return __( 'Adds advanced analytics tracking to the LearnDash Dashboard.', 'my-plugin' );
}
/**
* Boot the module.
*
* Called only when enabled and all dependencies are met.
*
* @since 1.0.0
* @return void
*/
protected function boot(): void {
// Load module files.
require_once $this->get_module_path() . 'class-analytics-tracker.php';
// Register hooks.
add_action( 'ld_dashboard_register_reports', array( $this, 'register_reports' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
// Register REST route.
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
}
/**
* Register analytics reports with the report registry.
*
* @since 1.0.0
* @return void
*/
public function register_reports(): void {
LD_Dashboard_Report_Registry::register( 'analytics-overview', 'My_Analytics_Overview_Report' );
}
/**
* Enqueue module assets.
*
* @since 1.0.0
* @return void
*/
public function enqueue_assets(): void {
if ( ! is_page() ) {
return;
}
wp_enqueue_script(
'my-analytics-module',
$this->get_module_url() . 'assets/analytics.js',
array( 'jquery' ),
'1.0.0',
true
);
}
/**
* Register REST routes.
*
* @since 1.0.0
* @return void
*/
public function register_rest_routes(): void {
register_rest_route(
'my-plugin/v1',
'/analytics',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'rest_get_analytics' ),
'permission_callback' => function() {
return current_user_can( 'manage_options' );
},
)
);
}
/**
* REST callback.
*
* @since 1.0.0
* @param WP_REST_Request $request REST request.
* @return WP_REST_Response
*/
public function rest_get_analytics( WP_REST_Request $request ): WP_REST_Response {
$data = My_Analytics::get_summary();
return rest_ensure_response( array( 'success' => true, 'data' => $data ) );
}
/**
* Provide settings configuration for the admin UI.
*
* @since 1.0.0
* @return array
*/
public function get_settings_config(): array {
$config = parent::get_settings_config();
// Add module-specific fields.
$config['fields'] = array(
array(
'id' => 'tracking_enabled',
'label' => __( 'Enable Tracking', 'my-plugin' ),
'type' => 'checkbox',
'default' => true,
),
);
return $config;
}
}
// Register the module via the action hook.
add_action( 'ld_dashboard_register_modules', function( $registry ) {
$registry->register( 'my-analytics', 'My_Analytics_Module' );
} );
// Check if active before using module features.
if ( ld_dashboard_is_module_active( 'my-analytics' ) ) {
$module = ld_dashboard_modules()->get( 'my-analytics' );
// Use module methods.
}
// React to module loading.
add_action( 'ld_dashboard_module_my-analytics_loaded', function( $module ) {
// Module is active — extend or decorate it.
} );

ID Class Dependency
zoom LD_Dashboard_Zoom_Module None (uses bundled Zoom API class)
buddypress LD_Dashboard_BP_Module plugin:buddypress (BuddyPress class)
todo LD_Dashboard_Todo_Module plugin:bp-user-todo-list (BP_User_Todo_List class)
uncanny-groups LD_Dashboard_Uncanny_Groups_Module Uncanny Groups plugin

Module files are autoloaded from includes/modules/{id}/class-ld-dashboard-{id}-module.php.