Changeset 36586 for trunk/src/wp-includes/class-wp-customize-nav-menus.php
- Timestamp:
- 02/19/2016 06:40:06 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-wp-customize-nav-menus.php
r36573 r36586 62 62 add_action( 'customize_controls_print_footer_scripts', array( $this, 'available_items_template' ) ); 63 63 add_action( 'customize_preview_init', array( $this, 'customize_preview_init' ) ); 64 65 // Selective Refresh partials. 66 add_filter( 'customize_dynamic_partial_args', array( $this, 'customize_dynamic_partial_args' ), 10, 2 ); 64 67 } 65 68 … … 376 379 'reorderLabelOff' => esc_attr__( 'Close reorder mode' ), 377 380 ), 378 ' menuItemTransport' => 'postMessage',381 'settingTransport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 379 382 'phpIntMax' => PHP_INT_MAX, 380 383 'defaultSettingValues' => array( … … 427 430 if ( preg_match( WP_Customize_Nav_Menu_Setting::ID_PATTERN, $setting_id ) ) { 428 431 $setting_args = array( 429 'type' => WP_Customize_Nav_Menu_Setting::TYPE, 432 'type' => WP_Customize_Nav_Menu_Setting::TYPE, 433 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 430 434 ); 431 435 } elseif ( preg_match( WP_Customize_Nav_Menu_Item_Setting::ID_PATTERN, $setting_id ) ) { 432 436 $setting_args = array( 433 'type' => WP_Customize_Nav_Menu_Item_Setting::TYPE, 437 'type' => WP_Customize_Nav_Menu_Item_Setting::TYPE, 438 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 434 439 ); 435 440 } … … 516 521 $setting = $this->manager->get_setting( $setting_id ); 517 522 if ( $setting ) { 518 $setting->transport = 'postMessage';523 $setting->transport = isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh'; 519 524 remove_filter( "customize_sanitize_{$setting_id}", 'absint' ); 520 525 add_filter( "customize_sanitize_{$setting_id}", array( $this, 'intval_base10' ) ); … … 524 529 'theme_supports' => 'menus', 525 530 'type' => 'theme_mod', 526 'transport' => 'postMessage',531 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 527 532 'default' => 0, 528 533 ) ); … … 550 555 551 556 $nav_menu_setting_id = 'nav_menu[' . $menu_id . ']'; 552 $this->manager->add_setting( new WP_Customize_Nav_Menu_Setting( $this->manager, $nav_menu_setting_id ) ); 557 $this->manager->add_setting( new WP_Customize_Nav_Menu_Setting( $this->manager, $nav_menu_setting_id, array( 558 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 559 ) ) ); 553 560 554 561 // Add the menu contents. … … 563 570 $value['nav_menu_term_id'] = $menu_id; 564 571 $this->manager->add_setting( new WP_Customize_Nav_Menu_Item_Setting( $this->manager, $menu_item_setting_id, array( 565 'value' => $value, 572 'value' => $value, 573 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 566 574 ) ) ); 567 575 … … 587 595 'type' => 'new_menu', 588 596 'default' => '', 589 'transport' => 'postMessage',597 'transport' => isset( $this->manager->selective_refresh ) ? 'postMessage' : 'refresh', 590 598 ) ); 591 599 … … 803 811 } 804 812 813 // 805 814 // Start functionality specific to partial-refresh of menu changes in Customizer preview. 806 const RENDER_AJAX_ACTION = 'customize_render_menu_partial'; 807 const RENDER_NONCE_POST_KEY = 'render-menu-nonce'; 808 const RENDER_QUERY_VAR = 'wp_customize_menu_render'; 809 810 /** 811 * The number of wp_nav_menu() calls which have happened in the preview. 812 * 813 * @since 4.3.0 814 * @access public 815 * @var int 816 */ 817 public $preview_nav_menu_instance_number = 0; 818 819 /** 820 * Nav menu args used for each instance. 821 * 822 * @since 4.3.0 823 * @access public 824 * @var array 825 */ 826 public $preview_nav_menu_instance_args = array(); 815 // 816 817 /** 818 * Filters arguments for dynamic nav_menu selective refresh partials. 819 * 820 * @since 4.5.0 821 * @access public 822 * 823 * @param array|false $partial_args Partial args. 824 * @param string $partial_id Partial ID. 825 * @return array Partial args 826 */ 827 public function customize_dynamic_partial_args( $partial_args, $partial_id ) { 828 829 if ( preg_match( '/^nav_menu_instance\[[0-9a-f]{32}\]$/', $partial_id ) ) { 830 if ( false === $partial_args ) { 831 $partial_args = array(); 832 } 833 $partial_args = array_merge( 834 $partial_args, 835 array( 836 'type' => 'nav_menu_instance', 837 'render_callback' => array( $this, 'render_nav_menu_partial' ), 838 'container_inclusive' => true, 839 ) 840 ); 841 } 842 843 return $partial_args; 844 } 827 845 828 846 /** … … 833 851 */ 834 852 public function customize_preview_init() { 835 add_action( 'template_redirect', array( $this, 'render_menu' ) );836 853 add_action( 'wp_enqueue_scripts', array( $this, 'customize_preview_enqueue_deps' ) ); 837 838 if ( ! isset( $_REQUEST[ self::RENDER_QUERY_VAR ] ) ) { 839 add_filter( 'wp_nav_menu_args', array( $this, 'filter_wp_nav_menu_args' ), 1000 ); 840 add_filter( 'wp_nav_menu', array( $this, 'filter_wp_nav_menu' ), 10, 2 ); 841 } 854 add_filter( 'wp_nav_menu_args', array( $this, 'filter_wp_nav_menu_args' ), 1000 ); 855 add_filter( 'wp_nav_menu', array( $this, 'filter_wp_nav_menu' ), 10, 2 ); 842 856 } 843 857 … … 847 861 * @since 4.3.0 848 862 * @access public 849 *850 863 * @see wp_nav_menu() 864 * @see WP_Customize_Widgets_Partial_Refresh::filter_dynamic_sidebar_params() 851 865 * 852 866 * @param array $args An array containing wp_nav_menu() arguments. … … 854 868 */ 855 869 public function filter_wp_nav_menu_args( $args ) { 856 $this->preview_nav_menu_instance_number += 1; 857 $args['instance_number'] = $this->preview_nav_menu_instance_number; 858 859 $can_partial_refresh = ( 870 /* 871 * The following conditions determine whether or not this instance of 872 * wp_nav_menu() can use selective refreshed. A wp_nav_menu() can be 873 * selective refreshed if... 874 */ 875 $can_selective_refresh = ( 876 // ...if wp_nav_menu() is directly echoing out the menu (and thus isn't manipulating the string after generated), 860 877 ! empty( $args['echo'] ) 861 878 && 879 // ...and if the fallback_cb can be serialized to JSON, since it will be included in the placement context data, 862 880 ( empty( $args['fallback_cb'] ) || is_string( $args['fallback_cb'] ) ) 863 881 && 882 // ...and if the walker can also be serialized to JSON, since it will be included in the placement context data as well, 864 883 ( empty( $args['walker'] ) || is_string( $args['walker'] ) ) 865 &&866 (884 // ...and if it has a theme location assigned or an assigned menu to display, 885 && ( 867 886 ! empty( $args['theme_location'] ) 868 887 || 869 888 ( ! empty( $args['menu'] ) && ( is_numeric( $args['menu'] ) || is_object( $args['menu'] ) ) ) 870 889 ) 890 && 891 // ...and if the nav menu would be rendered with a wrapper container element (upon which to attach data-* attributes). 892 ( 893 ! empty( $args['container'] ) 894 || 895 ( isset( $args['items_wrap'] ) && '<' === substr( $args['items_wrap'], 0, 1 ) ) 896 ) 871 897 ); 872 $args['can_partial_refresh'] = $can_partial_refresh; 873 874 $hashed_args = $args; 875 876 if ( ! $can_partial_refresh ) { 877 $hashed_args['fallback_cb'] = ''; 878 $hashed_args['walker'] = ''; 879 } 880 881 // Replace object menu arg with a term_id menu arg, as this exports better to JS and is easier to compare hashes. 882 if ( ! empty( $hashed_args['menu'] ) && is_object( $hashed_args['menu'] ) ) { 883 $hashed_args['menu'] = $hashed_args['menu']->term_id; 884 } 885 886 ksort( $hashed_args ); 887 $hashed_args['args_hash'] = $this->hash_nav_menu_args( $hashed_args ); 888 889 $this->preview_nav_menu_instance_args[ $this->preview_nav_menu_instance_number ] = $hashed_args; 898 899 if ( ! $can_selective_refresh ) { 900 return $args; 901 } 902 903 $exported_args = $args; 904 905 /* 906 * Replace object menu arg with a term_id menu arg, as this exports better 907 * to JS and is easier to compare hashes. 908 */ 909 if ( ! empty( $exported_args['menu'] ) && is_object( $exported_args['menu'] ) ) { 910 $exported_args['menu'] = $exported_args['menu']->term_id; 911 } 912 913 ksort( $exported_args ); 914 $exported_args['args_hmac'] = $this->hash_nav_menu_args( $exported_args ); 915 916 $args['customize_preview_nav_menus_args'] = $exported_args; 917 890 918 return $args; 891 919 } 892 920 893 921 /** 894 * Prepare wp_nav_menu() calls for partial refresh. Wraps output in container for refreshing. 922 * Prepares wp_nav_menu() calls for partial refresh. 923 * 924 * Injects attributes into container element. 895 925 * 896 926 * @since 4.3.0 … … 904 934 */ 905 935 public function filter_wp_nav_menu( $nav_menu_content, $args ) { 906 if ( ! empty( $args->can_partial_refresh ) && ! empty( $args->instance_number ) ) { 907 $nav_menu_content = preg_replace( 908 '/(?<=class=")/', 909 sprintf( 'partial-refreshable-nav-menu partial-refreshable-nav-menu-%1$d ', $args->instance_number ), 910 $nav_menu_content, 911 1 // Only update the class on the first element found, the menu container. 912 ); 936 if ( ! empty( $args->customize_preview_nav_menus_args ) ) { 937 $attributes = sprintf( ' data-customize-partial-id="%s"', esc_attr( 'nav_menu_instance[' . $args->customize_preview_nav_menus_args['args_hmac'] . ']' ) ); 938 $attributes .= ' data-customize-partial-type="nav_menu_instance"'; 939 $attributes .= sprintf( ' data-customize-partial-placement-context="%s"', esc_attr( wp_json_encode( $args->customize_preview_nav_menus_args ) ) ); 940 $nav_menu_content = preg_replace( '#^(<\w+)#', '$1 ' . $attributes, $nav_menu_content, 1 ); 913 941 } 914 942 return $nav_menu_content; … … 916 944 917 945 /** 918 * Hash (hmac) the arguments with the nonce and secret auth key to ensure they 919 * are not tampered with when submitted in the Ajax request. 946 * Hashes (hmac) the nav menu arguments to ensure they are not tampered with when 947 * submitted in the Ajax request. 948 * 949 * Note that the array is expected to be pre-sorted. 920 950 * 921 951 * @since 4.3.0 … … 923 953 * 924 954 * @param array $args The arguments to hash. 925 * @return string 955 * @return string Hashed nav menu arguments. 926 956 */ 927 957 public function hash_nav_menu_args( $args ) { 928 return wp_hash( wp_create_nonce( self::RENDER_AJAX_ACTION ) .serialize( $args ) );958 return wp_hash( serialize( $args ) ); 929 959 } 930 960 … … 936 966 */ 937 967 public function customize_preview_enqueue_deps() { 938 wp_enqueue_script( 'customize-preview-nav-menus' ); 968 if ( isset( $this->manager->selective_refresh ) ) { 969 $script = wp_scripts()->registered['customize-preview-nav-menus']; 970 $script->deps[] = 'customize-selective-refresh'; 971 } 972 973 wp_enqueue_script( 'customize-preview-nav-menus' ); // Note that we have overridden this. 939 974 wp_enqueue_style( 'customize-preview' ); 940 941 add_action( 'wp_print_footer_scripts', array( $this, 'export_preview_data' ) ); 942 } 943 944 /** 945 * Export data from PHP to JS. 946 * 947 * @since 4.3.0 975 } 976 977 /** 978 * Exports data from PHP to JS. 979 * 980 * @since 4.3.0 981 * @deprecated 4.5.0 Obsolete 948 982 * @access public 949 983 */ 950 984 public function export_preview_data() { 951 952 // Why not wp_localize_script? Because we're not localizing, and it forces values into strings. 953 $exports = array( 954 'renderQueryVar' => self::RENDER_QUERY_VAR, 955 'renderNonceValue' => wp_create_nonce( self::RENDER_AJAX_ACTION ), 956 'renderNoncePostKey' => self::RENDER_NONCE_POST_KEY, 957 'navMenuInstanceArgs' => $this->preview_nav_menu_instance_args, 958 'l10n' => array( 959 'editNavMenuItemTooltip' => __( 'Shift-click to edit this menu item.' ), 960 ), 961 ); 962 963 printf( '<script>var _wpCustomizePreviewNavMenusExports = %s;</script>', wp_json_encode( $exports ) ); 985 _deprecated_function( __METHOD__, '4.5.0' ); 964 986 } 965 987 … … 971 993 * 972 994 * @see wp_nav_menu() 973 */ 974 public function render_menu() { 975 if ( empty( $_POST[ self::RENDER_QUERY_VAR ] ) ) { 976 return; 977 } 978 979 $this->manager->remove_preview_signature(); 980 981 if ( empty( $_POST[ self::RENDER_NONCE_POST_KEY ] ) ) { 982 wp_send_json_error( 'missing_nonce_param' ); 983 } 984 985 if ( ! is_customize_preview() ) { 986 wp_send_json_error( 'expected_customize_preview' ); 987 } 988 989 if ( ! check_ajax_referer( self::RENDER_AJAX_ACTION, self::RENDER_NONCE_POST_KEY, false ) ) { 990 wp_send_json_error( 'nonce_check_fail' ); 991 } 992 993 if ( ! current_user_can( 'edit_theme_options' ) ) { 994 wp_send_json_error( 'unauthorized' ); 995 } 996 997 if ( ! isset( $_POST['wp_nav_menu_args'] ) ) { 998 wp_send_json_error( 'missing_param' ); 999 } 1000 1001 if ( ! isset( $_POST['wp_nav_menu_args_hash'] ) ) { 1002 wp_send_json_error( 'missing_param' ); 1003 } 1004 1005 $wp_nav_menu_args = json_decode( wp_unslash( $_POST['wp_nav_menu_args'] ), true ); 1006 if ( ! is_array( $wp_nav_menu_args ) ) { 1007 wp_send_json_error( 'wp_nav_menu_args_not_array' ); 1008 } 1009 1010 $wp_nav_menu_args_hash = sanitize_text_field( wp_unslash( $_POST['wp_nav_menu_args_hash'] ) ); 1011 if ( ! hash_equals( $this->hash_nav_menu_args( $wp_nav_menu_args ), $wp_nav_menu_args_hash ) ) { 1012 wp_send_json_error( 'wp_nav_menu_args_hash_mismatch' ); 1013 } 1014 1015 $wp_nav_menu_args['echo'] = false; 1016 wp_send_json_success( wp_nav_menu( $wp_nav_menu_args ) ); 995 * 996 * @param WP_Customize_Partial $partial Partial. 997 * @param array $nav_menu_args Nav menu args supplied as container context. 998 * @return string|false 999 */ 1000 public function render_nav_menu_partial( $partial, $nav_menu_args ) { 1001 unset( $partial ); 1002 1003 if ( ! isset( $nav_menu_args['args_hmac'] ) ) { 1004 // Error: missing_args_hmac. 1005 return false; 1006 } 1007 1008 $nav_menu_args_hmac = $nav_menu_args['args_hmac']; 1009 unset( $nav_menu_args['args_hmac'] ); 1010 1011 ksort( $nav_menu_args ); 1012 if ( ! hash_equals( $this->hash_nav_menu_args( $nav_menu_args ), $nav_menu_args_hmac ) ) { 1013 // Error: args_hmac_mismatch. 1014 return false; 1015 } 1016 1017 ob_start(); 1018 wp_nav_menu( $nav_menu_args ); 1019 $content = ob_get_clean(); 1020 1021 return $content; 1017 1022 } 1018 1023 }
Note: See TracChangeset
for help on using the changeset viewer.