sanitize_text_field( $minimum_core ), 'requires_api' => sanitize_text_field( $minimum_api ) ]; return false; } $capabilities = isset( $metadata['capabilities'] ) && is_array( $metadata['capabilities'] ) ? array_values( array_unique( array_filter( array_map( 'sanitize_key', $metadata['capabilities'] ) ) ) ) : []; self::$extensions[ $normalized_id ] = [ 'id' => $normalized_id, 'label' => sanitize_text_field( $metadata['label'] ?? $normalized_id ), 'version' => sanitize_text_field( $metadata['version'] ?? '' ), 'requires_core' => $minimum_core, 'requires_api' => $minimum_api, 'capabilities' => $capabilities, ]; unset( self::$compatibility_failures[ $normalized_id ] ); do_action( 'nyxonn_fx_extension_registered', $normalized_id, self::$extensions[ $normalized_id ] ); return true; } public static function get_extension( $id ) { $id = self::valid_id( $id ); return $id && isset( self::$extensions[ $id ] ) ? self::$extensions[ $id ] : null; } public static function get_extensions() { return self::$extensions; } public static function has_extension( $id ) { return null !== self::get_extension( $id ); } /** Store a pre-registered handle for loading exclusively on Builder routes. */ public static function register_builder_asset( $handle, $metadata = [] ) { $handle = self::valid_id( $handle ); $kind = sanitize_key( $metadata['kind'] ?? 'script' ); $owner = self::valid_id( $metadata['owner'] ?? 'extension' ); if ( ! $handle || ! $owner || ! in_array( $kind, [ 'script', 'style' ], true ) || isset( self::$builder_assets[ $kind . ':' . $handle ] ) ) return false; self::$builder_assets[ $kind . ':' . $handle ] = [ 'handle' => $handle, 'kind' => $kind, 'owner' => $owner ]; return true; } public static function get_builder_assets( $kind = '' ) { $kind = sanitize_key( $kind ); return array_values( array_filter( self::$builder_assets, static function ( $asset ) use ( $kind ) { return ! $kind || $kind === $asset['kind']; } ) ); } /** Register a routed internal-admin item; a callable prevents blank routes. */ public static function register_admin_navigation( $item ) { if ( ! is_array( $item ) ) return false; $slug = self::valid_id( $item['slug'] ?? '' ); $label = sanitize_text_field( $item['label'] ?? '' ); $capability = sanitize_key( $item['capability'] ?? 'manage_options' ); $callback = $item['callback'] ?? null; $core_routes = [ 'nyxonn-builder-pages', 'nyxonn-builder-builder-settings', 'nyxonn-builder-theme-builder', 'nyxonn-builder-global-design', 'nyxonn-builder-assets', 'nyxonn-builder-role-manager', 'nyxonn-builder-performance', 'nyxonn-builder-integrations', 'nyxonn-builder-tools', 'nyxonn-builder-settings', 'nyxonn-builder-upgrade-pro', 'nyxonn-builder-editor', 'nyxonn-builder-template-conditions', 'nyxonn-builder-templates' ]; if ( ! $slug || 0 !== strpos( $slug, 'nx_' ) || in_array( $slug, $core_routes, true ) || ! $label || ! $capability || ! is_callable( $callback ) || isset( self::$admin_navigation[ $slug ] ) ) return false; self::$admin_navigation[ $slug ] = [ 'slug' => $slug, 'label' => $label, 'page_title' => sanitize_text_field( $item['page_title'] ?? $label ), 'section' => sanitize_text_field( $item['section'] ?? '' ), 'icon' => sanitize_html_class( $item['icon'] ?? 'dashicons-admin-generic' ), 'order' => is_numeric( $item['order'] ?? null ) ? (float) $item['order'] : 999, 'capability' => $capability, 'callback' => $callback, 'owner' => self::valid_id( $item['owner'] ?? 'extension' ) ?: 'extension', ]; return true; } public static function get_admin_navigation() { return array_values( self::$admin_navigation ); } public static function get_compatibility_failures() { return self::$compatibility_failures; } public static function render_compatibility_notices() { if ( ! current_user_can( 'activate_plugins' ) ) return; foreach ( self::$compatibility_failures as $id => $requirements ) { printf( '

%s

', esc_html( sprintf( /* translators: 1: extension ID, 2: required core version, 3: required Extension API version. */ __( 'Nyxonn extension "%1$s" was not loaded because it requires core %2$s and Extension API %3$s.', 'nyxonn-builder' ), $id, $requirements['requires_core'] ?: __( 'any', 'nyxonn-builder' ), $requirements['requires_api'] ?: __( 'any', 'nyxonn-builder' ) ) ) ); } } private static function valid_id( $value ) { $value = is_string( $value ) ? trim( $value ) : ''; return $value && strlen( $value ) <= 100 && sanitize_key( $value ) === $value && preg_match( '/^[a-z][a-z0-9_-]*$/', $value ) ? $value : ''; } private static function valid_version_requirement( $version ) { return '' === (string) $version || ( strlen( (string) $version ) <= 32 && (bool) preg_match( '/^[0-9]+(?:\.[0-9]+){0,3}(?:[-+][A-Za-z0-9.-]+)?$/', (string) $version ) ); } } } namespace { function nyxonn_fx_get_core_version() { return defined( 'NYXONN_BUILDER_VERSION' ) ? NYXONN_BUILDER_VERSION : ''; } function nyxonn_fx_get_extension_api_version() { return \NyxonnBuilder\Extension_API::API_VERSION; } function nyxonn_fx_get_capabilities() { return \NyxonnBuilder\Extension_API::get_capabilities(); } function nyxonn_fx_is_core_loaded() { return \NyxonnBuilder\Extension_API::is_core_loaded(); } function nyxonn_fx_core_supports( $capability ) { return \NyxonnBuilder\Extension_API::core_supports( $capability ); } function nyxonn_fx_is_extension_compatible( $minimum_core = '', $minimum_api = '' ) { return \NyxonnBuilder\Extension_API::is_compatible( $minimum_core, $minimum_api ); } function nyxonn_fx_register_extension( $id, $metadata = [] ) { return \NyxonnBuilder\Extension_API::register_extension( $id, $metadata ); } function nyxonn_fx_get_extension( $id ) { return \NyxonnBuilder\Extension_API::get_extension( $id ); } function nyxonn_fx_get_extensions() { return \NyxonnBuilder\Extension_API::get_extensions(); } function nyxonn_fx_has_extension( $id ) { return \NyxonnBuilder\Extension_API::has_extension( $id ); } function nyxonn_fx_register_module( $definition ) { return class_exists( '\NyxonnBuilder\Module_Registry' ) && \NyxonnBuilder\Module_Registry::register_module( $definition ); } function nyxonn_fx_register_module_renderer( $type, $renderer, $metadata = [] ) { return class_exists( '\NyxonnBuilder\Module_Registry' ) && \NyxonnBuilder\Module_Registry::register_renderer( $type, $renderer, $metadata ); } function nyxonn_fx_register_dynamic_provider( $id, $metadata = [] ) { return class_exists( '\NyxonnBuilder\Dynamic_Content' ) && \NyxonnBuilder\Dynamic_Content::register_provider( $id, $metadata ); } function nyxonn_fx_register_dynamic_source( $provider, $id, $metadata, $resolver ) { return class_exists( '\NyxonnBuilder\Dynamic_Content' ) && \NyxonnBuilder\Dynamic_Content::register_source( $provider, $id, $metadata, $resolver ); } function nyxonn_fx_register_theme_condition( $id, $definition ) { return class_exists( '\NyxonnBuilder\Theme_Conditions' ) && \NyxonnBuilder\Theme_Conditions::register( $id, $definition ); } function nyxonn_fx_register_builder_asset( $handle, $metadata = [] ) { return \NyxonnBuilder\Extension_API::register_builder_asset( $handle, $metadata ); } function nyxonn_fx_register_admin_navigation( $item ) { return \NyxonnBuilder\Extension_API::register_admin_navigation( $item ); } function nyxonn_fx_register_feature( $id, $definition ) { return class_exists( '\NyxonnBuilder\Feature_Registry' ) && \NyxonnBuilder\Feature_Registry::register( $id, $definition ); } function nyxonn_fx_get_feature( $id ) { return class_exists( '\NyxonnBuilder\Feature_Registry' ) ? \NyxonnBuilder\Feature_Registry::get( $id ) : null; } function nyxonn_fx_is_feature_available( $id ) { return class_exists( '\NyxonnBuilder\Feature_Registry' ) && \NyxonnBuilder\Feature_Registry::is_available( $id ); } function nyxonn_fx_is_safe_mode() { return class_exists( '\NyxonnBuilder\Safe_Mode' ) && \NyxonnBuilder\Safe_Mode::is_enabled(); } }