[ 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          $script_modules = array();
 201          foreach ( wp_get_registered_entity_action_modules( $kind, $name ) as $module => $action_ids ) {
 202              $script_modules[] = array(
 203                  // A numeric module id is an integer as an array key.
 204                  'id'      => (string) $module,
 205                  'actions' => array_values( $action_ids ),
 206              );
 207          }
 208  
 209          $response = array(
 210              'kind'           => $kind,
 211              'name'           => $name,
 212              'actions'        => wp_get_registered_entity_actions( $kind, $name ),
 213              'script_modules' => $script_modules,
 214          );
 215  
 216          return rest_ensure_response( $response );
 217      }
 218  
 219      /**
 220       * Retrieves the item's schema, conforming to JSON Schema.
 221       *
 222       * @since 7.2.0
 223       *
 224       * @return array Item schema data.
 225       */
 226  	public function get_item_schema() {
 227          if ( $this->schema ) {
 228              return $this->add_additional_fields_schema( $this->schema );
 229          }
 230  
 231          $this->schema = array(
 232              '$schema'    => 'http://json-schema.org/draft-04/schema#',
 233              'title'      => 'entity-actions',
 234              'type'       => 'object',
 235              'properties' => array(
 236                  'kind'           => array(
 237                      'description' => __( 'Entity kind.' ),
 238                      'type'        => 'string',
 239                      'readonly'    => true,
 240                  ),
 241                  'name'           => array(
 242                      'description' => __( 'Entity name.' ),
 243                      'type'        => 'string',
 244                      'readonly'    => true,
 245                  ),
 246                  'actions'        => array(
 247                      'description' => __( 'The actions registered for the entity, in registration order.' ),
 248                      'type'        => 'array',
 249                      'readonly'    => true,
 250                      'items'       => $this->get_action_schema(),
 251                  ),
 252                  'script_modules' => array(
 253                      'description' => __( 'The script modules providing the JavaScript parts of the actions, each with the ids of the actions it applies to.' ),
 254                      'type'        => 'array',
 255                      'readonly'    => true,
 256                      'items'       => array(
 257                          'type'       => 'object',
 258                          'properties' => array(
 259                              'id'      => array(
 260                                  'description' => __( 'The id of the script module.' ),
 261                                  'type'        => 'string',
 262                              ),
 263                              'actions' => array(
 264                                  'description' => __( 'The ids of the actions the script module applies to.' ),
 265                                  'type'        => 'array',
 266                                  'items'       => array(
 267                                      'type' => 'string',
 268                                  ),
 269                              ),
 270                          ),
 271                      ),
 272                  ),
 273              ),
 274          );
 275  
 276          return $this->add_additional_fields_schema( $this->schema );
 277      }
 278  
 279      /**
 280       * Returns the schema for an action definition.
 281       *
 282       * Describes the serializable subset of the DataViews Action API, see
 283       * packages/dataviews/src/types/dataviews.ts. The properties that are
 284       * JavaScript callbacks, components, or elements (`callback`,
 285       * `RenderModal`, `isEligible`, `icon`, …) come from the script modules and
 286       * are not part of the definition. Additional properties are allowed, so a
 287       * plugin can register an action with properties of its own that its script
 288       * module understands.
 289       *
 290       * @since 7.2.0
 291       *
 292       * @return array Schema for an action definition.
 293       */
 294  	protected function get_action_schema() {
 295          return array(
 296              'type'                 => 'object',
 297              'properties'           => array(
 298                  'id'                => array(
 299                      'description' => __( 'The unique identifier of the action.' ),
 300                      'type'        => 'string',
 301                  ),
 302                  'origin'            => array(
 303                      'description' => __( 'Who registered the action: `core`, or the slug of a plugin or theme.' ),
 304                      'type'        => 'object',
 305                      'properties'  => array(
 306                          'registeredBy' => array(
 307                              'description' => __( 'Who registered the action.' ),
 308                              'type'        => 'string',
 309                          ),
 310                      ),
 311                  ),
 312                  'label'             => array(
 313                      'description' => __( 'The label of the action.' ),
 314                      'type'        => 'string',
 315                  ),
 316                  'isPrimary'         => array(
 317                      'description' => __( 'Whether the action is a primary action.' ),
 318                      'type'        => 'boolean',
 319                  ),
 320                  'supportsBulk'      => array(
 321                      'description' => __( 'Whether the action can be performed on several items at once.' ),
 322                      'type'        => 'boolean',
 323                  ),
 324                  'disabled'          => array(
 325                      'description' => __( 'Whether the action is disabled.' ),
 326                      'type'        => 'boolean',
 327                  ),
 328                  'context'           => array(
 329                      'description' => __( 'The context the action is limited to, such as `list` for the lists of items. Without it, the action shows everywhere.' ),
 330                      'type'        => 'string',
 331                      'enum'        => WP_Entity_Actions_Registry::SUPPORTED_VALUES['context'],
 332                  ),
 333                  'modalHeader'       => array(
 334                      'description' => __( 'The header of the modal of the action.' ),
 335                      'type'        => 'string',
 336                  ),
 337                  'hideModalHeader'   => array(
 338                      'description' => __( 'Whether to hide the header of the modal of the action.' ),
 339                      'type'        => 'boolean',
 340                  ),
 341                  'modalSize'         => array(
 342                      'description' => __( 'The size of the modal of the action.' ),
 343                      'type'        => 'string',
 344                      'enum'        => WP_Entity_Actions_Registry::SUPPORTED_VALUES['modalSize'],
 345                  ),
 346                  'modalFocusOnMount' => array(
 347                      'description' => __( 'Where the focus goes when the modal of the action opens.' ),
 348                      'type'        => array( 'boolean', 'string' ),
 349                  ),
 350              ),
 351              'additionalProperties' => true,
 352          );
 353      }
 354  }


Generated : Tue Oct 20 08:20:34 2026 Cross-referenced by PHPXref