[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

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 Manager 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       * @param WP_Customize_Partial|string $id   Customize Partial object, or Panel ID.
  87       * @param array                       $args {
  88       *  Optional. Array of properties for the new Partials object. Default empty array.
  89       *
  90       *  @type string   $type                  Type of the partial to be created.
  91       *  @type string   $selector              The jQuery selector to find the container element for the partial, that is, a partial's placement.
  92       *  @type array    $settings              IDs for settings tied to the partial.
  93       *  @type string   $primary_setting       The ID for the setting that this partial is primarily responsible for
  94       *                                        rendering. If not supplied, it will default to the ID of the first setting.
  95       *  @type string   $capability            Capability required to edit this partial.
  96       *                                        Normally this is empty and the capability is derived from the capabilities
  97       *                                        of the associated `$settings`.
  98       *  @type callable $render_callback       Render callback.
  99       *                                        Callback is called with one argument, the instance of WP_Customize_Partial.
 100       *                                        The callback can either echo the partial or return the partial as a string,
 101       *                                        or return false if error.
 102       *  @type bool     $container_inclusive   Whether the container element is included in the partial, or if only
 103       *                                        the contents are rendered.
 104       *  @type bool     $fallback_refresh      Whether to refresh the entire preview in case a partial cannot be refreshed.
 105       *                                        A partial render is considered a failure if the render_callback returns
 106       *                                        false.
 107       * }
 108       * @return WP_Customize_Partial             The instance of the panel that was added.
 109       */
 110  	public function add_partial( $id, $args = array() ) {
 111          if ( $id instanceof WP_Customize_Partial ) {
 112              $partial = $id;
 113          } else {
 114              $class = 'WP_Customize_Partial';
 115  
 116              /** This filter is documented in wp-includes/customize/class-wp-customize-selective-refresh.php */
 117              $args = apply_filters( 'customize_dynamic_partial_args', $args, $id );
 118  
 119              /** This filter is documented in wp-includes/customize/class-wp-customize-selective-refresh.php */
 120              $class = apply_filters( 'customize_dynamic_partial_class', $class, $id, $args );
 121  
 122              $partial = new $class( $this, $id, $args );
 123          }
 124  
 125          $this->partials[ $partial->id ] = $partial;
 126          return $partial;
 127      }
 128  
 129      /**
 130       * Retrieves a partial.
 131       *
 132       * @since 4.5.0
 133       *
 134       * @param string $id Customize Partial ID.
 135       * @return WP_Customize_Partial|null The partial, if set. Otherwise null.
 136       */
 137  	public function get_partial( $id ) {
 138          if ( isset( $this->partials[ $id ] ) ) {
 139              return $this->partials[ $id ];
 140          } else {
 141              return null;
 142          }
 143      }
 144  
 145      /**
 146       * Removes a partial.
 147       *
 148       * @since 4.5.0
 149       *
 150       * @param string $id Customize Partial ID.
 151       */
 152  	public function remove_partial( $id ) {
 153          unset( $this->partials[ $id ] );
 154      }
 155  
 156      /**
 157       * Initializes the Customizer preview.
 158       *
 159       * @since 4.5.0
 160       */
 161  	public function init_preview() {
 162          add_action( 'template_redirect', array( $this, 'handle_render_partials_request' ) );
 163          add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) );
 164      }
 165  
 166      /**
 167       * Enqueues preview scripts.
 168       *
 169       * @since 4.5.0
 170       */
 171  	public function enqueue_preview_scripts() {
 172          wp_enqueue_script( 'customize-selective-refresh' );
 173          add_action( 'wp_footer', array( $this, 'export_preview_data' ), 1000 );
 174      }
 175  
 176      /**
 177       * Exports data in preview after it has finished rendering so that partials can be added at runtime.
 178       *
 179       * @since 4.5.0
 180       */
 181  	public function export_preview_data() {
 182          $partials = array();
 183  
 184          foreach ( $this->partials() as $partial ) {
 185              if ( $partial->check_capabilities() ) {
 186                  $partials[ $partial->id ] = $partial->json();
 187              }
 188          }
 189  
 190          $switched_locale = switch_to_locale( get_user_locale() );
 191          $l10n            = array(
 192              'shiftClickToEdit' => __( 'Shift-click to edit this element.' ),
 193              'clickEditMenu'    => __( 'Click to edit this menu.' ),
 194              'clickEditWidget'  => __( 'Click to edit this widget.' ),
 195              'clickEditTitle'   => __( 'Click to edit the site title.' ),
 196              'clickEditMisc'    => __( 'Click to edit this element.' ),
 197              /* translators: %s: document.write() */
 198              'badDocumentWrite' => sprintf( __( '%s is forbidden' ), 'document.write()' ),
 199          );
 200          if ( $switched_locale ) {
 201              restore_previous_locale();
 202          }
 203  
 204          $exports = array(
 205              'partials'       => $partials,
 206              'renderQueryVar' => self::RENDER_QUERY_VAR,
 207              'l10n'           => $l10n,
 208          );
 209  
 210          // Export data to JS.
 211          echo sprintf( '<script>var _customizePartialRefreshExports = %s;</script>', wp_json_encode( $exports ) );
 212      }
 213  
 214      /**
 215       * Registers dynamically-created partials.
 216       *
 217       * @since 4.5.0
 218       *
 219       * @see WP_Customize_Manager::add_dynamic_settings()
 220       *
 221       * @param string[] $partial_ids Array of the partial IDs to add.
 222       * @return WP_Customize_Partial[] Array of added WP_Customize_Partial instances.
 223       */
 224  	public function add_dynamic_partials( $partial_ids ) {
 225          $new_partials = array();
 226  
 227          foreach ( $partial_ids as $partial_id ) {
 228  
 229              // Skip partials already created.
 230              $partial = $this->get_partial( $partial_id );
 231              if ( $partial ) {
 232                  continue;
 233              }
 234  
 235              $partial_args  = false;
 236              $partial_class = 'WP_Customize_Partial';
 237  
 238              /**
 239               * Filters a dynamic partial's constructor arguments.
 240               *
 241               * For a dynamic partial to be registered, this filter must be employed
 242               * to override the default false value with an array of args to pass to
 243               * the WP_Customize_Partial constructor.
 244               *
 245               * @since 4.5.0
 246               *
 247               * @param false|array $partial_args The arguments to the WP_Customize_Partial constructor.
 248               * @param string      $partial_id   ID for dynamic partial.
 249               */
 250              $partial_args = apply_filters( 'customize_dynamic_partial_args', $partial_args, $partial_id );
 251              if ( false === $partial_args ) {
 252                  continue;
 253              }
 254  
 255              /**
 256               * Filters the class used to construct partials.
 257               *
 258               * Allow non-statically created partials to be constructed with custom WP_Customize_Partial subclass.
 259               *
 260               * @since 4.5.0
 261               *
 262               * @param string $partial_class WP_Customize_Partial or a subclass.
 263               * @param string $partial_id    ID for dynamic partial.
 264               * @param array  $partial_args  The arguments to the WP_Customize_Partial constructor.
 265               */
 266              $partial_class = apply_filters( 'customize_dynamic_partial_class', $partial_class, $partial_id, $partial_args );
 267  
 268              $partial = new $partial_class( $this, $partial_id, $partial_args );
 269  
 270              $this->add_partial( $partial );
 271              $new_partials[] = $partial;
 272          }
 273          return $new_partials;
 274      }
 275  
 276      /**
 277       * Checks whether the request is for rendering partials.
 278       *
 279       * Note that this will not consider whether the request is authorized or valid,
 280       * just that essentially the route is a match.
 281       *
 282       * @since 4.5.0
 283       *
 284       * @return bool Whether the request is for rendering partials.
 285       */
 286  	public function is_render_partials_request() {
 287          return ! empty( $_POST[ self::RENDER_QUERY_VAR ] );
 288      }
 289  
 290      /**
 291       * Handles PHP errors triggered during rendering the partials.
 292       *
 293       * These errors will be relayed back to the client in the Ajax response.
 294       *
 295       * @since 4.5.0
 296       *
 297       * @param int    $errno   Error number.
 298       * @param string $errstr  Error string.
 299       * @param string $errfile Error file.
 300       * @param string $errline Error line.
 301       * @return true Always true.
 302       */
 303  	public function handle_error( $errno, $errstr, $errfile = null, $errline = null ) {
 304          $this->triggered_errors[] = array(
 305              'partial'      => $this->current_partial_id,
 306              'error_number' => $errno,
 307              'error_string' => $errstr,
 308              'error_file'   => $errfile,
 309              'error_line'   => $errline,
 310          );
 311          return true;
 312      }
 313  
 314      /**
 315       * Handles the Ajax request to return the rendered partials for the requested placements.
 316       *
 317       * @since 4.5.0
 318       */
 319  	public function handle_render_partials_request() {
 320          if ( ! $this->is_render_partials_request() ) {
 321              return;
 322          }
 323  
 324          /*
 325           * Note that is_customize_preview() returning true will entail that the
 326           * user passed the 'customize' capability check and the nonce check, since
 327           * WP_Customize_Manager::setup_theme() is where the previewing flag is set.
 328           */
 329          if ( ! is_customize_preview() ) {
 330              wp_send_json_error( 'expected_customize_preview', 403 );
 331          } elseif ( ! isset( $_POST['partials'] ) ) {
 332              wp_send_json_error( 'missing_partials', 400 );
 333          }
 334  
 335          // Ensure that doing selective refresh on 404 template doesn't result in fallback rendering behavior (full refreshes).
 336          status_header( 200 );
 337  
 338          $partials = json_decode( wp_unslash( $_POST['partials'] ), true );
 339  
 340          if ( ! is_array( $partials ) ) {
 341              wp_send_json_error( 'malformed_partials' );
 342          }
 343  
 344          $this->add_dynamic_partials( array_keys( $partials ) );
 345  
 346          /**
 347           * Fires immediately before partials are rendered.
 348           *
 349           * Plugins may do things like call wp_enqueue_scripts() and gather a list of the scripts
 350           * and styles which may get enqueued in the response.
 351           *
 352           * @since 4.5.0
 353           *
 354           * @param WP_Customize_Selective_Refresh $this     Selective refresh component.
 355           * @param array                          $partials Placements' context data for the partials rendered in the request.
 356           *                                                 The array is keyed by partial ID, with each item being an array of
 357           *                                                 the placements' context data.
 358           */
 359          do_action( 'customize_render_partials_before', $this, $partials );
 360  
 361          set_error_handler( array( $this, 'handle_error' ), error_reporting() );
 362  
 363          $contents = array();
 364  
 365          foreach ( $partials as $partial_id => $container_contexts ) {
 366              $this->current_partial_id = $partial_id;
 367  
 368              if ( ! is_array( $container_contexts ) ) {
 369                  wp_send_json_error( 'malformed_container_contexts' );
 370              }
 371  
 372              $partial = $this->get_partial( $partial_id );
 373  
 374              if ( ! $partial || ! $partial->check_capabilities() ) {
 375                  $contents[ $partial_id ] = null;
 376                  continue;
 377              }
 378  
 379              $contents[ $partial_id ] = array();
 380  
 381              // @todo The array should include not only the contents, but also whether the container is included?
 382              if ( empty( $container_contexts ) ) {
 383                  // Since there are no container contexts, render just once.
 384                  $contents[ $partial_id ][] = $partial->render( null );
 385              } else {
 386                  foreach ( $container_contexts as $container_context ) {
 387                      $contents[ $partial_id ][] = $partial->render( $container_context );
 388                  }
 389              }
 390          }
 391          $this->current_partial_id = null;
 392  
 393          restore_error_handler();
 394  
 395          /**
 396           * Fires immediately after partials are rendered.
 397           *
 398           * Plugins may do things like call wp_footer() to scrape scripts output and return them
 399           * via the {@see 'customize_render_partials_response'} filter.
 400           *
 401           * @since 4.5.0
 402           *
 403           * @param WP_Customize_Selective_Refresh $this     Selective refresh component.
 404           * @param array                          $partials Placements' context data for the partials rendered in the request.
 405           *                                                 The array is keyed by partial ID, with each item being an array of
 406           *                                                 the placements' context data.
 407           */
 408          do_action( 'customize_render_partials_after', $this, $partials );
 409  
 410          $response = array(
 411              'contents' => $contents,
 412          );
 413  
 414          if ( defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) {
 415              $response['errors'] = $this->triggered_errors;
 416          }
 417  
 418          $setting_validities             = $this->manager->validate_setting_values( $this->manager->unsanitized_post_values() );
 419          $exported_setting_validities    = array_map( array( $this->manager, 'prepare_setting_validity_for_js' ), $setting_validities );
 420          $response['setting_validities'] = $exported_setting_validities;
 421  
 422          /**
 423           * Filters the response from rendering the partials.
 424           *
 425           * Plugins may use this filter to inject `$scripts` and `$styles`, which are dependencies
 426           * for the partials being rendered. The response data will be available to the client via
 427           * the `render-partials-response` JS event, so the client can then inject the scripts and
 428           * styles into the DOM if they have not already been enqueued there.
 429           *
 430           * If plugins do this, they'll need to take care for any scripts that do `document.write()`
 431           * and make sure that these are not injected, or else to override the function to no-op,
 432           * or else the page will be destroyed.
 433           *
 434           * Plugins should be aware that `$scripts` and `$styles` may eventually be included by
 435           * default in the response.
 436           *
 437           * @since 4.5.0
 438           *
 439           * @param array $response {
 440           *     Response.
 441           *
 442           *     @type array $contents Associative array mapping a partial ID its corresponding array of contents
 443           *                           for the containers requested.
 444           *     @type array $errors   List of errors triggered during rendering of partials, if `WP_DEBUG_DISPLAY`
 445           *                           is enabled.
 446           * }
 447           * @param WP_Customize_Selective_Refresh $this     Selective refresh component.
 448           * @param array                          $partials Placements' context data for the partials rendered in the request.
 449           *                                                 The array is keyed by partial ID, with each item being an array of
 450           *                                                 the placements' context data.
 451           */
 452          $response = apply_filters( 'customize_render_partials_response', $response, $this, $partials );
 453  
 454          wp_send_json_success( $response );
 455      }
 456  }


Generated: Mon Jun 17 08:20:02 2019 Cross-referenced by PHPXref 0.7