$installed, 'active' => $active, 'version' => defined( 'BETTERDOCS_PRO_VERSION' ) ? (string) BETTERDOCS_PRO_VERSION : null, 'license_status' => $license, 'licensed' => 'valid' === $license, 'multiple_kb' => (bool) self::setting( 'multiple_kb' ), 'kb_taxonomy_registered' => function_exists( 'taxonomy_exists' ) && taxonomy_exists( 'knowledge_base' ) ]; return self::$probe; } /** * Collapses the probe into one state slug. * * Precedence: not installed, not active, setting off, unlicensed, ok — most * blocking first, and the licence last of the four because it blocks nothing. * * `pro_active_setting_off` deliberately outranks `pro_unlicensed`, which is * the one place this diverges from the written plan (ADR-034). ADR-004 says * the licence is reported and never enforced; if the licence outranked the * setting then on an unlicensed site — the ordinary state of a fresh Pro * install, and what the rig runs — a knowledge-base tool with Multiple * Knowledge Base switched off would answer "the licence is not valid" * instead of the actionable "switch the setting on": the licence would * quietly change what an agent is told. It still surfaces on its own * whenever nothing else is in the way. * * @since 4.9.0 * * @param array $probe Probe fields. * @param bool $kb_feature Whether the caller needs Multiple Knowledge Base. * @return string */ private static function resolve_state( array $probe, bool $kb_feature ): string { if ( empty( $probe['installed'] ) && empty( $probe['active'] ) ) { return 'pro_not_installed'; } if ( empty( $probe['active'] ) ) { return 'pro_not_active'; } if ( $kb_feature && empty( $probe['multiple_kb'] ) ) { return 'pro_active_setting_off'; } if ( empty( $probe['licensed'] ) ) { return 'pro_unlicensed'; } return 'ok'; } /** * Whether Pro's plugin file is on disk, active or not. * * `get_plugins()` is the accurate answer (it is what the Plugins screen * reads) but only exists once `wp-admin/includes/plugin.php` is loaded, which * is not the case on a front-end request. The file check is the fallback, and * is what a normal MCP request uses. * * @since 4.9.0 * * @return bool */ private static function pro_installed(): bool { if ( function_exists( 'get_plugins' ) ) { $plugins = get_plugins(); return is_array( $plugins ) && isset( $plugins[ self::PRO_BASENAME ] ); } return defined( 'WP_PLUGIN_DIR' ) && file_exists( WP_PLUGIN_DIR . '/' . self::PRO_BASENAME ); } /** * Whether Pro is active, asked of BetterDocs itself rather than of * WordPress, so multisite network activation and any future override are * answered the same way the rest of the plugin answers them. * * @since 4.9.0 * * @return bool */ private static function pro_active(): bool { if ( ! function_exists( 'betterdocs' ) ) { return false; } return (bool) betterdocs()->is_pro_active(); } /** * Reads one BetterDocs setting, tolerating a plugin that has not booted. * * @since 4.9.0 * * @param string $key Setting key. * @return mixed Null when settings are unreachable. */ private static function setting( string $key ) { if ( ! function_exists( 'betterdocs' ) ) { return null; } $plugin = betterdocs(); if ( ! is_object( $plugin ) || ! isset( $plugin->settings ) || ! is_object( $plugin->settings ) ) { return null; } return $plugin->settings->get( $key ); } }