Plugin.php (10246B)
1 <?php 2 // phpcs:ignoreFile 3 // This class was copied from JetPack (mostly) 4 // so will be a bit of work to refactor 5 /** 6 * Manage plugin dependencies 7 */ 8 9 namespace Extendify\ExtendifySdk; 10 11 class Plugin 12 { 13 /** 14 * Install and activate a plugin. 15 * 16 * @param string $slug Plugin slug. 17 * 18 * @return bool|WP_Error True if installation succeeded, error object otherwise. 19 */ 20 public static function install_and_activate_plugin($slug) 21 { 22 $plugin_id = self::get_plugin_id_by_slug($slug); 23 if (! $plugin_id) { 24 $installed = self::install_plugin($slug); 25 if (is_wp_error($installed)) { 26 return $installed; 27 } 28 $plugin_id = self::get_plugin_id_by_slug($slug); 29 } elseif (is_plugin_active($plugin_id)) { 30 return true; // Already installed and active. 31 } 32 33 if (! current_user_can('activate_plugins')) { 34 return new \WP_Error('not_allowed', __('You are not allowed to activate plugins on this site.', 'templates_sdk')); 35 } 36 37 $activated = \activate_plugin($plugin_id); 38 if (\is_wp_error($activated)) { 39 return $activated; 40 } 41 42 return true; 43 } 44 45 /** 46 * Install a plugin. 47 * 48 * @param string $slug Plugin slug. 49 * 50 * @return bool|WP_Error True if installation succeeded, error object otherwise. 51 */ 52 public static function install_plugin($slug) 53 { 54 if (\is_multisite() && !\current_user_can('manage_network')) { 55 return new \WP_Error('not_allowed', __('You are not allowed to install plugins on this site.', 'templates_sdk')); 56 } 57 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; 58 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 59 require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; 60 require_once ABSPATH . 'wp-admin/includes/file.php'; 61 require_once ABSPATH . 'wp-admin/includes/misc.php'; 62 $installer = new \Plugin_Upgrader(new Skin()); 63 $zip_url = self::generate_wordpress_org_plugin_download_link($slug); 64 65 $result = $installer->install($zip_url); 66 67 if (is_wp_error($result)) { 68 return $result; 69 } 70 71 $plugin = self::get_plugin_id_by_slug($slug); 72 $error_code = 'install_error'; 73 if (! $plugin) { 74 $error = __('There was an error installing your plugin', 'templates_sdk'); 75 } 76 77 if (! $result) { 78 $error_code = $installer->skin->get_main_error_code(); 79 $message = $installer->skin->get_main_error_message(); 80 $error = $message ? $message : __('An unknown error occurred during installation', 'templates_sdk'); 81 } 82 83 if (! empty($error)) { 84 return new \WP_Error($error_code, $error, 400); 85 } 86 87 return (array) $installer->skin->get_upgrade_messages(); 88 } 89 90 /** 91 * Get WordPress.org zip download link from a plugin slug 92 * 93 * @param string $plugin_slug Plugin slug. 94 */ 95 protected static function generate_wordpress_org_plugin_download_link($plugin_slug) 96 { 97 return "https://downloads.wordpress.org/plugin/$plugin_slug.latest-stable.zip"; 98 } 99 100 /** 101 * Get the plugin ID (composed of the plugin slug and the name of the main plugin file) from a plugin slug. 102 * 103 * @param string $slug Plugin slug. 104 */ 105 public static function get_plugin_id_by_slug($slug) 106 { 107 // Check if get_plugins() function exists. This is required on the front end of the 108 // site, since it is in a file that is normally only loaded in the admin. 109 if (! function_exists('get_plugins')) { 110 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 111 } 112 113 /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */ 114 $plugins = apply_filters('all_plugins', get_plugins()); 115 if (! is_array($plugins)) { 116 return false; 117 } 118 119 foreach ($plugins as $plugin_file => $plugin_data) { 120 if (self::get_slug_from_file_path($plugin_file) === $slug) { 121 return $plugin_file; 122 } 123 } 124 125 return false; 126 } 127 128 /** 129 * Get the plugin slug from the plugin ID (composed of the plugin slug and the name of the main plugin file) 130 * 131 * @param string $plugin_file Plugin file (ID -- e.g. hello-dolly/hello.php). 132 */ 133 protected static function get_slug_from_file_path($plugin_file) 134 { 135 // Similar to get_plugin_slug() method. 136 $slug = dirname($plugin_file); 137 if ('.' === $slug) { 138 $slug = preg_replace('/(.+)\.php$/', '$1', $plugin_file); 139 } 140 141 return $slug; 142 } 143 144 /** 145 * Get the activation status for a plugin. 146 * 147 * @since 8.9.0 148 * 149 * @param string $plugin_file The plugin file to check. 150 * @return string Either 'network-active', 'active' or 'inactive'. 151 */ 152 public static function get_plugin_status($plugin_file) 153 { 154 if (is_plugin_active_for_network($plugin_file)) { 155 return 'network-active'; 156 } 157 158 if (is_plugin_active($plugin_file)) { 159 return 'active'; 160 } 161 162 return 'inactive'; 163 } 164 165 /** 166 * Returns a list of all plugins in the site. 167 * 168 * @since 8.9.0 169 * @uses get_plugins() 170 * 171 * @return array 172 */ 173 public static function get_plugins() 174 { 175 if (! function_exists('get_plugins')) { 176 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 177 } 178 /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */ 179 $plugins = apply_filters('all_plugins', get_plugins()); 180 181 if (is_array($plugins) && ! empty($plugins)) { 182 foreach ($plugins as $plugin_slug => $plugin_data) { 183 $plugins[ $plugin_slug ]['active'] = in_array( 184 self::get_plugin_status($plugin_slug), 185 array( 'active', 'network-active' ), 186 true 187 ); 188 } 189 return $plugins; 190 } 191 192 return array(); 193 } 194 195 /** 196 * Will return info about a plugin 197 * 198 * @param string $identifier The key of the plugin info. 199 * @param string $plugin_id The plugin identifier string 'editorplus-sdk. 200 * @return string 201 */ 202 public static function getPluginInfo($identifier, $plugin_id) 203 { 204 if (!function_exists('get_plugins')) { 205 include_once ABSPATH . 'wp-admin/includes/plugin.php'; 206 } 207 208 foreach (get_plugins() as $plugin => $data) { 209 if ($data[$identifier] === $plugin_id) { 210 return $plugin; 211 } 212 } 213 214 return false; 215 } 216 } 217 218 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; 219 require_once ABSPATH . 'wp-admin/includes/file.php'; 220 221 /** 222 * Allows us to capture that the site doesn't have proper file system access. 223 * In order to update the plugin. 224 */ 225 class Skin extends \Automatic_Upgrader_Skin 226 { 227 /** 228 * Stores the last error key; 229 **/ 230 protected $main_error_code = 'install_error'; 231 232 /** 233 * Stores the last error message. 234 **/ 235 protected $main_error_message = 'An unknown error occurred during installation'; 236 237 /** 238 * Overwrites the set_upgrader to be able to tell if we e ven have the ability to write to the files. 239 * 240 * @param WP_Upgrader $upgrader 241 * 242 */ 243 public function set_upgrader(&$upgrader) 244 { 245 parent::set_upgrader($upgrader); 246 247 // Check if we even have permission to. 248 $result = $upgrader->fs_connect(array( WP_CONTENT_DIR, WP_PLUGIN_DIR )); 249 if (! $result) { 250 // set the string here since they are not available just yet 251 $upgrader->generic_strings(); 252 $this->feedback('fs_unavailable'); 253 } 254 } 255 256 /** 257 * Overwrites the error function 258 */ 259 public function error($error) 260 { 261 if (is_wp_error($error)) { 262 $this->feedback($error); 263 } 264 } 265 266 private function set_main_error_code($code) 267 { 268 // Don't set the process_failed as code since it is not that helpful unless we don't have one already set. 269 $this->main_error_code = ($code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $code); 270 } 271 272 private function set_main_error_message($message, $code) 273 { 274 // Don't set the process_failed as message since it is not that helpful unless we don't have one already set. 275 $this->main_error_message = ($code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $message); 276 } 277 278 public function get_main_error_code() 279 { 280 return $this->main_error_code; 281 } 282 283 public function get_main_error_message() 284 { 285 return $this->main_error_message; 286 } 287 288 /** 289 * Overwrites the feedback function 290 * 291 * @param string|array|WP_Error $data Data. 292 * @param mixed ...$args Optional text replacements. 293 */ 294 public function feedback($data, ...$args) 295 { 296 $current_error = null; 297 if (is_wp_error($data)) { 298 $this->set_main_error_code($data->get_error_code()); 299 $string = $data->get_error_message(); 300 } elseif (is_array($data)) { 301 return; 302 } else { 303 $string = $data; 304 } 305 306 if (! empty($this->upgrader->strings[$string])) { 307 $this->set_main_error_code($string); 308 309 $current_error = $string; 310 $string = $this->upgrader->strings[$string]; 311 } 312 313 if (strpos($string, '%') !== false) { 314 if (! empty($args)) { 315 $string = vsprintf($string, $args); 316 } 317 } 318 319 $string = trim($string); 320 $string = wp_kses( 321 $string, 322 array( 323 'a' => array( 324 'href' => true 325 ), 326 'br' => true, 327 'em' => true, 328 'strong' => true, 329 ) 330 ); 331 332 $this->set_main_error_message($string, $current_error); 333 $this->messages[] = $string; 334 } 335 }