| [ 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 $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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Oct 20 08:20:34 2026 | Cross-referenced by PHPXref |