PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.9
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.9
4.0.9 4.0.8 4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 All 150 releases
← All changes | vendor/codeinwp/themeisle-sdk/src/Modules/Featured_plugins.php +102 -10 4.0.8 → 4.0.9 View file →
@@ -149,8 +149,9 @@
149 149 }
150 150
151 151 if ( isset( $args->page ) && 1 === (int) $args->page && isset( $args->search ) && ! empty( $args->search ) ) {
152 152 $original_count = count( (array) $res->plugins );
153 + $res->plugins = $this->maybe_prepend_easy_mcp_plugin( $res->plugins, $args );
153 154 $res->plugins = $this->maybe_prepend_lms_plugin( $res->plugins, $args );
154 155
155 156 return $this->adjust_results_count( $res, count( (array) $res->plugins ) - $original_count );
156 157 }
@@ -181,19 +182,78 @@
181 182 * @return array
182 183 */
183 184 private function maybe_prepend_lms_plugin( $plugins, $args ) {
184 185 $search = isset( $args->search ) ? strtolower( $args->search ) : '';
185 - if ( $this->matches_lms_search_keywords( $search ) ) {
186 - $filter_slugs = apply_filters( 'themeisle_sdk_masteriyo_filter_slugs', [ 'learning-management-system' ] );
187 - $masteriyo = $this->get_plugins_filtered_from_author( $args, $filter_slugs, 'masteriyo' );
186 + if ( ! self::matches_lms_search_keywords( $search ) ) {
187 + return $plugins;
188 + }
188 189
189 - if ( ! empty( $masteriyo ) ) {
190 - // Remove existing copies of the injected plugins to avoid duplicates.
191 - $plugins = $this->remove_plugins_by_slug( (array) $plugins, $this->get_plugin_slugs( $masteriyo ) );
192 - $plugins = array_merge( $masteriyo, $plugins );
190 + $filter_slugs = apply_filters( 'themeisle_sdk_masteriyo_filter_slugs', [ 'learning-management-system' ] );
191 +
192 + return $this->prepend_author_plugins( $plugins, $args, $filter_slugs, 'masteriyo' );
193 + }
194 +
195 + /**
196 + * Prepend the given author's plugins to the list, removing existing copies to avoid duplicates.
197 + *
198 + * @param array $plugins The plugins array.
199 + * @param object $args The plugin API arguments.
200 + * @param array $filter_slugs The slugs to inject.
201 + * @param string $author The wp.org author that owns the slugs.
202 + *
203 + * @return array
204 + */
205 + private function prepend_author_plugins( $plugins, $args, $filter_slugs, $author ) {
206 + $injected = $this->get_plugins_filtered_from_author( $args, $filter_slugs, $author );
207 + if ( empty( $injected ) ) {
208 + return $plugins;
209 + }
210 +
211 + $plugins = $this->remove_plugins_by_slug( (array) $plugins, $this->get_plugin_slugs( $injected ) );
212 +
213 + return array_merge( $injected, $plugins );
214 + }
215 +
216 + /**
217 + * Prepend the Easy MCP AI plugin if the search query matches AI/MCP-related terms.
218 + *
219 + * @param array $plugins The plugins array.
220 + * @param object $args The plugin API arguments.
221 + * @return array
222 + */
223 + private function maybe_prepend_easy_mcp_plugin( $plugins, $args ) {
224 + $search = isset( $args->search ) ? strtolower( $args->search ) : '';
225 +
226 + // The LMS prepend keeps precedence — never stack two injected cards.
227 + if ( self::matches_lms_search_keywords( $search ) || ! self::matches_ai_search_keywords( $search ) ) {
228 + return $plugins;
229 + }
230 +
231 + $filter_slugs = apply_filters( 'themeisle_sdk_easy_mcp_filter_slugs', [ 'easy-mcp-ai' ] );
232 +
233 + return $this->prepend_author_plugins( $plugins, $args, $filter_slugs, 'easymcpai' );
234 + }
235 +
236 + /**
237 + * Check if a plugin search query contains AI/MCP terms or supported client brands.
238 + *
239 + * Brand names of unrelated AI vendors are deliberately not matched — only the
240 + * category terms and the clients Easy MCP AI itself integrates with.
241 + *
242 + * @param string $search Search query.
243 + *
244 + * @return bool True if the search query matches AI/MCP-related terms.
245 + */
246 + public static function matches_ai_search_keywords( $search ) {
247 + // Distinctive names match as substrings (for example, "claudecode").
248 + foreach ( array( 'model context protocol', 'claude', 'chatgpt' ) as $keyword ) {
249 + if ( false !== stripos( $search, $keyword ) ) {
250 + return true;
193 251 }
194 252 }
195 - return $plugins;
253 +
254 + // Short terms need boundaries (for example, not "email" or "fulfillment").
255 + return preg_match( '/(^|[^a-z0-9])(ai|mcp|llms?)([^a-z0-9]|$)/i', $search ) === 1;
196 256 }
197 257
198 258 /**
199 259 * Extract the slugs from a list of plugin entries.
@@ -257,9 +317,9 @@
257 317 * @param string $search Search query.
258 318 *
259 319 * @return bool True if the search query matches LMS-related terms.
260 320 */
261 - private function matches_lms_search_keywords( $search ) {
321 + public static function matches_lms_search_keywords( $search ) {
262 322 $lms_keywords = array(
263 323 'lms',
264 324 'learn',
265 325 'course',
@@ -298,9 +358,41 @@
298 358 $themeisle_filter_slugs = apply_filters( 'themeisle_sdk_themeisle_filter_slugs', [ 'otter-blocks', 'wp-cloudflare-page-cache' ] );
299 359 $filtered_from_themeisle = $this->get_plugins_filtered_from_author( $args, $themeisle_filter_slugs );
300 360 $featured = array_merge( $featured, $filtered_from_themeisle );
301 361
302 - return $featured;
362 + // Easy MCP AI targets the Abilities API, available since WordPress 6.9.
363 + if ( version_compare( get_bloginfo( 'version' ), '6.9', '>=' ) ) {
364 + $easy_mcp_filter_slugs = apply_filters( 'themeisle_sdk_easy_mcp_filter_slugs', [ 'easy-mcp-ai' ] );
365 + $filtered_from_easy_mcp = $this->get_plugins_filtered_from_author( $args, $easy_mcp_filter_slugs, 'easymcpai' );
366 + $featured = array_merge( $featured, $filtered_from_easy_mcp );
367 + }
368 +
369 + return $this->dedupe_plugins_by_slug( $featured );
370 + }
371 +
372 + /**
373 + * Remove entries with a duplicate slug from a list of plugins, keeping the first occurrence.
374 + *
375 + * @param array $plugins The plugins list.
376 + *
377 + * @return array
378 + */
379 + private function dedupe_plugins_by_slug( $plugins ) {
380 + $seen = [];
381 + $deduped = [];
382 +
383 + foreach ( $plugins as $plugin ) {
384 + $array_plugin = (array) $plugin;
385 + if ( isset( $array_plugin['slug'] ) ) {
386 + if ( isset( $seen[ $array_plugin['slug'] ] ) ) {
387 + continue;
388 + }
389 + $seen[ $array_plugin['slug'] ] = true;
390 + }
391 + $deduped[] = $plugin;
392 + }
393 +
394 + return $deduped;
303 395 }
304 396
305 397 /**
306 398 * Get plugins filtered from an author.