WP_Customize_Nav_Menu_Item_Setting

Customize Setting to represent a nav_menu.

Defined (1)

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

/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php  
  1. class WP_Customize_Nav_Menu_Item_Setting extends WP_Customize_Setting { 
  2.  
  3. const ID_PATTERN = '/^nav_menu_item\[(?P<id>-?\d+)\]$/'; 
  4.  
  5. const POST_TYPE = 'nav_menu_item'; 
  6.  
  7. const TYPE = 'nav_menu_item'; 
  8.  
  9. /** 
  10. * Setting type. 
  11. * @since 4.3.0 
  12. * @access public 
  13. * @var string 
  14. */ 
  15. public $type = self::TYPE; 
  16.  
  17. /** 
  18. * Default setting value. 
  19. * @since 4.3.0 
  20. * @access public 
  21. * @var array 
  22. * @see wp_setup_nav_menu_item() 
  23. */ 
  24. public $default = array( 
  25. // The $menu_item_data for wp_update_nav_menu_item(). 
  26. 'object_id' => 0,  
  27. 'object' => '', // Taxonomy name. 
  28. 'menu_item_parent' => 0, // A.K.A. menu-item-parent-id; note that post_parent is different, and not included. 
  29. 'position' => 0, // A.K.A. menu_order. 
  30. 'type' => 'custom', // Note that type_label is not included here. 
  31. 'title' => '',  
  32. 'url' => '',  
  33. 'target' => '',  
  34. 'attr_title' => '',  
  35. 'description' => '',  
  36. 'classes' => '',  
  37. 'xfn' => '',  
  38. 'status' => 'publish',  
  39. 'original_title' => '',  
  40. 'nav_menu_term_id' => 0, // This will be supplied as the $menu_id arg for wp_update_nav_menu_item(). 
  41. '_invalid' => false,  
  42. ); 
  43.  
  44. /** 
  45. * Default transport. 
  46. * @since 4.3.0 
  47. * @since 4.5.0 Default changed to 'refresh' 
  48. * @access public 
  49. * @var string 
  50. */ 
  51. public $transport = 'refresh'; 
  52.  
  53. /** 
  54. * The post ID represented by this setting instance. This is the db_id. 
  55. * A negative value represents a placeholder ID for a new menu not yet saved. 
  56. * @since 4.3.0 
  57. * @access public 
  58. * @var int 
  59. */ 
  60. public $post_id; 
  61.  
  62. /** 
  63. * Storage of pre-setup menu item to prevent wasted calls to wp_setup_nav_menu_item(). 
  64. * @since 4.3.0 
  65. * @access protected 
  66. * @var array 
  67. */ 
  68. protected $value; 
  69.  
  70. /** 
  71. * Previous (placeholder) post ID used before creating a new menu item. 
  72. * This value will be exported to JS via the customize_save_response filter 
  73. * so that JavaScript can update the settings to refer to the newly-assigned 
  74. * post ID. This value is always negative to indicate it does not refer to 
  75. * a real post. 
  76. * @since 4.3.0 
  77. * @access public 
  78. * @var int 
  79. * @see WP_Customize_Nav_Menu_Item_Setting::update() 
  80. * @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response() 
  81. */ 
  82. public $previous_post_id; 
  83.  
  84. /** 
  85. * When previewing or updating a menu item, this stores the previous nav_menu_term_id 
  86. * which ensures that we can apply the proper filters. 
  87. * @since 4.3.0 
  88. * @access public 
  89. * @var int 
  90. */ 
  91. public $original_nav_menu_term_id; 
  92.  
  93. /** 
  94. * Whether or not update() was called. 
  95. * @since 4.3.0 
  96. * @access protected 
  97. * @var bool 
  98. */ 
  99. protected $is_updated = false; 
  100.  
  101. /** 
  102. * Status for calling the update method, used in customize_save_response filter. 
  103. * See {@see 'customize_save_response'}. 
  104. * When status is inserted, the placeholder post ID is stored in $previous_post_id. 
  105. * When status is error, the error is stored in $update_error. 
  106. * @since 4.3.0 
  107. * @access public 
  108. * @var string updated|inserted|deleted|error 
  109. * @see WP_Customize_Nav_Menu_Item_Setting::update() 
  110. * @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response() 
  111. */ 
  112. public $update_status; 
  113.  
  114. /** 
  115. * Any error object returned by wp_update_nav_menu_item() when setting is updated. 
  116. * @since 4.3.0 
  117. * @access public 
  118. * @var WP_Error 
  119. * @see WP_Customize_Nav_Menu_Item_Setting::update() 
  120. * @see WP_Customize_Nav_Menu_Item_Setting::amend_customize_save_response() 
  121. */ 
  122. public $update_error; 
  123.  
  124. /** 
  125. * Constructor. 
  126. * Any supplied $args override class property defaults. 
  127. * @since 4.3.0 
  128. * @access public 
  129. * @param WP_Customize_Manager $manager Bootstrap Customizer instance. 
  130. * @param string $id An specific ID of the setting. Can be a 
  131. * theme mod or option name. 
  132. * @param array $args Optional. Setting arguments. 
  133. * @throws Exception If $id is not valid for this setting type. 
  134. */ 
  135. public function __construct( WP_Customize_Manager $manager, $id, array $args = array() ) { 
  136. if ( empty( $manager->nav_menus ) ) { 
  137. throw new Exception( 'Expected WP_Customize_Manager::$nav_menus to be set.' ); 
  138.  
  139. if ( ! preg_match( self::ID_PATTERN, $id, $matches ) ) { 
  140. throw new Exception( "Illegal widget setting ID: $id" ); 
  141.  
  142. $this->post_id = intval( $matches['id'] ); 
  143. add_action( 'wp_update_nav_menu_item', array( $this, 'flush_cached_value' ), 10, 2 ); 
  144.  
  145. parent::__construct( $manager, $id, $args ); 
  146.  
  147. // Ensure that an initially-supplied value is valid. 
  148. if ( isset( $this->value ) ) { 
  149. $this->populate_value(); 
  150. foreach ( array_diff( array_keys( $this->default ), array_keys( $this->value ) ) as $missing ) { 
  151. throw new Exception( "Supplied nav_menu_item value missing property: $missing" ); 
  152.  
  153.  
  154. /** 
  155. * Clear the cached value when this nav menu item is updated. 
  156. * @since 4.3.0 
  157. * @access public 
  158. * @param int $menu_id The term ID for the menu. 
  159. * @param int $menu_item_id The post ID for the menu item. 
  160. */ 
  161. public function flush_cached_value( $menu_id, $menu_item_id ) { 
  162. unset( $menu_id ); 
  163. if ( $menu_item_id === $this->post_id ) { 
  164. $this->value = null; 
  165.  
  166. /** 
  167. * Get the instance data for a given nav_menu_item setting. 
  168. * @since 4.3.0 
  169. * @access public 
  170. * @see wp_setup_nav_menu_item() 
  171. * @return array|false Instance data array, or false if the item is marked for deletion. 
  172. */ 
  173. public function value() { 
  174. if ( $this->is_previewed && $this->_previewed_blog_id === get_current_blog_id() ) { 
  175. $undefined = new stdClass(); // Symbol. 
  176. $post_value = $this->post_value( $undefined ); 
  177.  
  178. if ( $undefined === $post_value ) { 
  179. $value = $this->_original_value; 
  180. } else { 
  181. $value = $post_value; 
  182. } else if ( isset( $this->value ) ) { 
  183. $value = $this->value; 
  184. } else { 
  185. $value = false; 
  186.  
  187. // Note that a ID of less than one indicates a nav_menu not yet inserted. 
  188. if ( $this->post_id > 0 ) { 
  189. $post = get_post( $this->post_id ); 
  190. if ( $post && self::POST_TYPE === $post->post_type ) { 
  191. $value = (array) wp_setup_nav_menu_item( $post ); 
  192.  
  193. if ( ! is_array( $value ) ) { 
  194. $value = $this->default; 
  195.  
  196. // Cache the value for future calls to avoid having to re-call wp_setup_nav_menu_item(). 
  197. $this->value = $value; 
  198. $this->populate_value(); 
  199. $value = $this->value; 
  200.  
  201. return $value; 
  202.  
  203. /** 
  204. * Ensure that the value is fully populated with the necessary properties. 
  205. * Translates some properties added by wp_setup_nav_menu_item() and removes others. 
  206. * @since 4.3.0 
  207. * @access protected 
  208. * @see WP_Customize_Nav_Menu_Item_Setting::value() 
  209. */ 
  210. protected function populate_value() { 
  211. if ( ! is_array( $this->value ) ) { 
  212. return; 
  213.  
  214. if ( isset( $this->value['menu_order'] ) ) { 
  215. $this->value['position'] = $this->value['menu_order']; 
  216. unset( $this->value['menu_order'] ); 
  217. if ( isset( $this->value['post_status'] ) ) { 
  218. $this->value['status'] = $this->value['post_status']; 
  219. unset( $this->value['post_status'] ); 
  220.  
  221. if ( ! isset( $this->value['original_title'] ) ) { 
  222. $original_title = ''; 
  223. if ( 'post_type' === $this->value['type'] ) { 
  224. $original_title = get_the_title( $this->value['object_id'] ); 
  225. } elseif ( 'taxonomy' === $this->value['type'] ) { 
  226. $original_title = get_term_field( 'name', $this->value['object_id'], $this->value['object'], 'raw' ); 
  227. if ( is_wp_error( $original_title ) ) { 
  228. $original_title = ''; 
  229. $this->value['original_title'] = html_entity_decode( $original_title, ENT_QUOTES, get_bloginfo( 'charset' ) ); 
  230.  
  231. if ( ! isset( $this->value['nav_menu_term_id'] ) && $this->post_id > 0 ) { 
  232. $menus = wp_get_post_terms( $this->post_id, WP_Customize_Nav_Menu_Setting::TAXONOMY, array( 
  233. 'fields' => 'ids',  
  234. ) ); 
  235. if ( ! empty( $menus ) ) { 
  236. $this->value['nav_menu_term_id'] = array_shift( $menus ); 
  237. } else { 
  238. $this->value['nav_menu_term_id'] = 0; 
  239.  
  240. foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) { 
  241. if ( ! is_int( $this->value[ $key ] ) ) { 
  242. $this->value[ $key ] = intval( $this->value[ $key ] ); 
  243. foreach ( array( 'classes', 'xfn' ) as $key ) { 
  244. if ( is_array( $this->value[ $key ] ) ) { 
  245. $this->value[ $key ] = implode( ' ', $this->value[ $key ] ); 
  246.  
  247. if ( ! isset( $this->value['title'] ) ) { 
  248. $this->value['title'] = ''; 
  249.  
  250. if ( ! isset( $this->value['_invalid'] ) ) { 
  251. $this->value['_invalid'] = false; 
  252. $is_known_invalid = ( 
  253. ( ( 'post_type' === $this->value['type'] || 'post_type_archive' === $this->value['type'] ) && ! post_type_exists( $this->value['object'] ) ) 
  254. || 
  255. ( 'taxonomy' === $this->value['type'] && ! taxonomy_exists( $this->value['object'] ) ) 
  256. ); 
  257. if ( $is_known_invalid ) { 
  258. $this->value['_invalid'] = true; 
  259.  
  260. // Remove remaining properties available on a setup nav_menu_item post object which aren't relevant to the setting value. 
  261. $irrelevant_properties = array( 
  262. 'ID',  
  263. 'comment_count',  
  264. 'comment_status',  
  265. 'db_id',  
  266. 'filter',  
  267. 'guid',  
  268. 'ping_status',  
  269. 'pinged',  
  270. 'post_author',  
  271. 'post_content',  
  272. 'post_content_filtered',  
  273. 'post_date',  
  274. 'post_date_gmt',  
  275. 'post_excerpt',  
  276. 'post_mime_type',  
  277. 'post_modified',  
  278. 'post_modified_gmt',  
  279. 'post_name',  
  280. 'post_parent',  
  281. 'post_password',  
  282. 'post_title',  
  283. 'post_type',  
  284. 'to_ping',  
  285. ); 
  286. foreach ( $irrelevant_properties as $property ) { 
  287. unset( $this->value[ $property ] ); 
  288.  
  289. /** 
  290. * Handle previewing the setting. 
  291. * @since 4.3.0 
  292. * @since 4.4.0 Added boolean return value. 
  293. * @access public 
  294. * @see WP_Customize_Manager::post_value() 
  295. * @return bool False if method short-circuited due to no-op. 
  296. */ 
  297. public function preview() { 
  298. if ( $this->is_previewed ) { 
  299. return false; 
  300.  
  301. $undefined = new stdClass(); 
  302. $is_placeholder = ( $this->post_id < 0 ); 
  303. $is_dirty = ( $undefined !== $this->post_value( $undefined ) ); 
  304. if ( ! $is_placeholder && ! $is_dirty ) { 
  305. return false; 
  306.  
  307. $this->is_previewed = true; 
  308. $this->_original_value = $this->value(); 
  309. $this->original_nav_menu_term_id = $this->_original_value['nav_menu_term_id']; 
  310. $this->_previewed_blog_id = get_current_blog_id(); 
  311.  
  312. add_filter( 'wp_get_nav_menu_items', array( $this, 'filter_wp_get_nav_menu_items' ), 10, 3 ); 
  313.  
  314. $sort_callback = array( __CLASS__, 'sort_wp_get_nav_menu_items' ); 
  315. if ( ! has_filter( 'wp_get_nav_menu_items', $sort_callback ) ) { 
  316. add_filter( 'wp_get_nav_menu_items', array( __CLASS__, 'sort_wp_get_nav_menu_items' ), 1000, 3 ); 
  317.  
  318. // @todo Add get_post_metadata filters for plugins to add their data. 
  319.  
  320. return true; 
  321.  
  322. /** 
  323. * Filters the wp_get_nav_menu_items() result to supply the previewed menu items. 
  324. * @since 4.3.0 
  325. * @access public 
  326. * @see wp_get_nav_menu_items() 
  327. * @param array $items An array of menu item post objects. 
  328. * @param object $menu The menu object. 
  329. * @param array $args An array of arguments used to retrieve menu item objects. 
  330. * @return array Array of menu items,  
  331. */ 
  332. public function filter_wp_get_nav_menu_items( $items, $menu, $args ) { 
  333. $this_item = $this->value(); 
  334. $current_nav_menu_term_id = $this_item['nav_menu_term_id']; 
  335. unset( $this_item['nav_menu_term_id'] ); 
  336.  
  337. $should_filter = ( 
  338. $menu->term_id === $this->original_nav_menu_term_id 
  339. || 
  340. $menu->term_id === $current_nav_menu_term_id 
  341. ); 
  342. if ( ! $should_filter ) { 
  343. return $items; 
  344.  
  345. // Handle deleted menu item, or menu item moved to another menu. 
  346. $should_remove = ( 
  347. false === $this_item 
  348. || 
  349. true === $this_item['_invalid'] 
  350. || 
  351. $this->original_nav_menu_term_id === $menu->term_id 
  352. && 
  353. $current_nav_menu_term_id !== $this->original_nav_menu_term_id 
  354. ); 
  355. if ( $should_remove ) { 
  356. $filtered_items = array(); 
  357. foreach ( $items as $item ) { 
  358. if ( $item->db_id !== $this->post_id ) { 
  359. $filtered_items[] = $item; 
  360. return $filtered_items; 
  361.  
  362. $mutated = false; 
  363. $should_update = ( 
  364. is_array( $this_item ) 
  365. && 
  366. $current_nav_menu_term_id === $menu->term_id 
  367. ); 
  368. if ( $should_update ) { 
  369. foreach ( $items as $item ) { 
  370. if ( $item->db_id === $this->post_id ) { 
  371. foreach ( get_object_vars( $this->value_as_wp_post_nav_menu_item() ) as $key => $value ) { 
  372. $item->$key = $value; 
  373. $mutated = true; 
  374.  
  375. // Not found so we have to append it.. 
  376. if ( ! $mutated ) { 
  377. $items[] = $this->value_as_wp_post_nav_menu_item(); 
  378.  
  379. return $items; 
  380.  
  381. /** 
  382. * Re-apply the tail logic also applied on $items by wp_get_nav_menu_items(). 
  383. * @since 4.3.0 
  384. * @access public 
  385. * @static 
  386. * @see wp_get_nav_menu_items() 
  387. * @param array $items An array of menu item post objects. 
  388. * @param object $menu The menu object. 
  389. * @param array $args An array of arguments used to retrieve menu item objects. 
  390. * @return array Array of menu items,  
  391. */ 
  392. public static function sort_wp_get_nav_menu_items( $items, $menu, $args ) { 
  393. // @todo We should probably re-apply some constraints imposed by $args. 
  394. unset( $args['include'] ); 
  395.  
  396. // Remove invalid items only in front end. 
  397. if ( ! is_admin() ) { 
  398. $items = array_filter( $items, '_is_valid_nav_menu_item' ); 
  399.  
  400. if ( ARRAY_A === $args['output'] ) { 
  401. $GLOBALS['_menu_item_sort_prop'] = $args['output_key']; 
  402. usort( $items, '_sort_nav_menu_items' ); 
  403. $i = 1; 
  404.  
  405. foreach ( $items as $k => $item ) { 
  406. $items[ $k ]->{$args['output_key']} = $i++; 
  407.  
  408. return $items; 
  409.  
  410. /** 
  411. * Get the value emulated into a WP_Post and set up as a nav_menu_item. 
  412. * @since 4.3.0 
  413. * @access public 
  414. * @return WP_Post With wp_setup_nav_menu_item() applied. 
  415. */ 
  416. public function value_as_wp_post_nav_menu_item() { 
  417. $item = (object) $this->value(); 
  418. unset( $item->nav_menu_term_id ); 
  419.  
  420. $item->post_status = $item->status; 
  421. unset( $item->status ); 
  422.  
  423. $item->post_type = 'nav_menu_item'; 
  424. $item->menu_order = $item->position; 
  425. unset( $item->position ); 
  426.  
  427. if ( $item->title ) { 
  428. $item->post_title = $item->title; 
  429.  
  430. $item->ID = $this->post_id; 
  431. $item->db_id = $this->post_id; 
  432. $post = new WP_Post( (object) $item ); 
  433.  
  434. if ( empty( $post->post_author ) ) { 
  435. $post->post_author = get_current_user_id(); 
  436.  
  437. if ( ! isset( $post->type_label ) ) { 
  438. if ( 'post_type' === $post->type ) { 
  439. $object = get_post_type_object( $post->object ); 
  440. if ( $object ) { 
  441. $post->type_label = $object->labels->singular_name; 
  442. } else { 
  443. $post->type_label = $post->object; 
  444. } elseif ( 'taxonomy' == $post->type ) { 
  445. $object = get_taxonomy( $post->object ); 
  446. if ( $object ) { 
  447. $post->type_label = $object->labels->singular_name; 
  448. } else { 
  449. $post->type_label = $post->object; 
  450. } else { 
  451. $post->type_label = __( 'Custom Link' ); 
  452.  
  453. /** This filter is documented in wp-includes/nav-menu.php */ 
  454. $post->attr_title = apply_filters( 'nav_menu_attr_title', $post->attr_title ); 
  455.  
  456. /** This filter is documented in wp-includes/nav-menu.php */ 
  457. $post->description = apply_filters( 'nav_menu_description', wp_trim_words( $post->description, 200 ) ); 
  458.  
  459. /** This filter is documented in wp-includes/nav-menu.php */ 
  460. $post = apply_filters( 'wp_setup_nav_menu_item', $post ); 
  461.  
  462. return $post; 
  463.  
  464. /** 
  465. * Sanitize an input. 
  466. * Note that parent::sanitize() erroneously does wp_unslash() on $value, but 
  467. * we remove that in this override. 
  468. * @since 4.3.0 
  469. * @access public 
  470. * @param array $menu_item_value The value to sanitize. 
  471. * @return array|false|null Null if an input isn't valid. False if it is marked for deletion. 
  472. * Otherwise the sanitized value. 
  473. */ 
  474. public function sanitize( $menu_item_value ) { 
  475. // Menu is marked for deletion. 
  476. if ( false === $menu_item_value ) { 
  477. return $menu_item_value; 
  478.  
  479. // Invalid. 
  480. if ( ! is_array( $menu_item_value ) ) { 
  481. return null; 
  482.  
  483. $default = array( 
  484. 'object_id' => 0,  
  485. 'object' => '',  
  486. 'menu_item_parent' => 0,  
  487. 'position' => 0,  
  488. 'type' => 'custom',  
  489. 'title' => '',  
  490. 'url' => '',  
  491. 'target' => '',  
  492. 'attr_title' => '',  
  493. 'description' => '',  
  494. 'classes' => '',  
  495. 'xfn' => '',  
  496. 'status' => 'publish',  
  497. 'original_title' => '',  
  498. 'nav_menu_term_id' => 0,  
  499. '_invalid' => false,  
  500. ); 
  501. $menu_item_value = array_merge( $default, $menu_item_value ); 
  502. $menu_item_value = wp_array_slice_assoc( $menu_item_value, array_keys( $default ) ); 
  503. $menu_item_value['position'] = intval( $menu_item_value['position'] ); 
  504.  
  505. foreach ( array( 'object_id', 'menu_item_parent', 'nav_menu_term_id' ) as $key ) { 
  506. // Note we need to allow negative-integer IDs for previewed objects not inserted yet. 
  507. $menu_item_value[ $key ] = intval( $menu_item_value[ $key ] ); 
  508.  
  509. foreach ( array( 'type', 'object', 'target' ) as $key ) { 
  510. $menu_item_value[ $key ] = sanitize_key( $menu_item_value[ $key ] ); 
  511.  
  512. foreach ( array( 'xfn', 'classes' ) as $key ) { 
  513. $value = $menu_item_value[ $key ]; 
  514. if ( ! is_array( $value ) ) { 
  515. $value = explode( ' ', $value ); 
  516. $menu_item_value[ $key ] = implode( ' ', array_map( 'sanitize_html_class', $value ) ); 
  517.  
  518. $menu_item_value['original_title'] = sanitize_text_field( $menu_item_value['original_title'] ); 
  519.  
  520. // Apply the same filters as when calling wp_insert_post(). 
  521. $menu_item_value['title'] = wp_unslash( apply_filters( 'title_save_pre', wp_slash( $menu_item_value['title'] ) ) ); 
  522. $menu_item_value['attr_title'] = wp_unslash( apply_filters( 'excerpt_save_pre', wp_slash( $menu_item_value['attr_title'] ) ) ); 
  523. $menu_item_value['description'] = wp_unslash( apply_filters( 'content_save_pre', wp_slash( $menu_item_value['description'] ) ) ); 
  524.  
  525. $menu_item_value['url'] = esc_url_raw( $menu_item_value['url'] ); 
  526. if ( 'publish' !== $menu_item_value['status'] ) { 
  527. $menu_item_value['status'] = 'draft'; 
  528.  
  529. $menu_item_value['_invalid'] = (bool) $menu_item_value['_invalid']; 
  530.  
  531. /** This filter is documented in wp-includes/class-wp-customize-setting.php */ 
  532. return apply_filters( "customize_sanitize_{$this->id}", $menu_item_value, $this ); 
  533.  
  534. /** 
  535. * Creates/updates the nav_menu_item post for this setting. 
  536. * Any created menu items will have their assigned post IDs exported to the client 
  537. * via the {@see 'customize_save_response'} filter. Likewise, any errors will be 
  538. * exported to the client via the customize_save_response() filter. 
  539. * To delete a menu, the client can send false as the value. 
  540. * @since 4.3.0 
  541. * @access protected 
  542. * @see wp_update_nav_menu_item() 
  543. * @param array|false $value The menu item array to update. If false, then the menu item will be deleted 
  544. * entirely. See WP_Customize_Nav_Menu_Item_Setting::$default for what the value 
  545. * should consist of. 
  546. * @return null|void 
  547. */ 
  548. protected function update( $value ) { 
  549. if ( $this->is_updated ) { 
  550. return; 
  551.  
  552. $this->is_updated = true; 
  553. $is_placeholder = ( $this->post_id < 0 ); 
  554. $is_delete = ( false === $value ); 
  555.  
  556. // Update the cached value. 
  557. $this->value = $value; 
  558.  
  559. add_filter( 'customize_save_response', array( $this, 'amend_customize_save_response' ) ); 
  560.  
  561. if ( $is_delete ) { 
  562. // If the current setting post is a placeholder, a delete request is a no-op. 
  563. if ( $is_placeholder ) { 
  564. $this->update_status = 'deleted'; 
  565. } else { 
  566. $r = wp_delete_post( $this->post_id, true ); 
  567.  
  568. if ( false === $r ) { 
  569. $this->update_error = new WP_Error( 'delete_failure' ); 
  570. $this->update_status = 'error'; 
  571. } else { 
  572. $this->update_status = 'deleted'; 
  573. // @todo send back the IDs for all associated nav menu items deleted, so these settings (and controls) can be removed from Customizer? 
  574. } else { 
  575.  
  576. // Handle saving menu items for menus that are being newly-created. 
  577. if ( $value['nav_menu_term_id'] < 0 ) { 
  578. $nav_menu_setting_id = sprintf( 'nav_menu[%s]', $value['nav_menu_term_id'] ); 
  579. $nav_menu_setting = $this->manager->get_setting( $nav_menu_setting_id ); 
  580.  
  581. if ( ! $nav_menu_setting || ! ( $nav_menu_setting instanceof WP_Customize_Nav_Menu_Setting ) ) { 
  582. $this->update_status = 'error'; 
  583. $this->update_error = new WP_Error( 'unexpected_nav_menu_setting' ); 
  584. return; 
  585.  
  586. if ( false === $nav_menu_setting->save() ) { 
  587. $this->update_status = 'error'; 
  588. $this->update_error = new WP_Error( 'nav_menu_setting_failure' ); 
  589. return; 
  590.  
  591. if ( $nav_menu_setting->previous_term_id !== intval( $value['nav_menu_term_id'] ) ) { 
  592. $this->update_status = 'error'; 
  593. $this->update_error = new WP_Error( 'unexpected_previous_term_id' ); 
  594. return; 
  595.  
  596. $value['nav_menu_term_id'] = $nav_menu_setting->term_id; 
  597.  
  598. // Handle saving a nav menu item that is a child of a nav menu item being newly-created. 
  599. if ( $value['menu_item_parent'] < 0 ) { 
  600. $parent_nav_menu_item_setting_id = sprintf( 'nav_menu_item[%s]', $value['menu_item_parent'] ); 
  601. $parent_nav_menu_item_setting = $this->manager->get_setting( $parent_nav_menu_item_setting_id ); 
  602.  
  603. if ( ! $parent_nav_menu_item_setting || ! ( $parent_nav_menu_item_setting instanceof WP_Customize_Nav_Menu_Item_Setting ) ) { 
  604. $this->update_status = 'error'; 
  605. $this->update_error = new WP_Error( 'unexpected_nav_menu_item_setting' ); 
  606. return; 
  607.  
  608. if ( false === $parent_nav_menu_item_setting->save() ) { 
  609. $this->update_status = 'error'; 
  610. $this->update_error = new WP_Error( 'nav_menu_item_setting_failure' ); 
  611. return; 
  612.  
  613. if ( $parent_nav_menu_item_setting->previous_post_id !== intval( $value['menu_item_parent'] ) ) { 
  614. $this->update_status = 'error'; 
  615. $this->update_error = new WP_Error( 'unexpected_previous_post_id' ); 
  616. return; 
  617.  
  618. $value['menu_item_parent'] = $parent_nav_menu_item_setting->post_id; 
  619.  
  620. // Insert or update menu. 
  621. $menu_item_data = array( 
  622. 'menu-item-object-id' => $value['object_id'],  
  623. 'menu-item-object' => $value['object'],  
  624. 'menu-item-parent-id' => $value['menu_item_parent'],  
  625. 'menu-item-position' => $value['position'],  
  626. 'menu-item-type' => $value['type'],  
  627. 'menu-item-title' => $value['title'],  
  628. 'menu-item-url' => $value['url'],  
  629. 'menu-item-description' => $value['description'],  
  630. 'menu-item-attr-title' => $value['attr_title'],  
  631. 'menu-item-target' => $value['target'],  
  632. 'menu-item-classes' => $value['classes'],  
  633. 'menu-item-xfn' => $value['xfn'],  
  634. 'menu-item-status' => $value['status'],  
  635. ); 
  636.  
  637. $r = wp_update_nav_menu_item( 
  638. $value['nav_menu_term_id'],  
  639. $is_placeholder ? 0 : $this->post_id,  
  640. wp_slash( $menu_item_data ) 
  641. ); 
  642.  
  643. if ( is_wp_error( $r ) ) { 
  644. $this->update_status = 'error'; 
  645. $this->update_error = $r; 
  646. } else { 
  647. if ( $is_placeholder ) { 
  648. $this->previous_post_id = $this->post_id; 
  649. $this->post_id = $r; 
  650. $this->update_status = 'inserted'; 
  651. } else { 
  652. $this->update_status = 'updated'; 
  653.  
  654.  
  655. /** 
  656. * Export data for the JS client. 
  657. * @since 4.3.0 
  658. * @access public 
  659. * @see WP_Customize_Nav_Menu_Item_Setting::update() 
  660. * @param array $data Additional information passed back to the 'saved' event on `wp.customize`. 
  661. * @return array Save response data. 
  662. */ 
  663. public function amend_customize_save_response( $data ) { 
  664. if ( ! isset( $data['nav_menu_item_updates'] ) ) { 
  665. $data['nav_menu_item_updates'] = array(); 
  666.  
  667. $data['nav_menu_item_updates'][] = array( 
  668. 'post_id' => $this->post_id,  
  669. 'previous_post_id' => $this->previous_post_id,  
  670. 'error' => $this->update_error ? $this->update_error->get_error_code() : null,  
  671. 'status' => $this->update_status,  
  672. ); 
  673. return $data;