PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / trunk
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization vtrunk
4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 All 135 releases
← All changes | vendor/codeinwp/themeisle-sdk/src/Modules/Featured_plugins.php +102 -10 4.2.11 → trunk 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.