[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

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       * @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  }


Generated : Mon Jan 18 08:20:02 2021 Cross-referenced by PHPXref