[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/customize/ -> class-wp-customize-selective-refresh.php (source)

   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  }


Generated: Fri Apr 19 01:00:02 2024 Cross-referenced by PHPXref 0.7.1