Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
Plugins_Installer
0.00% covered (danger)
0.00%
0 / 76
0.00% covered (danger)
0.00%
0 / 10
1260
0.00% covered (danger)
0.00%
0 / 1
 ensure_plugin_functions_are_loaded
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 install_and_activate_plugin
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
42
 install_plugin
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
90
 generate_wordpress_org_plugin_download_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_plugin_id_by_slug
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 get_slug_from_file_path
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 get_plugin_status
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 is_plugin_active
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 is_plugin_active_for_network
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 get_plugins
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
20
 is_current_request_activating_plugin_from_plugins_screen
n/a
0 / 0
n/a
0 / 0
1
1<?php
2/**
3 * Plugins Library
4 *
5 * Helper functions for installing and activating plugins.
6 *
7 * Used by the REST API
8 *
9 * @package jetpack-plugins-installer
10 */
11
12namespace Automattic\Jetpack;
13
14use Plugin_Upgrader;
15use WP_Error;
16
17/**
18 * Plugins management tools.
19 */
20class Plugins_Installer {
21
22    /**
23     * Ensures that plugins functions are loaded, as they are only loaded in admin context by default.
24     */
25    private static function ensure_plugin_functions_are_loaded() {
26        if ( ! function_exists( 'get_plugins' ) ) {
27            require_once ABSPATH . 'wp-admin/includes/plugin.php';
28        }
29    }
30    /**
31     * Install and activate a plugin.
32     *
33     * @since-jetpack 5.8.0
34     *
35     * @param string $slug Plugin slug.
36     *
37     * @return bool|WP_Error True if installation succeeded, error object otherwise.
38     */
39    public static function install_and_activate_plugin( $slug ) {
40        $plugin_id = self::get_plugin_id_by_slug( $slug );
41
42        if ( ! $plugin_id ) {
43            $installed = self::install_plugin( $slug );
44            if ( is_wp_error( $installed ) ) {
45                return $installed;
46            }
47            $plugin_id = self::get_plugin_id_by_slug( $slug );
48        } elseif ( self::is_plugin_active( $plugin_id ) ) {
49            return true; // Already installed and active.
50        }
51
52        if ( ! current_user_can( 'activate_plugins' ) ) {
53            return new WP_Error( 'not_allowed', __( 'You are not allowed to activate plugins on this site.', 'jetpack-plugins-installer' ) );
54        }
55        $activated = activate_plugin( $plugin_id );
56        if ( is_wp_error( $activated ) ) {
57            return $activated;
58        }
59
60        return true;
61    }
62
63    /**
64     * Install a plugin.
65     *
66     * @since-jetpack 5.8.0
67     *
68     * @param string $slug Plugin slug.
69     *
70     * @return bool|WP_Error True if installation succeeded, error object otherwise.
71     */
72    public static function install_plugin( $slug ) {
73        if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
74            return new WP_Error( 'not_allowed', __( 'You are not allowed to install plugins on this site.', 'jetpack-plugins-installer' ) );
75        }
76
77        // Initialize admin filters to make sure WordPress post-install hooks run. Handles things like language packs.
78        include_once ABSPATH . '/wp-admin/includes/admin-filters.php';
79
80        $skin     = new Automatic_Install_Skin();
81        $upgrader = new Plugin_Upgrader( $skin );
82        $zip_url  = self::generate_wordpress_org_plugin_download_link( $slug );
83        $mc_stats = new A8c_Mc_Stats();
84
85        $result = $upgrader->install( $zip_url );
86
87        if ( is_wp_error( $result ) ) {
88            $mc_stats->add( 'install-plugin', "fail-$slug" );
89            return $result;
90        }
91
92        $plugin     = self::get_plugin_id_by_slug( $slug );
93        $error_code = 'install_error';
94        if ( ! $plugin ) {
95            $error = __( 'There was an error installing your plugin', 'jetpack-plugins-installer' );
96        }
97
98        if ( ! $result ) {
99            $error_code = $skin->get_main_error_code();
100            $message    = $skin->get_main_error_message();
101            $error      = $message ? $message : __( 'An unknown error occurred during installation', 'jetpack-plugins-installer' );
102        }
103
104        if ( ! empty( $error ) ) {
105            if ( 'download_failed' === $error_code ) {
106                // For backwards compatibility: versions prior to 3.9 would return no_package instead of download_failed.
107                $error_code = 'no_package';
108            }
109
110            $mc_stats->add( 'install-plugin', "fail-$slug" );
111            return new WP_Error( $error_code, $error, 400 );
112        }
113
114        $mc_stats->add( 'install-plugin', "success-$slug" );
115        return (array) $upgrader->skin->get_upgrade_messages();
116    }
117
118    /**
119     * Get WordPress.org zip download link from a plugin slug
120     *
121     * @param string $plugin_slug Plugin slug.
122     */
123    protected static function generate_wordpress_org_plugin_download_link( $plugin_slug ) {
124        return "https://downloads.wordpress.org/plugin/$plugin_slug.latest-stable.zip";
125    }
126
127    /**
128     * Get the plugin ID (composed of the plugin slug and the name of the main plugin file) from a plugin slug.
129     *
130     * @param string $slug Plugin slug.
131     */
132    public static function get_plugin_id_by_slug( $slug ) {
133        // Check if get_plugins() function exists. This is required on the front end of the
134        // site, since it is in a file that is normally only loaded in the admin.
135        if ( ! function_exists( 'get_plugins' ) ) {
136            require_once ABSPATH . 'wp-admin/includes/plugin.php';
137        }
138
139        /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
140        $plugins = apply_filters( 'all_plugins', get_plugins() );
141        if ( ! is_array( $plugins ) ) {
142            return false;
143        }
144
145        foreach ( $plugins as $plugin_file => $plugin_data ) {
146            if ( self::get_slug_from_file_path( $plugin_file ) === $slug ) {
147                return $plugin_file;
148            }
149        }
150
151        return false;
152    }
153
154    /**
155     * Get the plugin slug from the plugin ID (composed of the plugin slug and the name of the main plugin file)
156     *
157     * @param string $plugin_file Plugin file (ID -- e.g. hello-dolly/hello.php).
158     */
159    protected static function get_slug_from_file_path( $plugin_file ) {
160        // Similar to get_plugin_slug() method.
161        $slug = dirname( $plugin_file );
162        if ( '.' === $slug ) {
163            $slug = preg_replace( '/(.+)\.php$/', '$1', $plugin_file );
164        }
165
166        return $slug;
167    }
168
169    /**
170     * Get the activation status for a plugin.
171     *
172     * @since-jetpack 8.9.0
173     *
174     * @param string $plugin_file The plugin file to check.
175     * @return string Either 'network-active', 'active' or 'inactive'.
176     */
177    public static function get_plugin_status( $plugin_file ) {
178        if ( self::is_plugin_active_for_network( $plugin_file ) ) {
179            return 'network-active';
180        }
181
182        if ( self::is_plugin_active( $plugin_file ) ) {
183            return 'active';
184        }
185
186        return 'inactive';
187    }
188
189    /**
190     * Safely checks if the plugin is active
191     *
192     * @since 0.1.0
193     *
194     * @param string $plugin_file The plugin file to check.
195     * @return bool
196     */
197    public static function is_plugin_active( $plugin_file ) {
198        self::ensure_plugin_functions_are_loaded();
199        return is_plugin_active( $plugin_file );
200    }
201
202    /**
203     * Safely checks if the plugin is active for network
204     *
205     * @since 0.1.0
206     *
207     * @param string $plugin_file The plugin file to check.
208     * @return bool
209     */
210    public static function is_plugin_active_for_network( $plugin_file ) {
211        self::ensure_plugin_functions_are_loaded();
212        return is_plugin_active_for_network( $plugin_file );
213    }
214
215    /**
216     * Returns a list of all plugins in the site.
217     *
218     * @since-jetpack 8.9.0
219     * @uses get_plugins()
220     *
221     * @return array
222     */
223    public static function get_plugins() {
224        self::ensure_plugin_functions_are_loaded();
225        /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
226        $plugins = apply_filters( 'all_plugins', get_plugins() );
227
228        if ( is_array( $plugins ) && ! empty( $plugins ) ) {
229            foreach ( $plugins as $plugin_slug => $plugin_data ) {
230                $plugins[ $plugin_slug ]['active'] = in_array(
231                    self::get_plugin_status( $plugin_slug ),
232                    array( 'active', 'network-active' ),
233                    true
234                );
235            }
236            return $plugins;
237        }
238
239        return array();
240    }
241
242    /**
243     * Determine if the current request is activating a plugin from the plugins page.
244     *
245     * @deprecated 0.4.0
246     * @see Paths::is_current_request_activating_plugin_from_plugins_screen()
247     *
248     * @param string $plugin Plugin file path to check.
249     * @return bool
250     */
251    public static function is_current_request_activating_plugin_from_plugins_screen( $plugin ) {
252        _deprecated_function( __METHOD__, '0.4.0', 'Automattic\\Jetpack\\Paths::is_current_request_activating_plugin_from_plugins_screen()' );
253        return ( new Paths() )->is_current_request_activating_plugin_from_plugins_screen( $plugin );
254    }
255}