116 add_action( 'customize_controls_print_scripts', array( $this, 'print_scripts' ) ); |
116 add_action( 'customize_controls_print_scripts', array( $this, 'print_scripts' ) ); |
117 add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_footer_scripts' ) ); |
117 add_action( 'customize_controls_print_footer_scripts', array( $this, 'print_footer_scripts' ) ); |
118 add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_widget_control_templates' ) ); |
118 add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_widget_control_templates' ) ); |
119 add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) ); |
119 add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) ); |
120 add_filter( 'customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); |
120 add_filter( 'customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); |
|
121 add_filter( 'should_load_block_editor_scripts_and_styles', array( $this, 'should_load_block_editor_scripts_and_styles' ) ); |
121 |
122 |
122 add_action( 'dynamic_sidebar', array( $this, 'tally_rendered_widgets' ) ); |
123 add_action( 'dynamic_sidebar', array( $this, 'tally_rendered_widgets' ) ); |
123 add_filter( 'is_active_sidebar', array( $this, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 ); |
124 add_filter( 'is_active_sidebar', array( $this, 'tally_sidebars_via_is_active_sidebar_calls' ), 10, 2 ); |
124 add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 ); |
125 add_filter( 'dynamic_sidebar_has_widgets', array( $this, 'tally_sidebars_via_dynamic_sidebar_calls' ), 10, 2 ); |
125 |
126 |
365 * @global array $wp_registered_widget_controls |
366 * @global array $wp_registered_widget_controls |
366 * @global array $wp_registered_sidebars |
367 * @global array $wp_registered_sidebars |
367 */ |
368 */ |
368 public function customize_register() { |
369 public function customize_register() { |
369 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_sidebars; |
370 global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_sidebars; |
|
371 |
|
372 $use_widgets_block_editor = wp_use_widgets_block_editor(); |
370 |
373 |
371 add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 ); |
374 add_filter( 'sidebars_widgets', array( $this, 'preview_sidebars_widgets' ), 1 ); |
372 |
375 |
373 $sidebars_widgets = array_merge( |
376 $sidebars_widgets = array_merge( |
374 array( 'wp_inactive_widgets' => array() ), |
377 array( 'wp_inactive_widgets' => array() ), |
444 // Add section to contain controls. |
447 // Add section to contain controls. |
445 $section_id = sprintf( 'sidebar-widgets-%s', $sidebar_id ); |
448 $section_id = sprintf( 'sidebar-widgets-%s', $sidebar_id ); |
446 if ( $is_active_sidebar ) { |
449 if ( $is_active_sidebar ) { |
447 |
450 |
448 $section_args = array( |
451 $section_args = array( |
449 'title' => $wp_registered_sidebars[ $sidebar_id ]['name'], |
452 'title' => $wp_registered_sidebars[ $sidebar_id ]['name'], |
450 'description' => $wp_registered_sidebars[ $sidebar_id ]['description'], |
453 'priority' => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ), true ), |
451 'priority' => array_search( $sidebar_id, array_keys( $wp_registered_sidebars ), true ), |
454 'panel' => 'widgets', |
452 'panel' => 'widgets', |
455 'sidebar_id' => $sidebar_id, |
453 'sidebar_id' => $sidebar_id, |
|
454 ); |
456 ); |
|
457 |
|
458 if ( $use_widgets_block_editor ) { |
|
459 $section_args['description'] = ''; |
|
460 } else { |
|
461 $section_args['description'] = $wp_registered_sidebars[ $sidebar_id ]['description']; |
|
462 } |
455 |
463 |
456 /** |
464 /** |
457 * Filters Customizer widget section arguments for a given sidebar. |
465 * Filters Customizer widget section arguments for a given sidebar. |
458 * |
466 * |
459 * @since 3.9.0 |
467 * @since 3.9.0 |
465 $section_args = apply_filters( 'customizer_widgets_section_args', $section_args, $section_id, $sidebar_id ); |
473 $section_args = apply_filters( 'customizer_widgets_section_args', $section_args, $section_id, $sidebar_id ); |
466 |
474 |
467 $section = new WP_Customize_Sidebar_Section( $this->manager, $section_id, $section_args ); |
475 $section = new WP_Customize_Sidebar_Section( $this->manager, $section_id, $section_args ); |
468 $this->manager->add_section( $section ); |
476 $this->manager->add_section( $section ); |
469 |
477 |
470 $control = new WP_Widget_Area_Customize_Control( |
478 if ( $use_widgets_block_editor ) { |
|
479 $control = new WP_Sidebar_Block_Editor_Control( |
|
480 $this->manager, |
|
481 $setting_id, |
|
482 array( |
|
483 'section' => $section_id, |
|
484 'sidebar_id' => $sidebar_id, |
|
485 'label' => $section_args['title'], |
|
486 'description' => $section_args['description'], |
|
487 ) |
|
488 ); |
|
489 } else { |
|
490 $control = new WP_Widget_Area_Customize_Control( |
|
491 $this->manager, |
|
492 $setting_id, |
|
493 array( |
|
494 'section' => $section_id, |
|
495 'sidebar_id' => $sidebar_id, |
|
496 'priority' => count( $sidebar_widget_ids ), // place 'Add Widget' and 'Reorder' buttons at end. |
|
497 ) |
|
498 ); |
|
499 } |
|
500 |
|
501 $this->manager->add_control( $control ); |
|
502 |
|
503 $new_setting_ids[] = $setting_id; |
|
504 } |
|
505 } |
|
506 |
|
507 if ( ! $use_widgets_block_editor ) { |
|
508 // Add a control for each active widget (located in a sidebar). |
|
509 foreach ( $sidebar_widget_ids as $i => $widget_id ) { |
|
510 |
|
511 // Skip widgets that may have gone away due to a plugin being deactivated. |
|
512 if ( ! $is_active_sidebar || ! isset( $wp_registered_widgets[ $widget_id ] ) ) { |
|
513 continue; |
|
514 } |
|
515 |
|
516 $registered_widget = $wp_registered_widgets[ $widget_id ]; |
|
517 $setting_id = $this->get_setting_id( $widget_id ); |
|
518 $id_base = $wp_registered_widget_controls[ $widget_id ]['id_base']; |
|
519 |
|
520 $control = new WP_Widget_Form_Customize_Control( |
471 $this->manager, |
521 $this->manager, |
472 $setting_id, |
522 $setting_id, |
473 array( |
523 array( |
474 'section' => $section_id, |
524 'label' => $registered_widget['name'], |
475 'sidebar_id' => $sidebar_id, |
525 'section' => $section_id, |
476 'priority' => count( $sidebar_widget_ids ), // place 'Add Widget' and 'Reorder' buttons at end. |
526 'sidebar_id' => $sidebar_id, |
|
527 'widget_id' => $widget_id, |
|
528 'widget_id_base' => $id_base, |
|
529 'priority' => $i, |
|
530 'width' => $wp_registered_widget_controls[ $widget_id ]['width'], |
|
531 'height' => $wp_registered_widget_controls[ $widget_id ]['height'], |
|
532 'is_wide' => $this->is_wide_widget( $widget_id ), |
477 ) |
533 ) |
478 ); |
534 ); |
479 $new_setting_ids[] = $setting_id; |
|
480 |
|
481 $this->manager->add_control( $control ); |
535 $this->manager->add_control( $control ); |
482 } |
536 } |
483 } |
|
484 |
|
485 // Add a control for each active widget (located in a sidebar). |
|
486 foreach ( $sidebar_widget_ids as $i => $widget_id ) { |
|
487 |
|
488 // Skip widgets that may have gone away due to a plugin being deactivated. |
|
489 if ( ! $is_active_sidebar || ! isset( $wp_registered_widgets[ $widget_id ] ) ) { |
|
490 continue; |
|
491 } |
|
492 |
|
493 $registered_widget = $wp_registered_widgets[ $widget_id ]; |
|
494 $setting_id = $this->get_setting_id( $widget_id ); |
|
495 $id_base = $wp_registered_widget_controls[ $widget_id ]['id_base']; |
|
496 |
|
497 $control = new WP_Widget_Form_Customize_Control( |
|
498 $this->manager, |
|
499 $setting_id, |
|
500 array( |
|
501 'label' => $registered_widget['name'], |
|
502 'section' => $section_id, |
|
503 'sidebar_id' => $sidebar_id, |
|
504 'widget_id' => $widget_id, |
|
505 'widget_id_base' => $id_base, |
|
506 'priority' => $i, |
|
507 'width' => $wp_registered_widget_controls[ $widget_id ]['width'], |
|
508 'height' => $wp_registered_widget_controls[ $widget_id ]['height'], |
|
509 'is_wide' => $this->is_wide_widget( $widget_id ), |
|
510 ) |
|
511 ); |
|
512 $this->manager->add_control( $control ); |
|
513 } |
537 } |
514 } |
538 } |
515 |
539 |
516 if ( $this->manager->settings_previewed() ) { |
540 if ( $this->manager->settings_previewed() ) { |
517 foreach ( $new_setting_ids as $new_setting_id ) { |
541 foreach ( $new_setting_ids as $new_setting_id ) { |
803 $wp_scripts->add_data( |
827 $wp_scripts->add_data( |
804 'customize-widgets', |
828 'customize-widgets', |
805 'data', |
829 'data', |
806 sprintf( 'var _wpCustomizeWidgetsSettings = %s;', wp_json_encode( $settings ) ) |
830 sprintf( 'var _wpCustomizeWidgetsSettings = %s;', wp_json_encode( $settings ) ) |
807 ); |
831 ); |
|
832 |
|
833 /* |
|
834 * TODO: Update 'wp-customize-widgets' to not rely so much on things in |
|
835 * 'customize-widgets'. This will let us skip most of the above and not |
|
836 * enqueue 'customize-widgets' which saves bytes. |
|
837 */ |
|
838 |
|
839 if ( wp_use_widgets_block_editor() ) { |
|
840 $block_editor_context = new WP_Block_Editor_Context(); |
|
841 |
|
842 $editor_settings = get_block_editor_settings( |
|
843 get_legacy_widget_block_editor_settings(), |
|
844 $block_editor_context |
|
845 ); |
|
846 |
|
847 wp_add_inline_script( |
|
848 'wp-customize-widgets', |
|
849 sprintf( |
|
850 'wp.domReady( function() { |
|
851 wp.customizeWidgets.initialize( "widgets-customizer", %s ); |
|
852 } );', |
|
853 wp_json_encode( $editor_settings ) |
|
854 ) |
|
855 ); |
|
856 |
|
857 // Preload server-registered block schemas. |
|
858 wp_add_inline_script( |
|
859 'wp-blocks', |
|
860 'wp.blocks.unstable__bootstrapServerSideBlockDefinitions(' . wp_json_encode( get_block_editor_server_block_settings() ) . ');' |
|
861 ); |
|
862 |
|
863 wp_add_inline_script( |
|
864 'wp-blocks', |
|
865 sprintf( 'wp.blocks.setCategories( %s );', wp_json_encode( get_block_categories( $block_editor_context ) ) ), |
|
866 'after' |
|
867 ); |
|
868 |
|
869 wp_enqueue_script( 'wp-customize-widgets' ); |
|
870 wp_enqueue_style( 'wp-customize-widgets' ); |
|
871 |
|
872 /** This action is documented in edit-form-blocks.php */ |
|
873 do_action( 'enqueue_block_editor_assets' ); |
|
874 } |
808 } |
875 } |
809 |
876 |
810 /** |
877 /** |
811 * Renders the widget form control templates into the DOM. |
878 * Renders the widget form control templates into the DOM. |
812 * |
879 * |
886 if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) { |
953 if ( preg_match( $this->setting_id_patterns['sidebar_widgets'], $id, $matches ) ) { |
887 $args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' ); |
954 $args['sanitize_callback'] = array( $this, 'sanitize_sidebar_widgets' ); |
888 $args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' ); |
955 $args['sanitize_js_callback'] = array( $this, 'sanitize_sidebar_widgets_js_instance' ); |
889 $args['transport'] = current_theme_supports( 'customize-selective-refresh-widgets' ) ? 'postMessage' : 'refresh'; |
956 $args['transport'] = current_theme_supports( 'customize-selective-refresh-widgets' ) ? 'postMessage' : 'refresh'; |
890 } elseif ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) { |
957 } elseif ( preg_match( $this->setting_id_patterns['widget_instance'], $id, $matches ) ) { |
891 $args['sanitize_callback'] = array( $this, 'sanitize_widget_instance' ); |
958 $id_base = $matches['id_base']; |
892 $args['sanitize_js_callback'] = array( $this, 'sanitize_widget_js_instance' ); |
959 $args['sanitize_callback'] = function( $value ) use ( $id_base ) { |
|
960 return $this->sanitize_widget_instance( $value, $id_base ); |
|
961 }; |
|
962 $args['sanitize_js_callback'] = function( $value ) use ( $id_base ) { |
|
963 return $this->sanitize_widget_js_instance( $value, $id_base ); |
|
964 }; |
893 $args['transport'] = $this->is_widget_selective_refreshable( $matches['id_base'] ) ? 'postMessage' : 'refresh'; |
965 $args['transport'] = $this->is_widget_selective_refreshable( $matches['id_base'] ) ? 'postMessage' : 'refresh'; |
894 } |
966 } |
895 |
967 |
896 $args = array_merge( $args, $overrides ); |
968 $args = array_merge( $args, $overrides ); |
897 |
969 |
1107 $nonces['update-widget'] = wp_create_nonce( 'update-widget' ); |
1179 $nonces['update-widget'] = wp_create_nonce( 'update-widget' ); |
1108 return $nonces; |
1180 return $nonces; |
1109 } |
1181 } |
1110 |
1182 |
1111 /** |
1183 /** |
|
1184 * Tells the script loader to load the scripts and styles of custom blocks |
|
1185 * if the widgets block editor is enabled. |
|
1186 * |
|
1187 * @since 5.8.0 |
|
1188 * |
|
1189 * @param bool $is_block_editor_screen Current decision about loading block assets. |
|
1190 * @return bool Filtered decision about loading block assets. |
|
1191 */ |
|
1192 public function should_load_block_editor_scripts_and_styles( $is_block_editor_screen ) { |
|
1193 if ( wp_use_widgets_block_editor() ) { |
|
1194 return true; |
|
1195 } |
|
1196 |
|
1197 return $is_block_editor_screen; |
|
1198 } |
|
1199 |
|
1200 /** |
1112 * When previewing, ensures the proper previewing widgets are used. |
1201 * When previewing, ensures the proper previewing widgets are used. |
1113 * |
1202 * |
1114 * Because wp_get_sidebars_widgets() gets called early at {@see 'init' } (via |
1203 * Because wp_get_sidebars_widgets() gets called early at {@see 'init' } (via |
1115 * wp_convert_widget_settings()) and can set global variable `$_wp_sidebars_widgets` |
1204 * wp_convert_widget_settings()) and can set global variable `$_wp_sidebars_widgets` |
1116 * to the value of `get_option( 'sidebars_widgets' )` before the Customizer preview |
1205 * to the value of `get_option( 'sidebars_widgets' )` before the Customizer preview |
1310 * |
1399 * |
1311 * Unserialize the JS-instance for storing in the options. It's important that this filter |
1400 * Unserialize the JS-instance for storing in the options. It's important that this filter |
1312 * only get applied to an instance *once*. |
1401 * only get applied to an instance *once*. |
1313 * |
1402 * |
1314 * @since 3.9.0 |
1403 * @since 3.9.0 |
1315 * |
1404 * @since 5.8.0 Added the `$id_base` parameter. |
1316 * @param array $value Widget instance to sanitize. |
1405 * |
|
1406 * @global WP_Widget_Factory $wp_widget_factory |
|
1407 * |
|
1408 * @param array $value Widget instance to sanitize. |
|
1409 * @param string $id_base Optional. Base of the ID of the widget being sanitized. Default null. |
1317 * @return array|void Sanitized widget instance. |
1410 * @return array|void Sanitized widget instance. |
1318 */ |
1411 */ |
1319 public function sanitize_widget_instance( $value ) { |
1412 public function sanitize_widget_instance( $value, $id_base = null ) { |
|
1413 global $wp_widget_factory; |
|
1414 |
1320 if ( array() === $value ) { |
1415 if ( array() === $value ) { |
1321 return $value; |
1416 return $value; |
1322 } |
1417 } |
1323 |
1418 |
1324 if ( empty( $value['is_widget_customizer_js_value'] ) |
1419 if ( isset( $value['raw_instance'] ) && $id_base && wp_use_widgets_block_editor() ) { |
1325 || empty( $value['instance_hash_key'] ) |
1420 $widget_object = $wp_widget_factory->get_widget_object( $id_base ); |
1326 || empty( $value['encoded_serialized_instance'] ) ) { |
1421 if ( ! empty( $widget_object->widget_options['show_instance_in_rest'] ) ) { |
|
1422 if ( 'block' === $id_base && ! current_user_can( 'unfiltered_html' ) ) { |
|
1423 /* |
|
1424 * The content of the 'block' widget is not filtered on the |
|
1425 * fly while editing. Filter the content here to prevent |
|
1426 * vulnerabilities. |
|
1427 */ |
|
1428 $value['raw_instance']['content'] = wp_kses_post( $value['raw_instance']['content'] ); |
|
1429 } |
|
1430 |
|
1431 return $value['raw_instance']; |
|
1432 } |
|
1433 } |
|
1434 |
|
1435 if ( |
|
1436 empty( $value['is_widget_customizer_js_value'] ) || |
|
1437 empty( $value['instance_hash_key'] ) || |
|
1438 empty( $value['encoded_serialized_instance'] ) |
|
1439 ) { |
1327 return; |
1440 return; |
1328 } |
1441 } |
1329 |
1442 |
1330 $decoded = base64_decode( $value['encoded_serialized_instance'], true ); |
1443 $decoded = base64_decode( $value['encoded_serialized_instance'], true ); |
1331 if ( false === $decoded ) { |
1444 if ( false === $decoded ) { |
1346 |
1459 |
1347 /** |
1460 /** |
1348 * Converts a widget instance into JSON-representable format. |
1461 * Converts a widget instance into JSON-representable format. |
1349 * |
1462 * |
1350 * @since 3.9.0 |
1463 * @since 3.9.0 |
1351 * |
1464 * @since 5.8.0 Added the `$id_base` parameter. |
1352 * @param array $value Widget instance to convert to JSON. |
1465 * |
|
1466 * @global WP_Widget_Factory $wp_widget_factory |
|
1467 * |
|
1468 * @param array $value Widget instance to convert to JSON. |
|
1469 * @param string $id_base Optional. Base of the ID of the widget being sanitized. Default null. |
1353 * @return array JSON-converted widget instance. |
1470 * @return array JSON-converted widget instance. |
1354 */ |
1471 */ |
1355 public function sanitize_widget_js_instance( $value ) { |
1472 public function sanitize_widget_js_instance( $value, $id_base = null ) { |
|
1473 global $wp_widget_factory; |
|
1474 |
1356 if ( empty( $value['is_widget_customizer_js_value'] ) ) { |
1475 if ( empty( $value['is_widget_customizer_js_value'] ) ) { |
1357 $serialized = serialize( $value ); |
1476 $serialized = serialize( $value ); |
1358 |
1477 |
1359 $value = array( |
1478 $js_value = array( |
1360 'encoded_serialized_instance' => base64_encode( $serialized ), |
1479 'encoded_serialized_instance' => base64_encode( $serialized ), |
1361 'title' => empty( $value['title'] ) ? '' : $value['title'], |
1480 'title' => empty( $value['title'] ) ? '' : $value['title'], |
1362 'is_widget_customizer_js_value' => true, |
1481 'is_widget_customizer_js_value' => true, |
1363 'instance_hash_key' => $this->get_instance_hash_key( $serialized ), |
1482 'instance_hash_key' => $this->get_instance_hash_key( $serialized ), |
1364 ); |
1483 ); |
1365 } |
1484 |
|
1485 if ( $id_base && wp_use_widgets_block_editor() ) { |
|
1486 $widget_object = $wp_widget_factory->get_widget_object( $id_base ); |
|
1487 if ( ! empty( $widget_object->widget_options['show_instance_in_rest'] ) ) { |
|
1488 $js_value['raw_instance'] = (object) $value; |
|
1489 } |
|
1490 } |
|
1491 |
|
1492 return $js_value; |
|
1493 } |
|
1494 |
1366 return $value; |
1495 return $value; |
1367 } |
1496 } |
1368 |
1497 |
1369 /** |
1498 /** |
1370 * Strips out widget IDs for widgets which are no longer registered. |
1499 * Strips out widget IDs for widgets which are no longer registered. |
1496 * Override the incoming $_POST['customized'] for a newly-created widget's |
1625 * Override the incoming $_POST['customized'] for a newly-created widget's |
1497 * setting with the new $instance so that the preview filter currently |
1626 * setting with the new $instance so that the preview filter currently |
1498 * in place from WP_Customize_Setting::preview() will use this value |
1627 * in place from WP_Customize_Setting::preview() will use this value |
1499 * instead of the default widget instance value (an empty array). |
1628 * instead of the default widget instance value (an empty array). |
1500 */ |
1629 */ |
1501 $this->manager->set_post_value( $setting_id, $this->sanitize_widget_js_instance( $instance ) ); |
1630 $this->manager->set_post_value( $setting_id, $this->sanitize_widget_js_instance( $instance, $parsed_id['id_base'] ) ); |
1502 |
1631 |
1503 // Obtain the widget control with the updated instance in place. |
1632 // Obtain the widget control with the updated instance in place. |
1504 ob_start(); |
1633 ob_start(); |
1505 $form = $wp_registered_widget_controls[ $widget_id ]; |
1634 $form = $wp_registered_widget_controls[ $widget_id ]; |
1506 if ( $form ) { |
1635 if ( $form ) { |
1786 * @param int|string $index Index, name, or ID of the dynamic sidebar. |
1915 * @param int|string $index Index, name, or ID of the dynamic sidebar. |
1787 */ |
1916 */ |
1788 public function end_dynamic_sidebar( $index ) { |
1917 public function end_dynamic_sidebar( $index ) { |
1789 array_shift( $this->current_dynamic_sidebar_id_stack ); |
1918 array_shift( $this->current_dynamic_sidebar_id_stack ); |
1790 if ( ! $this->manager->selective_refresh->is_render_partials_request() ) { |
1919 if ( ! $this->manager->selective_refresh->is_render_partials_request() ) { |
1791 printf( "\n<!--dynamic_sidebar_after:%s:%d-->\n", esc_html( $index ), intval( $this->sidebar_instance_count[ $index ] ) ); |
1920 printf( "\n<!--dynamic_sidebar_after:%s:%d-->\n", esc_html( $index ), (int) $this->sidebar_instance_count[ $index ] ); |
1792 } |
1921 } |
1793 } |
1922 } |
1794 |
1923 |
1795 /** |
1924 /** |
1796 * Current sidebar being rendered. |
1925 * Current sidebar being rendered. |