Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Plugin_Compatibility
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 9
756
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 get_instance
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 check_plugin_compatibility
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 incompatible_plugin_notices
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 disable_plugin_activate_link
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 disable_plugin_install_link
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 find_incompatible_plugins
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
30
 get_disallowed_plugins
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 get_plugin_status
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * Plugin compatibility file.
4 *
5 * @package wpcomsh
6 */
7
8/**
9 * Class Jetpack_Plugin_Compatibility.
10 */
11class Jetpack_Plugin_Compatibility {
12
13    /**
14     * Plugin file locations and html messaging in the format:
15     * array(
16     *   'example-plugin/example-plugin.php' => 'example-plugin interferes with Jetpack sync and has been disabled.'
17     * ),
18     * The html messaging is presented as a dismissible error admin notice when an unsupported plugin is deactivated.
19     *
20     * @var string[]
21     */
22    public $incompatible_plugins = array(
23        // "reset" - break/interfere with provided functionality.
24        'advanced-wp-reset/advanced-wp-reset.php'         => '"advanced-wp-reset" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
25        'extended-wp-reset/extended-wp-reset.php'         => '"extended-wp-reset" has been deactivated, it interferes with site operation and is not supported on WordPress.com.',
26        'factory-reset/factory-reset.php'                 => '"factory-reset" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
27        'file-manager/file-manager.php'                   => '"file-manager" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
28        'hide-my-wp/index.php'                            => '"hide-my-wp" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
29        'plugins-garbage-collector/plugins-garbage-collector.php' => '"plugins-garbage-collector" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
30        'reset-wp/reset-wp.php'                           => '"reset-wp" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
31        'reset/data_reset.php'                            => '"reset" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
32        'ultimate-wp-reset/ultimate-wordpress-reset.php'  => '"ultimate-wp-reset" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
33        'username-changer/class-username-changer.php'     => '"username-changer" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
34        'vamtam-offline-jetpack/vamtam-offline-jetpack.php' => '"vamtam-offline-jetpack" has been deactivated, an active Jetpack Connection is required for your site to operate properly on WordPress.com.',
35        'wd-youtube/wd-youtube.php'                       => '"wd-youtube" has been deactivated, it interferes with site operation and is not supported on WordPress.com.',
36        'wordpress-reset/wordpress-reset.php'             => '"wordpress-reset" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
37        'wp-automatic/wp-automatic.php'                   => '"wp-automatic" has been deactivated, it interferes with site operation and is not supported on WordPress.com.',
38        'wp-clone-by-wp-academy/wpclone.php'              => '"wp-clone-by-wp-academy" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
39        'wp-file-manager/file_folder_manager.php'         => '"wp-file-manager" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
40        'wp-uninstaller-by-azed/wp-uninstaller-by-azed.php' => '"wp-uninstaller-by-azed" is not supported on WordPress.com.',
41        'wpmu-database-reset/wpmu-database-reset.php'     => '"wpmu-database-reset" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
42        'wps-hide-login/wps-hide-login.php'               => '"wps-hide-login" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
43        'wp-downgrade/wp-downgrade.php'                   => '"wp-downgrade" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
44
45        // Backup.
46        'backwpup/backwpup.php'                           => '"backwpup" has been deactivated, WordPress.com handles managing your site backups for you.',
47        'backwpup-pro/backwpup.php'                       => '"backwpup-pro" has been deactivated, WordPress.com handles managing your site backups for you.',
48        'duplicator/duplicator.php'                       => '"duplicator" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
49        'jetpack-backup/jetpack-backup.php'               => '"jetpack-backup" has been deactivated, WordPress.com handles managing your site backups for you.',
50        'siteground-migrator/siteground-migrator.php'     => '"siteground-migrator" has been deactivated, WordPress.com handles managing your site backups for you.',
51        'wp-backitup/wp-backitup.php'                     => '"wp-backitup" has been deactivated, WordPress.com handles managing your site backups for you.',
52
53        // Caching/performance.
54        'breeze/breeze.php'                               => '"breeze" has been deactivated, WordPress.com automatically handles caching for your site.',
55        'cache-enabler/cache-enabler.php'                 => '"cache-enabler" has been deactivated, WordPress.com automatically handles caching for your site.',
56        'comet-cache/comet-cache.php'                     => '"comet-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
57        'hyper-cache/plugin.php'                          => '"hyper-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
58        'object-cache-pro/object-cache-pro.php'           => '"object-cache-pro" has been deactivated, WordPress.com automatically handles caching for your site.',
59        'powered-cache/powered-cache.php'                 => '"powered-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
60        'redis-cache/redis-cache.php'                     => '"redis-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
61        'sg-cachepress/sg-cachepress.php'                 => '"sg-cachepress" has been deactivated, WordPress.com automatically handles caching for your site.',
62        'w3-total-cache/w3-total-cache.php'               => '"w3-total-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
63        'wp-fastest-cache/wpFastestCache.php'             => '"wp-fastest-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
64        'wp-optimizer/wp-optimizer.php'                   => '"wp-optimizer" has been deactivated, "performance" related plugins may break your site or cause issues and are not supported on WordPress.com.',
65        'wp-scss/wp-scss.php'                             => '"wp-scss" has been deactivated, WordPress.com automatically handles caching for your site.',
66        'wp-speed-of-light/wp-speed-of-light.php'         => '"wp-speed-of-light" has been deactivated, WordPress.com automatically handles caching for your site.',
67        'wp-super-cache/wp-cache.php'                     => '"wp-super-cache" has been deactivated, WordPress.com automatically handles caching for your site.',
68
69        // SQL heavy.
70        'another-wordpress-classifieds-plugin/awpcp.php'  => '"another-wordpress-classifieds-plugin" has been deactivated, it is known to cause severe database performance issues and is not supported.',
71        'mass-pagesposts-creator/mass-pages-posts-creator.php' => '"mass-pagesposts-creator" has been deactivated, it is known to cause severe database performance issues and is not supported.',
72        'ol_scrapes/ol_scrapes.php'                       => '"ol_scrapes" has been deactivated, it is known to cause severe database performance issues and is not supported.',
73        'post-views-counter/post-views-counter.php'       => '"post-views-counter" has been deactivated, plugins that insert or update the database on page load can cause severe performance issues for your site and are not supported.',
74        'top-10/top-10.php'                               => '"top-10" has been deactivated, it is known to cause severe database performance issues and is not supported.',
75        'userpro/index.php'                               => '"userpro" has been deactivated, it is known to cause severe database performance issues and is not supported.',
76        'wp-inject/wpinject.php'                          => '"wp-inject" has been deactivated, it is known to cause severe database performance issues and is not supported.',
77        'wp-postviews/wp-postviews.php'                   => '"wp-postviews" has been deactivated, plugins that insert or update the database on page load can cause severe performance issues for your site and are not supported.',
78        'wp-rss-aggregator/wp-rss-aggregator.php'         => '"wp-rss-aggregator" has been deactivated, it is known to cause severe database performance issues and is not supported.',
79        'wp-rss-feed-to-post/wp-rss-feed-to-post.php'     => '"wp-rss-feed-to-post" has been deactivated, it is known to cause severe database performance issues and is not supported.',
80        'wp-rss-wordai/wp-rss-wordai.php'                 => '"wp-rss-wordai" has been deactivated, it is known to cause severe database performance issues and is not supported.',
81        'wp-session-manager/wp-session-manager.php'       => '"wp-session-manager" has been deactivated, it is known to cause severe database performance issues and is not supported.',
82        'wp-slimstat/wp-slimstat.php'                     => '"wp-slimstat" has been deactivated, plugins that insert or update the database on page load can cause severe performance issues for your site and are not supported.',
83        'WPRobot5/wprobot.php'                            => '"WPRobot5" has been deactivated, plugins that insert or update the database on page load can cause severe performance issues for your site and are not supported.',
84
85        // Security.
86        'antihacker/antihacker.php'                       => '"antihacker" has been deactivated, "security" related plugins may break your site or cause performance issues for your site and are not supported on WordPress.com.',
87        'disable-xml-rpc-api/disable-xml-rpc-api.php'     => '"disable-xml-rpc-api" has been deactivated, XML-RPC is required for your Jetpack Connection on WordPress.com.',
88        'manage-xml-rpc/manage-xml-rpc.php'               => '"manage-xml-rpc" has been deactivated, XML-RPC is required for your Jetpack Connection on WordPress.com.',
89        'one-click-ssl/ssl.php'                           => '"one-click-ssl" has been deactivated, because it is not supported on WordPress.com.',
90        'really-simple-ssl/rlrsssl-really-simple-ssl.php' => '"really-simple-ssl" is not supported on WordPress.com.',
91        'really-simple-ssl-pro/really-simple-ssl-pro.php' => '"really-simple-ssl-pro" is not supported on WordPress.com.',
92        'stopbadbots/stopbadbots.php'                     => '"stopbadbots" has been deactivated, "security" related plugins may break your site or cause performance issues for your site and are not supported on WordPress.com.',
93        'wee-remove-xmlrpc-methods/wee-remove-xmlrpc-methods.php' => '"wee-remove-xmlrpc-methods" has been deactivated, XML-RPC is required for your Jetpack Connection on WordPress.com.',
94        'wp-hide-security-enhancer/wp-hide.php'           => '"wp-hide-security-enhancer" has been deactivated, "security" related plugins may break your site or cause performance issues for your site and are not supported on WordPress.com.',
95        'wp-simple-firewall/wp-simple-firewall.php'       => '"wp-simple-firewall" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
96        'wp-simple-firewall/icwp-wpsf.php'                => '"wp-simple-firewall" has been deactivated, it deletes data necessary to manage your site and is not supported on WordPress.com.',
97
98        // Spam.
99        'e-mail-broadcasting/e-mail-broadcasting.php'     => '"e-mail-broadcasting" has been deactivated, plugins that support sending e-mails in bulk are not supported on WordPress.com.',
100        'mailit/mailit.php'                               => '"mailit" has been deactivated, plugins that support sending e-mails in bulk are not supported on WordPress.com.',
101        'send-email-from-admin/send-email-from-admin.php' => '"send-email-from-admin" has been deactivated, plugins that support sending e-mails in bulk are not supported on WordPress.com.',
102
103        // Misc.
104        'adult-mass-photos-downloader/adult-mass-photos-downloader.php' => '"adult-mass-photos-downloader" is not supported on WordPress.com.',
105        'adult-mass-videos-embedder/adult-mass-videos-embedder.php' => '"adult-mass-videos-embedder" is not supported on WordPress.com.',
106        'automatic-video-posts/automatic-video-posts.php' => '"automatic-video-posts" is not supported on WordPress.com.',
107        'bwp-minify/bwp-minify.php'                       => '"bwp-minify" is not supported on WordPress.com.',
108        'db-access-adminer/db-access-adminer.php'         => '"db-access-adminer" is not supported on WordPress.com.',
109        'nginx-helper/nginx-helper.php'                   => '"nginx-helper" is not supported on WordPress.com.',
110        'p3/p3.php'                                       => '"p3" is not supported on WordPress.com.',
111        'pexlechris-adminer/pexlechris-adminer.php'       => '"pexlechris-adminer" is not supported on WordPress.com.',
112        'plugin-detective/plugin-detective.php'           => '"plugin-detective" is not supported on WordPress.com.',
113        'porn-embed/Porn-Embed.php'                       => '"porn-embed" is not supported on WordPress.com.',
114        'porn-videos-embed/porn-videos-embed.php'         => '"porn-videos-embed" is not supported on WordPress.com.',
115        'propellerads-official/propeller-ads.php'         => '"propellerads-official" is not supported on WordPress.com.',
116        'trafficzion/trafficzion.php'                     => '"trafficzion" is not supported on WordPress.com.',
117        'tubeace/tubeace.php'                             => '"tubeace" is not supported on WordPress.com.',
118        'woozone/plugin.php'                              => '"woozone" is not supported on WordPress.com.',
119        'wp-monero-miner-pro/monero-miner-pro.php'        => '"wp-monero-miner-pro" is not supported on WordPress.com.',
120        'wp-optimize-by-xtraffic/wp-optimize-by-xtraffic.php' => '"wp-optimize-by-xtraffic" is not supported on WordPress.com.',
121        'wpematico/wpematico.php'                         => '"wpematico" is not supported on WordPress.com.',
122        'wpstagecoach/wpstagecoach.php'                   => '"wpstagecoach" is not supported on WordPress.com.',
123        'yuzo-related-post/yuzo.php'                      => '"yuzo-related-post" is not supported on WordPress.com.',
124        'zapp-proxy-server/zapp-proxy-server.php'         => '"zapp-proxy-server" is not supported on WordPress.com.',
125    );
126
127    /**
128     * Admin notices.
129     *
130     * @var array
131     */
132    protected $admin_notices = array();
133
134    /**
135     * Jetpack_Plugin_Compatibility constructor.
136     */
137    protected function __construct() {
138        // Disable plugin activation for unsupported plugins.
139        add_action( 'load-plugins.php', array( $this, 'check_plugin_compatibility' ) );
140        // Replace "Activate" plugin link for plugins that should not be activated (plugins.php).
141        add_filter( 'plugin_action_links', array( $this, 'disable_plugin_activate_link' ), 10, 2 );
142        add_filter( 'network_admin_plugin_action_links', array( $this, 'disable_plugin_activate_link' ), 10, 2 );
143        // Replace "Install" plugin link for plugins that not should not be activated (plugin-install.php).
144        add_filter( 'plugin_install_action_links', array( $this, 'disable_plugin_install_link' ), 10, 2 );
145        // Print any notices about plugin deactivation.
146        add_action( 'admin_notices', array( $this, 'incompatible_plugin_notices' ) );
147        // Disable My Jetpack page.
148        add_filter(
149            'jetpack_my_jetpack_should_initialize',
150            function () {
151                $has_override = has_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' );
152                remove_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' );
153                $should_init = get_option( 'wpcom_admin_interface' ) === 'wp-admin';
154                if ( $has_override ) {
155                    add_filter( 'pre_option_wpcom_admin_interface', 'wpcom_admin_interface_pre_get_option' );
156                }
157
158                if ( ! $should_init && class_exists( '\Automattic\Jetpack\My_Jetpack\Initializer' ) ) {
159                    // My Jetpack REST API endpoints are used for more than just My Jetpack UI.
160                    add_action( 'rest_api_init', array( '\Automattic\Jetpack\My_Jetpack\Initializer', 'register_rest_endpoints' ) ); // @phan-suppress-current-line PhanUndeclaredClassInCallable
161                }
162                return $should_init;
163            }
164        );
165    }
166
167    /**
168     * Public getter to return a singleton instance of Jetpack_Plugin_Compatibility.
169     */
170    public static function get_instance(): Jetpack_Plugin_Compatibility {
171        static $instance = null;
172
173        if ( null === $instance ) {
174            $instance = new static();
175        }
176
177        return $instance;
178    }
179
180    /**
181     * Deactivates incompatible plugins.
182     */
183    public function check_plugin_compatibility() {
184        foreach ( $this->incompatible_plugins as $plugin => $message ) {
185            if ( ! is_plugin_active( $plugin ) ) {
186                continue;
187            }
188
189            deactivate_plugins( $plugin );
190
191            $this->admin_notices[] = '<div class="notice notice-error is-dismissible"><p><strong>' . esc_html( $message ) . '</strong></p></div>';
192            unset( $_GET['activate'] ); // phpcs:ignore WordPress.Security.NonceVerification
193        }
194    }
195
196    /**
197     * Displays admin notices.
198     */
199    public function incompatible_plugin_notices() {
200        foreach ( $this->admin_notices as $notice ) {
201            echo wp_kses_post( $notice );
202        }
203    }
204
205    /**
206     * Disables plugin activations links for incompatible plugins.
207     *
208     * @param array  $actions     Plugin actions.
209     * @param string $plugin_file Plugin file.
210     *
211     * @return array Filtered array of plugin actions.
212     */
213    public function disable_plugin_activate_link( $actions, $plugin_file ) {
214        if ( ! empty( $this->incompatible_plugins[ $plugin_file ] ) ) {
215            $actions['activate'] = 'Disabled';
216            unset( $actions['edit'] );
217        }
218        return $actions;
219    }
220
221    /**
222     * Disables plugin install links for incompatible plugins.
223     *
224     * @param array $action_links Plugin actions.
225     * @param array $plugin       Plugin information.
226     *
227     * @return string[]
228     */
229    public function disable_plugin_install_link( $action_links, $plugin ) {
230        $needle = "{$plugin['slug']}/";
231        foreach ( $this->incompatible_plugins as $disallowed_plugin => $message ) {
232            /*
233            * The naming convention of $disallowed_plugin is <slug>/<file>.php so we are checking if
234            * the string $needle is included into $disallowed_plugin from the `0` position.
235            */
236            if ( strpos( $disallowed_plugin, $needle ) === 0 ) {
237                $action_links = array( 'Not Supported' );
238                break;
239            }
240        }
241
242        return $action_links;
243    }
244
245    /**
246     * Find the incompatible plugins on the site.
247     *
248     * @return array
249     */
250    public function find_incompatible_plugins(): array {
251        // We don't apply the standard Core 'all_plugins' filter, so we are truly looking at all standard plugins.
252        $standard_plugins = get_plugins();
253
254        $disallowed_plugins = $this->get_disallowed_plugins();
255
256        $incompatible_plugins_on_site = array();
257
258        foreach ( $standard_plugins as $plugin_file => $plugin_details ) {
259            if ( ! array_key_exists( $plugin_file, $disallowed_plugins ) ) {
260                continue;
261            }
262
263            $incompatible_plugins_on_site[ $plugin_file ] = array(
264                'message' => $disallowed_plugins[ $plugin_file ],
265                'details' => $plugin_details,
266                'status'  => $this->get_plugin_status( $plugin_file ),
267            );
268        }
269
270        $mu_plugins = get_mu_plugins();
271
272        foreach ( $mu_plugins as $mu_plugin_file => $mu_plugin_details ) {
273            if ( ! array_key_exists( $mu_plugin_file, $disallowed_plugins ) ) {
274                continue;
275            }
276
277            $incompatible_plugins_on_site[ $mu_plugin_file ] = array(
278                'message' => $disallowed_plugins[ $mu_plugin_file ],
279                'details' => $mu_plugin_details,
280                'status'  => 'must-use',
281            );
282        }
283
284        return $incompatible_plugins_on_site;
285    }
286
287    /**
288     * Helper function to return disallowed plugins.
289     * When possible, this function will include platform-level plugins.
290     *
291     * @return string[]
292     */
293    protected function get_disallowed_plugins(): array {
294        if ( ! class_exists( 'Atomic_Platform_Mu_Plugin' ) || ! method_exists( 'Atomic_Platform_Mu_Plugin', 'get_disallowed_plugins' ) ) {
295            return $this->incompatible_plugins;
296        }
297
298        $platform_mu_plugin = new Atomic_Platform_Mu_Plugin();
299
300        // We prefer product-level messages to platform messages when there are conflicts.
301        return array_merge( $platform_mu_plugin->get_disallowed_plugins(), $this->incompatible_plugins );
302    }
303
304    /**
305     * Helper function to determine the status of a standard plugin.
306     *
307     * @param  string $plugin_file The full plugin filename.
308     * @return 'active-network'|'active'|'inactive'
309     */
310    protected function get_plugin_status( string $plugin_file ): string {
311        if ( is_plugin_active_for_network( $plugin_file ) ) {
312            return 'active-network';
313        }
314
315        if ( is_plugin_active( $plugin_file ) ) {
316            return 'active';
317        }
318
319        return 'inactive';
320    }
321}