wp/wp-includes/nav-menu.php
changeset 7 cf61fcea0001
parent 5 5e2f62d02dcd
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
    10 /**
    10 /**
    11  * Returns a navigation menu object.
    11  * Returns a navigation menu object.
    12  *
    12  *
    13  * @since 3.0.0
    13  * @since 3.0.0
    14  *
    14  *
    15  * @param string $menu Menu ID, slug, or name.
    15  * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
    16  * @return mixed false if $menu param isn't supplied or term does not exist, menu object if successful.
    16  * @return WP_Term|false False if $menu param isn't supplied or term does not exist, menu object if successful.
    17  */
    17  */
    18 function wp_get_nav_menu_object( $menu ) {
    18 function wp_get_nav_menu_object( $menu ) {
    19 	if ( ! $menu )
    19 	$menu_obj = false;
    20 		return false;
    20 
    21 
    21 	if ( is_object( $menu ) ) {
    22 	$menu_obj = get_term( $menu, 'nav_menu' );
    22 		$menu_obj = $menu;
    23 
    23 	}
    24 	if ( ! $menu_obj )
    24 
    25 		$menu_obj = get_term_by( 'slug', $menu, 'nav_menu' );
    25 	if ( $menu && ! $menu_obj ) {
    26 
    26 		$menu_obj = get_term( $menu, 'nav_menu' );
    27 	if ( ! $menu_obj )
    27 
    28 		$menu_obj = get_term_by( 'name', $menu, 'nav_menu' );
    28 		if ( ! $menu_obj ) {
    29 
    29 			$menu_obj = get_term_by( 'slug', $menu, 'nav_menu' );
    30 	if ( ! $menu_obj )
    30 		}
       
    31 
       
    32 		if ( ! $menu_obj ) {
       
    33 			$menu_obj = get_term_by( 'name', $menu, 'nav_menu' );
       
    34 		}
       
    35 	}
       
    36 
       
    37 	if ( ! $menu_obj || is_wp_error( $menu_obj ) ) {
    31 		$menu_obj = false;
    38 		$menu_obj = false;
    32 
    39 	}
    33 	return $menu_obj;
    40 
       
    41 	/**
       
    42 	 * Filters the nav_menu term retrieved for wp_get_nav_menu_object().
       
    43 	 *
       
    44 	 * @since 4.3.0
       
    45 	 *
       
    46 	 * @param WP_Term|false      $menu_obj Term from nav_menu taxonomy, or false if nothing had been found.
       
    47 	 * @param int|string|WP_Term $menu     The menu ID, slug, name, or object passed to wp_get_nav_menu_object().
       
    48 	 */
       
    49 	return apply_filters( 'wp_get_nav_menu_object', $menu_obj, $menu );
    34 }
    50 }
    35 
    51 
    36 /**
    52 /**
    37  * Check if the given ID is a navigation menu.
    53  * Check if the given ID is a navigation menu.
    38  *
    54  *
    39  * Returns true if it is; false otherwise.
    55  * Returns true if it is; false otherwise.
    40  *
    56  *
    41  * @since 3.0.0
    57  * @since 3.0.0
    42  *
    58  *
    43  * @param int|string $menu The menu to check (ID, slug, or name).
    59  * @param int|string|WP_Term $menu Menu ID, slug, name, or object of menu to check.
    44  * @return bool Whether the menu exists.
    60  * @return bool Whether the menu exists.
    45  */
    61  */
    46 function is_nav_menu( $menu ) {
    62 function is_nav_menu( $menu ) {
    47 	if ( ! $menu )
    63 	if ( ! $menu )
    48 		return false;
    64 		return false;
    59 
    75 
    60 	return false;
    76 	return false;
    61 }
    77 }
    62 
    78 
    63 /**
    79 /**
    64  * Register navigation menus for a theme.
    80  * Registers navigation menu locations for a theme.
    65  *
    81  *
    66  * @since 3.0.0
    82  * @since 3.0.0
       
    83  *
       
    84  * @global array $_wp_registered_nav_menus
    67  *
    85  *
    68  * @param array $locations Associative array of menu location identifiers (like a slug) and descriptive text.
    86  * @param array $locations Associative array of menu location identifiers (like a slug) and descriptive text.
    69  */
    87  */
    70 function register_nav_menus( $locations = array() ) {
    88 function register_nav_menus( $locations = array() ) {
    71 	global $_wp_registered_nav_menus;
    89 	global $_wp_registered_nav_menus;
    74 
    92 
    75 	$_wp_registered_nav_menus = array_merge( (array) $_wp_registered_nav_menus, $locations );
    93 	$_wp_registered_nav_menus = array_merge( (array) $_wp_registered_nav_menus, $locations );
    76 }
    94 }
    77 
    95 
    78 /**
    96 /**
    79  * Unregisters a navigation menu for a theme.
    97  * Unregisters a navigation menu location for a theme.
       
    98  *
       
    99  * @since 3.1.0
       
   100  * @global array $_wp_registered_nav_menus
    80  *
   101  *
    81  * @param string $location The menu location identifier.
   102  * @param string $location The menu location identifier.
    82  * @return bool True on success, false on failure.
   103  * @return bool True on success, false on failure.
    83  */
   104  */
    84 function unregister_nav_menu( $location ) {
   105 function unregister_nav_menu( $location ) {
    93 	}
   114 	}
    94 	return false;
   115 	return false;
    95 }
   116 }
    96 
   117 
    97 /**
   118 /**
    98  * Register a navigation menu for a theme.
   119  * Registers a navigation menu location for a theme.
    99  *
   120  *
   100  * @since 3.0.0
   121  * @since 3.0.0
   101  *
   122  *
   102  * @param string $location Menu location identifier, like a slug.
   123  * @param string $location    Menu location identifier, like a slug.
   103  * @param string $description Menu location descriptive text.
   124  * @param string $description Menu location descriptive text.
   104  */
   125  */
   105 function register_nav_menu( $location, $description ) {
   126 function register_nav_menu( $location, $description ) {
   106 	register_nav_menus( array( $location => $description ) );
   127 	register_nav_menus( array( $location => $description ) );
   107 }
   128 }
   108 /**
   129 /**
   109  * Returns an array of all registered navigation menus in a theme
   130  * Retrieves all registered navigation menu locations in a theme.
   110  *
   131  *
   111  * @since 3.0.0
   132  * @since 3.0.0
   112  * @return array
   133  *
       
   134  * @global array $_wp_registered_nav_menus
       
   135  *
       
   136  * @return array Registered navigation menu locations. If none are registered, an empty array.
   113  */
   137  */
   114 function get_registered_nav_menus() {
   138 function get_registered_nav_menus() {
   115 	global $_wp_registered_nav_menus;
   139 	global $_wp_registered_nav_menus;
   116 	if ( isset( $_wp_registered_nav_menus ) )
   140 	if ( isset( $_wp_registered_nav_menus ) )
   117 		return $_wp_registered_nav_menus;
   141 		return $_wp_registered_nav_menus;
   118 	return array();
   142 	return array();
   119 }
   143 }
   120 
   144 
   121 /**
   145 /**
   122  * Returns an array with the registered navigation menu locations and the menu assigned to it
   146  * Retrieves all registered navigation menu locations and the menus assigned to them.
   123  *
   147  *
   124  * @since 3.0.0
   148  * @since 3.0.0
   125  * @return array
   149  *
       
   150  * @return array Registered navigation menu locations and the menus assigned them.
       
   151  *               If none are registered, an empty array.
   126  */
   152  */
   127 
   153 
   128 function get_nav_menu_locations() {
   154 function get_nav_menu_locations() {
   129 	$locations = get_theme_mod( 'nav_menu_locations' );
   155 	$locations = get_theme_mod( 'nav_menu_locations' );
   130 	return ( is_array( $locations ) ) ? $locations : array();
   156 	return ( is_array( $locations ) ) ? $locations : array();
   131 }
   157 }
   132 
   158 
   133 /**
   159 /**
   134  * Whether a registered nav menu location has a menu assigned to it.
   160  * Determines whether a registered nav menu location has a menu assigned to it.
   135  *
   161  *
   136  * @since 3.0.0
   162  * @since 3.0.0
       
   163  *
   137  * @param string $location Menu location identifier.
   164  * @param string $location Menu location identifier.
   138  * @return bool Whether location has a menu.
   165  * @return bool Whether location has a menu.
   139  */
   166  */
   140 function has_nav_menu( $location ) {
   167 function has_nav_menu( $location ) {
       
   168 	$has_nav_menu = false;
       
   169 
   141 	$registered_nav_menus = get_registered_nav_menus();
   170 	$registered_nav_menus = get_registered_nav_menus();
   142 	if ( ! isset( $registered_nav_menus[ $location ] ) ) {
   171 	if ( isset( $registered_nav_menus[ $location ] ) ) {
   143 		return false;
   172 		$locations = get_nav_menu_locations();
   144 	}
   173 		$has_nav_menu = ! empty( $locations[ $location ] );
       
   174 	}
       
   175 
       
   176 	/**
       
   177 	 * Filters whether a nav menu is assigned to the specified location.
       
   178 	 *
       
   179 	 * @since 4.3.0
       
   180 	 *
       
   181 	 * @param bool   $has_nav_menu Whether there is a menu assigned to a location.
       
   182 	 * @param string $location     Menu location.
       
   183 	 */
       
   184 	return apply_filters( 'has_nav_menu', $has_nav_menu, $location );
       
   185 }
       
   186 
       
   187 /**
       
   188  * Returns the name of a navigation menu.
       
   189  *
       
   190  * @since 4.9.0
       
   191  *
       
   192  * @param string $location Menu location identifier.
       
   193  * @return string Menu name.
       
   194  */
       
   195 function wp_get_nav_menu_name( $location ) {
       
   196 	$menu_name = '';
   145 
   197 
   146 	$locations = get_nav_menu_locations();
   198 	$locations = get_nav_menu_locations();
   147 	return ( ! empty( $locations[ $location ] ) );
   199 
   148 }
   200 	if ( isset( $locations[ $location ] ) ) {
   149 
   201 		$menu = wp_get_nav_menu_object( $locations[ $location ] );
   150 /**
   202 
   151  * Determine whether the given ID is a nav menu item.
   203 		if ( $menu && $menu->name ) {
       
   204 			$menu_name = $menu->name;
       
   205 		}
       
   206 	}
       
   207 
       
   208 	/**
       
   209 	 * Filters the navigation menu name being returned.
       
   210 	 *
       
   211 	 * @since 4.9.0
       
   212 	 *
       
   213 	 * @param string $menu_name Menu name.
       
   214 	 * @param string $location  Menu location identifier.
       
   215 	 */
       
   216 	return apply_filters( 'wp_get_nav_menu_name', $menu_name, $location );
       
   217 }
       
   218 
       
   219 /**
       
   220  * Determines whether the given ID is a nav menu item.
   152  *
   221  *
   153  * @since 3.0.0
   222  * @since 3.0.0
   154  *
   223  *
   155  * @param int $menu_item_id The ID of the potential nav menu item.
   224  * @param int $menu_item_id The ID of the potential nav menu item.
   156  * @return bool Whether the given ID is that of a nav menu item.
   225  * @return bool Whether the given ID is that of a nav menu item.
   158 function is_nav_menu_item( $menu_item_id = 0 ) {
   227 function is_nav_menu_item( $menu_item_id = 0 ) {
   159 	return ( ! is_wp_error( $menu_item_id ) && ( 'nav_menu_item' == get_post_type( $menu_item_id ) ) );
   228 	return ( ! is_wp_error( $menu_item_id ) && ( 'nav_menu_item' == get_post_type( $menu_item_id ) ) );
   160 }
   229 }
   161 
   230 
   162 /**
   231 /**
   163  * Create a Navigation Menu.
   232  * Creates a navigation menu.
       
   233  *
       
   234  * Note that `$menu_name` is expected to be pre-slashed.
   164  *
   235  *
   165  * @since 3.0.0
   236  * @since 3.0.0
   166  *
   237  *
   167  * @param string $menu_name Menu name.
   238  * @param string $menu_name Menu name.
   168  * @return int|WP_Error Menu ID on success, WP_Error object on failure.
   239  * @return int|WP_Error Menu ID on success, WP_Error object on failure.
   169  */
   240  */
   170 function wp_create_nav_menu( $menu_name ) {
   241 function wp_create_nav_menu( $menu_name ) {
       
   242 	// expected_slashed ($menu_name)
   171 	return wp_update_nav_menu_object( 0, array( 'menu-name' => $menu_name ) );
   243 	return wp_update_nav_menu_object( 0, array( 'menu-name' => $menu_name ) );
   172 }
   244 }
   173 
   245 
   174 /**
   246 /**
   175  * Delete a Navigation Menu.
   247  * Delete a Navigation Menu.
   176  *
   248  *
   177  * @since 3.0.0
   249  * @since 3.0.0
   178  *
   250  *
   179  * @param string $menu Menu ID, slug, or name.
   251  * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
   180  * @return bool|WP_Error True on success, false or WP_Error object on failure.
   252  * @return bool|WP_Error True on success, false or WP_Error object on failure.
   181  */
   253  */
   182 function wp_delete_nav_menu( $menu ) {
   254 function wp_delete_nav_menu( $menu ) {
   183 	$menu = wp_get_nav_menu_object( $menu );
   255 	$menu = wp_get_nav_menu_object( $menu );
   184 	if ( ! $menu )
   256 	if ( ! $menu )
   216 }
   288 }
   217 
   289 
   218 /**
   290 /**
   219  * Save the properties of a menu or create a new menu with those properties.
   291  * Save the properties of a menu or create a new menu with those properties.
   220  *
   292  *
   221  * @since 3.0.0
   293  * Note that `$menu_data` is expected to be pre-slashed.
   222  *
   294  *
   223  * @param int $menu_id The ID of the menu or "0" to create a new menu.
   295  * @since 3.0.0
       
   296  *
       
   297  * @param int   $menu_id   The ID of the menu or "0" to create a new menu.
   224  * @param array $menu_data The array of menu data.
   298  * @param array $menu_data The array of menu data.
   225  * @return int|WP_Error Menu ID on success, WP_Error object on failure.
   299  * @return int|WP_Error Menu ID on success, WP_Error object on failure.
   226  */
   300  */
   227 function wp_update_nav_menu_object( $menu_id = 0, $menu_data = array() ) {
   301 function wp_update_nav_menu_object( $menu_id = 0, $menu_data = array() ) {
       
   302 	// expected_slashed ($menu_data)
   228 	$menu_id = (int) $menu_id;
   303 	$menu_id = (int) $menu_id;
   229 
   304 
   230 	$_menu = wp_get_nav_menu_object( $menu_id );
   305 	$_menu = wp_get_nav_menu_object( $menu_id );
   231 
   306 
   232 	$args = array(
   307 	$args = array(
   241 	if (
   316 	if (
   242 		$_possible_existing &&
   317 		$_possible_existing &&
   243 		! is_wp_error( $_possible_existing ) &&
   318 		! is_wp_error( $_possible_existing ) &&
   244 		isset( $_possible_existing->term_id ) &&
   319 		isset( $_possible_existing->term_id ) &&
   245 		$_possible_existing->term_id != $menu_id
   320 		$_possible_existing->term_id != $menu_id
   246 	)
   321 	) {
   247 		return new WP_Error( 'menu_exists', sprintf( __('The menu name <strong>%s</strong> conflicts with another menu name. Please try another.'), esc_html( $menu_data['menu-name'] ) ) );
   322 		return new WP_Error( 'menu_exists',
       
   323 			/* translators: %s: menu name */
       
   324 			sprintf( __( 'The menu name %s conflicts with another menu name. Please try another.' ),
       
   325 				'<strong>' . esc_html( $menu_data['menu-name'] ) . '</strong>'
       
   326 			)
       
   327 		);
       
   328 	}
   248 
   329 
   249 	// menu doesn't already exist, so create a new menu
   330 	// menu doesn't already exist, so create a new menu
   250 	if ( ! $_menu || is_wp_error( $_menu ) ) {
   331 	if ( ! $_menu || is_wp_error( $_menu ) ) {
   251 		$menu_exists = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
   332 		$menu_exists = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
   252 
   333 
   253 		if ( $menu_exists )
   334 		if ( $menu_exists ) {
   254 			return new WP_Error( 'menu_exists', sprintf( __('The menu name <strong>%s</strong> conflicts with another menu name. Please try another.'), esc_html( $menu_data['menu-name'] ) ) );
   335 			return new WP_Error( 'menu_exists',
       
   336 				/* translators: %s: menu name */
       
   337 				sprintf( __( 'The menu name %s conflicts with another menu name. Please try another.' ),
       
   338 					'<strong>' . esc_html( $menu_data['menu-name'] ) . '</strong>'
       
   339 				)
       
   340 			);
       
   341 		}
   255 
   342 
   256 		$_menu = wp_insert_term( $menu_data['menu-name'], 'nav_menu', $args );
   343 		$_menu = wp_insert_term( $menu_data['menu-name'], 'nav_menu', $args );
   257 
   344 
   258 		if ( is_wp_error( $_menu ) )
   345 		if ( is_wp_error( $_menu ) )
   259 			return $_menu;
   346 			return $_menu;
   279 	$update_response = wp_update_term( $menu_id, 'nav_menu', $args );
   366 	$update_response = wp_update_term( $menu_id, 'nav_menu', $args );
   280 
   367 
   281 	if ( is_wp_error( $update_response ) )
   368 	if ( is_wp_error( $update_response ) )
   282 		return $update_response;
   369 		return $update_response;
   283 
   370 
       
   371 	$menu_id = (int) $update_response['term_id'];
       
   372 
   284 	/**
   373 	/**
   285 	 * Fires after a navigation menu has been successfully updated.
   374 	 * Fires after a navigation menu has been successfully updated.
   286 	 *
   375 	 *
   287 	 * @since 3.0.0
   376 	 * @since 3.0.0
   288 	 *
   377 	 *
   294 }
   383 }
   295 
   384 
   296 /**
   385 /**
   297  * Save the properties of a menu item or create a new one.
   386  * Save the properties of a menu item or create a new one.
   298  *
   387  *
   299  * @since 3.0.0
   388  * The menu-item-title, menu-item-description, and menu-item-attr-title are expected
   300  *
   389  * to be pre-slashed since they are passed directly into `wp_insert_post()`.
   301  * @param int $menu_id The ID of the menu. Required. If "0", makes the menu item a draft orphan.
   390  *
   302  * @param int $menu_item_db_id The ID of the menu item. If "0", creates a new menu item.
   391  * @since 3.0.0
   303  * @param array $menu_item_data The menu item's data.
   392  *
       
   393  * @param int   $menu_id         The ID of the menu. Required. If "0", makes the menu item a draft orphan.
       
   394  * @param int   $menu_item_db_id The ID of the menu item. If "0", creates a new menu item.
       
   395  * @param array $menu_item_data  The menu item's data.
   304  * @return int|WP_Error The menu item's database ID or WP_Error object on failure.
   396  * @return int|WP_Error The menu item's database ID or WP_Error object on failure.
   305  */
   397  */
   306 function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item_data = array() ) {
   398 function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item_data = array() ) {
   307 	$menu_id = (int) $menu_id;
   399 	$menu_id = (int) $menu_id;
   308 	$menu_item_db_id = (int) $menu_item_db_id;
   400 	$menu_item_db_id = (int) $menu_item_db_id;
   365 		} elseif ( 'post_type' == $args['menu-item-type'] ) {
   457 		} elseif ( 'post_type' == $args['menu-item-type'] ) {
   366 
   458 
   367 			$original_object = get_post( $args['menu-item-object-id'] );
   459 			$original_object = get_post( $args['menu-item-object-id'] );
   368 			$original_parent = (int) $original_object->post_parent;
   460 			$original_parent = (int) $original_object->post_parent;
   369 			$original_title = $original_object->post_title;
   461 			$original_title = $original_object->post_title;
       
   462 		} elseif ( 'post_type_archive' == $args['menu-item-type'] ) {
       
   463 			$original_object = get_post_type_object( $args['menu-item-object'] );
       
   464 			if ( $original_object ) {
       
   465 				$original_title = $original_object->labels->archives;
       
   466 			}
   370 		}
   467 		}
   371 
   468 
   372 		if ( $args['menu-item-title'] == $original_title )
   469 		if ( $args['menu-item-title'] == $original_title )
   373 			$args['menu-item-title'] = '';
   470 			$args['menu-item-title'] = '';
   374 
   471 
   395 		$post['ID'] = 0;
   492 		$post['ID'] = 0;
   396 		$post['post_status'] = 'publish' == $args['menu-item-status'] ? 'publish' : 'draft';
   493 		$post['post_status'] = 'publish' == $args['menu-item-status'] ? 'publish' : 'draft';
   397 		$menu_item_db_id = wp_insert_post( $post );
   494 		$menu_item_db_id = wp_insert_post( $post );
   398 		if ( ! $menu_item_db_id	|| is_wp_error( $menu_item_db_id ) )
   495 		if ( ! $menu_item_db_id	|| is_wp_error( $menu_item_db_id ) )
   399 			return $menu_item_db_id;
   496 			return $menu_item_db_id;
       
   497 
       
   498 		/**
       
   499 		 * Fires immediately after a new navigation menu item has been added.
       
   500 		 *
       
   501 		 * @since 4.4.0
       
   502 		 *
       
   503 		 * @see wp_update_nav_menu_item()
       
   504 		 *
       
   505 		 * @param int   $menu_id         ID of the updated menu.
       
   506 		 * @param int   $menu_item_db_id ID of the new menu item.
       
   507 		 * @param array $args            An array of arguments used to update/add the menu item.
       
   508 		 */
       
   509 		do_action( 'wp_add_nav_menu_item', $menu_id, $menu_item_db_id, $args );
   400 	}
   510 	}
   401 
   511 
   402 	// Associate the menu item with the menu term
   512 	// Associate the menu item with the menu term
   403 	// Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms()
   513 	// Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms()
   404 	 if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {
   514 	 if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {
   457  *
   567  *
   458  * @since 3.0.0
   568  * @since 3.0.0
   459  * @since 4.1.0 Default value of the 'orderby' argument was changed from 'none'
   569  * @since 4.1.0 Default value of the 'orderby' argument was changed from 'none'
   460  *              to 'name'.
   570  *              to 'name'.
   461  *
   571  *
   462  * @param array $args Optional. Array of arguments passed on to {@see get_terms()}.
   572  * @param array $args Optional. Array of arguments passed on to get_terms().
   463  *                    Default empty array.
   573  *                    Default empty array.
   464  * @return array Menu objects.
   574  * @return array Menu objects.
   465  */
   575  */
   466 function wp_get_nav_menus( $args = array() ) {
   576 function wp_get_nav_menus( $args = array() ) {
   467 	$defaults = array( 'hide_empty' => false, 'orderby' => 'name' );
   577 	$defaults = array( 'hide_empty' => false, 'orderby' => 'name' );
   468 	$args = wp_parse_args( $args, $defaults );
   578 	$args = wp_parse_args( $args, $defaults );
   469 
   579 
   470 	/**
   580 	/**
   471 	 * Filter the navigation menu objects being returned.
   581 	 * Filters the navigation menu objects being returned.
   472 	 *
   582 	 *
   473 	 * @since 3.0.0
   583 	 * @since 3.0.0
   474 	 *
   584 	 *
   475 	 * @see get_terms()
   585 	 * @see get_terms()
   476 	 *
   586 	 *
   479 	 */
   589 	 */
   480 	return apply_filters( 'wp_get_nav_menus', get_terms( 'nav_menu',  $args), $args );
   590 	return apply_filters( 'wp_get_nav_menus', get_terms( 'nav_menu',  $args), $args );
   481 }
   591 }
   482 
   592 
   483 /**
   593 /**
   484  * Sort menu items by the desired key.
       
   485  *
       
   486  * @since 3.0.0
       
   487  * @access private
       
   488  *
       
   489  * @param object $a The first object to compare
       
   490  * @param object $b The second object to compare
       
   491  * @return int -1, 0, or 1 if $a is considered to be respectively less than, equal to, or greater than $b.
       
   492  */
       
   493 function _sort_nav_menu_items( $a, $b ) {
       
   494 	global $_menu_item_sort_prop;
       
   495 
       
   496 	if ( empty( $_menu_item_sort_prop ) )
       
   497 		return 0;
       
   498 
       
   499 	if ( ! isset( $a->$_menu_item_sort_prop ) || ! isset( $b->$_menu_item_sort_prop ) )
       
   500 		return 0;
       
   501 
       
   502 	$_a = (int) $a->$_menu_item_sort_prop;
       
   503 	$_b = (int) $b->$_menu_item_sort_prop;
       
   504 
       
   505 	if ( $a->$_menu_item_sort_prop == $b->$_menu_item_sort_prop )
       
   506 		return 0;
       
   507 	elseif ( $_a == $a->$_menu_item_sort_prop && $_b == $b->$_menu_item_sort_prop )
       
   508 		return $_a < $_b ? -1 : 1;
       
   509 	else
       
   510 		return strcmp( $a->$_menu_item_sort_prop, $b->$_menu_item_sort_prop );
       
   511 }
       
   512 
       
   513 /**
       
   514  * Return if a menu item is valid.
   594  * Return if a menu item is valid.
   515  *
   595  *
   516  * @link https://core.trac.wordpress.org/ticket/13958
   596  * @link https://core.trac.wordpress.org/ticket/13958
   517  *
   597  *
   518  * @since 3.2.0
   598  * @since 3.2.0
   520  *
   600  *
   521  * @param object $item The menu item to check.
   601  * @param object $item The menu item to check.
   522  * @return bool False if invalid, otherwise true.
   602  * @return bool False if invalid, otherwise true.
   523  */
   603  */
   524 function _is_valid_nav_menu_item( $item ) {
   604 function _is_valid_nav_menu_item( $item ) {
   525 	if ( ! empty( $item->_invalid ) )
   605 	return empty( $item->_invalid );
   526 		return false;
   606 }
   527 
   607 
   528 	return true;
   608 /**
   529 }
   609  * Retrieves all menu items of a navigation menu.
   530 
   610  *
   531 /**
   611  * Note: Most arguments passed to the `$args` parameter – save for 'output_key' – are
   532  * Return all menu items of a navigation menu.
   612  * specifically for retrieving nav_menu_item posts from get_posts() and may only
   533  *
   613  * indirectly affect the ultimate ordering and content of the resulting nav menu
   534  * @since 3.0.0
   614  * items that get returned from this function.
   535  *
   615  *
   536  * @param string $menu Menu name, ID, or slug.
   616  * @since 3.0.0
   537  * @param array  $args Optional. Arguments to pass to {@see get_posts()}.
   617  *
   538  * @return mixed $items Array of menu items, otherwise false.
   618  * @global string $_menu_item_sort_prop
       
   619  * @staticvar array $fetched
       
   620  *
       
   621  * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
       
   622  * @param array              $args {
       
   623  *     Optional. Arguments to pass to get_posts().
       
   624  *
       
   625  *     @type string $order       How to order nav menu items as queried with get_posts(). Will be ignored
       
   626  *                               if 'output' is ARRAY_A. Default 'ASC'.
       
   627  *     @type string $orderby     Field to order menu items by as retrieved from get_posts(). Supply an orderby
       
   628  *                               field via 'output_key' to affect the output order of nav menu items.
       
   629  *                               Default 'menu_order'.
       
   630  *     @type string $post_type   Menu items post type. Default 'nav_menu_item'.
       
   631  *     @type string $post_status Menu items post status. Default 'publish'.
       
   632  *     @type string $output      How to order outputted menu items. Default ARRAY_A.
       
   633  *     @type string $output_key  Key to use for ordering the actual menu items that get returned. Note that
       
   634  *                               that is not a get_posts() argument and will only affect output of menu items
       
   635  *                               processed in this function. Default 'menu_order'.
       
   636  *     @type bool   $nopaging    Whether to retrieve all menu items (true) or paginate (false). Default true.
       
   637  * }
       
   638  * @return false|array $items Array of menu items, otherwise false.
   539  */
   639  */
   540 function wp_get_nav_menu_items( $menu, $args = array() ) {
   640 function wp_get_nav_menu_items( $menu, $args = array() ) {
   541 	$menu = wp_get_nav_menu_object( $menu );
   641 	$menu = wp_get_nav_menu_object( $menu );
   542 
   642 
   543 	if ( ! $menu )
   643 	if ( ! $menu ) {
   544 		return false;
   644 		return false;
       
   645 	}
   545 
   646 
   546 	static $fetched = array();
   647 	static $fetched = array();
   547 
   648 
   548 	$items = get_objects_in_term( $menu->term_id, 'nav_menu' );
   649 	$items = get_objects_in_term( $menu->term_id, 'nav_menu' );
   549 
   650 	if ( is_wp_error( $items ) ) {
   550 	if ( empty( $items ) )
   651 		return false;
   551 		return $items;
   652 	}
   552 
   653 
   553 	$defaults = array( 'order' => 'ASC', 'orderby' => 'menu_order', 'post_type' => 'nav_menu_item',
   654 	$defaults = array( 'order' => 'ASC', 'orderby' => 'menu_order', 'post_type' => 'nav_menu_item',
   554 		'post_status' => 'publish', 'output' => ARRAY_A, 'output_key' => 'menu_order', 'nopaging' => true );
   655 		'post_status' => 'publish', 'output' => ARRAY_A, 'output_key' => 'menu_order', 'nopaging' => true );
   555 	$args = wp_parse_args( $args, $defaults );
   656 	$args = wp_parse_args( $args, $defaults );
   556 	$args['include'] = $items;
   657 	$args['include'] = $items;
   557 
   658 
   558 	$items = get_posts( $args );
   659 	if ( ! empty( $items ) ) {
   559 
   660 		$items = get_posts( $args );
   560 	if ( is_wp_error( $items ) || ! is_array( $items ) )
   661 	} else {
   561 		return false;
   662 		$items = array();
       
   663 	}
   562 
   664 
   563 	// Get all posts and terms at once to prime the caches
   665 	// Get all posts and terms at once to prime the caches
   564 	if ( empty( $fetched[$menu->term_id] ) || wp_using_ext_object_cache() ) {
   666 	if ( empty( $fetched[ $menu->term_id ] ) && ! wp_using_ext_object_cache() ) {
   565 		$fetched[$menu->term_id] = true;
   667 		$fetched[$menu->term_id] = true;
   566 		$posts = array();
   668 		$posts = array();
   567 		$terms = array();
   669 		$terms = array();
   568 		foreach ( $items as $item ) {
   670 		foreach ( $items as $item ) {
   569 			$object_id = get_post_meta( $item->ID, '_menu_item_object_id', true );
   671 			$object_id = get_post_meta( $item->ID, '_menu_item_object_id', true );
   583 		}
   685 		}
   584 		unset($posts);
   686 		unset($posts);
   585 
   687 
   586 		if ( ! empty( $terms ) ) {
   688 		if ( ! empty( $terms ) ) {
   587 			foreach ( array_keys($terms) as $taxonomy ) {
   689 			foreach ( array_keys($terms) as $taxonomy ) {
   588 				get_terms($taxonomy, array('include' => $terms[$taxonomy]) );
   690 				get_terms( $taxonomy, array(
       
   691 					'include' => $terms[ $taxonomy ],
       
   692 					'hierarchical' => false,
       
   693 				) );
   589 			}
   694 			}
   590 		}
   695 		}
   591 		unset($terms);
   696 		unset($terms);
   592 	}
   697 	}
   593 
   698 
   594 	$items = array_map( 'wp_setup_nav_menu_item', $items );
   699 	$items = array_map( 'wp_setup_nav_menu_item', $items );
   595 
   700 
   596 	if ( ! is_admin() ) // Remove invalid items only in frontend
   701 	if ( ! is_admin() ) { // Remove invalid items only in front end
   597 		$items = array_filter( $items, '_is_valid_nav_menu_item' );
   702 		$items = array_filter( $items, '_is_valid_nav_menu_item' );
       
   703 	}
   598 
   704 
   599 	if ( ARRAY_A == $args['output'] ) {
   705 	if ( ARRAY_A == $args['output'] ) {
   600 		$GLOBALS['_menu_item_sort_prop'] = $args['output_key'];
   706 		$items = wp_list_sort( $items, array(
   601 		usort($items, '_sort_nav_menu_items');
   707 			$args['output_key'] => 'ASC',
       
   708 		) );
   602 		$i = 1;
   709 		$i = 1;
   603 		foreach( $items as $k => $item ) {
   710 		foreach ( $items as $k => $item ) {
   604 			$items[$k]->$args['output_key'] = $i++;
   711 			$items[$k]->{$args['output_key']} = $i++;
   605 		}
   712 		}
   606 	}
   713 	}
   607 
   714 
   608 	/**
   715 	/**
   609 	 * Filter the navigation menu items being returned.
   716 	 * Filters the navigation menu items being returned.
   610 	 *
   717 	 *
   611 	 * @since 3.0.0
   718 	 * @since 3.0.0
   612 	 *
   719 	 *
   613 	 * @param array  $items An array of menu item post objects.
   720 	 * @param array  $items An array of menu item post objects.
   614 	 * @param object $menu  The menu object.
   721 	 * @param object $menu  The menu object.
   615 	 * @param array  $args  An array of arguments used to retrieve menu item objects.
   722 	 * @param array  $args  An array of arguments used to retrieve menu item objects.
   616 	 */
   723 	 */
   617 	return apply_filters( 'wp_get_nav_menu_items',  $items, $menu, $args );
   724 	return apply_filters( 'wp_get_nav_menu_items', $items, $menu, $args );
   618 }
   725 }
   619 
   726 
   620 /**
   727 /**
   621  * Decorates a menu item object with the shared navigation menu item properties.
   728  * Decorates a menu item object with the shared navigation menu item properties.
   622  *
   729  *
   623  * Properties:
   730  * Properties:
   624  * - db_id: 		The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
   731  * - ID:               The term_id if the menu item represents a taxonomy term.
   625  * - object_id:		The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
   732  * - attr_title:       The title attribute of the link element for this menu item.
   626  * - type:		The family of objects originally represented, such as "post_type" or "taxonomy."
   733  * - classes:          The array of class attribute values for the link element of this menu item.
   627  * - object:		The type of object originally represented, such as "category," "post", or "attachment."
   734  * - db_id:            The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
   628  * - type_label:	The singular label used to describe this type of menu item.
   735  * - description:      The description of this menu item.
   629  * - post_parent:	The DB ID of the original object's parent object, if any (0 otherwise).
   736  * - menu_item_parent: The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise.
   630  * - menu_item_parent: 	The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise.
   737  * - object:           The type of object originally represented, such as "category," "post", or "attachment."
   631  * - url:		The URL to which this menu item points.
   738  * - object_id:        The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
   632  * - title:		The title of this menu item.
   739  * - post_parent:      The DB ID of the original object's parent object, if any (0 otherwise).
   633  * - target: 		The target attribute of the link element for this menu item.
   740  * - post_title:       A "no title" label if menu item represents a post that lacks a title.
   634  * - attr_title:	The title attribute of the link element for this menu item.
   741  * - target:           The target attribute of the link element for this menu item.
   635  * - classes:		The array of class attribute values for the link element of this menu item.
   742  * - title:            The title of this menu item.
   636  * - xfn:		The XFN relationship expressed in the link of this menu item.
   743  * - type:             The family of objects originally represented, such as "post_type" or "taxonomy."
   637  * - description:	The description of this menu item.
   744  * - type_label:       The singular label used to describe this type of menu item.
       
   745  * - url:              The URL to which this menu item points.
       
   746  * - xfn:              The XFN relationship expressed in the link of this menu item.
       
   747  * - _invalid:         Whether the menu item represents an object that no longer exists.
   638  *
   748  *
   639  * @since 3.0.0
   749  * @since 3.0.0
   640  *
   750  *
   641  * @param object $menu_item The menu item to modify.
   751  * @param object $menu_item The menu item to modify.
   642  * @return object $menu_item The menu item with standard menu item properties.
   752  * @return object $menu_item The menu item with standard menu item properties.
   643  */
   753  */
   644 function wp_setup_nav_menu_item( $menu_item ) {
   754 function wp_setup_nav_menu_item( $menu_item ) {
   645 	if ( isset( $menu_item->post_type ) ) {
   755 	if ( isset( $menu_item->post_type ) ) {
   646 		if ( 'nav_menu_item' == $menu_item->post_type ) {
   756 		if ( 'nav_menu_item' == $menu_item->post_type ) {
   647 			$menu_item->db_id = (int) $menu_item->ID;
   757 			$menu_item->db_id = (int) $menu_item->ID;
   648 			$menu_item->menu_item_parent = empty( $menu_item->menu_item_parent ) ? get_post_meta( $menu_item->ID, '_menu_item_menu_item_parent', true ) : $menu_item->menu_item_parent;
   758 			$menu_item->menu_item_parent = ! isset( $menu_item->menu_item_parent ) ? get_post_meta( $menu_item->ID, '_menu_item_menu_item_parent', true ) : $menu_item->menu_item_parent;
   649 			$menu_item->object_id = empty( $menu_item->object_id ) ? get_post_meta( $menu_item->ID, '_menu_item_object_id', true ) : $menu_item->object_id;
   759 			$menu_item->object_id = ! isset( $menu_item->object_id ) ? get_post_meta( $menu_item->ID, '_menu_item_object_id', true ) : $menu_item->object_id;
   650 			$menu_item->object = empty( $menu_item->object ) ? get_post_meta( $menu_item->ID, '_menu_item_object', true ) : $menu_item->object;
   760 			$menu_item->object = ! isset( $menu_item->object ) ? get_post_meta( $menu_item->ID, '_menu_item_object', true ) : $menu_item->object;
   651 			$menu_item->type = empty( $menu_item->type ) ? get_post_meta( $menu_item->ID, '_menu_item_type', true ) : $menu_item->type;
   761 			$menu_item->type = ! isset( $menu_item->type ) ? get_post_meta( $menu_item->ID, '_menu_item_type', true ) : $menu_item->type;
   652 
   762 
   653 			if ( 'post_type' == $menu_item->type ) {
   763 			if ( 'post_type' == $menu_item->type ) {
   654 				$object = get_post_type_object( $menu_item->object );
   764 				$object = get_post_type_object( $menu_item->object );
   655 				if ( $object ) {
   765 				if ( $object ) {
   656 					$menu_item->type_label = $object->labels->singular_name;
   766 					$menu_item->type_label = $object->labels->singular_name;
   657 				} else {
   767 				} else {
   658 					$menu_item->type_label = $menu_item->object;
   768 					$menu_item->type_label = $menu_item->object;
   659 					$menu_item->_invalid = true;
   769 					$menu_item->_invalid = true;
   660 				}
   770 				}
   661 
   771 
       
   772 				if ( 'trash' === get_post_status( $menu_item->object_id ) ) {
       
   773 					$menu_item->_invalid = true;
       
   774 				}
       
   775 
   662 				$menu_item->url = get_permalink( $menu_item->object_id );
   776 				$menu_item->url = get_permalink( $menu_item->object_id );
   663 
   777 
   664 				$original_object = get_post( $menu_item->object_id );
   778 				$original_object = get_post( $menu_item->object_id );
   665 				$original_title = $original_object->post_title;
   779 				/** This filter is documented in wp-includes/post-template.php */
       
   780 				$original_title = apply_filters( 'the_title', $original_object->post_title, $original_object->ID );
   666 
   781 
   667 				if ( '' === $original_title ) {
   782 				if ( '' === $original_title ) {
       
   783 					/* translators: %d: ID of a post */
   668 					$original_title = sprintf( __( '#%d (no title)' ), $original_object->ID );
   784 					$original_title = sprintf( __( '#%d (no title)' ), $original_object->ID );
   669 				}
   785 				}
   670 
   786 
   671 				$menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
   787 				$menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
   672 
   788 
       
   789 			} elseif ( 'post_type_archive' == $menu_item->type ) {
       
   790 				$object =  get_post_type_object( $menu_item->object );
       
   791 				if ( $object ) {
       
   792 					$menu_item->title = '' == $menu_item->post_title ? $object->labels->archives : $menu_item->post_title;
       
   793 					$post_type_description = $object->description;
       
   794 				} else {
       
   795 					$menu_item->_invalid = true;
       
   796 					$post_type_description = '';
       
   797 				}
       
   798 
       
   799 				$menu_item->type_label = __( 'Post Type Archive' );
       
   800 				$post_content = wp_trim_words( $menu_item->post_content, 200 );
       
   801 				$post_type_description = '' == $post_content ? $post_type_description : $post_content;
       
   802 				$menu_item->url = get_post_type_archive_link( $menu_item->object );
   673 			} elseif ( 'taxonomy' == $menu_item->type ) {
   803 			} elseif ( 'taxonomy' == $menu_item->type ) {
   674 				$object = get_taxonomy( $menu_item->object );
   804 				$object = get_taxonomy( $menu_item->object );
   675 				if ( $object ) {
   805 				if ( $object ) {
   676 					$menu_item->type_label = $object->labels->singular_name;
   806 					$menu_item->type_label = $object->labels->singular_name;
   677 				} else {
   807 				} else {
   688 				$menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
   818 				$menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
   689 
   819 
   690 			} else {
   820 			} else {
   691 				$menu_item->type_label = __('Custom Link');
   821 				$menu_item->type_label = __('Custom Link');
   692 				$menu_item->title = $menu_item->post_title;
   822 				$menu_item->title = $menu_item->post_title;
   693 				$menu_item->url = empty( $menu_item->url ) ? get_post_meta( $menu_item->ID, '_menu_item_url', true ) : $menu_item->url;
   823 				$menu_item->url = ! isset( $menu_item->url ) ? get_post_meta( $menu_item->ID, '_menu_item_url', true ) : $menu_item->url;
   694 			}
   824 			}
   695 
   825 
   696 			$menu_item->target = empty( $menu_item->target ) ? get_post_meta( $menu_item->ID, '_menu_item_target', true ) : $menu_item->target;
   826 			$menu_item->target = ! isset( $menu_item->target ) ? get_post_meta( $menu_item->ID, '_menu_item_target', true ) : $menu_item->target;
   697 
   827 
   698 			/**
   828 			/**
   699 			 * Filter a navigation menu item's title attribute.
   829 			 * Filters a navigation menu item's title attribute.
   700 			 *
   830 			 *
   701 			 * @since 3.0.0
   831 			 * @since 3.0.0
   702 			 *
   832 			 *
   703 			 * @param string $item_title The menu item title attribute.
   833 			 * @param string $item_title The menu item title attribute.
   704 			 */
   834 			 */
   705 			$menu_item->attr_title = empty( $menu_item->attr_title ) ? apply_filters( 'nav_menu_attr_title', $menu_item->post_excerpt ) : $menu_item->attr_title;
   835 			$menu_item->attr_title = ! isset( $menu_item->attr_title ) ? apply_filters( 'nav_menu_attr_title', $menu_item->post_excerpt ) : $menu_item->attr_title;
   706 
   836 
   707 			if ( empty( $menu_item->description ) ) {
   837 			if ( ! isset( $menu_item->description ) ) {
   708 				/**
   838 				/**
   709 				 * Filter a navigation menu item's description.
   839 				 * Filters a navigation menu item's description.
   710 				 *
   840 				 *
   711 				 * @since 3.0.0
   841 				 * @since 3.0.0
   712 				 *
   842 				 *
   713 				 * @param string $description The menu item description.
   843 				 * @param string $description The menu item description.
   714 				 */
   844 				 */
   715 				$menu_item->description = apply_filters( 'nav_menu_description', wp_trim_words( $menu_item->post_content, 200 ) );
   845 				$menu_item->description = apply_filters( 'nav_menu_description', wp_trim_words( $menu_item->post_content, 200 ) );
   716 			}
   846 			}
   717 
   847 
   718 			$menu_item->classes = empty( $menu_item->classes ) ? (array) get_post_meta( $menu_item->ID, '_menu_item_classes', true ) : $menu_item->classes;
   848 			$menu_item->classes = ! isset( $menu_item->classes ) ? (array) get_post_meta( $menu_item->ID, '_menu_item_classes', true ) : $menu_item->classes;
   719 			$menu_item->xfn = empty( $menu_item->xfn ) ? get_post_meta( $menu_item->ID, '_menu_item_xfn', true ) : $menu_item->xfn;
   849 			$menu_item->xfn = ! isset( $menu_item->xfn ) ? get_post_meta( $menu_item->ID, '_menu_item_xfn', true ) : $menu_item->xfn;
   720 		} else {
   850 		} else {
   721 			$menu_item->db_id = 0;
   851 			$menu_item->db_id = 0;
   722 			$menu_item->menu_item_parent = 0;
   852 			$menu_item->menu_item_parent = 0;
   723 			$menu_item->object_id = (int) $menu_item->ID;
   853 			$menu_item->object_id = (int) $menu_item->ID;
   724 			$menu_item->type = 'post_type';
   854 			$menu_item->type = 'post_type';
   725 
   855 
   726 			$object = get_post_type_object( $menu_item->post_type );
   856 			$object = get_post_type_object( $menu_item->post_type );
   727 			$menu_item->object = $object->name;
   857 			$menu_item->object = $object->name;
   728 			$menu_item->type_label = $object->labels->singular_name;
   858 			$menu_item->type_label = $object->labels->singular_name;
   729 
   859 
   730 			if ( '' === $menu_item->post_title )
   860 			if ( '' === $menu_item->post_title ) {
       
   861 				/* translators: %d: ID of a post */
   731 				$menu_item->post_title = sprintf( __( '#%d (no title)' ), $menu_item->ID );
   862 				$menu_item->post_title = sprintf( __( '#%d (no title)' ), $menu_item->ID );
       
   863 			}
   732 
   864 
   733 			$menu_item->title = $menu_item->post_title;
   865 			$menu_item->title = $menu_item->post_title;
   734 			$menu_item->url = get_permalink( $menu_item->ID );
   866 			$menu_item->url = get_permalink( $menu_item->ID );
   735 			$menu_item->target = '';
   867 			$menu_item->target = '';
   736 
   868 
   763 		$menu_item->xfn = '';
   895 		$menu_item->xfn = '';
   764 
   896 
   765 	}
   897 	}
   766 
   898 
   767 	/**
   899 	/**
   768 	 * Filter a navigation menu item object.
   900 	 * Filters a navigation menu item object.
   769 	 *
   901 	 *
   770 	 * @since 3.0.0
   902 	 * @since 3.0.0
   771 	 *
   903 	 *
   772 	 * @param object $menu_item The menu item object.
   904 	 * @param object $menu_item The menu item object.
   773 	 */
   905 	 */
   777 /**
   909 /**
   778  * Get the menu items associated with a particular object.
   910  * Get the menu items associated with a particular object.
   779  *
   911  *
   780  * @since 3.0.0
   912  * @since 3.0.0
   781  *
   913  *
   782  * @param int $object_id The ID of the original object.
   914  * @param int    $object_id   The ID of the original object.
   783  * @param string $object_type The type of object, such as "taxonomy" or "post_type."
   915  * @param string $object_type The type of object, such as "taxonomy" or "post_type."
   784  * @param string $taxonomy If $object_type is "taxonomy", $taxonomy is the name of the tax that $object_id belongs to
   916  * @param string $taxonomy    If $object_type is "taxonomy", $taxonomy is the name of the tax that $object_id belongs to
   785  * @return array The array of menu item IDs; empty array if none;
   917  * @return array The array of menu item IDs; empty array if none;
   786  */
   918  */
   787 function wp_get_associated_nav_menu_items( $object_id = 0, $object_type = 'post_type', $taxonomy = '' ) {
   919 function wp_get_associated_nav_menu_items( $object_id = 0, $object_type = 'post_type', $taxonomy = '' ) {
   788 	$object_id = (int) $object_id;
   920 	$object_id = (int) $object_id;
   789 	$menu_item_ids = array();
   921 	$menu_item_ids = array();
   796 			'post_status' => 'any',
   928 			'post_status' => 'any',
   797 			'post_type' => 'nav_menu_item',
   929 			'post_type' => 'nav_menu_item',
   798 			'posts_per_page' => -1,
   930 			'posts_per_page' => -1,
   799 		)
   931 		)
   800 	);
   932 	);
   801 	foreach( (array) $menu_items as $menu_item ) {
   933 	foreach ( (array) $menu_items as $menu_item ) {
   802 		if ( isset( $menu_item->ID ) && is_nav_menu_item( $menu_item->ID ) ) {
   934 		if ( isset( $menu_item->ID ) && is_nav_menu_item( $menu_item->ID ) ) {
   803 			$menu_item_type = get_post_meta( $menu_item->ID, '_menu_item_type', true );
   935 			$menu_item_type = get_post_meta( $menu_item->ID, '_menu_item_type', true );
   804 			if (
   936 			if (
   805 				'post_type' == $object_type &&
   937 				'post_type' == $object_type &&
   806 				'post_type' == $menu_item_type
   938 				'post_type' == $menu_item_type
   831 function _wp_delete_post_menu_item( $object_id = 0 ) {
   963 function _wp_delete_post_menu_item( $object_id = 0 ) {
   832 	$object_id = (int) $object_id;
   964 	$object_id = (int) $object_id;
   833 
   965 
   834 	$menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'post_type' );
   966 	$menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'post_type' );
   835 
   967 
   836 	foreach( (array) $menu_item_ids as $menu_item_id ) {
   968 	foreach ( (array) $menu_item_ids as $menu_item_id ) {
   837 		wp_delete_post( $menu_item_id, true );
   969 		wp_delete_post( $menu_item_id, true );
   838 	}
   970 	}
   839 }
   971 }
   840 
   972 
   841 /**
   973 /**
   842  * Callback for handling a menu item when its original object is deleted.
   974  * Serves as a callback for handling a menu item when its original object is deleted.
   843  *
   975  *
   844  * @since 3.0.0
   976  * @since 3.0.0
   845  * @access private
   977  * @access private
   846  *
   978  *
   847  * @param int $object_id The ID of the original object being trashed.
   979  * @param int    $object_id Optional. The ID of the original object being trashed. Default 0.
   848  *
   980  * @param int    $tt_id     Term taxonomy ID. Unused.
       
   981  * @param string $taxonomy  Taxonomy slug.
   849  */
   982  */
   850 function _wp_delete_tax_menu_item( $object_id = 0, $tt_id, $taxonomy ) {
   983 function _wp_delete_tax_menu_item( $object_id = 0, $tt_id, $taxonomy ) {
   851 	$object_id = (int) $object_id;
   984 	$object_id = (int) $object_id;
   852 
   985 
   853 	$menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'taxonomy', $taxonomy );
   986 	$menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'taxonomy', $taxonomy );
   854 
   987 
   855 	foreach( (array) $menu_item_ids as $menu_item_id ) {
   988 	foreach ( (array) $menu_item_ids as $menu_item_id ) {
   856 		wp_delete_post( $menu_item_id, true );
   989 		wp_delete_post( $menu_item_id, true );
   857 	}
   990 	}
   858 }
   991 }
   859 
   992 
   860 /**
   993 /**
   863  * @since 3.0.0
   996  * @since 3.0.0
   864  * @access private
   997  * @access private
   865  *
   998  *
   866  * @param string $new_status The new status of the post object.
   999  * @param string $new_status The new status of the post object.
   867  * @param string $old_status The old status of the post object.
  1000  * @param string $old_status The old status of the post object.
   868  * @param object $post The post object being transitioned from one status to another.
  1001  * @param object $post       The post object being transitioned from one status to another.
   869  * @return void
       
   870  */
  1002  */
   871 function _wp_auto_add_pages_to_menu( $new_status, $old_status, $post ) {
  1003 function _wp_auto_add_pages_to_menu( $new_status, $old_status, $post ) {
   872 	if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post->post_type )
  1004 	if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post->post_type )
   873 		return;
  1005 		return;
   874 	if ( ! empty( $post->post_parent ) )
  1006 	if ( ! empty( $post->post_parent ) )
   896 				continue 2;
  1028 				continue 2;
   897 		}
  1029 		}
   898 		wp_update_nav_menu_item( $menu_id, 0, $args );
  1030 		wp_update_nav_menu_item( $menu_id, 0, $args );
   899 	}
  1031 	}
   900 }
  1032 }
       
  1033 
       
  1034 /**
       
  1035  * Delete auto-draft posts associated with the supplied changeset.
       
  1036  *
       
  1037  * @since 4.8.0
       
  1038  * @access private
       
  1039  *
       
  1040  * @param int $post_id Post ID for the customize_changeset.
       
  1041  */
       
  1042 function _wp_delete_customize_changeset_dependent_auto_drafts( $post_id ) {
       
  1043 	$post = get_post( $post_id );
       
  1044 
       
  1045 	if ( ! $post || 'customize_changeset' !== $post->post_type ) {
       
  1046 		return;
       
  1047 	}
       
  1048 
       
  1049 	$data = json_decode( $post->post_content, true );
       
  1050 	if ( empty( $data['nav_menus_created_posts']['value'] ) ) {
       
  1051 		return;
       
  1052 	}
       
  1053 	remove_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
       
  1054 	foreach ( $data['nav_menus_created_posts']['value'] as $stub_post_id ) {
       
  1055 		if ( empty( $stub_post_id ) ) {
       
  1056 			continue;
       
  1057 		}
       
  1058 		if ( 'auto-draft' === get_post_status( $stub_post_id ) ) {
       
  1059 			wp_delete_post( $stub_post_id, true );
       
  1060 		} elseif ( 'draft' === get_post_status( $stub_post_id ) ) {
       
  1061 			wp_trash_post( $stub_post_id );
       
  1062 			delete_post_meta( $stub_post_id, '_customize_changeset_uuid' );
       
  1063 		}
       
  1064 	}
       
  1065 	add_action( 'delete_post', '_wp_delete_customize_changeset_dependent_auto_drafts' );
       
  1066 }
       
  1067 
       
  1068 /**
       
  1069  * Handle menu config after theme change.
       
  1070  *
       
  1071  * @access private
       
  1072  * @since 4.9.0
       
  1073  */
       
  1074 function _wp_menus_changed() {
       
  1075 	$old_nav_menu_locations    = get_option( 'theme_switch_menu_locations', array() );
       
  1076 	$new_nav_menu_locations    = get_nav_menu_locations();
       
  1077 	$mapped_nav_menu_locations = wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations );
       
  1078 
       
  1079 	set_theme_mod( 'nav_menu_locations', $mapped_nav_menu_locations );
       
  1080 	delete_option( 'theme_switch_menu_locations' );
       
  1081 }
       
  1082 
       
  1083 /**
       
  1084  * Maps nav menu locations according to assignments in previously active theme.
       
  1085  *
       
  1086  * @since 4.9.0
       
  1087  *
       
  1088  * @param array $new_nav_menu_locations New nav menu locations assignments.
       
  1089  * @param array $old_nav_menu_locations Old nav menu locations assignments.
       
  1090  * @return array Nav menus mapped to new nav menu locations.
       
  1091  */
       
  1092 function wp_map_nav_menu_locations( $new_nav_menu_locations, $old_nav_menu_locations ) {
       
  1093 	$registered_nav_menus   = get_registered_nav_menus();
       
  1094 	$new_nav_menu_locations = array_intersect_key( $new_nav_menu_locations, $registered_nav_menus );
       
  1095 
       
  1096 	// Short-circuit if there are no old nav menu location assignments to map.
       
  1097 	if ( empty( $old_nav_menu_locations ) ) {
       
  1098 		return $new_nav_menu_locations;
       
  1099 	}
       
  1100 
       
  1101 	// If old and new theme have just one location, map it and we're done.
       
  1102 	if ( 1 === count( $old_nav_menu_locations ) && 1 === count( $registered_nav_menus ) ) {
       
  1103 		$new_nav_menu_locations[ key( $registered_nav_menus ) ] = array_pop( $old_nav_menu_locations );
       
  1104 		return $new_nav_menu_locations;
       
  1105 	}
       
  1106 
       
  1107 	$old_locations = array_keys( $old_nav_menu_locations );
       
  1108 
       
  1109 	// Map locations with the same slug.
       
  1110 	foreach ( $registered_nav_menus as $location => $name ) {
       
  1111 		if ( in_array( $location, $old_locations, true ) ) {
       
  1112 			$new_nav_menu_locations[ $location ] = $old_nav_menu_locations[ $location ];
       
  1113 			unset( $old_nav_menu_locations[ $location ] );
       
  1114 		}
       
  1115 	}
       
  1116 
       
  1117 	// If there are no old nav menu locations left, then we're done.
       
  1118 	if ( empty( $old_nav_menu_locations ) ) {
       
  1119 		return $new_nav_menu_locations;
       
  1120 	}
       
  1121 
       
  1122 	/*
       
  1123 	 * If old and new theme both have locations that contain phrases
       
  1124 	 * from within the same group, make an educated guess and map it.
       
  1125 	 */
       
  1126 	$common_slug_groups = array(
       
  1127 		array( 'primary', 'menu-1', 'main', 'header', 'navigation', 'top' ),
       
  1128 		array( 'secondary', 'menu-2', 'footer', 'subsidiary', 'bottom' ),
       
  1129 		array( 'social' ),
       
  1130 	);
       
  1131 
       
  1132 	// Go through each group...
       
  1133 	foreach ( $common_slug_groups as $slug_group ) {
       
  1134 
       
  1135 		// ...and see if any of these slugs...
       
  1136 		foreach ( $slug_group as $slug ) {
       
  1137 
       
  1138 			// ...and any of the new menu locations...
       
  1139 			foreach ( $registered_nav_menus as $new_location => $name ) {
       
  1140 
       
  1141 				// ...actually match!
       
  1142 				if ( false === stripos( $new_location, $slug ) && false === stripos( $slug, $new_location ) ) {
       
  1143 					continue;
       
  1144 				}
       
  1145 
       
  1146 				// Then see if any of the old locations...
       
  1147 				foreach ( $old_nav_menu_locations as $location => $menu_id ) {
       
  1148 
       
  1149 					// ...and any slug in the same group...
       
  1150 					foreach ( $slug_group as $slug ) {
       
  1151 
       
  1152 						// ... have a match as well.
       
  1153 						if ( false === stripos( $location, $slug ) && false === stripos( $slug, $location ) ) {
       
  1154 							continue;
       
  1155 						}
       
  1156 
       
  1157 						// Make sure this location wasn't mapped and removed previously.
       
  1158 						if ( ! empty( $old_nav_menu_locations[ $location ] ) ) {
       
  1159 
       
  1160 							// We have a match that can be mapped!
       
  1161 							$new_nav_menu_locations[ $new_location ] = $old_nav_menu_locations[ $location ];
       
  1162 
       
  1163 							// Remove the mapped location so it can't be mapped again.
       
  1164 							unset( $old_nav_menu_locations[ $location ] );
       
  1165 
       
  1166 							// Go back and check the next new menu location.
       
  1167 							continue 3;
       
  1168 						}
       
  1169 					} // endforeach ( $slug_group as $slug )
       
  1170 				} // endforeach ( $old_nav_menu_locations as $location => $menu_id )
       
  1171 			} // endforeach foreach ( $registered_nav_menus as $new_location => $name )
       
  1172 		} // endforeach ( $slug_group as $slug )
       
  1173 	} // endforeach ( $common_slug_groups as $slug_group )
       
  1174 
       
  1175 	return $new_nav_menu_locations;
       
  1176 }