HEX
Server: LiteSpeed
System: Linux node612.namehero.net 4.18.0-553.121.1.lve.el8.x86_64 #1 SMP Thu Apr 30 16:40:41 UTC 2026 x86_64
User: dfwparty (1186)
PHP: 8.3.31
Disabled: NONE
Upload Files
File: /home/dfwparty/public_html/wp-content/plugins/ultimate-elementor/abilities/class-uael-abilities.php
<?php
/**
 * UAEL Abilities Bootstrap.
 *
 * Detects whether HFE is active and routes to the correct mode:
 * - Scenario A (standalone): Own registry, 36 handlers, own Angie, own MCP server.
 * - Scenario B (both active): Hook 12 Pro-specific handlers into HFE's registry.
 *
 * @package UAEL
 * @since 1.45.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// Only proceed if Abilities API exists (WP 6.9+).
if ( ! function_exists( 'wp_register_ability' ) ) {
	return;
}

/**
 * Class UAEL_Abilities_Bootstrap
 *
 * @since 1.45.0
 */
class UAEL_Abilities_Bootstrap {

	/**
	 * Singleton instance.
	 *
	 * @var self|null
	 */
	private static $instance = null;

	/**
	 * Whether HFE is active and has the handler-based registry.
	 *
	 * @var bool
	 */
	private $hfe_active = false;

	/**
	 * Registry instance (standalone mode only).
	 *
	 * @var UAEL_Ability_Registry|null
	 */
	private $registry = null;

	/**
	 * Get singleton instance.
	 *
	 * @return self
	 */
	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Get the registry instance.
	 *
	 * @return UAEL_Ability_Registry|null
	 */
	public function get_registry() {
		return $this->registry;
	}

	/**
	 * Constructor.
	 */
	private function __construct() {
		$this->hfe_active = defined( 'HFE_VER' ) && class_exists( 'HFE_Abilities_Loader' );

		// Load the handler interface (needed by both modes).
		require_once UAEL_DIR . 'abilities/contracts/interface-uael-ability-handler.php';

		if ( $this->hfe_active ) {
			$this->init_dual_mode();
		} else {
			$this->init_standalone_mode();
		}
	}

	/**
	 * Scenario B: Both Pro + HFE active.
	 *
	 * Hook 12 Pro-specific handlers into HFE's registry via the
	 * uae_mcp_register_abilities action.
	 *
	 * @return void
	 */
	private function init_dual_mode() {
		// Register Pro-specific categories before abilities are discovered.
		add_action( 'wp_abilities_api_categories_init', array( $this, 'register_pro_categories' ) );
		add_action( 'uae_mcp_register_abilities', array( $this, 'register_pro_handlers_into_hfe' ) );
	}

	/**
	 * Register Pro-specific ability categories.
	 *
	 * Needed in both modes — standalone registers all categories,
	 * dual mode only registers the Pro-specific ones.
	 *
	 * @return void
	 */
	public function register_pro_categories() {
		$categories = array(
			'uael-skins'    => array(
				'label'       => __( 'UAE Post Skins', 'uael' ),
				'description' => __( 'Manage post display skins.', 'uael' ),
			),
			'uael-settings' => array(
				'label'       => __( 'UAE Plugin Settings', 'uael' ),
				'description' => __( 'Manage integration API keys and branding settings.', 'uael' ),
			),
			'uael-info'     => array(
				'label'       => __( 'UAE Information', 'uael' ),
				'description' => __( 'Read-only plugin info: version, widgets, categories.', 'uael' ),
			),
		);

		foreach ( $categories as $slug => $args ) {
			wp_register_ability_category( $slug, $args );
		}
	}

	/**
	 * Scenario A: Pro standalone (no HFE).
	 *
	 * Own registry, all handlers, Angie bridge, MCP server.
	 *
	 * @return void
	 */
	private function init_standalone_mode() {
		// Framework + registry are loaded unconditionally so the settings UI
		// can introspect the ability catalogue (via the REST endpoint) even
		// when AI tools are turned off.
		$this->load_framework();

		$this->registry = new UAEL_Ability_Registry();

		// Master switch — opt-in, off by default (matches UAE Lite). Side
		// effects (registering abilities, MCP server, Angie) only attach when
		// the user enables them.
		$settings         = get_option( 'uae_mcp_settings', null );
		$enable_abilities = is_array( $settings ) ? ! empty( $settings['enable_abilities'] ) : false;

		if ( ! $enable_abilities ) {
			return;
		}

		add_action( 'wp_abilities_api_categories_init', array( $this, 'register_categories' ) );
		add_action( 'wp_abilities_api_init', array( $this, 'register_abilities' ) );

		// Dedicated MCP server — only when the user explicitly opts in.
		if ( ! empty( $settings['dedicated_server'] ) ) {
			add_action( 'mcp_adapter_init', array( $this, 'register_mcp_server' ), 20 );
		}

		// Angie bridge — only when the user explicitly opts in.
		if ( ! empty( $settings['angie_enabled'] ) ) {
			$this->init_angie();
		}
	}

	/**
	 * Load framework files for standalone mode.
	 *
	 * @return void
	 */
	private function load_framework() {
		$base = UAEL_DIR . 'abilities/';

		require_once $base . 'registry/class-uael-ability-registry.php';
		require_once $base . 'trait-uael-abilities-helpers.php';
		require_once $base . 'class-uael-element-helpers.php';
	}

	/**
	 * Register all ability categories (standalone mode).
	 *
	 * @return void
	 */
	public function register_categories() {
		$branding    = \UltimateElementor\Classes\UAEL_Helper::get_white_labels();
		$plugin_name = ! empty( $branding['plugin']['name'] ) ? $branding['plugin']['name'] : __( 'Ultimate Addons for Elementor', 'uael' );

		$categories = array(
			'uael-widgets'     => array(
				'label'       => __( 'UAE Widget Management', 'uael' ),
				'description' => __( 'List, activate, and deactivate Elementor widgets.', 'uael' ),
			),
			'uael-skins'       => array(
				'label'       => __( 'UAE Post Skins', 'uael' ),
				'description' => __( 'Manage post display skins.', 'uael' ),
			),
			'uael-builder'     => array(
				'label'       => __( 'UAE Page Builder', 'uael' ),
				'description' => __( 'Build and manage page content with Elementor widgets.', 'uael' ),
			),
			'uael-pages'       => array(
				'label'       => __( 'UAE Page Management', 'uael' ),
				'description' => __( 'Create, update, and manage WordPress pages.', 'uael' ),
			),
			'uael-settings'    => array(
				'label'       => __( 'UAE Plugin Settings', 'uael' ),
				'description' => __( 'Manage integration API keys and branding settings.', 'uael' ),
			),
			'uael-info'        => array(
				'label'       => $plugin_name . ' ' . __( 'Information', 'uael' ),
				'description' => __( 'Read-only plugin info: version, widgets, categories.', 'uael' ),
			),
			'uael-design'      => array(
				'label'       => __( 'UAE Design System', 'uael' ),
				'description' => __( 'Read the site\'s global colors, fonts, and spacing.', 'uael' ),
			),
			'uael-maintenance' => array(
				'label'       => __( 'UAE Maintenance', 'uael' ),
				'description' => __( 'Cache clearing and maintenance tasks.', 'uael' ),
			),
		);

		foreach ( $categories as $slug => $args ) {
			wp_register_ability_category( $slug, $args );
		}
	}

	/**
	 * Ensure handler classes are loaded and registered in the registry.
	 *
	 * Safe to call multiple times — skips if handlers are already populated.
	 * Does NOT call discover() or wp_register_ability(); use register_abilities()
	 * for full registration with the WP Abilities API.
	 *
	 * @return void
	 */
	public function ensure_handlers_loaded() {
		if ( ! $this->registry || ! empty( $this->registry->get_handler_names() ) ) {
			return;
		}

		$this->load_pro_handlers();
		$this->load_standalone_handlers();
		$this->register_all_handlers();
	}

	/**
	 * Load and register all handlers (standalone mode).
	 *
	 * @return void
	 */
	public function register_abilities() {
		$this->load_pro_handlers();
		$this->load_standalone_handlers();
		$this->register_all_handlers();
		$this->registry->discover();
	}

	/**
	 * Load Pro-specific handler files (always loaded in both modes).
	 *
	 * @return void
	 */
	private function load_pro_handlers() {
		$base = UAEL_DIR . 'abilities/handlers/';

		// Skins.
		require_once $base . 'skins/class-uael-skins-list-handler.php';
		require_once $base . 'skins/class-uael-skins-activate-handler.php';
		require_once $base . 'skins/class-uael-skins-deactivate-handler.php';
		require_once $base . 'skins/class-uael-skins-bulk-toggle-handler.php';

		// Branding.
		require_once $base . 'branding/class-uael-branding-get-handler.php';
		require_once $base . 'branding/class-uael-branding-update-handler.php';

		// Integrations.
		require_once $base . 'integrations/class-uael-integrations-get-handler.php';
		require_once $base . 'integrations/class-uael-integrations-update-handler.php';
		require_once $base . 'integrations/class-uael-integrations-validate-handler.php';

		// Pro Info.
		require_once $base . 'pro-info/class-uael-pro-info-get-handler.php';
		require_once $base . 'pro-info/class-uael-pro-info-widget-handler.php';
		require_once $base . 'pro-info/class-uael-pro-info-categories-handler.php';
	}

	/**
	 * Load standalone handler files (only when HFE is NOT active).
	 *
	 * @return void
	 */
	private function load_standalone_handlers() {
		$base = UAEL_DIR . 'abilities/handlers/';

		// Widgets.
		require_once $base . 'widgets/class-uael-widget-list-handler.php';
		require_once $base . 'widgets/class-uael-widget-activate-handler.php';
		require_once $base . 'widgets/class-uael-widget-deactivate-handler.php';
		require_once $base . 'widgets/class-uael-widget-bulk-toggle-handler.php';
		require_once $base . 'widgets/class-uael-widget-deactivate-unused-handler.php';
		require_once $base . 'widgets/class-uael-widget-usage-handler.php';

		// Builder.
		require_once $base . 'builder/class-uael-build-handler.php';
		require_once $base . 'builder/class-uael-insert-widget-handler.php';
		require_once $base . 'builder/class-uael-update-widget-handler.php';
		require_once $base . 'builder/class-uael-remove-element-handler.php';
		require_once $base . 'builder/class-uael-move-element-handler.php';
		require_once $base . 'builder/class-uael-structure-handler.php';
		require_once $base . 'builder/class-uael-add-section-handler.php';
		require_once $base . 'builder/class-uael-add-column-handler.php';
		require_once $base . 'builder/class-uael-css-handler.php';
		require_once $base . 'builder/class-uael-widget-types-handler.php';
		require_once $base . 'builder/class-uael-schema-handler.php';
		require_once $base . 'builder/class-uael-undo-handler.php';

		// Pages.
		require_once $base . 'pages/class-uael-page-create-handler.php';
		require_once $base . 'pages/class-uael-page-list-handler.php';
		require_once $base . 'pages/class-uael-page-delete-handler.php';
		require_once $base . 'pages/class-uael-page-restore-handler.php';
		require_once $base . 'pages/class-uael-page-update-status-handler.php';
		require_once $base . 'pages/class-uael-page-update-meta-handler.php';

		// Design System.
		require_once $base . 'design-system/class-uael-design-tokens-handler.php';

		// Maintenance.
		require_once $base . 'maintenance/class-uael-maintenance-clear-cache-handler.php';
	}

	/**
	 * Register all handler instances with the registry (standalone mode).
	 *
	 * @return void
	 */
	private function register_all_handlers() {
		$handlers = array(
			// Pro-specific (12).
			new UAEL_Skins_List_Handler(),
			new UAEL_Skins_Activate_Handler(),
			new UAEL_Skins_Deactivate_Handler(),
			new UAEL_Skins_Bulk_Toggle_Handler(),
			new UAEL_Branding_Get_Handler(),
			new UAEL_Branding_Update_Handler(),
			new UAEL_Integrations_Get_Handler(),
			new UAEL_Integrations_Update_Handler(),
			new UAEL_Integrations_Validate_Handler(),
			new UAEL_Pro_Info_Get_Handler(),
			new UAEL_Pro_Info_Widget_Handler(),
			new UAEL_Pro_Info_Categories_Handler(),

			// Standalone (24).
			new UAEL_Widget_List_Handler(),
			new UAEL_Widget_Activate_Handler(),
			new UAEL_Widget_Deactivate_Handler(),
			new UAEL_Widget_Bulk_Toggle_Handler(),
			new UAEL_Widget_Deactivate_Unused_Handler(),
			new UAEL_Widget_Usage_Handler(),
			new UAEL_Build_Handler(),
			new UAEL_Insert_Widget_Handler(),
			new UAEL_Update_Widget_Handler(),
			new UAEL_Remove_Element_Handler(),
			new UAEL_Move_Element_Handler(),
			new UAEL_Structure_Handler(),
			new UAEL_Add_Section_Handler(),
			new UAEL_Add_Column_Handler(),
			new UAEL_CSS_Handler(),
			new UAEL_Widget_Types_Handler(),
			new UAEL_Schema_Handler(),
			new UAEL_Undo_Handler(),
			new UAEL_Page_Create_Handler(),
			new UAEL_Page_List_Handler(),
			new UAEL_Page_Delete_Handler(),
			new UAEL_Page_Restore_Handler(),
			new UAEL_Page_Update_Status_Handler(),
			new UAEL_Page_Update_Meta_Handler(),
			new UAEL_Design_Tokens_Handler(),
			new UAEL_Maintenance_Clear_Cache_Handler(),
		);

		foreach ( $handlers as $handler ) {
			$this->registry->register( $handler, 'uael' );
		}
	}

	/**
	 * Register Pro-specific handlers into HFE's registry (dual mode).
	 *
	 * Called via the uae_mcp_register_abilities hook when both plugins are active.
	 *
	 * @param object $hfe_registry HFE_Ability_Registry instance.
	 * @return void
	 */
	public function register_pro_handlers_into_hfe( $hfe_registry ) {
		// Load the handler interface if not already loaded by HFE.
		// HFE uses HFE_Ability_Handler but our handlers implement UAEL_Ability_Handler.
		// Since HFE's registry type-hints HFE_Ability_Handler, we need our handlers
		// to also implement that interface. For now, load Pro handlers and register
		// them — HFE's registry accepts any object with get_name(), get_registration_args(), execute().
		$this->load_pro_handlers();

		$pro_handlers = array(
			new UAEL_Skins_List_Handler(),
			new UAEL_Skins_Activate_Handler(),
			new UAEL_Skins_Deactivate_Handler(),
			new UAEL_Skins_Bulk_Toggle_Handler(),
			new UAEL_Branding_Get_Handler(),
			new UAEL_Branding_Update_Handler(),
			new UAEL_Integrations_Get_Handler(),
			new UAEL_Integrations_Update_Handler(),
			new UAEL_Integrations_Validate_Handler(),
			new UAEL_Pro_Info_Get_Handler(),
			new UAEL_Pro_Info_Widget_Handler(),
			new UAEL_Pro_Info_Categories_Handler(),
		);

		foreach ( $pro_handlers as $handler ) {
			$hfe_registry->register( $handler, 'uael' );
		}
	}

	/**
	 * Register dedicated MCP server (standalone mode).
	 *
	 * @param object $mcp_adapter The MCP Adapter instance.
	 * @return void
	 */
	public function register_mcp_server( $mcp_adapter ) {
		$ability_names = $this->registry->get_registered_names();

		if ( empty( $ability_names ) ) {
			return;
		}

		$settings = get_option( 'uae_mcp_settings', array() );

		// Strict opt-in — the server is only created when explicitly enabled.
		if ( empty( $settings['dedicated_server'] ) ) {
			return;
		}

		$branding    = \UltimateElementor\Classes\UAEL_Helper::get_white_labels();
		$plugin_name = ! empty( $branding['plugin']['name'] ) ? $branding['plugin']['name'] : __( 'UAE Pro', 'uael' );

		// Transport class fallback — older MCP Adapter builds expose
		// HttpTransport at a different namespace.
		$transport_class = class_exists( '\WP\MCP\Transport\HttpTransport' )
			? \WP\MCP\Transport\HttpTransport::class
			: '\WP\MCP\Transport\Http\RestTransport';

		// Explicit error + observability handlers — newer MCP Adapter releases
		// require concrete classes rather than null.
		$error_handler = class_exists( '\WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler' )
			? \WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class
			: null;

		$observability = class_exists( '\WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler' )
			? \WP\MCP\Infrastructure\Observability\NullMcpObservabilityHandler::class
			: null;

		$mcp_adapter->create_server(
			'uae-mcp-server',
			'uae',
			'mcp',
			$plugin_name . ' ' . __( 'MCP Server', 'uael' ),
			__( 'AI tools for Ultimate Addons for Elementor — manage widgets, build pages, and configure settings.', 'uael' ),
			'v' . ( defined( 'UAEL_VER' ) ? UAEL_VER : '0.0.0' ),
			array( $transport_class ),
			$error_handler,
			$observability,
			$ability_names,
			array(),
			array()
		);
	}

	/**
	 * Initialize Angie integration (standalone mode).
	 *
	 * @return void
	 */
	public function init_angie() {
		// Loaded here (not in load_framework) so the Angie class is only required
		// when the Angie integration is actually enabled.
		require_once UAEL_DIR . 'angie/class-uael-angie.php';
		new UAEL_Angie( $this->registry );
	}
}

// Boot.
UAEL_Abilities_Bootstrap::instance();