[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

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

   1  <?php
   2  /**
   3   * REST API: WP_REST_Entity_Actions_Controller class
   4   *
   5   * @package    WordPress
   6   * @subpackage REST_API
   7   * @since      7.2.0
   8   */
   9  
  10  /**
  11   * Controller which provides a REST endpoint for retrieving the actions
  12   * registered for a given entity type.
  13   *
  14   * The actions are the ones registered on the server on the
  15   * `wp_entity_actions_api_init` action: the serializable part of each action, plus
  16   * the script modules that provide the JavaScript parts (callbacks, modals,
  17   * eligibility checks, icons), each with the ids of the actions it applies
  18   * to. The client merges both into the actions it shows.
  19   *
  20   * @since 7.2.0
  21   *
  22   * @see WP_REST_Controller
  23   */
  24  class WP_REST_Entity_Actions_Controller extends WP_REST_Controller {
  25  
  26      /**
  27       * Constructor.
  28       *
  29       * @since 7.2.0
  30       */
  31  	public function __construct() {
  32          $this->namespace = 'wp/v2';
  33          $this->rest_base = 'entity-actions';
  34      }
  35  
  36      /**
  37       * Registers the routes for the controller.
  38       *
  39       * @since 7.2.0
  40       */
  41  	public function register_routes() {
  42          if ( $this->is_route_of_entity_items() ) {
  43              return;
  44          }
  45  
  46          register_rest_route(
  47              $this->namespace,
  48              '/' . $this->rest_base,
  49              array(
  50                  array(
  51                      'methods'             => WP_REST_Server::READABLE,
  52                      'callback'            => array( $this, 'get_items' ),
  53                      'permission_callback' => array( $this, 'get_items_permissions_check' ),
  54                      'args'                => array(
  55                          'kind' => array(
  56                              'description' => __( 'Entity kind.' ),
  57                              'type'        => 'string',
  58                              'minLength'   => 1,
  59                              'required'    => true,
  60                          ),
  61                          'name' => array(
  62                              'description' => __( 'Entity name.' ),
  63                              'type'        => 'string',
  64                              'minLength'   => 1,
  65                              'required'    => true,
  66                          ),
  67                      ),
  68                  ),
  69                  'schema' => array( $this, 'get_public_item_schema' ),
  70              )
  71          );
  72      }
  73  
  74      /**
  75       * Checks whether a post type or taxonomy has its items on the route of
  76       * the controller, reporting it with _doing_it_wrong().
  77       *
  78       * The route is then left to the post type or taxonomy: registering it
  79       * as well would not serve the actions, as the route of the items comes
  80       * first, and would replace the schema of that route.
  81       *
  82       * @since 7.2.0
  83       *
  84       * @return bool Whether a post type or taxonomy has its items on the route.
  85       */
  86  	protected function is_route_of_entity_items() {
  87          $objects = array_merge(
  88              array_values( get_post_types( array( 'show_in_rest' => true ), 'objects' ) ),
  89              array_values( get_taxonomies( array( 'show_in_rest' => true ), 'objects' ) )
  90          );
  91          foreach ( $objects as $object ) {
  92              $namespace = $object->rest_namespace ? $object->rest_namespace : 'wp/v2';
  93              $base      = $object->rest_base ? $object->rest_base : $object->name;
  94              if ( $this->namespace === $namespace && $this->rest_base === $base ) {
  95                  _doing_it_wrong(
  96                      __METHOD__,
  97                      sprintf(
  98                          /* translators: 1: REST route, e.g. /wp/v2/entity-actions. 2: Post type or taxonomy name. */
  99                          __( 'The %1$s route serves the items of the "%2$s" post type or taxonomy, so the actions of entities are not available.' ),
 100                          '/' . $this->namespace . '/' . $this->rest_base,
 101                          $object->name
 102                      ),
 103                      '7.2.0'
 104                  );
 105                  return true;
 106              }
 107          }
 108  
 109          return false;
 110      }
 111  
 112      /**
 113       * Checks if a given request has access to read the actions of an entity.
 114       *
 115       * @since 7.2.0
 116       *
 117       * @param WP_REST_Request $request Full details about the request.
 118       * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
 119       */
 120  	public function get_items_permissions_check( $request ) {
 121          $kind = $request->get_param( 'kind' );
 122          $name = $request->get_param( 'name' );
 123  
 124          $capability = $this->get_required_capability( $kind, $name );
 125  
 126          if ( null === $capability ) {
 127              return new WP_Error(
 128                  'rest_entity_actions_invalid_entity',
 129                  __( 'Invalid entity kind or name.' ),
 130                  array( 'status' => 404 )
 131              );
 132          }
 133  
 134          if ( ! current_user_can( $capability ) ) {
 135              return new WP_Error(
 136                  'rest_cannot_read',
 137                  __( 'Sorry, you are not allowed to read the actions of this entity.' ),
 138                  array( 'status' => rest_authorization_required_code() )
 139              );
 140          }
 141  
 142          return true;
 143      }
 144  
 145      /**
 146       * Resolves the capability required to read the actions of an entity.
 147       *
 148       * Mirrors the fields endpoint, which gates the same entities: post types
 149       * use their own `edit_posts` capability (which honors custom
 150       * `capability_type` registrations), taxonomies use `manage_terms`, and
 151       * root-level entities use `manage_options`. A post type or taxonomy that is
 152       * not registered, or not exposed to the REST API, resolves to `null` so the
 153       * request is treated as referencing an unknown entity.
 154       *
 155       * Any other kind falls back to `edit_posts`, so actions registered for a
 156       * custom entity kind stay readable behind a baseline capability.
 157       *
 158       * @since 7.2.0
 159       *
 160       * @param string $kind The entity kind (e.g. `postType`).
 161       * @param string $name The entity name (e.g. `page`).
 162       * @return string|null Capability required to read the actions, or null if
 163       *                     the entity is not registered.
 164       */
 165  	protected function get_required_capability( $kind, $name ) {
 166          switch ( $kind ) {
 167              case 'postType':
 168                  $post_type = get_post_type_object( $name );
 169                  if ( $post_type && $post_type->show_in_rest ) {
 170                      return $post_type->cap->edit_posts;
 171                  }
 172                  return null;
 173  
 174              case 'taxonomy':
 175                  $taxonomy = get_taxonomy( $name );
 176                  if ( $taxonomy && $taxonomy->show_in_rest ) {
 177                      return $taxonomy->cap->manage_terms;
 178                  }
 179                  return null;
 180  
 181              case 'root':
 182                  return 'manage_options';
 183          }
 184  
 185          return 'edit_posts';
 186      }
 187  
 188      /**
 189       * Returns the actions registered for the given entity type.
 190       *
 191       * @since 7.2.0
 192       *
 193       * @param WP_REST_Request $request Full details about the request.
 194       * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
 195       */
 196  	public function get_items( $request ) {
 197          $kind = $request->get_param( 'kind' );
 198          $name = $request->get_param( 'name' );
 199  
 200          $response = array(
 201              'kind'           => $kind,
 202              'name'           => $name,
 203              'actions'        => wp_get_registered_entity_actions( $kind, $name ),
 204              'script_modules' => wp_get_registered_entity_action_modules( $kind, $name ),
 205          );
 206  
 207          return rest_ensure_response( $response );
 208      }
 209  
 210      /**
 211       * Retrieves the item's schema, conforming to JSON Schema.
 212       *
 213       * @since 7.2.0
 214       *
 215       * @return array Item schema data.
 216       */
 217  	public function get_item_schema() {
 218          if ( $this->schema ) {
 219              return $this->add_additional_fields_schema( $this->schema );
 220          }
 221  
 222          $this->schema = array(
 223              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 224              'title'      => 'entity-actions',
 225              'type'       => 'object',
 226              'properties' => array(
 227                  'kind'           => array(
 228                      'description' => __( 'Entity kind.' ),
 229                      'type'        => 'string',
 230                      'readonly'    => true,
 231                  ),
 232                  'name'           => array(
 233                      'description' => __( 'Entity name.' ),
 234                      'type'        => 'string',
 235                      'readonly'    => true,
 236                  ),
 237                  'actions'        => array(
 238                      'description' => __( 'The actions registered for the entity, in registration order.' ),
 239                      'type'        => 'array',
 240                      'readonly'    => true,
 241                      'items'       => $this->get_action_schema(),
 242                  ),
 243                  'script_modules' => array(
 244                      'description' => __( 'The script modules providing the JavaScript parts of the actions, each with the ids of the actions it applies to.' ),
 245                      'type'        => 'array',
 246                      'readonly'    => true,
 247                      'items'       => array(
 248                          'type'       => 'object',
 249                          'properties' => array(
 250                              'id'      => array(
 251                                  'description' => __( 'The id of the script module.' ),
 252                                  'type'        => 'string',
 253                              ),
 254                              'actions' => array(
 255                                  'description' => __( 'The ids of the actions the script module applies to.' ),
 256                                  'type'        => 'array',
 257                                  'items'       => array(
 258                                      'type' => 'string',
 259                                  ),
 260                              ),
 261                          ),
 262                      ),
 263                  ),
 264              ),
 265          );
 266  
 267          return $this->add_additional_fields_schema( $this->schema );
 268      }
 269  
 270      /**
 271       * Returns the schema for an action definition.
 272       *
 273       * Describes the serializable subset of the DataViews Action API, see
 274       * packages/dataviews/src/types/dataviews.ts. The properties that are
 275       * JavaScript callbacks, components, or elements (`callback`,
 276       * `RenderModal`, `isEligible`, `icon`, …) come from the script modules and
 277       * are not part of the definition. Additional properties are allowed, so a
 278       * plugin can register an action with properties of its own that its script
 279       * module understands.
 280       *
 281       * @since 7.2.0
 282       *
 283       * @return array Schema for an action definition.
 284       */
 285  	protected function get_action_schema() {
 286          return array(
 287              'type'                 => 'object',
 288              'properties'           => array(
 289                  'id'                => array(
 290                      'description' => __( 'The unique identifier of the action.' ),
 291                      'type'        => 'string',
 292                  ),
 293                  'origin'            => array(
 294                      'description' => __( 'Who registered the action: `core`, or the slug of a plugin or theme.' ),
 295                      'type'        => 'object',
 296                      'properties'  => array(
 297                          'registeredBy' => array(
 298                              'description' => __( 'Who registered the action.' ),
 299                              'type'        => 'string',
 300                          ),
 301                      ),
 302                  ),
 303                  'label'             => array(
 304                      'description' => __( 'The label of the action.' ),
 305                      'type'        => 'string',
 306                  ),
 307                  'isPrimary'         => array(
 308                      'description' => __( 'Whether the action is a primary action.' ),
 309                      'type'        => 'boolean',
 310                  ),
 311                  'supportsBulk'      => array(
 312                      'description' => __( 'Whether the action can be performed on several items at once.' ),
 313                      'type'        => 'boolean',
 314                  ),
 315                  'disabled'          => array(
 316                      'description' => __( 'Whether the action is disabled.' ),
 317                      'type'        => 'boolean',
 318                  ),
 319                  'context'           => array(
 320                      'description' => __( 'The context the action is limited to, such as `list` for the lists of items. Without it, the action shows everywhere.' ),
 321                      'type'        => 'string',
 322                      'enum'        => WP_Entity_Actions_Registry::SUPPORTED_VALUES['context'],
 323                  ),
 324                  'modalHeader'       => array(
 325                      'description' => __( 'The header of the modal of the action.' ),
 326                      'type'        => 'string',
 327                  ),
 328                  'hideModalHeader'   => array(
 329                      'description' => __( 'Whether to hide the header of the modal of the action.' ),
 330                      'type'        => 'boolean',
 331                  ),
 332                  'modalSize'         => array(
 333                      'description' => __( 'The size of the modal of the action.' ),
 334                      'type'        => 'string',
 335                      'enum'        => WP_Entity_Actions_Registry::SUPPORTED_VALUES['modalSize'],
 336                  ),
 337                  'modalFocusOnMount' => array(
 338                      'description' => __( 'Where the focus goes when the modal of the action opens.' ),
 339                      'type'        => array( 'boolean', 'string' ),
 340                  ),
 341              ),
 342              'additionalProperties' => true,
 343          );
 344      }
 345  }


Generated : Mon Nov 9 08:20:01 2026 Cross-referenced by PHPXref