class-wp-rest-sidebars-controller.php (14398B)
1 <?php 2 /** 3 * REST API: WP_REST_Sidebars_Controller class 4 * 5 * Original code from {@link https://github.com/martin-pettersson/wp-rest-api-sidebars Martin Pettersson (martin_pettersson@outlook.com)}. 6 * 7 * @package WordPress 8 * @subpackage REST_API 9 * @since 5.8.0 10 */ 11 12 /** 13 * Core class used to manage a site's sidebars. 14 * 15 * @since 5.8.0 16 * 17 * @see WP_REST_Controller 18 */ 19 class WP_REST_Sidebars_Controller extends WP_REST_Controller { 20 21 /** 22 * Sidebars controller constructor. 23 * 24 * @since 5.8.0 25 */ 26 public function __construct() { 27 $this->namespace = 'wp/v2'; 28 $this->rest_base = 'sidebars'; 29 } 30 31 /** 32 * Registers the controllers routes. 33 * 34 * @since 5.8.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' => array( 46 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 47 ), 48 ), 49 'schema' => array( $this, 'get_public_item_schema' ), 50 ) 51 ); 52 53 register_rest_route( 54 $this->namespace, 55 '/' . $this->rest_base . '/(?P<id>[\w-]+)', 56 array( 57 array( 58 'methods' => WP_REST_Server::READABLE, 59 'callback' => array( $this, 'get_item' ), 60 'permission_callback' => array( $this, 'get_item_permissions_check' ), 61 'args' => array( 62 'id' => array( 63 'description' => __( 'The id of a registered sidebar' ), 64 'type' => 'string', 65 ), 66 'context' => $this->get_context_param( array( 'default' => 'view' ) ), 67 ), 68 ), 69 array( 70 'methods' => WP_REST_Server::EDITABLE, 71 'callback' => array( $this, 'update_item' ), 72 'permission_callback' => array( $this, 'update_item_permissions_check' ), 73 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), 74 ), 75 'schema' => array( $this, 'get_public_item_schema' ), 76 ) 77 ); 78 } 79 80 /** 81 * Checks if a given request has access to get sidebars. 82 * 83 * @since 5.8.0 84 * 85 * @param WP_REST_Request $request Full details about the request. 86 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 87 */ 88 public function get_items_permissions_check( $request ) { 89 return $this->do_permissions_check(); 90 } 91 92 /** 93 * Retrieves the list of sidebars (active or inactive). 94 * 95 * @since 5.8.0 96 * 97 * @param WP_REST_Request $request Full details about the request. 98 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 99 */ 100 public function get_items( $request ) { 101 retrieve_widgets(); 102 103 $data = array(); 104 foreach ( wp_get_sidebars_widgets() as $id => $widgets ) { 105 $sidebar = $this->get_sidebar( $id ); 106 107 if ( ! $sidebar ) { 108 continue; 109 } 110 111 $data[] = $this->prepare_response_for_collection( 112 $this->prepare_item_for_response( $sidebar, $request ) 113 ); 114 } 115 116 return rest_ensure_response( $data ); 117 } 118 119 /** 120 * Checks if a given request has access to get a single sidebar. 121 * 122 * @since 5.8.0 123 * 124 * @param WP_REST_Request $request Full details about the request. 125 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 126 */ 127 public function get_item_permissions_check( $request ) { 128 return $this->do_permissions_check(); 129 } 130 131 /** 132 * Retrieves one sidebar from the collection. 133 * 134 * @since 5.8.0 135 * 136 * @param WP_REST_Request $request Full details about the request. 137 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 138 */ 139 public function get_item( $request ) { 140 retrieve_widgets(); 141 142 $sidebar = $this->get_sidebar( $request['id'] ); 143 144 if ( ! $sidebar ) { 145 return new WP_Error( 'rest_sidebar_not_found', __( 'No sidebar exists with that id.' ), array( 'status' => 404 ) ); 146 } 147 148 return $this->prepare_item_for_response( $sidebar, $request ); 149 } 150 151 /** 152 * Checks if a given request has access to update sidebars. 153 * 154 * @since 5.8.0 155 * 156 * @param WP_REST_Request $request Full details about the request. 157 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 158 */ 159 public function update_item_permissions_check( $request ) { 160 return $this->do_permissions_check(); 161 } 162 163 /** 164 * Updates a sidebar. 165 * 166 * @since 5.8.0 167 * 168 * @param WP_REST_Request $request Full details about the request. 169 * @return WP_REST_Response Response object on success, or WP_Error object on failure. 170 */ 171 public function update_item( $request ) { 172 if ( isset( $request['widgets'] ) ) { 173 $sidebars = wp_get_sidebars_widgets(); 174 175 foreach ( $sidebars as $sidebar_id => $widgets ) { 176 foreach ( $widgets as $i => $widget_id ) { 177 // This automatically removes the passed widget ids from any other sidebars in use. 178 if ( $sidebar_id !== $request['id'] && in_array( $widget_id, $request['widgets'], true ) ) { 179 unset( $sidebars[ $sidebar_id ][ $i ] ); 180 } 181 182 // This automatically removes omitted widget ids to the inactive sidebar. 183 if ( $sidebar_id === $request['id'] && ! in_array( $widget_id, $request['widgets'], true ) ) { 184 $sidebars['wp_inactive_widgets'][] = $widget_id; 185 } 186 } 187 } 188 189 $sidebars[ $request['id'] ] = $request['widgets']; 190 191 wp_set_sidebars_widgets( $sidebars ); 192 } 193 194 $request['context'] = 'edit'; 195 196 $sidebar = $this->get_sidebar( $request['id'] ); 197 198 /** 199 * Fires after a sidebar is updated via the REST API. 200 * 201 * @since 5.8.0 202 * 203 * @param array $sidebar The updated sidebar. 204 * @param WP_REST_Request $request Request object. 205 */ 206 do_action( 'rest_save_sidebar', $sidebar, $request ); 207 208 return $this->prepare_item_for_response( $sidebar, $request ); 209 } 210 211 /** 212 * Checks if the user has permissions to make the request. 213 * 214 * @since 5.8.0 215 * 216 * @return true|WP_Error True if the request has read access, WP_Error object otherwise. 217 */ 218 protected function do_permissions_check() { 219 // Verify if the current user has edit_theme_options capability. 220 // This capability is required to access the widgets screen. 221 if ( ! current_user_can( 'edit_theme_options' ) ) { 222 return new WP_Error( 223 'rest_cannot_manage_widgets', 224 __( 'Sorry, you are not allowed to manage widgets on this site.' ), 225 array( 'status' => rest_authorization_required_code() ) 226 ); 227 } 228 229 return true; 230 } 231 232 /** 233 * Retrieves the registered sidebar with the given id. 234 * 235 * @since 5.8.0 236 * 237 * @global array $wp_registered_sidebars The registered sidebars. 238 * 239 * @param string|int $id ID of the sidebar. 240 * @return array|null The discovered sidebar, or null if it is not registered. 241 */ 242 protected function get_sidebar( $id ) { 243 global $wp_registered_sidebars; 244 245 foreach ( (array) $wp_registered_sidebars as $sidebar ) { 246 if ( $sidebar['id'] === $id ) { 247 return $sidebar; 248 } 249 } 250 251 if ( 'wp_inactive_widgets' === $id ) { 252 return array( 253 'id' => 'wp_inactive_widgets', 254 'name' => __( 'Inactive widgets' ), 255 ); 256 } 257 258 return null; 259 } 260 261 /** 262 * Prepares a single sidebar output for response. 263 * 264 * @since 5.8.0 265 * 266 * @global array $wp_registered_sidebars The registered sidebars. 267 * @global array $wp_registered_widgets The registered widgets. 268 * 269 * @param array $raw_sidebar Sidebar instance. 270 * @param WP_REST_Request $request Full details about the request. 271 * @return WP_REST_Response Prepared response object. 272 */ 273 public function prepare_item_for_response( $raw_sidebar, $request ) { 274 global $wp_registered_sidebars, $wp_registered_widgets; 275 276 $id = $raw_sidebar['id']; 277 $sidebar = array( 'id' => $id ); 278 279 if ( isset( $wp_registered_sidebars[ $id ] ) ) { 280 $registered_sidebar = $wp_registered_sidebars[ $id ]; 281 282 $sidebar['status'] = 'active'; 283 $sidebar['name'] = isset( $registered_sidebar['name'] ) ? $registered_sidebar['name'] : ''; 284 $sidebar['description'] = isset( $registered_sidebar['description'] ) ? wp_sidebar_description( $id ) : ''; 285 $sidebar['class'] = isset( $registered_sidebar['class'] ) ? $registered_sidebar['class'] : ''; 286 $sidebar['before_widget'] = isset( $registered_sidebar['before_widget'] ) ? $registered_sidebar['before_widget'] : ''; 287 $sidebar['after_widget'] = isset( $registered_sidebar['after_widget'] ) ? $registered_sidebar['after_widget'] : ''; 288 $sidebar['before_title'] = isset( $registered_sidebar['before_title'] ) ? $registered_sidebar['before_title'] : ''; 289 $sidebar['after_title'] = isset( $registered_sidebar['after_title'] ) ? $registered_sidebar['after_title'] : ''; 290 } else { 291 $sidebar['status'] = 'inactive'; 292 $sidebar['name'] = $raw_sidebar['name']; 293 $sidebar['description'] = ''; 294 $sidebar['class'] = ''; 295 } 296 297 $fields = $this->get_fields_for_response( $request ); 298 if ( rest_is_field_included( 'widgets', $fields ) ) { 299 $sidebars = wp_get_sidebars_widgets(); 300 $widgets = array_filter( 301 isset( $sidebars[ $sidebar['id'] ] ) ? $sidebars[ $sidebar['id'] ] : array(), 302 static function ( $widget_id ) use ( $wp_registered_widgets ) { 303 return isset( $wp_registered_widgets[ $widget_id ] ); 304 } 305 ); 306 307 $sidebar['widgets'] = array_values( $widgets ); 308 } 309 310 $schema = $this->get_item_schema(); 311 $data = array(); 312 foreach ( $schema['properties'] as $property_id => $property ) { 313 if ( isset( $sidebar[ $property_id ] ) && true === rest_validate_value_from_schema( $sidebar[ $property_id ], $property ) ) { 314 $data[ $property_id ] = $sidebar[ $property_id ]; 315 } elseif ( isset( $property['default'] ) ) { 316 $data[ $property_id ] = $property['default']; 317 } 318 } 319 320 $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 321 $data = $this->add_additional_fields_to_object( $data, $request ); 322 $data = $this->filter_response_by_context( $data, $context ); 323 324 $response = rest_ensure_response( $data ); 325 326 $response->add_links( $this->prepare_links( $sidebar ) ); 327 328 /** 329 * Filters the REST API response for a sidebar. 330 * 331 * @since 5.8.0 332 * 333 * @param WP_REST_Response $response The response object. 334 * @param array $raw_sidebar The raw sidebar data. 335 * @param WP_REST_Request $request The request object. 336 */ 337 return apply_filters( 'rest_prepare_sidebar', $response, $raw_sidebar, $request ); 338 } 339 340 /** 341 * Prepares links for the sidebar. 342 * 343 * @since 5.8.0 344 * 345 * @param array $sidebar Sidebar. 346 * @return array Links for the given widget. 347 */ 348 protected function prepare_links( $sidebar ) { 349 return array( 350 'collection' => array( 351 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ), 352 ), 353 'self' => array( 354 'href' => rest_url( sprintf( '%s/%s/%s', $this->namespace, $this->rest_base, $sidebar['id'] ) ), 355 ), 356 'https://api.w.org/widget' => array( 357 'href' => add_query_arg( 'sidebar', $sidebar['id'], rest_url( '/wp/v2/widgets' ) ), 358 'embeddable' => true, 359 ), 360 ); 361 } 362 363 /** 364 * Retrieves the block type' schema, conforming to JSON Schema. 365 * 366 * @since 5.8.0 367 * 368 * @return array Item schema data. 369 */ 370 public function get_item_schema() { 371 if ( $this->schema ) { 372 return $this->add_additional_fields_schema( $this->schema ); 373 } 374 375 $schema = array( 376 '$schema' => 'http://json-schema.org/draft-04/schema#', 377 'title' => 'sidebar', 378 'type' => 'object', 379 'properties' => array( 380 'id' => array( 381 'description' => __( 'ID of sidebar.' ), 382 'type' => 'string', 383 'context' => array( 'embed', 'view', 'edit' ), 384 'readonly' => true, 385 ), 386 'name' => array( 387 'description' => __( 'Unique name identifying the sidebar.' ), 388 'type' => 'string', 389 'context' => array( 'embed', 'view', 'edit' ), 390 'readonly' => true, 391 ), 392 'description' => array( 393 'description' => __( 'Description of sidebar.' ), 394 'type' => 'string', 395 'context' => array( 'embed', 'view', 'edit' ), 396 'readonly' => true, 397 ), 398 'class' => array( 399 'description' => __( 'Extra CSS class to assign to the sidebar in the Widgets interface.' ), 400 'type' => 'string', 401 'context' => array( 'embed', 'view', 'edit' ), 402 'readonly' => true, 403 ), 404 'before_widget' => array( 405 'description' => __( 'HTML content to prepend to each widget\'s HTML output when assigned to this sidebar. Default is an opening list item element.' ), 406 'type' => 'string', 407 'default' => '', 408 'context' => array( 'embed', 'view', 'edit' ), 409 'readonly' => true, 410 ), 411 'after_widget' => array( 412 'description' => __( 'HTML content to append to each widget\'s HTML output when assigned to this sidebar. Default is a closing list item element.' ), 413 'type' => 'string', 414 'default' => '', 415 'context' => array( 'embed', 'view', 'edit' ), 416 'readonly' => true, 417 ), 418 'before_title' => array( 419 'description' => __( 'HTML content to prepend to the sidebar title when displayed. Default is an opening h2 element.' ), 420 'type' => 'string', 421 'default' => '', 422 'context' => array( 'embed', 'view', 'edit' ), 423 'readonly' => true, 424 ), 425 'after_title' => array( 426 'description' => __( 'HTML content to append to the sidebar title when displayed. Default is a closing h2 element.' ), 427 'type' => 'string', 428 'default' => '', 429 'context' => array( 'embed', 'view', 'edit' ), 430 'readonly' => true, 431 ), 432 'status' => array( 433 'description' => __( 'Status of sidebar.' ), 434 'type' => 'string', 435 'enum' => array( 'active', 'inactive' ), 436 'context' => array( 'embed', 'view', 'edit' ), 437 'readonly' => true, 438 ), 439 'widgets' => array( 440 'description' => __( 'Nested widgets.' ), 441 'type' => 'array', 442 'items' => array( 443 'type' => array( 'object', 'string' ), 444 ), 445 'default' => array(), 446 'context' => array( 'embed', 'view', 'edit' ), 447 ), 448 ), 449 ); 450 451 $this->schema = $schema; 452 453 return $this->add_additional_fields_schema( $this->schema ); 454 } 455 }