Changeset 35383 for trunk/src/wp-includes/class-wp-customize-setting.php
- Timestamp:
- 10/24/2015 06:10:17 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/class-wp-customize-setting.php
r35308 r35383 770 770 } 771 771 772 /** 773 * A setting that is used to filter a value, but will not save the results. 774 * 775 * Results should be properly handled using another setting or callback. 776 * 777 * @since 3.4.0 778 * 779 * @see WP_Customize_Setting 780 */ 781 class WP_Customize_Filter_Setting extends WP_Customize_Setting { 782 783 /** 784 * @since 3.4.0 785 */ 786 public function update( $value ) {} 787 } 788 789 /** 790 * A setting that is used to filter a value, but will not save the results. 791 * 792 * Results should be properly handled using another setting or callback. 793 * 794 * @since 3.4.0 795 * 796 * @see WP_Customize_Setting 797 */ 798 final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting { 799 public $id = 'header_image_data'; 800 801 /** 802 * @since 3.4.0 803 * 804 * @global Custom_Image_Header $custom_image_header 805 * 806 * @param $value 807 */ 808 public function update( $value ) { 809 global $custom_image_header; 810 811 // If the value doesn't exist (removed or random), 812 // use the header_image value. 813 if ( ! $value ) 814 $value = $this->manager->get_setting('header_image')->post_value(); 815 816 if ( is_array( $value ) && isset( $value['choice'] ) ) 817 $custom_image_header->set_header_image( $value['choice'] ); 818 else 819 $custom_image_header->set_header_image( $value ); 820 } 821 } 822 823 /** 824 * Customizer Background Image Setting class. 825 * 826 * @since 3.4.0 827 * 828 * @see WP_Customize_Setting 829 */ 830 final class WP_Customize_Background_Image_Setting extends WP_Customize_Setting { 831 public $id = 'background_image_thumb'; 832 833 /** 834 * @since 3.4.0 835 * 836 * @param $value 837 */ 838 public function update( $value ) { 839 remove_theme_mod( 'background_image_thumb' ); 840 } 841 } 842 843 /** 844 * Customize Setting to represent a nav_menu. 845 * 846 * Subclass of WP_Customize_Setting to represent a nav_menu taxonomy term, and 847 * the IDs for the nav_menu_items associated with the nav menu. 848 * 849 * @since 4.3.0 850 * 851 * @see WP_Customize_Setting 852 */ 853 class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 854 855 const ID_PATTERN = '/^nav_menu_item\[(?P<id>-?\d+)\]$/'; 856 857 const POST_TYPE = 'nav_menu_item'; 858 859 const TYPE = 'nav_menu_item'; 860 861 /** 862 * Setting type. 863 * 864 * @since 4.3.0 865 * @access public 866 * @var string 867 */ 868 public $type = self::TYPE; 869 870 /** 871 * Default setting value. 872 * 873 * @since 4.3.0 874 * @access public 875 * @var array 876 * 877 * @see wp_setup_nav_menu_item() 878 */ 879 public $default = array( 880 // The $menu_item_data for wp_update_nav_menu_item(). 881 'object_id' => 0, 882 'object' => '', // Taxonomy name. 883 'menu_item_parent' => 0, // A.K.A. menu-item-parent-id; note that post_parent is different, and not included. 884 'position' => 0, // A.K.A. menu_order. 885 'type' => 'custom', // Note that type_label is not included here. 886 'title' => '', 887 'url' => '', 888 'target' => '', 889 'attr_title' => '', 890 'description' => '', 891 'classes' => '', 892 'xfn' => '', 893 'status' => 'publish', 894 'original_title' => '', 895 'nav_menu_term_id' => 0, // This will be supplied as the $menu_id arg for wp_update_nav_menu_item(). 896 '_invalid' => false, 897 ); 898 899 /** 900 * Default transport. 901 * 902 * @since 4.3.0 903 * @access public 904 * @var string 905 */ 906 public $transport = 'postMessage'; 907 908 /** 909 * The post ID represented by this setting instance. This is the db_id. 910 * 911 * A negative value represents a placeholder ID for a new menu not yet saved. 912 * 913 * @since 4.3.0 914 * @access public 915 * @var int 916 */ 917 public $post_id; 918 919 /** 920 * Storage of pre-setup menu item to prevent wasted calls to wp_setup_nav_menu_item(). 921 * 922 * @since 4.3.0 923 * @access protected 924 * @var array 925 */ 926 protected $value; 927 928 /** 929 * Previous (placeholder) post ID used before creating a new menu item. 930 * 931 * This value will be exported to JS via the customize_save_response filter 932 * so that JavaScript can update the settings to refer to the newly-assigned 933 * post ID. This value is always negative to indicate it does not refer to 934 * a real post. 935 * 936 * @since 4.3.0 937 * @access public 938 * @var int 939 * 940 * @see WP_Customize_Nav_Menu_Item_Setting::update() 941 * @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response() 942 */ 943 public $previous_post_id; 944 945 /** 946 * When previewing or updating a menu item, this stores the previous nav_menu_term_id 947 * which ensures that we can apply the proper filters. 948 * 949 * @since 4.3.0 950 * @access public 951 * @var int 952 */ 953 public $original_nav_menu_term_id; 954 955 /** 956 * Whether or not preview() was called. 957 * 958 * @since 4.3.0 959 * @access protected 960 * @var bool 961 */ 962 protected $is_previewed = false; 963 964 /** 965 * Whether or not update() was called. 966 * 967 * @since 4.3.0 968 * @access protected 969 * @var bool 970 */ 971 protected $is_updated = false; 972 973 /** 974 * Status for calling the update method, used in customize_save_response filter. 975 * 976 * When status is inserted, the placeholder post ID is stored in $previous_post_id. 977 * When status is error, the error is stored in $update_error. 978 * 979 * @since 4.3.0 980 * @access public 981 * @var string updated|inserted|deleted|error 982 * 983 * @see WP_Customize_Nav_Menu_Item_Setting::update() 984 * @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response() 985 */ 986 public $update_status; 987 988 /** 989 * Any error object returned by wp_update_nav_menu_item() when setting is updated. 990 * 991 * @since 4.3.0 992 * @access public 993 * @var WP_Error 994 * 995 * @see WP_Customize_Nav_Menu_Item_Setting::update() 996 * @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response() 997 */ 998 public $update_error; 999 1000 /** 1001 * Constructor. 1002 * 1003 * Any supplied $args override class property defaults. 1004 * 1005 * @since 4.3.0 1006 * @access public 1007 * 1008 * @param WP_Customize_Manager $manager Bootstrap Customizer instance. 1009 * @param string $id An specific ID of the setting. Can be a 1010 * theme mod or option name. 1011 * @param array $args Optional. Setting arguments. 1012 * 1013 * @throws Exception If $id is not valid for this setting type. 1014 */ 1015 public function __construct( WP_Customize_Manager $manager, $id, array $args = array() ) { 1016 if ( empty( $manager->nav_menus ) ) { 1017 throw new Exception( 'Expected WP_Customize_Manager::$nav_menus to be set.' ); 1018 } 1019 1020 if ( ! preg_match( self::ID_PATTERN, $id, $matches ) ) { 1021 throw new Exception( "Illegal widget setting ID: $id" ); 1022 } 1023 1024 $this->post_id = intval( $matches['id'] ); 1025 add_action( 'wp_update_nav_menu_item', array( $this, 'flush_cached_value' ), 10, 2 ); 1026 1027 parent::__construct( $manager, $id, $args ); 1028 1029 // Ensure that an initially-supplied value is valid. 1030 if ( isset( $this->value ) ) { 1031 $this->populate_value(); 1032 foreach ( array_diff( array_keys( $this->default ), array_keys( $this->value ) ) as $missing ) { 1033 throw new Exception( "Supplied nav_menu_item value missing property: $missing" ); 1034 } 1035 } 1036 1037 } 1038 1039 /** 1040 * Clear the cached value when this nav menu item is updated. 1041 * 1042 * @since 4.3.0 1043 * @access public 1044 * 1045 * @param int $menu_id The term ID for the menu. 1046 * @param int $menu_item_id The post ID for the menu item. 1047 */ 1048 public function flush_cached_value( $menu_id, $menu_item_id ) { 1049 unset( $menu_id ); 1050 if ( $menu_item_id === $this->post_id ) { 1051 $this->value = null; 1052 } 1053 } 1054 1055 /** 1056 * Get the instance data for a given nav_menu_item setting. 1057 * 1058 * @since 4.3.0 1059 * @access public 1060 * 1061 * @see wp_setup_nav_menu_item() 1062 * 1063 * @return array|false Instance data array, or false if the item is marked for deletion. 1064 */ 1065 public function value() { 1066 if ( $this->is_previewed && $this->_previewed_blog_id === get_current_blog_id() ) { 1067 $undefined = new stdClass(); // Symbol. 1068 $post_value = $this->post_value( $undefined ); 1069 1070 if ( $undefined === $post_value ) { 1071 $value = $this->_original_value; 1072 } else { 1073 $value = $post_value; 1074 } 1075 } else if ( isset( $this->value ) ) { 1076 $value = $this->value; 1077 } else { 1078 $value = false; 1079 1080 // Note that a ID of less than one indicates a nav_menu not yet inserted. 1081 if ( $this->post_id > 0 ) { 1082 $post = get_post( $this->post_id ); 1083 if ( $post && self::POST_TYPE === $post->post_type ) { 1084 $value = (array) wp_setup_nav_menu_item( $post ); 1085 } 1086 } 1087 1088 if ( ! is_array( $value ) ) { 1089 $value = $this->default; 1090 } 1091 1092 // Cache the value for future calls to avoid having to re-call wp_setup_nav_menu_item(). 1093 $this->value = $value; 1094 $this->populate_value(); 1095 $value = $this->value; 1096 } 1097 1098 return $value; 1099 } 1100 1101 /** 1102 * Ensure that the value is fully populated with the necessary properties. 1103 * 1104 * Translates some properties added by wp_setup_nav_menu_item() and removes others. 1105 * 1106 * @since 4.3.0 1107 * @access protected 1108 * 1109 * @see WP_Customize_Nav_Menu_Item_Setting::value() 1110 */ 1111 protected function populate_value() { 1112 if ( ! is_array( $this->value ) ) { 1113 return; 1114 } 1115 1116 if ( isset( $this->value['menu_order'] ) ) { 1117 $this->value['position'] = $this->value['menu_order']; 1118 unset( $this->value['menu_order'] ); 1119 } 1120 if ( isset( $this->value['post_status'] ) ) { 1121 $this->value['status'] = $this->value['post_status']; 1122 unset( $this->value['post_status'] ); 1123 } 1124 1125 if ( ! isset( $this->value['original_title'] ) ) { 1126 $original_title = ''; 1127 if ( 'post_type' === $this->value['type'] ) { 1128 $original_title = get_the_title( $this->value['object_id'] ); 1129 } elseif ( 'taxonomy' === $this->value['type'] ) { 1130 $original_title = get_term_field( 'name', $this->value['object_id'], $this->value['object'], 'raw' ); 1131 if ( is_wp_error( $original_title ) ) { 1132 $original_title = ''; 1133 } 1134 } 1135 $this->value['original_title'] = html_entity_decode( $original_title, ENT_QUOTES, get_bloginfo( 'charset' ) ); 1136 } 1137 1138 if ( ! isset( $this->value['nav_menu_term_id'] ) && $this->post_id > 0 ) { 1139 $menus = wp_get_post_terms( $this->post_id, WP_Customize_Nav_Menu_Setting::TAXONOMY, array( 1140 'fields' => 'ids', 1141 ) ); 1142 if ( ! empty( $menus ) ) { 1143 $this->value['nav_menu_term_id'] = array_shift( $menus ); 1144 } else { 1145 $this->value['nav_menu_term_id'] = 0; 1146 } 1147 } 1148 1149 foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) { 1150 if ( ! is_int( $this->value[ $key ] ) ) { 1151 $this->value[ $key ] = intval( $this->value[ $key ] ); 1152 } 1153 } 1154 foreach ( array( 'classes', 'xfn' ) as $key ) { 1155 if ( is_array( $this->value[ $key ] ) ) { 1156 $this->value[ $key ] = implode( ' ', $this->value[ $key ] ); 1157 } 1158 } 1159 1160 if ( ! isset( $this->value['_invalid'] ) ) { 1161 $this->value['_invalid'] = ( 1162 ( 'post_type' === $this->value['type'] && ! post_type_exists( $this->value['object'] ) ) 1163 || 1164 ( 'taxonomy' === $this->value['type'] && ! taxonomy_exists( $this->value['object'] ) ) 1165 ); 1166 } 1167 1168 // Remove remaining properties available on a setup nav_menu_item post object which aren't relevant to the setting value. 1169 $irrelevant_properties = array( 1170 'ID', 1171 'comment_count', 1172 'comment_status', 1173 'db_id', 1174 'filter', 1175 'guid', 1176 'ping_status', 1177 'pinged', 1178 'post_author', 1179 'post_content', 1180 'post_content_filtered', 1181 'post_date', 1182 'post_date_gmt', 1183 'post_excerpt', 1184 'post_mime_type', 1185 'post_modified', 1186 'post_modified_gmt', 1187 'post_name', 1188 'post_parent', 1189 'post_password', 1190 'post_title', 1191 'post_type', 1192 'to_ping', 1193 ); 1194 foreach ( $irrelevant_properties as $property ) { 1195 unset( $this->value[ $property ] ); 1196 } 1197 } 1198 1199 /** 1200 * Handle previewing the setting. 1201 * 1202 * @since 4.3.0 1203 * @since 4.4.0 Added boolean return value. 1204 * @access public 1205 * 1206 * @see WP_Customize_Manager::post_value() 1207 * 1208 * @return bool False if method short-circuited due to no-op. 1209 */ 1210 public function preview() { 1211 if ( $this->is_previewed ) { 1212 return false; 1213 } 1214 1215 $undefined = new stdClass(); 1216 $is_placeholder = ( $this->post_id < 0 ); 1217 $is_dirty = ( $undefined !== $this->post_value( $undefined ) ); 1218 if ( ! $is_placeholder && ! $is_dirty ) { 1219 return false; 1220 } 1221 1222 $this->is_previewed = true; 1223 $this->_original_value = $this->value(); 1224 $this->original_nav_menu_term_id = $this->_original_value['nav_menu_term_id']; 1225 $this->_previewed_blog_id = get_current_blog_id(); 1226 1227 add_filter( 'wp_get_nav_menu_items', array( $this, 'filter_wp_get_nav_menu_items' ), 10, 3 ); 1228 1229 $sort_callback = array( __CLASS__, 'sort_wp_get_nav_menu_items' ); 1230 if ( ! has_filter( 'wp_get_nav_menu_items', $sort_callback ) ) { 1231 add_filter( 'wp_get_nav_menu_items', array( __CLASS__, 'sort_wp_get_nav_menu_items' ), 1000, 3 ); 1232 } 1233 1234 // @todo Add get_post_metadata filters for plugins to add their data. 1235 1236 return true; 1237 } 1238 1239 /** 1240 * Filter the wp_get_nav_menu_items() result to supply the previewed menu items. 1241 * 1242 * @since 4.3.0 1243 * @access public 1244 * 1245 * @see wp_get_nav_menu_items() 1246 * 1247 * @param array $items An array of menu item post objects. 1248 * @param object $menu The menu object. 1249 * @param array $args An array of arguments used to retrieve menu item objects. 1250 * @return array Array of menu items, 1251 */ 1252 public function filter_wp_get_nav_menu_items( $items, $menu, $args ) { 1253 $this_item = $this->value(); 1254 $current_nav_menu_term_id = $this_item['nav_menu_term_id']; 1255 unset( $this_item['nav_menu_term_id'] ); 1256 1257 $should_filter = ( 1258 $menu->term_id === $this->original_nav_menu_term_id 1259 || 1260 $menu->term_id === $current_nav_menu_term_id 1261 ); 1262 if ( ! $should_filter ) { 1263 return $items; 1264 } 1265 1266 // Handle deleted menu item, or menu item moved to another menu. 1267 $should_remove = ( 1268 false === $this_item 1269 || 1270 true === $this_item['_invalid'] 1271 || 1272 ( 1273 $this->original_nav_menu_term_id === $menu->term_id 1274 && 1275 $current_nav_menu_term_id !== $this->original_nav_menu_term_id 1276 ) 1277 ); 1278 if ( $should_remove ) { 1279 $filtered_items = array(); 1280 foreach ( $items as $item ) { 1281 if ( $item->db_id !== $this->post_id ) { 1282 $filtered_items[] = $item; 1283 } 1284 } 1285 return $filtered_items; 1286 } 1287 1288 $mutated = false; 1289 $should_update = ( 1290 is_array( $this_item ) 1291 && 1292 $current_nav_menu_term_id === $menu->term_id 1293 ); 1294 if ( $should_update ) { 1295 foreach ( $items as $item ) { 1296 if ( $item->db_id === $this->post_id ) { 1297 foreach ( get_object_vars( $this->value_as_wp_post_nav_menu_item() ) as $key => $value ) { 1298 $item->$key = $value; 1299 } 1300 $mutated = true; 1301 } 1302 } 1303 1304 // Not found so we have to append it.. 1305 if ( ! $mutated ) { 1306 $items[] = $this->value_as_wp_post_nav_menu_item(); 1307 } 1308 } 1309 1310 return $items; 1311 } 1312 1313 /** 1314 * Re-apply the tail logic also applied on $items by wp_get_nav_menu_items(). 1315 * 1316 * @since 4.3.0 1317 * @access public 1318 * @static 1319 * 1320 * @see wp_get_nav_menu_items() 1321 * 1322 * @param array $items An array of menu item post objects. 1323 * @param object $menu The menu object. 1324 * @param array $args An array of arguments used to retrieve menu item objects. 1325 * @return array Array of menu items, 1326 */ 1327 public static function sort_wp_get_nav_menu_items( $items, $menu, $args ) { 1328 // @todo We should probably re-apply some constraints imposed by $args. 1329 unset( $args['include'] ); 1330 1331 // Remove invalid items only in frontend. 1332 if ( ! is_admin() ) { 1333 $items = array_filter( $items, '_is_valid_nav_menu_item' ); 1334 } 1335 1336 if ( ARRAY_A === $args['output'] ) { 1337 $GLOBALS['_menu_item_sort_prop'] = $args['output_key']; 1338 usort( $items, '_sort_nav_menu_items' ); 1339 $i = 1; 1340 1341 foreach ( $items as $k => $item ) { 1342 $items[ $k ]->{$args['output_key']} = $i++; 1343 } 1344 } 1345 1346 return $items; 1347 } 1348 1349 /** 1350 * Get the value emulated into a WP_Post and set up as a nav_menu_item. 1351 * 1352 * @since 4.3.0 1353 * @access public 1354 * 1355 * @return WP_Post With wp_setup_nav_menu_item() applied. 1356 */ 1357 public function value_as_wp_post_nav_menu_item() { 1358 $item = (object) $this->value(); 1359 unset( $item->nav_menu_term_id ); 1360 1361 $item->post_status = $item->status; 1362 unset( $item->status ); 1363 1364 $item->post_type = 'nav_menu_item'; 1365 $item->menu_order = $item->position; 1366 unset( $item->position ); 1367 1368 if ( $item->title ) { 1369 $item->post_title = $item->title; 1370 } 1371 1372 $item->ID = $this->post_id; 1373 $item->db_id = $this->post_id; 1374 $post = new WP_Post( (object) $item ); 1375 1376 if ( empty( $post->post_author ) ) { 1377 $post->post_author = get_current_user_id(); 1378 } 1379 1380 if ( ! isset( $post->type_label ) ) { 1381 if ( 'post_type' === $post->type ) { 1382 $object = get_post_type_object( $post->object ); 1383 if ( $object ) { 1384 $post->type_label = $object->labels->singular_name; 1385 } else { 1386 $post->type_label = $post->object; 1387 } 1388 } elseif ( 'taxonomy' == $post->type ) { 1389 $object = get_taxonomy( $post->object ); 1390 if ( $object ) { 1391 $post->type_label = $object->labels->singular_name; 1392 } else { 1393 $post->type_label = $post->object; 1394 } 1395 } else { 1396 $post->type_label = __( 'Custom Link' ); 1397 } 1398 } 1399 1400 return $post; 1401 } 1402 1403 /** 1404 * Sanitize an input. 1405 * 1406 * Note that parent::sanitize() erroneously does wp_unslash() on $value, but 1407 * we remove that in this override. 1408 * 1409 * @since 4.3.0 1410 * @access public 1411 * 1412 * @param array $menu_item_value The value to sanitize. 1413 * @return array|false|null Null if an input isn't valid. False if it is marked for deletion. 1414 * Otherwise the sanitized value. 1415 */ 1416 public function sanitize( $menu_item_value ) { 1417 // Menu is marked for deletion. 1418 if ( false === $menu_item_value ) { 1419 return $menu_item_value; 1420 } 1421 1422 // Invalid. 1423 if ( ! is_array( $menu_item_value ) ) { 1424 return null; 1425 } 1426 1427 $default = array( 1428 'object_id' => 0, 1429 'object' => '', 1430 'menu_item_parent' => 0, 1431 'position' => 0, 1432 'type' => 'custom', 1433 'title' => '', 1434 'url' => '', 1435 'target' => '', 1436 'attr_title' => '', 1437 'description' => '', 1438 'classes' => '', 1439 'xfn' => '', 1440 'status' => 'publish', 1441 'original_title' => '', 1442 'nav_menu_term_id' => 0, 1443 '_invalid' => false, 1444 ); 1445 $menu_item_value = array_merge( $default, $menu_item_value ); 1446 $menu_item_value = wp_array_slice_assoc( $menu_item_value, array_keys( $default ) ); 1447 $menu_item_value['position'] = max( 0, intval( $menu_item_value['position'] ) ); 1448 1449 foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) { 1450 // Note we need to allow negative-integer IDs for previewed objects not inserted yet. 1451 $menu_item_value[ $key ] = intval( $menu_item_value[ $key ] ); 1452 } 1453 1454 foreach ( array( 'type', 'object', 'target' ) as $key ) { 1455 $menu_item_value[ $key ] = sanitize_key( $menu_item_value[ $key ] ); 1456 } 1457 1458 foreach ( array( 'xfn', 'classes' ) as $key ) { 1459 $value = $menu_item_value[ $key ]; 1460 if ( ! is_array( $value ) ) { 1461 $value = explode( ' ', $value ); 1462 } 1463 $menu_item_value[ $key ] = implode( ' ', array_map( 'sanitize_html_class', $value ) ); 1464 } 1465 1466 foreach ( array( 'title', 'attr_title', 'description', 'original_title' ) as $key ) { 1467 // @todo Should esc_attr() the attr_title as well? 1468 $menu_item_value[ $key ] = sanitize_text_field( $menu_item_value[ $key ] ); 1469 } 1470 1471 $menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] ); 1472 if ( ! get_post_status_object( $menu_item_value['status'] ) ) { 1473 $menu_item_value['status'] = 'publish'; 1474 } 1475 1476 $menu_item_value['_invalid'] = (bool) $menu_item_value['_invalid']; 1477 1478 /** This filter is documented in wp-includes/class-wp-customize-setting.php */ 1479 return apply_filters( "customize_sanitize_{$this->id}", $menu_item_value, $this ); 1480 } 1481 1482 /** 1483 * Create/update the nav_menu_item post for this setting. 1484 * 1485 * Any created menu items will have their assigned post IDs exported to the client 1486 * via the customize_save_response filter. Likewise, any errors will be exported 1487 * to the client via the customize_save_response() filter. 1488 * 1489 * To delete a menu, the client can send false as the value. 1490 * 1491 * @since 4.3.0 1492 * @access protected 1493 * 1494 * @see wp_update_nav_menu_item() 1495 * 1496 * @param array|false $value The menu item array to update. If false, then the menu item will be deleted 1497 * entirely. See WP_Customize_Nav_Menu_Item_Setting::$default for what the value 1498 * should consist of. 1499 * @return null|void 1500 */ 1501 protected function update( $value ) { 1502 if ( $this->is_updated ) { 1503 return; 1504 } 1505 1506 $this->is_updated = true; 1507 $is_placeholder = ( $this->post_id < 0 ); 1508 $is_delete = ( false === $value ); 1509 1510 // Update the cached value. 1511 $this->value = $value; 1512 1513 add_filter( 'customize_save_response', array( $this, 'amend_customize_save_response' ) ); 1514 1515 if ( $is_delete ) { 1516 // If the current setting post is a placeholder, a delete request is a no-op. 1517 if ( $is_placeholder ) { 1518 $this->update_status = 'deleted'; 1519 } else { 1520 $r = wp_delete_post( $this->post_id, true ); 1521 1522 if ( false === $r ) { 1523 $this->update_error = new WP_Error( 'delete_failure' ); 1524 $this->update_status = 'error'; 1525 } else { 1526 $this->update_status = 'deleted'; 1527 } 1528 // @todo send back the IDs for all associated nav menu items deleted, so these settings (and controls) can be removed from Customizer? 1529 } 1530 } else { 1531 1532 // Handle saving menu items for menus that are being newly-created. 1533 if ( $value['nav_menu_term_id'] < 0 ) { 1534 $nav_menu_setting_id = sprintf( 'nav_menu[%s]', $value['nav_menu_term_id'] ); 1535 $nav_menu_setting = $this->manager->get_setting( $nav_menu_setting_id ); 1536 1537 if ( ! $nav_menu_setting || ! ( $nav_menu_setting instanceof WP_Customize_Nav_Menu_Setting ) ) { 1538 $this->update_status = 'error'; 1539 $this->update_error = new WP_Error( 'unexpected_nav_menu_setting' ); 1540 return; 1541 } 1542 1543 if ( false === $nav_menu_setting->save() ) { 1544 $this->update_status = 'error'; 1545 $this->update_error = new WP_Error( 'nav_menu_setting_failure' ); 1546 return; 1547 } 1548 1549 if ( $nav_menu_setting->previous_term_id !== intval( $value['nav_menu_term_id'] ) ) { 1550 $this->update_status = 'error'; 1551 $this->update_error = new WP_Error( 'unexpected_previous_term_id' ); 1552 return; 1553 } 1554 1555 $value['nav_menu_term_id'] = $nav_menu_setting->term_id; 1556 } 1557 1558 // Handle saving a nav menu item that is a child of a nav menu item being newly-created. 1559 if ( $value['menu_item_parent'] < 0 ) { 1560 $parent_nav_menu_item_setting_id = sprintf( 'nav_menu_item[%s]', $value['menu_item_parent'] ); 1561 $parent_nav_menu_item_setting = $this->manager->get_setting( $parent_nav_menu_item_setting_id ); 1562 1563 if ( ! $parent_nav_menu_item_setting || ! ( $parent_nav_menu_item_setting instanceof WP_Customize_Nav_Menu_Item_Setting ) ) { 1564 $this->update_status = 'error'; 1565 $this->update_error = new WP_Error( 'unexpected_nav_menu_item_setting' ); 1566 return; 1567 } 1568 1569 if ( false === $parent_nav_menu_item_setting->save() ) { 1570 $this->update_status = 'error'; 1571 $this->update_error = new WP_Error( 'nav_menu_item_setting_failure' ); 1572 return; 1573 } 1574 1575 if ( $parent_nav_menu_item_setting->previous_post_id !== intval( $value['menu_item_parent'] ) ) { 1576 $this->update_status = 'error'; 1577 $this->update_error = new WP_Error( 'unexpected_previous_post_id' ); 1578 return; 1579 } 1580 1581 $value['menu_item_parent'] = $parent_nav_menu_item_setting->post_id; 1582 } 1583 1584 // Insert or update menu. 1585 $menu_item_data = array( 1586 'menu-item-object-id' => $value['object_id'], 1587 'menu-item-object' => $value['object'], 1588 'menu-item-parent-id' => $value['menu_item_parent'], 1589 'menu-item-position' => $value['position'], 1590 'menu-item-type' => $value['type'], 1591 'menu-item-title' => $value['title'], 1592 'menu-item-url' => $value['url'], 1593 'menu-item-description' => $value['description'], 1594 'menu-item-attr-title' => $value['attr_title'], 1595 'menu-item-target' => $value['target'], 1596 'menu-item-classes' => $value['classes'], 1597 'menu-item-xfn' => $value['xfn'], 1598 'menu-item-status' => $value['status'], 1599 ); 1600 1601 $r = wp_update_nav_menu_item( 1602 $value['nav_menu_term_id'], 1603 $is_placeholder ? 0 : $this->post_id, 1604 $menu_item_data 1605 ); 1606 1607 if ( is_wp_error( $r ) ) { 1608 $this->update_status = 'error'; 1609 $this->update_error = $r; 1610 } else { 1611 if ( $is_placeholder ) { 1612 $this->previous_post_id = $this->post_id; 1613 $this->post_id = $r; 1614 $this->update_status = 'inserted'; 1615 } else { 1616 $this->update_status = 'updated'; 1617 } 1618 } 1619 } 1620 1621 } 1622 1623 /** 1624 * Export data for the JS client. 1625 * 1626 * @since 4.3.0 1627 * @access public 1628 * 1629 * @see WP_Customize_Nav_Menu_Item_Setting::update() 1630 * 1631 * @param array $data Additional information passed back to the 'saved' event on `wp.customize`. 1632 * @return array Save response data. 1633 */ 1634 public function amend_customize_save_response( $data ) { 1635 if ( ! isset( $data['nav_menu_item_updates'] ) ) { 1636 $data['nav_menu_item_updates'] = array(); 1637 } 1638 1639 $data['nav_menu_item_updates'][] = array( 1640 'post_id' => $this->post_id, 1641 'previous_post_id' => $this->previous_post_id, 1642 'error' => $this->update_error ? $this->update_error->get_error_code() : null, 1643 'status' => $this->update_status, 1644 ); 1645 return $data; 1646 } 1647 } 1648 1649 /** 1650 * Customize Setting to represent a nav_menu. 1651 * 1652 * Subclass of WP_Customize_Setting to represent a nav_menu taxonomy term, and 1653 * the IDs for the nav_menu_items associated with the nav menu. 1654 * 1655 * @since 4.3.0 1656 * 1657 * @see wp_get_nav_menu_object() 1658 * @see WP_Customize_Setting 1659 */ 1660 class WP_Customize_Nav_Menu_Setting extends WP_Customize_Setting { 1661 1662 const ID_PATTERN = '/^nav_menu\[(?P<id>-?\d+)\]$/'; 1663 1664 const TAXONOMY = 'nav_menu'; 1665 1666 const TYPE = 'nav_menu'; 1667 1668 /** 1669 * Setting type. 1670 * 1671 * @since 4.3.0 1672 * @access public 1673 * @var string 1674 */ 1675 public $type = self::TYPE; 1676 1677 /** 1678 * Default setting value. 1679 * 1680 * @since 4.3.0 1681 * @access public 1682 * @var array 1683 * 1684 * @see wp_get_nav_menu_object() 1685 */ 1686 public $default = array( 1687 'name' => '', 1688 'description' => '', 1689 'parent' => 0, 1690 'auto_add' => false, 1691 ); 1692 1693 /** 1694 * Default transport. 1695 * 1696 * @since 4.3.0 1697 * @access public 1698 * @var string 1699 */ 1700 public $transport = 'postMessage'; 1701 1702 /** 1703 * The term ID represented by this setting instance. 1704 * 1705 * A negative value represents a placeholder ID for a new menu not yet saved. 1706 * 1707 * @since 4.3.0 1708 * @access public 1709 * @var int 1710 */ 1711 public $term_id; 1712 1713 /** 1714 * Previous (placeholder) term ID used before creating a new menu. 1715 * 1716 * This value will be exported to JS via the customize_save_response filter 1717 * so that JavaScript can update the settings to refer to the newly-assigned 1718 * term ID. This value is always negative to indicate it does not refer to 1719 * a real term. 1720 * 1721 * @since 4.3.0 1722 * @access public 1723 * @var int 1724 * 1725 * @see WP_Customize_Nav_Menu_Setting::update() 1726 * @see WP_Customize_Nav_Menu_Setting::amend_customize_save_response() 1727 */ 1728 public $previous_term_id; 1729 1730 /** 1731 * Whether or not preview() was called. 1732 * 1733 * @since 4.3.0 1734 * @access protected 1735 * @var bool 1736 */ 1737 protected $is_previewed = false; 1738 1739 /** 1740 * Whether or not update() was called. 1741 * 1742 * @since 4.3.0 1743 * @access protected 1744 * @var bool 1745 */ 1746 protected $is_updated = false; 1747 1748 /** 1749 * Status for calling the update method, used in customize_save_response filter. 1750 * 1751 * When status is inserted, the placeholder term ID is stored in $previous_term_id. 1752 * When status is error, the error is stored in $update_error. 1753 * 1754 * @since 4.3.0 1755 * @access public 1756 * @var string updated|inserted|deleted|error 1757 * 1758 * @see WP_Customize_Nav_Menu_Setting::update() 1759 * @see WP_Customize_Nav_Menu_Setting::amend_customize_save_response() 1760 */ 1761 public $update_status; 1762 1763 /** 1764 * Any error object returned by wp_update_nav_menu_object() when setting is updated. 1765 * 1766 * @since 4.3.0 1767 * @access public 1768 * @var WP_Error 1769 * 1770 * @see WP_Customize_Nav_Menu_Setting::update() 1771 * @see WP_Customize_Nav_Menu_Setting::amend_customize_save_response() 1772 */ 1773 public $update_error; 1774 1775 /** 1776 * Constructor. 1777 * 1778 * Any supplied $args override class property defaults. 1779 * 1780 * @since 4.3.0 1781 * @access public 1782 * 1783 * @param WP_Customize_Manager $manager Bootstrap Customizer instance. 1784 * @param string $id An specific ID of the setting. Can be a 1785 * theme mod or option name. 1786 * @param array $args Optional. Setting arguments. 1787 * 1788 * @throws Exception If $id is not valid for this setting type. 1789 */ 1790 public function __construct( WP_Customize_Manager $manager, $id, array $args = array() ) { 1791 if ( empty( $manager->nav_menus ) ) { 1792 throw new Exception( 'Expected WP_Customize_Manager::$nav_menus to be set.' ); 1793 } 1794 1795 if ( ! preg_match( self::ID_PATTERN, $id, $matches ) ) { 1796 throw new Exception( "Illegal widget setting ID: $id" ); 1797 } 1798 1799 $this->term_id = intval( $matches['id'] ); 1800 1801 parent::__construct( $manager, $id, $args ); 1802 } 1803 1804 /** 1805 * Get the instance data for a given widget setting. 1806 * 1807 * @since 4.3.0 1808 * @access public 1809 * 1810 * @see wp_get_nav_menu_object() 1811 * 1812 * @return array Instance data. 1813 */ 1814 public function value() { 1815 if ( $this->is_previewed && $this->_previewed_blog_id === get_current_blog_id() ) { 1816 $undefined = new stdClass(); // Symbol. 1817 $post_value = $this->post_value( $undefined ); 1818 1819 if ( $undefined === $post_value ) { 1820 $value = $this->_original_value; 1821 } else { 1822 $value = $post_value; 1823 } 1824 } else { 1825 $value = false; 1826 1827 // Note that a term_id of less than one indicates a nav_menu not yet inserted. 1828 if ( $this->term_id > 0 ) { 1829 $term = wp_get_nav_menu_object( $this->term_id ); 1830 1831 if ( $term ) { 1832 $value = wp_array_slice_assoc( (array) $term, array_keys( $this->default ) ); 1833 1834 $nav_menu_options = (array) get_option( 'nav_menu_options', array() ); 1835 $value['auto_add'] = false; 1836 1837 if ( isset( $nav_menu_options['auto_add'] ) && is_array( $nav_menu_options['auto_add'] ) ) { 1838 $value['auto_add'] = in_array( $term->term_id, $nav_menu_options['auto_add'] ); 1839 } 1840 } 1841 } 1842 1843 if ( ! is_array( $value ) ) { 1844 $value = $this->default; 1845 } 1846 } 1847 return $value; 1848 } 1849 1850 /** 1851 * Handle previewing the setting. 1852 * 1853 * @since 4.3.0 1854 * @since 4.4.0 Added boolean return value 1855 * @access public 1856 * 1857 * @see WP_Customize_Manager::post_value() 1858 * 1859 * @return bool False if method short-circuited due to no-op. 1860 */ 1861 public function preview() { 1862 if ( $this->is_previewed ) { 1863 return false; 1864 } 1865 1866 $undefined = new stdClass(); 1867 $is_placeholder = ( $this->term_id < 0 ); 1868 $is_dirty = ( $undefined !== $this->post_value( $undefined ) ); 1869 if ( ! $is_placeholder && ! $is_dirty ) { 1870 return false; 1871 } 1872 1873 $this->is_previewed = true; 1874 $this->_original_value = $this->value(); 1875 $this->_previewed_blog_id = get_current_blog_id(); 1876 1877 add_filter( 'wp_get_nav_menus', array( $this, 'filter_wp_get_nav_menus' ), 10, 2 ); 1878 add_filter( 'wp_get_nav_menu_object', array( $this, 'filter_wp_get_nav_menu_object' ), 10, 2 ); 1879 add_filter( 'default_option_nav_menu_options', array( $this, 'filter_nav_menu_options' ) ); 1880 add_filter( 'option_nav_menu_options', array( $this, 'filter_nav_menu_options' ) ); 1881 1882 return true; 1883 } 1884 1885 /** 1886 * Filter the wp_get_nav_menus() result to ensure the inserted menu object is included, and the deleted one is removed. 1887 * 1888 * @since 4.3.0 1889 * @access public 1890 * 1891 * @see wp_get_nav_menus() 1892 * 1893 * @param array $menus An array of menu objects. 1894 * @param array $args An array of arguments used to retrieve menu objects. 1895 * @return array 1896 */ 1897 public function filter_wp_get_nav_menus( $menus, $args ) { 1898 if ( get_current_blog_id() !== $this->_previewed_blog_id ) { 1899 return $menus; 1900 } 1901 1902 $setting_value = $this->value(); 1903 $is_delete = ( false === $setting_value ); 1904 $index = -1; 1905 1906 // Find the existing menu item's position in the list. 1907 foreach ( $menus as $i => $menu ) { 1908 if ( (int) $this->term_id === (int) $menu->term_id || (int) $this->previous_term_id === (int) $menu->term_id ) { 1909 $index = $i; 1910 break; 1911 } 1912 } 1913 1914 if ( $is_delete ) { 1915 // Handle deleted menu by removing it from the list. 1916 if ( -1 !== $index ) { 1917 array_splice( $menus, $index, 1 ); 1918 } 1919 } else { 1920 // Handle menus being updated or inserted. 1921 $menu_obj = (object) array_merge( array( 1922 'term_id' => $this->term_id, 1923 'term_taxonomy_id' => $this->term_id, 1924 'slug' => sanitize_title( $setting_value['name'] ), 1925 'count' => 0, 1926 'term_group' => 0, 1927 'taxonomy' => self::TAXONOMY, 1928 'filter' => 'raw', 1929 ), $setting_value ); 1930 1931 array_splice( $menus, $index, ( -1 === $index ? 0 : 1 ), array( $menu_obj ) ); 1932 } 1933 1934 // Make sure the menu objects get re-sorted after an update/insert. 1935 if ( ! $is_delete && ! empty( $args['orderby'] ) ) { 1936 $this->_current_menus_sort_orderby = $args['orderby']; 1937 usort( $menus, array( $this, '_sort_menus_by_orderby' ) ); 1938 } 1939 // @todo add support for $args['hide_empty'] === true 1940 1941 return $menus; 1942 } 1943 1944 /** 1945 * Temporary non-closure passing of orderby value to function. 1946 * 1947 * @since 4.3.0 1948 * @access protected 1949 * @var string 1950 * 1951 * @see WP_Customize_Nav_Menu_Setting::filter_wp_get_nav_menus() 1952 * @see WP_Customize_Nav_Menu_Setting::_sort_menus_by_orderby() 1953 */ 1954 protected $_current_menus_sort_orderby; 1955 1956 /** 1957 * Sort menu objects by the class-supplied orderby property. 1958 * 1959 * This is a workaround for a lack of closures. 1960 * 1961 * @since 4.3.0 1962 * @access protected 1963 * @param object $menu1 1964 * @param object $menu2 1965 * @return int 1966 * 1967 * @see WP_Customize_Nav_Menu_Setting::filter_wp_get_nav_menus() 1968 */ 1969 protected function _sort_menus_by_orderby( $menu1, $menu2 ) { 1970 $key = $this->_current_menus_sort_orderby; 1971 return strcmp( $menu1->$key, $menu2->$key ); 1972 } 1973 1974 /** 1975 * Filter the wp_get_nav_menu_object() result to supply the previewed menu object. 1976 * 1977 * Requesting a nav_menu object by anything but ID is not supported. 1978 * 1979 * @since 4.3.0 1980 * @access public 1981 * 1982 * @see wp_get_nav_menu_object() 1983 * 1984 * @param object|null $menu_obj Object returned by wp_get_nav_menu_object(). 1985 * @param string $menu_id ID of the nav_menu term. Requests by slug or name will be ignored. 1986 * @return object|null 1987 */ 1988 public function filter_wp_get_nav_menu_object( $menu_obj, $menu_id ) { 1989 $ok = ( 1990 get_current_blog_id() === $this->_previewed_blog_id 1991 && 1992 is_int( $menu_id ) 1993 && 1994 $menu_id === $this->term_id 1995 ); 1996 if ( ! $ok ) { 1997 return $menu_obj; 1998 } 1999 2000 $setting_value = $this->value(); 2001 2002 // Handle deleted menus. 2003 if ( false === $setting_value ) { 2004 return false; 2005 } 2006 2007 // Handle sanitization failure by preventing short-circuiting. 2008 if ( null === $setting_value ) { 2009 return $menu_obj; 2010 } 2011 2012 $menu_obj = (object) array_merge( array( 2013 'term_id' => $this->term_id, 2014 'term_taxonomy_id' => $this->term_id, 2015 'slug' => sanitize_title( $setting_value['name'] ), 2016 'count' => 0, 2017 'term_group' => 0, 2018 'taxonomy' => self::TAXONOMY, 2019 'filter' => 'raw', 2020 ), $setting_value ); 2021 2022 return $menu_obj; 2023 } 2024 2025 /** 2026 * Filter the nav_menu_options option to include this menu's auto_add preference. 2027 * 2028 * @since 4.3.0 2029 * @access public 2030 * 2031 * @param array $nav_menu_options Nav menu options including auto_add. 2032 * @return array (Kaybe) modified nav menu options. 2033 */ 2034 public function filter_nav_menu_options( $nav_menu_options ) { 2035 if ( $this->_previewed_blog_id !== get_current_blog_id() ) { 2036 return $nav_menu_options; 2037 } 2038 2039 $menu = $this->value(); 2040 $nav_menu_options = $this->filter_nav_menu_options_value( 2041 $nav_menu_options, 2042 $this->term_id, 2043 false === $menu ? false : $menu['auto_add'] 2044 ); 2045 2046 return $nav_menu_options; 2047 } 2048 2049 /** 2050 * Sanitize an input. 2051 * 2052 * Note that parent::sanitize() erroneously does wp_unslash() on $value, but 2053 * we remove that in this override. 2054 * 2055 * @since 4.3.0 2056 * @access public 2057 * 2058 * @param array $value The value to sanitize. 2059 * @return array|false|null Null if an input isn't valid. False if it is marked for deletion. 2060 * Otherwise the sanitized value. 2061 */ 2062 public function sanitize( $value ) { 2063 // Menu is marked for deletion. 2064 if ( false === $value ) { 2065 return $value; 2066 } 2067 2068 // Invalid. 2069 if ( ! is_array( $value ) ) { 2070 return null; 2071 } 2072 2073 $default = array( 2074 'name' => '', 2075 'description' => '', 2076 'parent' => 0, 2077 'auto_add' => false, 2078 ); 2079 $value = array_merge( $default, $value ); 2080 $value = wp_array_slice_assoc( $value, array_keys( $default ) ); 2081 2082 $value['name'] = trim( esc_html( $value['name'] ) ); // This sanitization code is used in wp-admin/nav-menus.php. 2083 $value['description'] = sanitize_text_field( $value['description'] ); 2084 $value['parent'] = max( 0, intval( $value['parent'] ) ); 2085 $value['auto_add'] = ! empty( $value['auto_add'] ); 2086 2087 if ( '' === $value['name'] ) { 2088 $value['name'] = _x( '(unnamed)', 'Missing menu name.' ); 2089 } 2090 2091 /** This filter is documented in wp-includes/class-wp-customize-setting.php */ 2092 return apply_filters( "customize_sanitize_{$this->id}", $value, $this ); 2093 } 2094 2095 /** 2096 * Storage for data to be sent back to client in customize_save_response filter. 2097 * 2098 * @access protected 2099 * @since 4.3.0 2100 * @var array 2101 * 2102 * @see WP_Customize_Nav_Menu_Setting::amend_customize_save_response() 2103 */ 2104 protected $_widget_nav_menu_updates = array(); 2105 2106 /** 2107 * Create/update the nav_menu term for this setting. 2108 * 2109 * Any created menus will have their assigned term IDs exported to the client 2110 * via the customize_save_response filter. Likewise, any errors will be exported 2111 * to the client via the customize_save_response() filter. 2112 * 2113 * To delete a menu, the client can send false as the value. 2114 * 2115 * @since 4.3.0 2116 * @access protected 2117 * 2118 * @see wp_update_nav_menu_object() 2119 * 2120 * @param array|false $value { 2121 * The value to update. Note that slug cannot be updated via wp_update_nav_menu_object(). 2122 * If false, then the menu will be deleted entirely. 2123 * 2124 * @type string $name The name of the menu to save. 2125 * @type string $description The term description. Default empty string. 2126 * @type int $parent The id of the parent term. Default 0. 2127 * @type bool $auto_add Whether pages will auto_add to this menu. Default false. 2128 * } 2129 * @return null|void 2130 */ 2131 protected function update( $value ) { 2132 if ( $this->is_updated ) { 2133 return; 2134 } 2135 2136 $this->is_updated = true; 2137 $is_placeholder = ( $this->term_id < 0 ); 2138 $is_delete = ( false === $value ); 2139 2140 add_filter( 'customize_save_response', array( $this, 'amend_customize_save_response' ) ); 2141 2142 $auto_add = null; 2143 if ( $is_delete ) { 2144 // If the current setting term is a placeholder, a delete request is a no-op. 2145 if ( $is_placeholder ) { 2146 $this->update_status = 'deleted'; 2147 } else { 2148 $r = wp_delete_nav_menu( $this->term_id ); 2149 2150 if ( is_wp_error( $r ) ) { 2151 $this->update_status = 'error'; 2152 $this->update_error = $r; 2153 } else { 2154 $this->update_status = 'deleted'; 2155 $auto_add = false; 2156 } 2157 } 2158 } else { 2159 // Insert or update menu. 2160 $menu_data = wp_array_slice_assoc( $value, array( 'description', 'parent' ) ); 2161 $menu_data['menu-name'] = $value['name']; 2162 2163 $menu_id = $is_placeholder ? 0 : $this->term_id; 2164 $r = wp_update_nav_menu_object( $menu_id, $menu_data ); 2165 $original_name = $menu_data['menu-name']; 2166 $name_conflict_suffix = 1; 2167 while ( is_wp_error( $r ) && 'menu_exists' === $r->get_error_code() ) { 2168 $name_conflict_suffix += 1; 2169 /* translators: 1: original menu name, 2: duplicate count */ 2170 $menu_data['menu-name'] = sprintf( __( '%1$s (%2$d)' ), $original_name, $name_conflict_suffix ); 2171 $r = wp_update_nav_menu_object( $menu_id, $menu_data ); 2172 } 2173 2174 if ( is_wp_error( $r ) ) { 2175 $this->update_status = 'error'; 2176 $this->update_error = $r; 2177 } else { 2178 if ( $is_placeholder ) { 2179 $this->previous_term_id = $this->term_id; 2180 $this->term_id = $r; 2181 $this->update_status = 'inserted'; 2182 } else { 2183 $this->update_status = 'updated'; 2184 } 2185 2186 $auto_add = $value['auto_add']; 2187 } 2188 } 2189 2190 if ( null !== $auto_add ) { 2191 $nav_menu_options = $this->filter_nav_menu_options_value( 2192 (array) get_option( 'nav_menu_options', array() ), 2193 $this->term_id, 2194 $auto_add 2195 ); 2196 update_option( 'nav_menu_options', $nav_menu_options ); 2197 } 2198 2199 if ( 'inserted' === $this->update_status ) { 2200 // Make sure that new menus assigned to nav menu locations use their new IDs. 2201 foreach ( $this->manager->settings() as $setting ) { 2202 if ( ! preg_match( '/^nav_menu_locations\[/', $setting->id ) ) { 2203 continue; 2204 } 2205 2206 $post_value = $setting->post_value( null ); 2207 if ( ! is_null( $post_value ) && $this->previous_term_id === intval( $post_value ) ) { 2208 $this->manager->set_post_value( $setting->id, $this->term_id ); 2209 $setting->save(); 2210 } 2211 } 2212 2213 // Make sure that any nav_menu widgets referencing the placeholder nav menu get updated and sent back to client. 2214 foreach ( array_keys( $this->manager->unsanitized_post_values() ) as $setting_id ) { 2215 $nav_menu_widget_setting = $this->manager->get_setting( $setting_id ); 2216 if ( ! $nav_menu_widget_setting || ! preg_match( '/^widget_nav_menu\[/', $nav_menu_widget_setting->id ) ) { 2217 continue; 2218 } 2219 2220 $widget_instance = $nav_menu_widget_setting->post_value(); // Note that this calls WP_Customize_Widgets::sanitize_widget_instance(). 2221 if ( empty( $widget_instance['nav_menu'] ) || intval( $widget_instance['nav_menu'] ) !== $this->previous_term_id ) { 2222 continue; 2223 } 2224 2225 $widget_instance['nav_menu'] = $this->term_id; 2226 $updated_widget_instance = $this->manager->widgets->sanitize_widget_js_instance( $widget_instance ); 2227 $this->manager->set_post_value( $nav_menu_widget_setting->id, $updated_widget_instance ); 2228 $nav_menu_widget_setting->save(); 2229 2230 $this->_widget_nav_menu_updates[ $nav_menu_widget_setting->id ] = $updated_widget_instance; 2231 } 2232 } 2233 } 2234 2235 /** 2236 * Updates a nav_menu_options array. 2237 * 2238 * @since 4.3.0 2239 * @access protected 2240 * 2241 * @see WP_Customize_Nav_Menu_Setting::filter_nav_menu_options() 2242 * @see WP_Customize_Nav_Menu_Setting::update() 2243 * 2244 * @param array $nav_menu_options Array as returned by get_option( 'nav_menu_options' ). 2245 * @param int $menu_id The term ID for the given menu. 2246 * @param bool $auto_add Whether to auto-add or not. 2247 * @return array (Maybe) modified nav_menu_otions array. 2248 */ 2249 protected function filter_nav_menu_options_value( $nav_menu_options, $menu_id, $auto_add ) { 2250 $nav_menu_options = (array) $nav_menu_options; 2251 if ( ! isset( $nav_menu_options['auto_add'] ) ) { 2252 $nav_menu_options['auto_add'] = array(); 2253 } 2254 2255 $i = array_search( $menu_id, $nav_menu_options['auto_add'] ); 2256 if ( $auto_add && false === $i ) { 2257 array_push( $nav_menu_options['auto_add'], $this->term_id ); 2258 } elseif ( ! $auto_add && false !== $i ) { 2259 array_splice( $nav_menu_options['auto_add'], $i, 1 ); 2260 } 2261 2262 return $nav_menu_options; 2263 } 2264 2265 /** 2266 * Export data for the JS client. 2267 * 2268 * @since 4.3.0 2269 * @access public 2270 * 2271 * @see WP_Customize_Nav_Menu_Setting::update() 2272 * 2273 * @param array $data Additional information passed back to the 'saved' event on `wp.customize`. 2274 * @return array Export data. 2275 */ 2276 public function amend_customize_save_response( $data ) { 2277 if ( ! isset( $data['nav_menu_updates'] ) ) { 2278 $data['nav_menu_updates'] = array(); 2279 } 2280 if ( ! isset( $data['widget_nav_menu_updates'] ) ) { 2281 $data['widget_nav_menu_updates'] = array(); 2282 } 2283 2284 $data['nav_menu_updates'][] = array( 2285 'term_id' => $this->term_id, 2286 'previous_term_id' => $this->previous_term_id, 2287 'error' => $this->update_error ? $this->update_error->get_error_code() : null, 2288 'status' => $this->update_status, 2289 'saved_value' => 'deleted' === $this->update_status ? null : $this->value(), 2290 ); 2291 2292 $data['widget_nav_menu_updates'] = array_merge( 2293 $data['widget_nav_menu_updates'], 2294 $this->_widget_nav_menu_updates 2295 ); 2296 $this->_widget_nav_menu_updates = array(); 2297 2298 return $data; 2299 } 2300 } 772 /** WP_Customize_Filter_Setting class */ 773 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-filter-setting.php' ); 774 775 /** WP_Customize_Header_Image_Setting class */ 776 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-setting.php' ); 777 778 /** WP_Customize_Background_Image_Setting class */ 779 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-setting.php' ); 780 781 /** WP_Customize_Nav_Menu_Item_Setting class */ 782 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-setting.php' ); 783 784 /** WP_Customize_Nav_Menu_Setting class */ 785 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-setting.php' );
Note: See TracChangeset
for help on using the changeset viewer.