[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * REST API: WP_REST_Plugins_Controller class 4 * 5 * @package WordPress 6 * @subpackage REST_API 7 * @since 5.5.0 8 */ 9 10 /** 11 * Core class to access plugins via the REST API. 12 * 13 * @since 5.5.0 14 * 15 * @see WP_REST_Controller 16 */ 17 class WP_REST_Plugins_Controller extends WP_REST_Controller { 18 19 const PATTERN = '[^.\/]+(?:\/[^.\/]+)?'; 20 21 /** 22 * Plugins controller constructor. 23 * 24 * @since 5.5.0 25 */ 26 public function __construct() { 27 $this->namespace = 'wp/v2'; 28 $this->rest_base = 'plugins'; 29 } 30 31 /** 32 * Registers the routes for the plugins controller. 33 * 34 * @since 5.5.0 35 */ 36 public function register_routes() { 37 register_rest_route( 38 $this->namespace, 39 '/' . $this->rest_base, 40 array( 41 array( 42 'methods' => WP_REST_Server::READABLE, 43 'callback' => array( $this, 'get_items' ), 44 'permission_callback' => array( $this, 'get_items_permissions_check' ), 45 'args' => $this->get_collection_params(), 46 ), 47 array( 48 'methods' => WP_REST_Server::CREATABLE, 49 'callback' => array( $this, 'create_item' ), 50 'permission_callback' => array( $this, 'create_item_permissions_check' ), 51 'args' => array( 52 'slug' => array( 53 'type' => 'string', 54 'required' => true, 55 'description' => __( 'WordPress.org plugin directory slug.' ), 56 'pattern' => '[\w\-]+', 57 ), 58 'status' => array( 59 'description' => __( 'The plugin activation status.' ), 60 'type' => 'string', 61 'enum' => is_multisite() ? array( 'inactive', 'active', 'network-active' ) : array( 'inactive', 'active' ), 62 'default' => 'inactive', 63 ), 64 ), 65 ), 66 'schema' => array( $this, 'get_public_item_schema' ), 67 ) 68 ); 69 70 register_rest_route( 71 $this->namespace, 72 '/' . $this->rest_base . '/(?P<plugin>' . self::PATTERN . ')', 73 array( 74 array( 75 'methods' => WP_REST_Server::READABLE, 76 'callback' => array( $this, 'get_item' ), 77 'permission_callback' => array( $this, 'get_item_permissions_check' ), 78 ), 79 array( 80 'methods' => WP_REST_Server::EDITABLE, 81 'callback' => array( $this, 'update_item' ), 82 'permission_callback' => array( $this, 'update_item_permissions_check' ), 83 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), 84 ), 85 array( 86 'methods' => WP_REST_Server::DELETABLE, 87 'callback' => array( $this, 'delete_item' ), 88 'permission_callback' => array( $this, 'delete_item_permissions_check' ), 89 ), 90 'args' => array( 91 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 92 'plugin' => array( 93 'type' => 'string', 94 'pattern' => self::PATTERN, 95 'validate_callback' => array( $this, 'validate_plugin_param' ), 96 'sanitize_callback' => array( $this, 'sanitize_plugin_param' ), 97 ), 98 ), 99 'schema' => array( $this, 'get_public_item_schema' ), 100 ) 101 ); 102 } 103 104 /** 105 * Checks if a given request has access to get plugins. 106 * 107 * @since 5.5.0 108 * 109 * @param WP_REST_Request $request Full details about the request. 110 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 111 */ 112 public function get_items_permissions_check( $request ) { 113 if ( ! current_user_can( 'activate_plugins' ) ) { 114 return new WP_Error( 115 'rest_cannot_view_plugins', 116 __( 'Sorry, you are not allowed to manage plugins for this site.' ), 117 array( 'status' => rest_authorization_required_code() ) 118 ); 119 } 120 121 return true; 122 } 123 124 /** 125 * Retrieves a collection of plugins. 126 * 127 * @since 5.5.0 128 * 129 * @param WP_REST_Request $request Full details about the request. 130 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 131 */ 132 public function get_items( $request ) { 133 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 134 135 $plugins = array(); 136 137 foreach ( get_plugins() as $file => $data ) { 138 if ( is_wp_error( $this->check_read_permission( $file ) ) ) { 139 continue; 140 } 141 142 $data['_file'] = $file; 143 144 if ( ! $this->does_plugin_match_request( $request, $data ) ) { 145 continue; 146 } 147 148 $plugins[] = $this->prepare_response_for_collection( $this->prepare_item_for_response( $data, $request ) ); 149 } 150 151 return new WP_REST_Response( $plugins ); 152 } 153 154 /** 155 * Checks if a given request has access to get a specific plugin. 156 * 157 * @since 5.5.0 158 * 159 * @param WP_REST_Request $request Full details about the request. 160 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise. 161 */ 162 public function get_item_permissions_check( $request ) { 163 if ( ! current_user_can( 'activate_plugins' ) ) { 164 return new WP_Error( 165 'rest_cannot_view_plugin', 166 __( 'Sorry, you are not allowed to manage plugins for this site.' ), 167 array( 'status' => rest_authorization_required_code() ) 168 ); 169 } 170 171 $can_read = $this->check_read_permission( $request['plugin'] ); 172 173 if ( is_wp_error( $can_read ) ) { 174 return $can_read; 175 } 176 177 return true; 178 } 179 180 /** 181 * Retrieves one plugin from the site. 182 * 183 * @since 5.5.0 184 * 185 * @param WP_REST_Request $request Full details about the request. 186 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 187 */ 188 public function get_item( $request ) { 189 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 190 191 $data = $this->get_plugin_data( $request['plugin'] ); 192 193 if ( is_wp_error( $data ) ) { 194 return $data; 195 } 196 197 return $this->prepare_item_for_response( $data, $request ); 198 } 199 200 /** 201 * Checks if the given plugin can be viewed by the current user. 202 * 203 * On multisite, this hides non-active network only plugins if the user does not have permission 204 * to manage network plugins. 205 * 206 * @since 5.5.0 207 * 208 * @param string $plugin The plugin file to check. 209 * @return true|WP_Error True if can read, a WP_Error instance otherwise. 210 */ 211 protected function check_read_permission( $plugin ) { 212 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 213 214 if ( ! $this->is_plugin_installed( $plugin ) ) { 215 return new WP_Error( 'rest_plugin_not_found', __( 'Plugin not found.' ), array( 'status' => 404 ) ); 216 } 217 218 if ( ! is_multisite() ) { 219 return true; 220 } 221 222 if ( ! is_network_only_plugin( $plugin ) || is_plugin_active( $plugin ) || current_user_can( 'manage_network_plugins' ) ) { 223 return true; 224 } 225 226 return new WP_Error( 227 'rest_cannot_view_plugin', 228 __( 'Sorry, you are not allowed to manage this plugin.' ), 229 array( 'status' => rest_authorization_required_code() ) 230 ); 231 } 232 233 /** 234 * Checks if a given request has access to upload plugins. 235 * 236 * @since 5.5.0 237 * 238 * @param WP_REST_Request $request Full details about the request. 239 * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise. 240 */ 241 public function create_item_permissions_check( $request ) { 242 if ( ! current_user_can( 'install_plugins' ) ) { 243 return new WP_Error( 244 'rest_cannot_install_plugin', 245 __( 'Sorry, you are not allowed to install plugins on this site.' ), 246 array( 'status' => rest_authorization_required_code() ) 247 ); 248 } 249 250 if ( 'inactive' !== $request['status'] && ! current_user_can( 'activate_plugins' ) ) { 251 return new WP_Error( 252 'rest_cannot_activate_plugin', 253 __( 'Sorry, you are not allowed to activate plugins.' ), 254 array( 255 'status' => rest_authorization_required_code(), 256 ) 257 ); 258 } 259 260 return true; 261 } 262 263 /** 264 * Uploads a plugin and optionally activates it. 265 * 266 * @since 5.5.0 267 * 268 * @param WP_REST_Request $request Full details about the request. 269 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 270 */ 271 public function create_item( $request ) { 272 require_once ABSPATH . 'wp-admin/includes/file.php'; 273 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 274 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; 275 require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; 276 277 $slug = $request['slug']; 278 279 // Verify filesystem is accessible first. 280 $filesystem_available = $this->is_filesystem_available(); 281 if ( is_wp_error( $filesystem_available ) ) { 282 return $filesystem_available; 283 } 284 285 $api = plugins_api( 286 'plugin_information', 287 array( 288 'slug' => $slug, 289 'fields' => array( 290 'sections' => false, 291 'language_packs' => true, 292 ), 293 ) 294 ); 295 296 if ( is_wp_error( $api ) ) { 297 if ( false !== strpos( $api->get_error_message(), 'Plugin not found.' ) ) { 298 $api->add_data( array( 'status' => 404 ) ); 299 } else { 300 $api->add_data( array( 'status' => 500 ) ); 301 } 302 303 return $api; 304 } 305 306 $skin = new WP_Ajax_Upgrader_Skin(); 307 $upgrader = new Plugin_Upgrader( $skin ); 308 309 $result = $upgrader->install( $api->download_link ); 310 311 if ( is_wp_error( $result ) ) { 312 $result->add_data( array( 'status' => 500 ) ); 313 314 return $result; 315 } 316 317 // This should be the same as $result above. 318 if ( is_wp_error( $skin->result ) ) { 319 $skin->result->add_data( array( 'status' => 500 ) ); 320 321 return $skin->result; 322 } 323 324 if ( $skin->get_errors()->has_errors() ) { 325 $error = $skin->get_errors(); 326 $error->add_data( array( 'status' => 500 ) ); 327 328 return $error; 329 } 330 331 if ( is_null( $result ) ) { 332 global $wp_filesystem; 333 // Pass through the error from WP_Filesystem if one was raised. 334 if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { 335 return new WP_Error( 'unable_to_connect_to_filesystem', $wp_filesystem->errors->get_error_message(), array( 'status' => 500 ) ); 336 } 337 338 return new WP_Error( 'unable_to_connect_to_filesystem', __( 'Unable to connect to the filesystem. Please confirm your credentials.' ), array( 'status' => 500 ) ); 339 } 340 341 $file = $upgrader->plugin_info(); 342 343 if ( ! $file ) { 344 return new WP_Error( 'unable_to_determine_installed_plugin', __( 'Unable to determine what plugin was installed.' ), array( 'status' => 500 ) ); 345 } 346 347 if ( 'inactive' !== $request['status'] ) { 348 $can_change_status = $this->plugin_status_permission_check( $file, $request['status'], 'inactive' ); 349 350 if ( is_wp_error( $can_change_status ) ) { 351 return $can_change_status; 352 } 353 354 $changed_status = $this->handle_plugin_status( $file, $request['status'], 'inactive' ); 355 356 if ( is_wp_error( $changed_status ) ) { 357 return $changed_status; 358 } 359 } 360 361 // Install translations. 362 $installed_locales = array_values( get_available_languages() ); 363 /** This filter is documented in wp-includes/update.php */ 364 $installed_locales = apply_filters( 'plugins_update_check_locales', $installed_locales ); 365 366 $language_packs = array_map( 367 function( $item ) { 368 return (object) $item; 369 }, 370 $api->language_packs 371 ); 372 373 $language_packs = array_filter( 374 $language_packs, 375 function( $pack ) use ( $installed_locales ) { 376 return in_array( $pack->language, $installed_locales, true ); 377 } 378 ); 379 380 if ( $language_packs ) { 381 $lp_upgrader = new Language_Pack_Upgrader( $skin ); 382 383 // Install all applicable language packs for the plugin. 384 $lp_upgrader->bulk_upgrade( $language_packs ); 385 } 386 387 $path = WP_PLUGIN_DIR . '/' . $file; 388 $data = get_plugin_data( $path, false, false ); 389 $data['_file'] = $file; 390 391 $response = $this->prepare_item_for_response( $data, $request ); 392 $response->set_status( 201 ); 393 $response->header( 'Location', rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, substr( $file, 0, - 4 ) ) ) ); 394 395 return $response; 396 } 397 398 /** 399 * Checks if a given request has access to update a specific plugin. 400 * 401 * @since 5.5.0 402 * 403 * @param WP_REST_Request $request Full details about the request. 404 * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. 405 */ 406 public function update_item_permissions_check( $request ) { 407 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 408 409 if ( ! current_user_can( 'activate_plugins' ) ) { 410 return new WP_Error( 411 'rest_cannot_manage_plugins', 412 __( 'Sorry, you are not allowed to manage plugins for this site.' ), 413 array( 'status' => rest_authorization_required_code() ) 414 ); 415 } 416 417 $can_read = $this->check_read_permission( $request['plugin'] ); 418 419 if ( is_wp_error( $can_read ) ) { 420 return $can_read; 421 } 422 423 $status = $this->get_plugin_status( $request['plugin'] ); 424 425 if ( $request['status'] && $status !== $request['status'] ) { 426 $can_change_status = $this->plugin_status_permission_check( $request['plugin'], $request['status'], $status ); 427 428 if ( is_wp_error( $can_change_status ) ) { 429 return $can_change_status; 430 } 431 } 432 433 return true; 434 } 435 436 /** 437 * Updates one plugin. 438 * 439 * @since 5.5.0 440 * 441 * @param WP_REST_Request $request Full details about the request. 442 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 443 */ 444 public function update_item( $request ) { 445 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 446 447 $data = $this->get_plugin_data( $request['plugin'] ); 448 449 if ( is_wp_error( $data ) ) { 450 return $data; 451 } 452 453 $status = $this->get_plugin_status( $request['plugin'] ); 454 455 if ( $request['status'] && $status !== $request['status'] ) { 456 $handled = $this->handle_plugin_status( $request['plugin'], $request['status'], $status ); 457 458 if ( is_wp_error( $handled ) ) { 459 return $handled; 460 } 461 } 462 463 $this->update_additional_fields_for_object( $data, $request ); 464 465 $request['context'] = 'edit'; 466 467 return $this->prepare_item_for_response( $data, $request ); 468 } 469 470 /** 471 * Checks if a given request has access to delete a specific plugin. 472 * 473 * @since 5.5.0 474 * 475 * @param WP_REST_Request $request Full details about the request. 476 * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. 477 */ 478 public function delete_item_permissions_check( $request ) { 479 if ( ! current_user_can( 'activate_plugins' ) ) { 480 return new WP_Error( 481 'rest_cannot_manage_plugins', 482 __( 'Sorry, you are not allowed to manage plugins for this site.' ), 483 array( 'status' => rest_authorization_required_code() ) 484 ); 485 } 486 487 if ( ! current_user_can( 'delete_plugins' ) ) { 488 return new WP_Error( 489 'rest_cannot_manage_plugins', 490 __( 'Sorry, you are not allowed to delete plugins for this site.' ), 491 array( 'status' => rest_authorization_required_code() ) 492 ); 493 } 494 495 $can_read = $this->check_read_permission( $request['plugin'] ); 496 497 if ( is_wp_error( $can_read ) ) { 498 return $can_read; 499 } 500 501 return true; 502 } 503 504 /** 505 * Deletes one plugin from the site. 506 * 507 * @since 5.5.0 508 * 509 * @param WP_REST_Request $request Full details about the request. 510 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 511 */ 512 public function delete_item( $request ) { 513 require_once ABSPATH . 'wp-admin/includes/file.php'; 514 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 515 516 $data = $this->get_plugin_data( $request['plugin'] ); 517 518 if ( is_wp_error( $data ) ) { 519 return $data; 520 } 521 522 if ( is_plugin_active( $request['plugin'] ) ) { 523 return new WP_Error( 524 'rest_cannot_delete_active_plugin', 525 __( 'Cannot delete an active plugin. Please deactivate it first.' ), 526 array( 'status' => 400 ) 527 ); 528 } 529 530 $filesystem_available = $this->is_filesystem_available(); 531 if ( is_wp_error( $filesystem_available ) ) { 532 return $filesystem_available; 533 } 534 535 $prepared = $this->prepare_item_for_response( $data, $request ); 536 $deleted = delete_plugins( array( $request['plugin'] ) ); 537 538 if ( is_wp_error( $deleted ) ) { 539 $deleted->add_data( array( 'status' => 500 ) ); 540 541 return $deleted; 542 } 543 544 return new WP_REST_Response( 545 array( 546 'deleted' => true, 547 'previous' => $prepared->get_data(), 548 ) 549 ); 550 } 551 552 /** 553 * Prepares the plugin for the REST response. 554 * 555 * @since 5.5.0 556 * 557 * @param mixed $item Unmarked up and untranslated plugin data from {@see get_plugin_data()}. 558 * @param WP_REST_Request $request Request object. 559 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 560 */ 561 public function prepare_item_for_response( $item, $request ) { 562 $item = _get_plugin_data_markup_translate( $item['_file'], $item, false ); 563 $marked = _get_plugin_data_markup_translate( $item['_file'], $item, true ); 564 565 $data = array( 566 'plugin' => substr( $item['_file'], 0, - 4 ), 567 'status' => $this->get_plugin_status( $item['_file'] ), 568 'name' => $item['Name'], 569 'plugin_uri' => $item['PluginURI'], 570 'author' => $item['Author'], 571 'author_uri' => $item['AuthorURI'], 572 'description' => array( 573 'raw' => $item['Description'], 574 'rendered' => $marked['Description'], 575 ), 576 'version' => $item['Version'], 577 'network_only' => $item['Network'], 578 'requires_wp' => $item['RequiresWP'], 579 'requires_php' => $item['RequiresPHP'], 580 'textdomain' => $item['TextDomain'], 581 ); 582 583 $data = $this->add_additional_fields_to_object( $data, $request ); 584 585 $response = new WP_REST_Response( $data ); 586 $response->add_links( $this->prepare_links( $item ) ); 587 588 /** 589 * Filters plugin data for a REST API response. 590 * 591 * @since 5.5.0 592 * 593 * @param WP_REST_Response $response The response object. 594 * @param array $item The plugin item from {@see get_plugin_data()}. 595 * @param WP_REST_Request $request The request object. 596 */ 597 return apply_filters( 'rest_prepare_plugin', $response, $item, $request ); 598 } 599 600 /** 601 * Prepares links for the request. 602 * 603 * @since 5.5.0 604 * 605 * @param array $item The plugin item. 606 * @return array[] 607 */ 608 protected function prepare_links( $item ) { 609 return array( 610 'self' => array( 611 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, substr( $item['_file'], 0, - 4 ) ) ), 612 ), 613 ); 614 } 615 616 /** 617 * Gets the plugin header data for a plugin. 618 * 619 * @since 5.5.0 620 * 621 * @param string $plugin The plugin file to get data for. 622 * @return array|WP_Error The plugin data, or a WP_Error if the plugin is not installed. 623 */ 624 protected function get_plugin_data( $plugin ) { 625 $plugins = get_plugins(); 626 627 if ( ! isset( $plugins[ $plugin ] ) ) { 628 return new WP_Error( 'rest_plugin_not_found', __( 'Plugin not found.' ), array( 'status' => 404 ) ); 629 } 630 631 $data = $plugins[ $plugin ]; 632 $data['_file'] = $plugin; 633 634 return $data; 635 } 636 637 /** 638 * Get's the activation status for a plugin. 639 * 640 * @since 5.5.0 641 * 642 * @param string $plugin The plugin file to check. 643 * @return string Either 'network-active', 'active' or 'inactive'. 644 */ 645 protected function get_plugin_status( $plugin ) { 646 if ( is_plugin_active_for_network( $plugin ) ) { 647 return 'network-active'; 648 } 649 650 if ( is_plugin_active( $plugin ) ) { 651 return 'active'; 652 } 653 654 return 'inactive'; 655 } 656 657 /** 658 * Handle updating a plugin's status. 659 * 660 * @since 5.5.0 661 * 662 * @param string $plugin The plugin file to update. 663 * @param string $new_status The plugin's new status. 664 * @param string $current_status The plugin's current status. 665 * 666 * @return true|WP_Error 667 */ 668 protected function plugin_status_permission_check( $plugin, $new_status, $current_status ) { 669 if ( is_multisite() && ( 'network-active' === $current_status || 'network-active' === $new_status ) && ! current_user_can( 'manage_network_plugins' ) ) { 670 return new WP_Error( 671 'rest_cannot_manage_network_plugins', 672 __( 'Sorry, you are not allowed to manage network plugins.' ), 673 array( 'status' => rest_authorization_required_code() ) 674 ); 675 } 676 677 if ( ( 'active' === $new_status || 'network-active' === $new_status ) && ! current_user_can( 'activate_plugin', $plugin ) ) { 678 return new WP_Error( 679 'rest_cannot_activate_plugin', 680 __( 'Sorry, you are not allowed to activate this plugin.' ), 681 array( 'status' => rest_authorization_required_code() ) 682 ); 683 } 684 685 if ( 'inactive' === $new_status && ! current_user_can( 'deactivate_plugin', $plugin ) ) { 686 return new WP_Error( 687 'rest_cannot_deactivate_plugin', 688 __( 'Sorry, you are not allowed to deactivate this plugin.' ), 689 array( 'status' => rest_authorization_required_code() ) 690 ); 691 } 692 693 return true; 694 } 695 696 /** 697 * Handle updating a plugin's status. 698 * 699 * @since 5.5.0 700 * 701 * @param string $plugin The plugin file to update. 702 * @param string $new_status The plugin's new status. 703 * @param string $current_status The plugin's current status. 704 * @return true|WP_Error 705 */ 706 protected function handle_plugin_status( $plugin, $new_status, $current_status ) { 707 if ( 'inactive' === $new_status ) { 708 deactivate_plugins( $plugin, false, 'network-active' === $current_status ); 709 710 return true; 711 } 712 713 if ( 'active' === $new_status && 'network-active' === $current_status ) { 714 return true; 715 } 716 717 $network_activate = 'network-active' === $new_status; 718 719 if ( is_multisite() && ! $network_activate && is_network_only_plugin( $plugin ) ) { 720 return new WP_Error( 721 'rest_network_only_plugin', 722 __( 'Network only plugin must be network activated.' ), 723 array( 'status' => 400 ) 724 ); 725 } 726 727 $activated = activate_plugin( $plugin, '', $network_activate ); 728 729 if ( is_wp_error( $activated ) ) { 730 $activated->add_data( array( 'status' => 500 ) ); 731 732 return $activated; 733 } 734 735 return true; 736 } 737 738 /** 739 * Checks that the "plugin" parameter is a valid path. 740 * 741 * @since 5.5.0 742 * 743 * @param string $file The plugin file parameter. 744 * @return bool 745 */ 746 public function validate_plugin_param( $file ) { 747 if ( ! is_string( $file ) || ! preg_match( '/' . self::PATTERN . '/u', $file ) ) { 748 return false; 749 } 750 751 $validated = validate_file( plugin_basename( $file ) ); 752 753 return 0 === $validated; 754 } 755 756 /** 757 * Sanitizes the "plugin" parameter to be a proper plugin file with ".php" appended. 758 * 759 * @since 5.5.0 760 * 761 * @param string $file The plugin file parameter. 762 * @return string 763 */ 764 public function sanitize_plugin_param( $file ) { 765 return plugin_basename( sanitize_text_field( $file . '.php' ) ); 766 } 767 768 /** 769 * Checks if the plugin matches the requested parameters. 770 * 771 * @since 5.5.0 772 * 773 * @param WP_REST_Request $request The request to require the plugin matches against. 774 * @param array $item The plugin item. 775 * 776 * @return bool 777 */ 778 protected function does_plugin_match_request( $request, $item ) { 779 $search = $request['search']; 780 781 if ( $search ) { 782 $matched_search = false; 783 784 foreach ( $item as $field ) { 785 if ( is_string( $field ) && false !== strpos( strip_tags( $field ), $search ) ) { 786 $matched_search = true; 787 break; 788 } 789 } 790 791 if ( ! $matched_search ) { 792 return false; 793 } 794 } 795 796 $status = $request['status']; 797 798 if ( $status && ! in_array( $this->get_plugin_status( $item['_file'] ), $status, true ) ) { 799 return false; 800 } 801 802 return true; 803 } 804 805 /** 806 * Checks if the plugin is installed. 807 * 808 * @since 5.5.0 809 * 810 * @param string $plugin The plugin file. 811 * @return bool 812 */ 813 protected function is_plugin_installed( $plugin ) { 814 return file_exists( WP_PLUGIN_DIR . '/' . $plugin ); 815 } 816 817 /** 818 * Determine if the endpoints are available. 819 * 820 * Only the 'Direct' filesystem transport, and SSH/FTP when credentials are stored are supported at present. 821 * 822 * @since 5.5.0 823 * 824 * @return true|WP_Error True if filesystem is available, WP_Error otherwise. 825 */ 826 protected function is_filesystem_available() { 827 $filesystem_method = get_filesystem_method(); 828 829 if ( 'direct' === $filesystem_method ) { 830 return true; 831 } 832 833 ob_start(); 834 $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() ); 835 ob_end_clean(); 836 837 if ( $filesystem_credentials_are_stored ) { 838 return true; 839 } 840 841 return new WP_Error( 'fs_unavailable', __( 'The filesystem is currently unavailable for managing plugins.' ), array( 'status' => 500 ) ); 842 } 843 844 /** 845 * Retrieves the plugin's schema, conforming to JSON Schema. 846 * 847 * @since 5.5.0 848 * 849 * @return array Item schema data. 850 */ 851 public function get_item_schema() { 852 if ( $this->schema ) { 853 return $this->add_additional_fields_schema( $this->schema ); 854 } 855 856 $this->schema = array( 857 '$schema' => 'http://json-schema.org/draft-04/schema#', 858 'title' => 'plugin', 859 'type' => 'object', 860 'properties' => array( 861 'plugin' => array( 862 'description' => __( 'The plugin file.' ), 863 'type' => 'string', 864 'pattern' => self::PATTERN, 865 'readonly' => true, 866 'context' => array( 'view', 'edit', 'embed' ), 867 ), 868 'status' => array( 869 'description' => __( 'The plugin activation status.' ), 870 'type' => 'string', 871 'enum' => is_multisite() ? array( 'inactive', 'active', 'network-active' ) : array( 'inactive', 'active' ), 872 'context' => array( 'view', 'edit', 'embed' ), 873 ), 874 'name' => array( 875 'description' => __( 'The plugin name.' ), 876 'type' => 'string', 877 'readonly' => true, 878 'context' => array( 'view', 'edit', 'embed' ), 879 ), 880 'plugin_uri' => array( 881 'description' => __( 'The plugin\'s website address.' ), 882 'type' => 'string', 883 'format' => 'uri', 884 'readonly' => true, 885 'context' => array( 'view', 'edit' ), 886 ), 887 'author' => array( 888 'description' => __( 'The plugin author.' ), 889 'type' => 'object', 890 'readonly' => true, 891 'context' => array( 'view', 'edit' ), 892 ), 893 'author_uri' => array( 894 'description' => __( 'Plugin author\'s website address.' ), 895 'type' => 'string', 896 'format' => 'uri', 897 'readonly' => true, 898 'context' => array( 'view', 'edit' ), 899 ), 900 'description' => array( 901 'description' => __( 'The plugin description.' ), 902 'type' => 'object', 903 'readonly' => true, 904 'context' => array( 'view', 'edit' ), 905 'properties' => array( 906 'raw' => array( 907 'description' => __( 'The raw plugin description.' ), 908 'type' => 'string', 909 ), 910 'rendered' => array( 911 'description' => __( 'The plugin description formatted for display.' ), 912 'type' => 'string', 913 ), 914 ), 915 ), 916 'version' => array( 917 'description' => __( 'The plugin version number.' ), 918 'type' => 'string', 919 'readonly' => true, 920 'context' => array( 'view', 'edit' ), 921 ), 922 'network_only' => array( 923 'description' => __( 'Whether the plugin can only be activated network-wide.' ), 924 'type' => 'boolean', 925 'readonly' => true, 926 'context' => array( 'view', 'edit', 'embed' ), 927 ), 928 'requires_wp' => array( 929 'description' => __( 'Minimum required version of WordPress.' ), 930 'type' => 'string', 931 'readonly' => true, 932 'context' => array( 'view', 'edit', 'embed' ), 933 ), 934 'requires_php' => array( 935 'description' => __( 'Minimum required version of PHP.' ), 936 'type' => 'string', 937 'readonly' => true, 938 'context' => array( 'view', 'edit', 'embed' ), 939 ), 940 'textdomain' => array( 941 'description' => __( 'The plugin\'s text domain.' ), 942 'type' => 'string', 943 'readonly' => true, 944 'context' => array( 'view', 'edit' ), 945 ), 946 ), 947 ); 948 949 return $this->add_additional_fields_schema( $this->schema ); 950 } 951 952 /** 953 * Retrieves the query params for the collections. 954 * 955 * @since 5.5.0 956 * 957 * @return array Query parameters for the collection. 958 */ 959 public function get_collection_params() { 960 $query_params = parent::get_collection_params(); 961 962 $query_params['context']['default'] = 'view'; 963 964 $query_params['status'] = array( 965 'description' => __( 'Limits results to plugins with the given status.' ), 966 'type' => 'array', 967 'items' => array( 968 'type' => 'string', 969 'enum' => is_multisite() ? array( 'inactive', 'active', 'network-active' ) : array( 'inactive', 'active' ), 970 ), 971 ); 972 973 unset( $query_params['page'], $query_params['per_page'] ); 974 975 return $query_params; 976 } 977 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Mon Jan 18 08:20:02 2021 | Cross-referenced by PHPXref |