15 ) ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return array(); } $body = wp_remote_retrieve_body( $response ); $catalog = json_decode( $body, true ); if ( ! \is_array( $catalog ) ) { return array(); } set_transient( self::TRANSIENT_KEY, $catalog, self::CACHE_TTL ); return $catalog; } /** * Get extensions enriched with local install/active status. * * @return array */ public function get_extensions(): array { $catalog = $this->get_catalog(); if ( empty( $catalog ) ) { return array(); } if ( ! \function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $installed_plugins = get_plugins(); $active_plugins = get_option( 'active_plugins', array() ); $network_plugins = array(); if ( is_multisite() ) { $network_plugins = array_keys( get_site_option( 'active_sitewide_plugins', array() ) ); } $auto_updates = (array) get_site_option( 'auto_update_plugins', array() ); $extensions = array(); foreach ( $catalog as $entry ) { $slug = $entry['slug'] ?? ''; $plugin_file = $this->find_plugin_file( $slug, $installed_plugins ); $status = 'not_installed'; if ( $plugin_file ) { $local_version = $installed_plugins[ $plugin_file ]['Version'] ?? ''; $remote_version = $entry['latest_version'] ?? $entry['version'] ?? ''; $is_active = \in_array( $plugin_file, $active_plugins, true ) || \in_array( $plugin_file, $network_plugins, true ); $has_update = $remote_version && version_compare( $local_version, $remote_version, '<' ); if ( $has_update && $is_active ) { $status = 'update_available'; } elseif ( $is_active ) { $status = 'active'; } else { $status = 'inactive'; } $entry['installed_version'] = $local_version; $entry['plugin_file'] = $plugin_file; $entry['auto_update'] = \in_array( $plugin_file, $auto_updates, true ); if ( $has_update ) { $entry['has_update'] = true; } if ( ! empty( $entry['settings_url'] ) && \is_string( $entry['settings_url'] ) ) { $entry['settings_url'] = admin_url( $entry['settings_url'] ); } } $entry['status'] = $status; $extensions[] = $entry; } return $extensions; } /** * Find the plugin file path for a given extension slug. * * Looks for a plugin directory matching the slug. * * @param string $slug Extension slug. * @param array $installed_plugins Installed plugins from get_plugins(). * * @return string|null Plugin file path or null if not found. */ private function find_plugin_file( string $slug, array $installed_plugins ): ?string { foreach ( array_keys( $installed_plugins ) as $plugin_file ) { if ( 0 === strpos( $plugin_file, $slug . '/' ) ) { return $plugin_file; } } return null; } /** * Clear the catalog transient cache. * * @return bool */ public function clear_cache(): bool { if ( false === get_transient( self::TRANSIENT_KEY ) ) { return true; } return delete_transient( self::TRANSIENT_KEY ); } /** * Clear the catalog cache after a plugin change. * * @return void */ public function clear_cache_on_plugin_change(): void { $this->clear_cache(); } }