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


Generated: Sun Apr 28 01:00:03 2024 Cross-referenced by PHPXref 0.7.1