[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/rest-api/endpoints/ -> class-wp-rest-plugins-controller.php (source)

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


Generated: Wed Aug 12 01:00:03 2020 Cross-referenced by PHPXref 0.7.1