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/Promotions.php +198 -0 4.2.12 → trunk View file →
@@ -112,8 +112,15 @@
112 112 */
113 113 private $option_masteriyo = 'themeisle_sdk_promotions_masteriyo_installed';
114 114
115 115 /**
116 + * Option key for Easy MCP AI promos.
117 + *
118 + * @var string
119 + */
120 + private $option_easy_mcp = 'themeisle_sdk_promotions_easy_mcp_installed';
121 +
122 + /**
116 123 * Loaded promotion.
117 124 *
118 125 * @var string
119 126 */
@@ -166,8 +173,9 @@
166 173 $promotions_to_load[] = 'hyve';
167 174 $promotions_to_load[] = 'wp_full_pay';
168 175 $promotions_to_load[] = 'feedzy_import';
169 176 $promotions_to_load[] = 'learning-management-system';
177 + $promotions_to_load[] = 'easy-mcp';
170 178
171 179 if ( defined( 'NEVE_VERSION' ) || defined( 'WPMM_PATH' ) || defined( 'OTTER_BLOCKS_VERSION' ) || defined( 'OBFX_URL' ) ) {
172 180 $promotions_to_load[] = 'feedzy_embed';
173 181 }
@@ -476,8 +484,12 @@
476 484 if ( isset( $_GET['wp_full_pay_reference_key'] ) ) {
477 485 update_option( 'wp_full_pay_reference_key', sanitize_key( $_GET['wp_full_pay_reference_key'] ) );
478 486 }
479 487
488 + if ( isset( $_GET['easy_mcp_reference_key'] ) ) {
489 + update_option( 'easy_mcp_reference_key', sanitize_key( $_GET['easy_mcp_reference_key'] ) );
490 + }
491 +
480 492 if ( isset( $_GET['feedzy_reference_key'] ) || ( isset( $_GET['from'], $_GET['plugin'] ) && $_GET['from'] === 'import' && str_starts_with( sanitize_key( $_GET['plugin'] ), 'feedzy' ) ) ) {
481 493 update_option( 'feedzy_reference_key', sanitize_key( $_GET['feedzy_reference_key'] ?? 'i-' . $this->product->get_key() ) );
482 494 update_option( $this->option_feedzy, 1 );
483 495 }
@@ -579,8 +591,18 @@
579 591 'show_in_rest' => true,
580 592 'default' => false,
581 593 )
582 594 );
595 + register_setting(
596 + 'themeisle_sdk_settings',
597 + $this->option_easy_mcp,
598 + array(
599 + 'type' => 'boolean',
600 + 'sanitize_callback' => 'rest_sanitize_boolean',
601 + 'show_in_rest' => true,
602 + 'default' => false,
603 + )
604 + );
583 605 }
584 606
585 607 /**
586 608 * Check if the path is writable.
@@ -651,8 +673,14 @@
651 673 $has_feedzy = defined( 'FEEDZY_BASEFILE' ) || $this->is_plugin_installed( 'feedzy-rss-feedss' );
652 674 $had_feedzy_from_promo = get_option( $this->option_feedzy, false );
653 675 $had_masteriyo_from_promo = get_option( $this->option_masteriyo, false );
654 676 $has_masteriyo_conditions = $is_min_php_7_2 && ! $had_masteriyo_from_promo && ! $this->has_active_lms_plugin() && $can_check_plugin_install && $this->has_lms_tagline();
677 + $has_easy_mcp = defined( 'EASY_MCP_AI_VERSION' ) || $this->any_plugin_dir_exists( array( 'easy-mcp-ai' ) );
678 + $had_easy_mcp_from_promo = get_option( $this->option_easy_mcp, false );
679 + $has_easy_mcp_base = $is_min_php_7_4 && ! $has_easy_mcp && ! $had_easy_mcp_from_promo && version_compare( get_bloginfo( 'version' ), '7.0', '>=' );
680 + $has_easy_mcp_conditions = $has_easy_mcp_base && $can_check_plugin_install && ! $this->has_mcp_plugin_installed() && $this->has_ai_usage_signal();
681 + // On the own-profile screen the Application Passwords section itself is the intent signal.
682 + $has_easy_mcp_profile_conditions = $this->can_check_profile_promo() && $has_easy_mcp_base && wp_is_application_passwords_available_for_user( wp_get_current_user() ) && ! $this->has_mcp_plugin_installed();
655 683
656 684 $all = [
657 685 'optimole' => [
658 686 'om-editor' => [
@@ -764,8 +792,18 @@
764 792 'env' => $has_masteriyo_conditions,
765 793 'screen' => 'plugin-install',
766 794 ],
767 795 ],
796 + 'easy-mcp' => [
797 + 'easy-mcp-plugins-install' => [
798 + 'env' => $has_easy_mcp_conditions,
799 + 'screen' => 'plugin-install',
800 + ],
801 + 'easy-mcp-profile' => [
802 + 'env' => $has_easy_mcp_profile_conditions,
803 + 'screen' => 'profile',
804 + ],
805 + ],
768 806 ];
769 807
770 808 foreach ( $all as $slug => $data ) {
771 809 foreach ( $data as $key => $conditions ) {
@@ -831,8 +869,9 @@
831 869 $is_posts = isset( $current_screen->id ) && $current_screen->id === 'edit-post';
832 870 $is_editor = method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor();
833 871 $is_theme_install = isset( $current_screen->id ) && ( $current_screen->id === 'theme-install' );
834 872 $is_plugin_install = isset( $current_screen->id ) && ( $current_screen->id === 'plugin-install' );
873 + $is_profile = isset( $current_screen->id ) && $current_screen->id === 'profile';
835 874 $is_product = isset( $current_screen->id ) && $current_screen->id === 'product';
836 875 $is_import = isset( $current_screen->id ) && $current_screen->id === 'import';
837 876 $is_cf7_install = isset( $current_screen->id ) && function_exists( 'str_contains' ) ? str_contains( $current_screen->id, 'page_wpcf7' ) : false;
838 877
@@ -922,8 +961,13 @@
922 961 if ( ! $is_plugin_install ) {
923 962 unset( $this->promotions[ $slug ][ $key ] );
924 963 }
925 964 break;
965 + case 'profile':
966 + if ( ! $is_profile ) {
967 + unset( $this->promotions[ $slug ][ $key ] );
968 + }
969 + break;
926 970 }
927 971 }
928 972
929 973 $return = array_merge( $return, $this->promotions[ $slug ] );
@@ -974,8 +1018,12 @@
974 1018 if ( $this->get_upsells_dismiss_time( 'masteriyo-plugins-install' ) === false ) {
975 1019 add_action( 'admin_notices', [ $this, 'render_masteriyo_notice' ] );
976 1020 }
977 1021
1022 + if ( $this->get_upsells_dismiss_time( 'easy-mcp-plugins-install' ) === false || $this->get_upsells_dismiss_time( 'easy-mcp-profile' ) === false ) {
1023 + add_action( 'admin_notices', [ $this, 'render_easy_mcp_notice' ] );
1024 + }
1025 +
978 1026 add_action( 'load-import.php', [ $this, 'add_import' ] );
979 1027 $this->load_woo_promos();
980 1028
981 1029 return;
@@ -1043,8 +1091,13 @@
1043 1091 case 'masteriyo-plugins-install':
1044 1092 add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
1045 1093 add_action( 'admin_notices', [ $this, 'render_masteriyo_notice' ] );
1046 1094 break;
1095 + case 'easy-mcp-plugins-install':
1096 + case 'easy-mcp-profile':
1097 + add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] );
1098 + add_action( 'admin_notices', [ $this, 'render_easy_mcp_notice' ] );
1099 + break;
1047 1100 }
1048 1101 }
1049 1102
1050 1103 /**
@@ -1138,8 +1191,10 @@
1138 1191 'wpFullPayActivationUrl' => $this->get_plugin_activation_link( 'wp-full-stripe-free' ),
1139 1192 'wpFullPayDash' => esc_url( add_query_arg( [ 'page' => 'wpfs-settings-stripe' ], admin_url( 'admin.php' ) ) ),
1140 1193 'masteriyoActivationUrl' => $this->get_plugin_activation_link( 'masteriyo' ),
1141 1194 'masteriyoDash' => esc_url( add_query_arg( [ 'page' => 'masteriyo-onboard' ], admin_url( 'index.php' ) ) ),
1195 + 'easyMcpActivationUrl' => add_query_arg( 'easy_mcp_reference_key', 'n-' . $this->product->get_key(), remove_query_arg( 'optimole_reference_key', $this->get_plugin_activation_link( 'easy-mcp-ai' ) ) ),
1196 + 'easyMcpDash' => esc_url( add_query_arg( [ 'page' => 'easy-mcp-ai' ], admin_url( 'admin.php' ) ) ),
1142 1197 'nevePreviewURL' => esc_url( add_query_arg( [ 'theme' => 'neve' ], admin_url( 'theme-install.php' ) ) ),
1143 1198 'neveAction' => $neve_action,
1144 1199 'activateNeveURL' => esc_url(
1145 1200 add_query_arg(
@@ -1205,8 +1260,20 @@
1205 1260 echo '<div id="ti-masteriyo-notice" class="notice notice-info ti-sdk-om-notice"></div>';
1206 1261 }
1207 1262
1208 1263 /**
1264 + * Render Easy MCP AI notice.
1265 + */
1266 + public function render_easy_mcp_notice() {
1267 + // Cards are injected only when a paid product loaded Featured_plugins; only then can this request already show one.
1268 + if ( apply_filters( 'themeisle_sdk_plugin_api_filter_registered', false ) && $this->is_easy_mcp_card_request() ) {
1269 + return;
1270 + }
1271 +
1272 + echo '<div id="ti-easy-mcp-notice" class="notice notice-info ti-sdk-om-notice"></div>';
1273 + }
1274 +
1275 + /**
1209 1276 * Add promo to attachment modal.
1210 1277 *
1211 1278 * @param array $fields Fields array.
1212 1279 * @param \WP_Post $post Post object.
@@ -1678,8 +1745,139 @@
1678 1745 }
1679 1746 }
1680 1747
1681 1748 return false;
1749 + }
1750 +
1751 + /**
1752 + * Check if the site shows signs of AI usage.
1753 + *
1754 + * @return bool True if an AI plugin is installed or a REST API application password is in use.
1755 + */
1756 + public function has_ai_usage_signal() {
1757 + return $this->has_ai_plugin_installed() || $this->has_application_password_in_use();
1758 + }
1759 +
1760 + /**
1761 + * Check if the current plugin-install request already surfaces the Easy MCP AI card,
1762 + * so the notice is not stacked on top of it: the Featured tab injects the card on
1763 + * WordPress 6.9+ and AI-related searches prepend it.
1764 + *
1765 + * @return bool True if the Easy MCP AI card is expected on the current request.
1766 + */
1767 + private function is_easy_mcp_card_request() {
1768 + $current_screen = get_current_screen();
1769 + if ( ! isset( $current_screen->id ) || 'plugin-install' !== $current_screen->id ) {
1770 + return false;
1771 + }
1772 +
1773 + // phpcs:disable WordPress.Security.NonceVerification.Recommended
1774 + $tab = isset( $_GET['tab'] ) ? sanitize_key( $_GET['tab'] ) : '';
1775 + $search = isset( $_GET['s'] ) ? strtolower( sanitize_text_field( wp_unslash( $_GET['s'] ) ) ) : '';
1776 + $paged = isset( $_GET['paged'] ) ? (int) $_GET['paged'] : 1;
1777 + // phpcs:enable WordPress.Security.NonceVerification.Recommended
1778 +
1779 + // Cards are only injected on the first results page.
1780 + if ( $paged > 1 ) {
1781 + return false;
1782 + }
1783 +
1784 + if ( '' === $tab || 'featured' === $tab ) {
1785 + return version_compare( get_bloginfo( 'version' ), '6.9', '>=' );
1786 + }
1787 +
1788 + if ( '' !== $search ) {
1789 + return Featured_Plugins::matches_ai_search_keywords( $search ) && ! Featured_Plugins::matches_lms_search_keywords( $search );
1790 + }
1791 +
1792 + return false;
1793 + }
1794 +
1795 + /**
1796 + * Check if a known AI plugin is installed, active or not.
1797 + *
1798 + * @return bool True if an AI plugin is installed, false otherwise.
1799 + */
1800 + private function has_ai_plugin_installed() {
1801 + return $this->any_plugin_dir_exists(
1802 + array(
1803 + 'ai-engine',
1804 + 'gpt3-ai-content-generator',
1805 + 'getgenie',
1806 + 'bertha-ai',
1807 + 'hyve',
1808 + 'hyve-lite',
1809 + )
1810 + );
1811 + }
1812 +
1813 + /**
1814 + * Check if any of the given plugin slugs has a directory in the plugins folder.
1815 + *
1816 + * @param array $slugs Plugin slugs.
1817 + *
1818 + * @return bool True if at least one plugin directory exists, false otherwise.
1819 + */
1820 + private function any_plugin_dir_exists( $slugs ) {
1821 + foreach ( $slugs as $slug ) {
1822 + if ( is_dir( WP_PLUGIN_DIR . '/' . $slug ) ) {
1823 + return true;
1824 + }
1825 + }
1826 +
1827 + return false;
1828 + }
1829 +
1830 + /**
1831 + * Check if a REST API application password is in use on the site.
1832 + *
1833 + * @return bool True if an application password is in use, false otherwise.
1834 + */
1835 + private function has_application_password_in_use() {
1836 + return class_exists( 'WP_Application_Passwords' ) && method_exists( 'WP_Application_Passwords', 'is_in_use' ) && \WP_Application_Passwords::is_in_use();
1837 + }
1838 +
1839 + /**
1840 + * Check if the current request is a profile screen where the Application
1841 + * Passwords section lives — a user wiring up an external REST client.
1842 + *
1843 + * @return bool True if a profile promo can be displayed, false otherwise.
1844 + */
1845 + private function can_check_profile_promo() {
1846 + if ( ! is_admin() || ! current_user_can( 'install_plugins' ) ) {
1847 + return false;
1848 + }
1849 +
1850 + $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
1851 + if ( isset( $current_screen->id ) ) {
1852 + return 'profile' === $current_screen->id;
1853 + }
1854 +
1855 + global $pagenow;
1856 +
1857 + return isset( $pagenow ) && 'profile.php' === $pagenow;
1858 + }
1859 +
1860 + /**
1861 + * Check if a competing MCP server plugin is installed, active or not.
1862 + *
1863 + * AI Engine ships an optional MCP feature but is intentionally kept in the
1864 + * AI-usage signals instead — its primary identity is an AI framework.
1865 + *
1866 + * @return bool True if an MCP server plugin is installed, false otherwise.
1867 + */
1868 + private function has_mcp_plugin_installed() {
1869 + return $this->any_plugin_dir_exists(
1870 + array(
1871 + 'vibe-ai',
1872 + 'royal-mcp',
1873 + 'stifli-flex-mcp',
1874 + 'miniorange-secure-mcp-server',
1875 + 'wsp-mcp-ai-agents-connector',
1876 + 'cowboy-mcp',
1877 + 'nibwp',
1878 + )
1879 + );
1682 1880 }
1683 1881
1684 1882 /**
1685 1883 * Check if the current request can display a plugin install promo.