[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Customize API: WP_Customize_Selective_Refresh class 4 * 5 * @package WordPress 6 * @subpackage Customize 7 * @since 4.5.0 8 */ 9 10 /** 11 * Core Customizer class for implementing selective refresh. 12 * 13 * @since 4.5.0 14 */ 15 final class WP_Customize_Selective_Refresh { 16 17 /** 18 * Query var used in requests to render partials. 19 * 20 * @since 4.5.0 21 */ 22 const RENDER_QUERY_VAR = 'wp_customize_render_partials'; 23 24 /** 25 * Customize manager. 26 * 27 * @since 4.5.0 28 * @var WP_Customize_Manager 29 */ 30 public $manager; 31 32 /** 33 * Registered instances of WP_Customize_Partial. 34 * 35 * @since 4.5.0 36 * @var WP_Customize_Partial[] 37 */ 38 protected $partials = array(); 39 40 /** 41 * Log of errors triggered when partials are rendered. 42 * 43 * @since 4.5.0 44 * @var array 45 */ 46 protected $triggered_errors = array(); 47 48 /** 49 * Keep track of the current partial being rendered. 50 * 51 * @since 4.5.0 52 * @var string|null 53 */ 54 protected $current_partial_id; 55 56 /** 57 * Plugin bootstrap for Partial Refresh functionality. 58 * 59 * @since 4.5.0 60 * 61 * @param WP_Customize_Manager $manager Customizer bootstrap instance. 62 */ 63 public function __construct( WP_Customize_Manager $manager ) { 64 $this->manager = $manager; 65 require_once ABSPATH . WPINC . '/customize/class-wp-customize-partial.php'; 66 67 add_action( 'customize_preview_init', array( $this, 'init_preview' ) ); 68 } 69 70 /** 71 * Retrieves the registered partials. 72 * 73 * @since 4.5.0 74 * 75 * @return array Partials. 76 */ 77 public function partials() { 78 return $this->partials; 79 } 80 81 /** 82 * Adds a partial. 83 * 84 * @since 4.5.0 85 * 86 * @see WP_Customize_Partial::__construct() 87 * 88 * @param WP_Customize_Partial|string $id Customize Partial object, or Partial ID. 89 * @param array $args Optional. Array of properties for the new Partials object. 90 * See WP_Customize_Partial::__construct() for information 91 * on accepted arguments. Default empty array. 92 * @return WP_Customize_Partial The instance of the partial that was added. 93 */ 94 public function add_partial( $id, $args = array() ) { 95 if ( $id instanceof WP_Customize_Partial ) { 96 $partial = $id; 97 } else { 98 $class = 'WP_Customize_Partial'; 99 100 /** This filter is documented in wp-includes/customize/class-wp-customize-selective-refresh.php */ 101 $args = apply_filters( 'customize_dynamic_partial_args', $args, $id ); 102 103 /** This filter is documented in wp-includes/customize/class-wp-customize-selective-refresh.php */ 104 $class = apply_filters( 'customize_dynamic_partial_class', $class, $id, $args ); 105 106 $partial = new $class( $this, $id, $args ); 107 } 108 109 $this->partials[ $partial->id ] = $partial; 110 return $partial; 111 } 112 113 /** 114 * Retrieves a partial. 115 * 116 * @since 4.5.0 117 * 118 * @param string $id Customize Partial ID. 119 * @return WP_Customize_Partial|null The partial, if set. Otherwise null. 120 */ 121 public function get_partial( $id ) { 122 if ( isset( $this->partials[ $id ] ) ) { 123 return $this->partials[ $id ]; 124 } else { 125 return null; 126 } 127 } 128 129 /** 130 * Removes a partial. 131 * 132 * @since 4.5.0 133 * 134 * @param string $id Customize Partial ID. 135 */ 136 public function remove_partial( $id ) { 137 unset( $this->partials[ $id ] ); 138 } 139 140 /** 141 * Initializes the Customizer preview. 142 * 143 * @since 4.5.0 144 */ 145 public function init_preview() { 146 add_action( 'template_redirect', array( $this, 'handle_render_partials_request' ) ); 147 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) ); 148 } 149 150 /** 151 * Enqueues preview scripts. 152 * 153 * @since 4.5.0 154 */ 155 public function enqueue_preview_scripts() { 156 wp_enqueue_script( 'customize-selective-refresh' ); 157 add_action( 'wp_footer', array( $this, 'export_preview_data' ), 1000 ); 158 } 159 160 /** 161 * Exports data in preview after it has finished rendering so that partials can be added at runtime. 162 * 163 * @since 4.5.0 164 */ 165 public function export_preview_data() { 166 $partials = array(); 167 168 foreach ( $this->partials() as $partial ) { 169 if ( $partial->check_capabilities() ) { 170 $partials[ $partial->id ] = $partial->json(); 171 } 172 } 173 174 $switched_locale = switch_to_locale( get_user_locale() ); 175 $l10n = array( 176 'shiftClickToEdit' => __( 'Shift-click to edit this element.' ), 177 'clickEditMenu' => __( 'Click to edit this menu.' ), 178 'clickEditWidget' => __( 'Click to edit this widget.' ), 179 'clickEditTitle' => __( 'Click to edit the site title.' ), 180 'clickEditMisc' => __( 'Click to edit this element.' ), 181 /* translators: %s: document.write() */ 182 'badDocumentWrite' => sprintf( __( '%s is forbidden' ), 'document.write()' ), 183 ); 184 if ( $switched_locale ) { 185 restore_previous_locale(); 186 } 187 188 $exports = array( 189 'partials' => $partials, 190 'renderQueryVar' => self::RENDER_QUERY_VAR, 191 'l10n' => $l10n, 192 ); 193 194 // Export data to JS. 195 printf( '<script>var _customizePartialRefreshExports = %s;</script>', wp_json_encode( $exports ) ); 196 } 197 198 /** 199 * Registers dynamically-created partials. 200 * 201 * @since 4.5.0 202 * 203 * @see WP_Customize_Manager::add_dynamic_settings() 204 * 205 * @param string[] $partial_ids Array of the partial IDs to add. 206 * @return WP_Customize_Partial[] Array of added WP_Customize_Partial instances. 207 */ 208 public function add_dynamic_partials( $partial_ids ) { 209 $new_partials = array(); 210 211 foreach ( $partial_ids as $partial_id ) { 212 213 // Skip partials already created. 214 $partial = $this->get_partial( $partial_id ); 215 if ( $partial ) { 216 continue; 217 } 218 219 $partial_args = false; 220 $partial_class = 'WP_Customize_Partial'; 221 222 /** 223 * Filters a dynamic partial's constructor arguments. 224 * 225 * For a dynamic partial to be registered, this filter must be employed 226 * to override the default false value with an array of args to pass to 227 * the WP_Customize_Partial constructor. 228 * 229 * @since 4.5.0 230 * 231 * @param false|array $partial_args The arguments to the WP_Customize_Partial constructor. 232 * @param string $partial_id ID for dynamic partial. 233 */ 234 $partial_args = apply_filters( 'customize_dynamic_partial_args', $partial_args, $partial_id ); 235 if ( false === $partial_args ) { 236 continue; 237 } 238 239 /** 240 * Filters the class used to construct partials. 241 * 242 * Allow non-statically created partials to be constructed with custom WP_Customize_Partial subclass. 243 * 244 * @since 4.5.0 245 * 246 * @param string $partial_class WP_Customize_Partial or a subclass. 247 * @param string $partial_id ID for dynamic partial. 248 * @param array $partial_args The arguments to the WP_Customize_Partial constructor. 249 */ 250 $partial_class = apply_filters( 'customize_dynamic_partial_class', $partial_class, $partial_id, $partial_args ); 251 252 $partial = new $partial_class( $this, $partial_id, $partial_args ); 253 254 $this->add_partial( $partial ); 255 $new_partials[] = $partial; 256 } 257 return $new_partials; 258 } 259 260 /** 261 * Checks whether the request is for rendering partials. 262 * 263 * Note that this will not consider whether the request is authorized or valid, 264 * just that essentially the route is a match. 265 * 266 * @since 4.5.0 267 * 268 * @return bool Whether the request is for rendering partials. 269 */ 270 public function is_render_partials_request() { 271 return ! empty( $_POST[ self::RENDER_QUERY_VAR ] ); 272 } 273 274 /** 275 * Handles PHP errors triggered during rendering the partials. 276 * 277 * These errors will be relayed back to the client in the Ajax response. 278 * 279 * @since 4.5.0 280 * 281 * @param int $errno Error number. 282 * @param string $errstr Error string. 283 * @param string $errfile Error file. 284 * @param int $errline Error line. 285 * @return true Always true. 286 */ 287 public function handle_error( $errno, $errstr, $errfile = null, $errline = null ) { 288 $this->triggered_errors[] = array( 289 'partial' => $this->current_partial_id, 290 'error_number' => $errno, 291 'error_string' => $errstr, 292 'error_file' => $errfile, 293 'error_line' => $errline, 294 ); 295 return true; 296 } 297 298 /** 299 * Handles the Ajax request to return the rendered partials for the requested placements. 300 * 301 * @since 4.5.0 302 */ 303 public function handle_render_partials_request() { 304 if ( ! $this->is_render_partials_request() ) { 305 return; 306 } 307 308 /* 309 * Note that is_customize_preview() returning true will entail that the 310 * user passed the 'customize' capability check and the nonce check, since 311 * WP_Customize_Manager::setup_theme() is where the previewing flag is set. 312 */ 313 if ( ! is_customize_preview() ) { 314 wp_send_json_error( 'expected_customize_preview', 403 ); 315 } elseif ( ! isset( $_POST['partials'] ) ) { 316 wp_send_json_error( 'missing_partials', 400 ); 317 } 318 319 // Ensure that doing selective refresh on 404 template doesn't result in fallback rendering behavior (full refreshes). 320 status_header( 200 ); 321 322 $partials = json_decode( wp_unslash( $_POST['partials'] ), true ); 323 324 if ( ! is_array( $partials ) ) { 325 wp_send_json_error( 'malformed_partials' ); 326 } 327 328 $this->add_dynamic_partials( array_keys( $partials ) ); 329 330 /** 331 * Fires immediately before partials are rendered. 332 * 333 * Plugins may do things like call wp_enqueue_scripts() and gather a list of the scripts 334 * and styles which may get enqueued in the response. 335 * 336 * @since 4.5.0 337 * 338 * @param WP_Customize_Selective_Refresh $refresh Selective refresh component. 339 * @param array $partials Placements' context data for the partials rendered in the request. 340 * The array is keyed by partial ID, with each item being an array of 341 * the placements' context data. 342 */ 343 do_action( 'customize_render_partials_before', $this, $partials ); 344 345 set_error_handler( array( $this, 'handle_error' ), error_reporting() ); 346 347 $contents = array(); 348 349 foreach ( $partials as $partial_id => $container_contexts ) { 350 $this->current_partial_id = $partial_id; 351 352 if ( ! is_array( $container_contexts ) ) { 353 wp_send_json_error( 'malformed_container_contexts' ); 354 } 355 356 $partial = $this->get_partial( $partial_id ); 357 358 if ( ! $partial || ! $partial->check_capabilities() ) { 359 $contents[ $partial_id ] = null; 360 continue; 361 } 362 363 $contents[ $partial_id ] = array(); 364 365 // @todo The array should include not only the contents, but also whether the container is included? 366 if ( empty( $container_contexts ) ) { 367 // Since there are no container contexts, render just once. 368 $contents[ $partial_id ][] = $partial->render( null ); 369 } else { 370 foreach ( $container_contexts as $container_context ) { 371 $contents[ $partial_id ][] = $partial->render( $container_context ); 372 } 373 } 374 } 375 $this->current_partial_id = null; 376 377 restore_error_handler(); 378 379 /** 380 * Fires immediately after partials are rendered. 381 * 382 * Plugins may do things like call wp_footer() to scrape scripts output and return them 383 * via the {@see 'customize_render_partials_response'} filter. 384 * 385 * @since 4.5.0 386 * 387 * @param WP_Customize_Selective_Refresh $refresh Selective refresh component. 388 * @param array $partials Placements' context data for the partials rendered in the request. 389 * The array is keyed by partial ID, with each item being an array of 390 * the placements' context data. 391 */ 392 do_action( 'customize_render_partials_after', $this, $partials ); 393 394 $response = array( 395 'contents' => $contents, 396 ); 397 398 if ( defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) { 399 $response['errors'] = $this->triggered_errors; 400 } 401 402 $setting_validities = $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() ); 403 $exported_setting_validities = array_map( array( $this->manager, 'prepare_setting_validity_for_js' ), $setting_validities ); 404 $response['setting_validities'] = $exported_setting_validities; 405 406 /** 407 * Filters the response from rendering the partials. 408 * 409 * Plugins may use this filter to inject `$scripts` and `$styles`, which are dependencies 410 * for the partials being rendered. The response data will be available to the client via 411 * the `render-partials-response` JS event, so the client can then inject the scripts and 412 * styles into the DOM if they have not already been enqueued there. 413 * 414 * If plugins do this, they'll need to take care for any scripts that do `document.write()` 415 * and make sure that these are not injected, or else to override the function to no-op, 416 * or else the page will be destroyed. 417 * 418 * Plugins should be aware that `$scripts` and `$styles` may eventually be included by 419 * default in the response. 420 * 421 * @since 4.5.0 422 * 423 * @param array $response { 424 * Response. 425 * 426 * @type array $contents Associative array mapping a partial ID its corresponding array of contents 427 * for the containers requested. 428 * @type array $errors List of errors triggered during rendering of partials, if `WP_DEBUG_DISPLAY` 429 * is enabled. 430 * } 431 * @param WP_Customize_Selective_Refresh $refresh Selective refresh component. 432 * @param array $partials Placements' context data for the partials rendered in the request. 433 * The array is keyed by partial ID, with each item being an array of 434 * the placements' context data. 435 */ 436 $response = apply_filters( 'customize_render_partials_response', $response, $this, $partials ); 437 438 wp_send_json_success( $response ); 439 } 440 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:03 2024 | Cross-referenced by PHPXref 0.7.1 |