WP_Customize_Selective_Refresh

Core Customizer class for implementing selective refresh.

Defined (1)

The class is defined in the following location(s).

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