| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon Nov 9 08:20:01 2026 | Cross-referenced by PHPXref |