Creating Custom Modules

Get Started

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.


LDDashboardModule_Base

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

All custom modules must extend this abstract class.

Abstract Methods (required)

MethodReturnDescription
get_id(): stringstringUnique module identifier (e.g., my-analytics). Must be a valid slug.
get_name(): stringstringHuman-readable module name for display in settings.
get_description(): stringstringShort description of what the module does.
boot(): voidvoidModule initialization — register hooks, load files, enqueue assets. Called only when all dependencies pass.

Dependency System

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:

PrefixCheckExample
plugin:Class lookup for known plugins, then is<em>plugin</em>active()plugin:buddypress
class:class_exists()class:BuddyPress
function:function_exists()function:bp<em>is</em>active
constant:defined()constant:BP_VERSION

Known plugins with class-based checks: buddypressBuddyPress, bp-user-todo-listBP<em>User</em>Todo<em>List, learndashSFWD</em>LMS, woocommerceWooCommerce.

Initialization Flow

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)

Optional Override Methods

MethodDefaultDescription
is_enabled(): boolReads ld<em>dashboard</em>module_settings optionOverride to use a different settings source.
get<em>settings</em>config(): arrayBase config with id, name, description, requires, enabledOverride to add module-specific settings fields for the admin UI.

Protected Helpers

MethodDescription
get<em>module</em>path(): stringReturns the module’s directory path with trailing slash.
get<em>module</em>url(): stringReturns the module’s URL with trailing slash.

LDDashboardModule_Registry

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

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

Registration

Register modules inside the ld<em>dashboard</em>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 <em>doing</em>it_wrong().

Global Helpers

// 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' );

Module Settings

Modules can be toggled in the admin via Settings → Integrations. The enabled state is stored in the ld<em>dashboard</em>module<em>settings WordPress option as an associative array of module</em>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.
}

Complete Example: Analytics Module

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' );
} );

Using the Module Elsewhere

// 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.
} );

Built-in Modules

IDClassDependency
zoomLD<em>Dashboard</em>Zoom_ModuleNone (uses bundled Zoom API class)
buddypressLD<em>Dashboard</em>BP_Moduleplugin:buddypress (BuddyPress class)
todoLD<em>Dashboard</em>Todo_Moduleplugin:bp-user-todo-list (BP<em>User</em>Todo_List class)
uncanny-groupsLD<em>Dashboard</em>Uncanny<em>Groups</em>ModuleUncanny Groups plugin

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

Last updated: March 4, 2026