home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-includes / nav-menu.php < prev    next >
Encoding:
PHP Script  |  2017-10-25  |  38.6 KB  |  1,177 lines

  1. <?php
  2. /**
  3.  * Navigation Menu functions
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Nav_Menus
  7.  * @since 3.0.0
  8.  */
  9.  
  10. /**
  11.  * Returns a navigation menu object.
  12.  *
  13.  * @since 3.0.0
  14.  *
  15.  * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
  16.  * @return WP_Term|false False if $menu param isn't supplied or term does not exist, menu object if successful.
  17.  */
  18. function wp_get_nav_menu_object( $menu ) {
  19.     $menu_obj = false;
  20.  
  21.     if ( is_object( $menu ) ) {
  22.         $menu_obj = $menu;
  23.     }
  24.  
  25.     if ( $menu && ! $menu_obj ) {
  26.         $menu_obj = get_term( $menu, 'nav_menu' );
  27.  
  28.         if ( ! $menu_obj ) {
  29.             $menu_obj = get_term_by( 'slug', $menu, 'nav_menu' );
  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 ) ) {
  38.         $menu_obj = false;
  39.     }
  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 );
  50. }
  51.  
  52. /**
  53.  * Check if the given ID is a navigation menu.
  54.  *
  55.  * Returns true if it is; false otherwise.
  56.  *
  57.  * @since 3.0.0
  58.  *
  59.  * @param int|string|WP_Term $menu Menu ID, slug, name, or object of menu to check.
  60.  * @return bool Whether the menu exists.
  61.  */
  62. function is_nav_menu( $menu ) {
  63.     if ( ! $menu )
  64.         return false;
  65.  
  66.     $menu_obj = wp_get_nav_menu_object( $menu );
  67.  
  68.     if (
  69.         $menu_obj &&
  70.         ! is_wp_error( $menu_obj ) &&
  71.         ! empty( $menu_obj->taxonomy ) &&
  72.         'nav_menu' == $menu_obj->taxonomy
  73.     )
  74.         return true;
  75.  
  76.     return false;
  77. }
  78.  
  79. /**
  80.  * Registers navigation menu locations for a theme.
  81.  *
  82.  * @since 3.0.0
  83.  *
  84.  * @global array $_wp_registered_nav_menus
  85.  *
  86.  * @param array $locations Associative array of menu location identifiers (like a slug) and descriptive text.
  87.  */
  88. function register_nav_menus( $locations = array() ) {
  89.     global $_wp_registered_nav_menus;
  90.  
  91.     add_theme_support( 'menus' );
  92.  
  93.     $_wp_registered_nav_menus = array_merge( (array) $_wp_registered_nav_menus, $locations );
  94. }
  95.  
  96. /**
  97.  * Unregisters a navigation menu location for a theme.
  98.  *
  99.  * @since 3.1.0
  100.  * @global array $_wp_registered_nav_menus
  101.  *
  102.  * @param string $location The menu location identifier.
  103.  * @return bool True on success, false on failure.
  104.  */
  105. function unregister_nav_menu( $location ) {
  106.     global $_wp_registered_nav_menus;
  107.  
  108.     if ( is_array( $_wp_registered_nav_menus ) && isset( $_wp_registered_nav_menus[$location] ) ) {
  109.         unset( $_wp_registered_nav_menus[$location] );
  110.         if ( empty( $_wp_registered_nav_menus ) ) {
  111.             _remove_theme_support( 'menus' );
  112.         }
  113.         return true;
  114.     }
  115.     return false;
  116. }
  117.  
  118. /**
  119.  * Registers a navigation menu location for a theme.
  120.  *
  121.  * @since 3.0.0
  122.  *
  123.  * @param string $location    Menu location identifier, like a slug.
  124.  * @param string $description Menu location descriptive text.
  125.  */
  126. function register_nav_menu( $location, $description ) {
  127.     register_nav_menus( array( $location => $description ) );
  128. }
  129. /**
  130.  * Retrieves all registered navigation menu locations in a theme.
  131.  *
  132.  * @since 3.0.0
  133.  *
  134.  * @global array $_wp_registered_nav_menus
  135.  *
  136.  * @return array Registered navigation menu locations. If none are registered, an empty array.
  137.  */
  138. function get_registered_nav_menus() {
  139.     global $_wp_registered_nav_menus;
  140.     if ( isset( $_wp_registered_nav_menus ) )
  141.         return $_wp_registered_nav_menus;
  142.     return array();
  143. }
  144.  
  145. /**
  146.  * Retrieves all registered navigation menu locations and the menus assigned to them.
  147.  *
  148.  * @since 3.0.0
  149.  *
  150.  * @return array Registered navigation menu locations and the menus assigned them.
  151.  *               If none are registered, an empty array.
  152.  */
  153.  
  154. function get_nav_menu_locations() {
  155.     $locations = get_theme_mod( 'nav_menu_locations' );
  156.     return ( is_array( $locations ) ) ? $locations : array();
  157. }
  158.  
  159. /**
  160.  * Determines whether a registered nav menu location has a menu assigned to it.
  161.  *
  162.  * @since 3.0.0
  163.  *
  164.  * @param string $location Menu location identifier.
  165.  * @return bool Whether location has a menu.
  166.  */
  167. function has_nav_menu( $location ) {
  168.     $has_nav_menu = false;
  169.  
  170.     $registered_nav_menus = get_registered_nav_menus();
  171.     if ( isset( $registered_nav_menus[ $location ] ) ) {
  172.         $locations = get_nav_menu_locations();
  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 = '';
  197.  
  198.     $locations = get_nav_menu_locations();
  199.  
  200.     if ( isset( $locations[ $location ] ) ) {
  201.         $menu = wp_get_nav_menu_object( $locations[ $location ] );
  202.  
  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.
  221.  *
  222.  * @since 3.0.0
  223.  *
  224.  * @param int $menu_item_id The ID of the potential nav menu item.
  225.  * @return bool Whether the given ID is that of a nav menu item.
  226.  */
  227. function is_nav_menu_item( $menu_item_id = 0 ) {
  228.     return ( ! is_wp_error( $menu_item_id ) && ( 'nav_menu_item' == get_post_type( $menu_item_id ) ) );
  229. }
  230.  
  231. /**
  232.  * Creates a navigation menu.
  233.  *
  234.  * Note that `$menu_name` is expected to be pre-slashed.
  235.  *
  236.  * @since 3.0.0
  237.  *
  238.  * @param string $menu_name Menu name.
  239.  * @return int|WP_Error Menu ID on success, WP_Error object on failure.
  240.  */
  241. function wp_create_nav_menu( $menu_name ) {
  242.     // expected_slashed ($menu_name)
  243.     return wp_update_nav_menu_object( 0, array( 'menu-name' => $menu_name ) );
  244. }
  245.  
  246. /**
  247.  * Delete a Navigation Menu.
  248.  *
  249.  * @since 3.0.0
  250.  *
  251.  * @param int|string|WP_Term $menu Menu ID, slug, name, or object.
  252.  * @return bool|WP_Error True on success, false or WP_Error object on failure.
  253.  */
  254. function wp_delete_nav_menu( $menu ) {
  255.     $menu = wp_get_nav_menu_object( $menu );
  256.     if ( ! $menu )
  257.         return false;
  258.  
  259.     $menu_objects = get_objects_in_term( $menu->term_id, 'nav_menu' );
  260.     if ( ! empty( $menu_objects ) ) {
  261.         foreach ( $menu_objects as $item ) {
  262.             wp_delete_post( $item );
  263.         }
  264.     }
  265.  
  266.     $result = wp_delete_term( $menu->term_id, 'nav_menu' );
  267.  
  268.     // Remove this menu from any locations.
  269.     $locations = get_nav_menu_locations();
  270.     foreach ( $locations as $location => $menu_id ) {
  271.         if ( $menu_id == $menu->term_id )
  272.             $locations[ $location ] = 0;
  273.     }
  274.     set_theme_mod( 'nav_menu_locations', $locations );
  275.  
  276.     if ( $result && !is_wp_error($result) )
  277.  
  278.         /**
  279.          * Fires after a navigation menu has been successfully deleted.
  280.          *
  281.          * @since 3.0.0
  282.          *
  283.          * @param int $term_id ID of the deleted menu.
  284.          */
  285.         do_action( 'wp_delete_nav_menu', $menu->term_id );
  286.  
  287.     return $result;
  288. }
  289.  
  290. /**
  291.  * Save the properties of a menu or create a new menu with those properties.
  292.  *
  293.  * Note that `$menu_data` is expected to be pre-slashed.
  294.  *
  295.  * @since 3.0.0
  296.  *
  297.  * @param int   $menu_id   The ID of the menu or "0" to create a new menu.
  298.  * @param array $menu_data The array of menu data.
  299.  * @return int|WP_Error Menu ID on success, WP_Error object on failure.
  300.  */
  301. function wp_update_nav_menu_object( $menu_id = 0, $menu_data = array() ) {
  302.     // expected_slashed ($menu_data)
  303.     $menu_id = (int) $menu_id;
  304.  
  305.     $_menu = wp_get_nav_menu_object( $menu_id );
  306.  
  307.     $args = array(
  308.         'description' => ( isset( $menu_data['description'] ) ? $menu_data['description']  : '' ),
  309.         'name'        => ( isset( $menu_data['menu-name']   ) ? $menu_data['menu-name']    : '' ),
  310.         'parent'      => ( isset( $menu_data['parent']      ) ? (int) $menu_data['parent'] : 0  ),
  311.         'slug'        => null,
  312.     );
  313.  
  314.     // double-check that we're not going to have one menu take the name of another
  315.     $_possible_existing = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
  316.     if (
  317.         $_possible_existing &&
  318.         ! is_wp_error( $_possible_existing ) &&
  319.         isset( $_possible_existing->term_id ) &&
  320.         $_possible_existing->term_id != $menu_id
  321.     ) {
  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.     }
  329.  
  330.     // menu doesn't already exist, so create a new menu
  331.     if ( ! $_menu || is_wp_error( $_menu ) ) {
  332.         $menu_exists = get_term_by( 'name', $menu_data['menu-name'], 'nav_menu' );
  333.  
  334.         if ( $menu_exists ) {
  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.         }
  342.  
  343.         $_menu = wp_insert_term( $menu_data['menu-name'], 'nav_menu', $args );
  344.  
  345.         if ( is_wp_error( $_menu ) )
  346.             return $_menu;
  347.  
  348.         /**
  349.          * Fires after a navigation menu is successfully created.
  350.          *
  351.          * @since 3.0.0
  352.          *
  353.          * @param int   $term_id   ID of the new menu.
  354.          * @param array $menu_data An array of menu data.
  355.          */
  356.         do_action( 'wp_create_nav_menu', $_menu['term_id'], $menu_data );
  357.  
  358.         return (int) $_menu['term_id'];
  359.     }
  360.  
  361.     if ( ! $_menu || ! isset( $_menu->term_id ) )
  362.         return 0;
  363.  
  364.     $menu_id = (int) $_menu->term_id;
  365.  
  366.     $update_response = wp_update_term( $menu_id, 'nav_menu', $args );
  367.  
  368.     if ( is_wp_error( $update_response ) )
  369.         return $update_response;
  370.  
  371.     $menu_id = (int) $update_response['term_id'];
  372.  
  373.     /**
  374.      * Fires after a navigation menu has been successfully updated.
  375.      *
  376.      * @since 3.0.0
  377.      *
  378.      * @param int   $menu_id   ID of the updated menu.
  379.      * @param array $menu_data An array of menu data.
  380.      */
  381.     do_action( 'wp_update_nav_menu', $menu_id, $menu_data );
  382.     return $menu_id;
  383. }
  384.  
  385. /**
  386.  * Save the properties of a menu item or create a new one.
  387.  *
  388.  * The menu-item-title, menu-item-description, and menu-item-attr-title are expected
  389.  * to be pre-slashed since they are passed directly into `wp_insert_post()`.
  390.  *
  391.  * @since 3.0.0
  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.
  396.  * @return int|WP_Error The menu item's database ID or WP_Error object on failure.
  397.  */
  398. function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item_data = array() ) {
  399.     $menu_id = (int) $menu_id;
  400.     $menu_item_db_id = (int) $menu_item_db_id;
  401.  
  402.     // make sure that we don't convert non-nav_menu_item objects into nav_menu_item objects
  403.     if ( ! empty( $menu_item_db_id ) && ! is_nav_menu_item( $menu_item_db_id ) )
  404.         return new WP_Error( 'update_nav_menu_item_failed', __( 'The given object ID is not that of a menu item.' ) );
  405.  
  406.     $menu = wp_get_nav_menu_object( $menu_id );
  407.  
  408.     if ( ! $menu && 0 !== $menu_id ) {
  409.         return new WP_Error( 'invalid_menu_id', __( 'Invalid menu ID.' ) );
  410.     }
  411.  
  412.     if ( is_wp_error( $menu ) ) {
  413.         return $menu;
  414.     }
  415.  
  416.     $defaults = array(
  417.         'menu-item-db-id' => $menu_item_db_id,
  418.         'menu-item-object-id' => 0,
  419.         'menu-item-object' => '',
  420.         'menu-item-parent-id' => 0,
  421.         'menu-item-position' => 0,
  422.         'menu-item-type' => 'custom',
  423.         'menu-item-title' => '',
  424.         'menu-item-url' => '',
  425.         'menu-item-description' => '',
  426.         'menu-item-attr-title' => '',
  427.         'menu-item-target' => '',
  428.         'menu-item-classes' => '',
  429.         'menu-item-xfn' => '',
  430.         'menu-item-status' => '',
  431.     );
  432.  
  433.     $args = wp_parse_args( $menu_item_data, $defaults );
  434.  
  435.     if ( 0 == $menu_id ) {
  436.         $args['menu-item-position'] = 1;
  437.     } elseif ( 0 == (int) $args['menu-item-position'] ) {
  438.         $menu_items = 0 == $menu_id ? array() : (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );
  439.         $last_item = array_pop( $menu_items );
  440.         $args['menu-item-position'] = ( $last_item && isset( $last_item->menu_order ) ) ? 1 + $last_item->menu_order : count( $menu_items );
  441.     }
  442.  
  443.     $original_parent = 0 < $menu_item_db_id ? get_post_field( 'post_parent', $menu_item_db_id ) : 0;
  444.  
  445.     if ( 'custom' != $args['menu-item-type'] ) {
  446.         /* if non-custom menu item, then:
  447.             * use original object's URL
  448.             * blank default title to sync with original object's
  449.         */
  450.  
  451.         $args['menu-item-url'] = '';
  452.  
  453.         $original_title = '';
  454.         if ( 'taxonomy' == $args['menu-item-type'] ) {
  455.             $original_parent = get_term_field( 'parent', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );
  456.             $original_title = get_term_field( 'name', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );
  457.         } elseif ( 'post_type' == $args['menu-item-type'] ) {
  458.  
  459.             $original_object = get_post( $args['menu-item-object-id'] );
  460.             $original_parent = (int) $original_object->post_parent;
  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.             }
  467.         }
  468.  
  469.         if ( $args['menu-item-title'] == $original_title )
  470.             $args['menu-item-title'] = '';
  471.  
  472.         // hack to get wp to create a post object when too many properties are empty
  473.         if ( '' ==  $args['menu-item-title'] && '' == $args['menu-item-description'] )
  474.             $args['menu-item-description'] = ' ';
  475.     }
  476.  
  477.     // Populate the menu item object
  478.     $post = array(
  479.         'menu_order' => $args['menu-item-position'],
  480.         'ping_status' => 0,
  481.         'post_content' => $args['menu-item-description'],
  482.         'post_excerpt' => $args['menu-item-attr-title'],
  483.         'post_parent' => $original_parent,
  484.         'post_title' => $args['menu-item-title'],
  485.         'post_type' => 'nav_menu_item',
  486.     );
  487.  
  488.     $update = 0 != $menu_item_db_id;
  489.  
  490.     // New menu item. Default is draft status
  491.     if ( ! $update ) {
  492.         $post['ID'] = 0;
  493.         $post['post_status'] = 'publish' == $args['menu-item-status'] ? 'publish' : 'draft';
  494.         $menu_item_db_id = wp_insert_post( $post );
  495.         if ( ! $menu_item_db_id    || is_wp_error( $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 );
  510.     }
  511.  
  512.     // Associate the menu item with the menu term
  513.     // Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms()
  514.      if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {
  515.         wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' );
  516.     }
  517.  
  518.     if ( 'custom' == $args['menu-item-type'] ) {
  519.         $args['menu-item-object-id'] = $menu_item_db_id;
  520.         $args['menu-item-object'] = 'custom';
  521.     }
  522.  
  523.     $menu_item_db_id = (int) $menu_item_db_id;
  524.  
  525.     update_post_meta( $menu_item_db_id, '_menu_item_type', sanitize_key($args['menu-item-type']) );
  526.     update_post_meta( $menu_item_db_id, '_menu_item_menu_item_parent', strval( (int) $args['menu-item-parent-id'] ) );
  527.     update_post_meta( $menu_item_db_id, '_menu_item_object_id', strval( (int) $args['menu-item-object-id'] ) );
  528.     update_post_meta( $menu_item_db_id, '_menu_item_object', sanitize_key($args['menu-item-object']) );
  529.     update_post_meta( $menu_item_db_id, '_menu_item_target', sanitize_key($args['menu-item-target']) );
  530.  
  531.     $args['menu-item-classes'] = array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-classes'] ) );
  532.     $args['menu-item-xfn'] = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-xfn'] ) ) );
  533.     update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );
  534.     update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );
  535.     update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw($args['menu-item-url']) );
  536.  
  537.     if ( 0 == $menu_id )
  538.         update_post_meta( $menu_item_db_id, '_menu_item_orphaned', (string) time() );
  539.     elseif ( get_post_meta( $menu_item_db_id, '_menu_item_orphaned' ) )
  540.         delete_post_meta( $menu_item_db_id, '_menu_item_orphaned' );
  541.  
  542.     // Update existing menu item. Default is publish status
  543.     if ( $update ) {
  544.         $post['ID'] = $menu_item_db_id;
  545.         $post['post_status'] = 'draft' == $args['menu-item-status'] ? 'draft' : 'publish';
  546.         wp_update_post( $post );
  547.     }
  548.  
  549.     /**
  550.      * Fires after a navigation menu item has been updated.
  551.      *
  552.      * @since 3.0.0
  553.      *
  554.      * @see wp_update_nav_menu_item()
  555.      *
  556.      * @param int   $menu_id         ID of the updated menu.
  557.      * @param int   $menu_item_db_id ID of the updated menu item.
  558.      * @param array $args            An array of arguments used to update a menu item.
  559.      */
  560.     do_action( 'wp_update_nav_menu_item', $menu_id, $menu_item_db_id, $args );
  561.  
  562.     return $menu_item_db_id;
  563. }
  564.  
  565. /**
  566.  * Returns all navigation menu objects.
  567.  *
  568.  * @since 3.0.0
  569.  * @since 4.1.0 Default value of the 'orderby' argument was changed from 'none'
  570.  *              to 'name'.
  571.  *
  572.  * @param array $args Optional. Array of arguments passed on to get_terms().
  573.  *                    Default empty array.
  574.  * @return array Menu objects.
  575.  */
  576. function wp_get_nav_menus( $args = array() ) {
  577.     $defaults = array( 'hide_empty' => false, 'orderby' => 'name' );
  578.     $args = wp_parse_args( $args, $defaults );
  579.  
  580.     /**
  581.      * Filters the navigation menu objects being returned.
  582.      *
  583.      * @since 3.0.0
  584.      *
  585.      * @see get_terms()
  586.      *
  587.      * @param array $menus An array of menu objects.
  588.      * @param array $args  An array of arguments used to retrieve menu objects.
  589.      */
  590.     return apply_filters( 'wp_get_nav_menus', get_terms( 'nav_menu',  $args), $args );
  591. }
  592.  
  593. /**
  594.  * Return if a menu item is valid.
  595.  *
  596.  * @link https://core.trac.wordpress.org/ticket/13958
  597.  *
  598.  * @since 3.2.0
  599.  * @access private
  600.  *
  601.  * @param object $item The menu item to check.
  602.  * @return bool False if invalid, otherwise true.
  603.  */
  604. function _is_valid_nav_menu_item( $item ) {
  605.     return empty( $item->_invalid );
  606. }
  607.  
  608. /**
  609.  * Retrieves all menu items of a navigation menu.
  610.  *
  611.  * Note: Most arguments passed to the `$args` parameter ΓÇô save for 'output_key' ΓÇô are
  612.  * specifically for retrieving nav_menu_item posts from get_posts() and may only
  613.  * indirectly affect the ultimate ordering and content of the resulting nav menu
  614.  * items that get returned from this function.
  615.  *
  616.  * @since 3.0.0
  617.  *
  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.
  639.  */
  640. function wp_get_nav_menu_items( $menu, $args = array() ) {
  641.     $menu = wp_get_nav_menu_object( $menu );
  642.  
  643.     if ( ! $menu ) {
  644.         return false;
  645.     }
  646.  
  647.     static $fetched = array();
  648.  
  649.     $items = get_objects_in_term( $menu->term_id, 'nav_menu' );
  650.     if ( is_wp_error( $items ) ) {
  651.         return false;
  652.     }
  653.  
  654.     $defaults = array( 'order' => 'ASC', 'orderby' => 'menu_order', 'post_type' => 'nav_menu_item',
  655.         'post_status' => 'publish', 'output' => ARRAY_A, 'output_key' => 'menu_order', 'nopaging' => true );
  656.     $args = wp_parse_args( $args, $defaults );
  657.     $args['include'] = $items;
  658.  
  659.     if ( ! empty( $items ) ) {
  660.         $items = get_posts( $args );
  661.     } else {
  662.         $items = array();
  663.     }
  664.  
  665.     // Get all posts and terms at once to prime the caches
  666.     if ( empty( $fetched[ $menu->term_id ] ) && ! wp_using_ext_object_cache() ) {
  667.         $fetched[$menu->term_id] = true;
  668.         $posts = array();
  669.         $terms = array();
  670.         foreach ( $items as $item ) {
  671.             $object_id = get_post_meta( $item->ID, '_menu_item_object_id', true );
  672.             $object    = get_post_meta( $item->ID, '_menu_item_object',    true );
  673.             $type      = get_post_meta( $item->ID, '_menu_item_type',      true );
  674.  
  675.             if ( 'post_type' == $type )
  676.                 $posts[$object][] = $object_id;
  677.             elseif ( 'taxonomy' == $type)
  678.                 $terms[$object][] = $object_id;
  679.         }
  680.  
  681.         if ( ! empty( $posts ) ) {
  682.             foreach ( array_keys($posts) as $post_type ) {
  683.                 get_posts( array('post__in' => $posts[$post_type], 'post_type' => $post_type, 'nopaging' => true, 'update_post_term_cache' => false) );
  684.             }
  685.         }
  686.         unset($posts);
  687.  
  688.         if ( ! empty( $terms ) ) {
  689.             foreach ( array_keys($terms) as $taxonomy ) {
  690.                 get_terms( $taxonomy, array(
  691.                     'include' => $terms[ $taxonomy ],
  692.                     'hierarchical' => false,
  693.                 ) );
  694.             }
  695.         }
  696.         unset($terms);
  697.     }
  698.  
  699.     $items = array_map( 'wp_setup_nav_menu_item', $items );
  700.  
  701.     if ( ! is_admin() ) { // Remove invalid items only in front end
  702.         $items = array_filter( $items, '_is_valid_nav_menu_item' );
  703.     }
  704.  
  705.     if ( ARRAY_A == $args['output'] ) {
  706.         $items = wp_list_sort( $items, array(
  707.             $args['output_key'] => 'ASC',
  708.         ) );
  709.         $i = 1;
  710.         foreach ( $items as $k => $item ) {
  711.             $items[$k]->{$args['output_key']} = $i++;
  712.         }
  713.     }
  714.  
  715.     /**
  716.      * Filters the navigation menu items being returned.
  717.      *
  718.      * @since 3.0.0
  719.      *
  720.      * @param array  $items An array of menu item post objects.
  721.      * @param object $menu  The menu object.
  722.      * @param array  $args  An array of arguments used to retrieve menu item objects.
  723.      */
  724.     return apply_filters( 'wp_get_nav_menu_items', $items, $menu, $args );
  725. }
  726.  
  727. /**
  728.  * Decorates a menu item object with the shared navigation menu item properties.
  729.  *
  730.  * Properties:
  731.  * - ID:               The term_id if the menu item represents a taxonomy term.
  732.  * - attr_title:       The title attribute of the link element for this menu item.
  733.  * - classes:          The array of class attribute values for the link element of this menu item.
  734.  * - db_id:            The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist).
  735.  * - description:      The description of this menu item.
  736.  * - 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."
  738.  * - object_id:        The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories.
  739.  * - post_parent:      The DB ID of the original object's parent object, if any (0 otherwise).
  740.  * - post_title:       A "no title" label if menu item represents a post that lacks a title.
  741.  * - target:           The target attribute of the link element for this menu item.
  742.  * - title:            The title of this menu item.
  743.  * - type:             The family of objects originally represented, such as "post_type" or "taxonomy."
  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.
  748.  *
  749.  * @since 3.0.0
  750.  *
  751.  * @param object $menu_item The menu item to modify.
  752.  * @return object $menu_item The menu item with standard menu item properties.
  753.  */
  754. function wp_setup_nav_menu_item( $menu_item ) {
  755.     if ( isset( $menu_item->post_type ) ) {
  756.         if ( 'nav_menu_item' == $menu_item->post_type ) {
  757.             $menu_item->db_id = (int) $menu_item->ID;
  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;
  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;
  760.             $menu_item->object = ! isset( $menu_item->object ) ? get_post_meta( $menu_item->ID, '_menu_item_object', true ) : $menu_item->object;
  761.             $menu_item->type = ! isset( $menu_item->type ) ? get_post_meta( $menu_item->ID, '_menu_item_type', true ) : $menu_item->type;
  762.  
  763.             if ( 'post_type' == $menu_item->type ) {
  764.                 $object = get_post_type_object( $menu_item->object );
  765.                 if ( $object ) {
  766.                     $menu_item->type_label = $object->labels->singular_name;
  767.                 } else {
  768.                     $menu_item->type_label = $menu_item->object;
  769.                     $menu_item->_invalid = true;
  770.                 }
  771.  
  772.                 if ( 'trash' === get_post_status( $menu_item->object_id ) ) {
  773.                     $menu_item->_invalid = true;
  774.                 }
  775.  
  776.                 $menu_item->url = get_permalink( $menu_item->object_id );
  777.  
  778.                 $original_object = get_post( $menu_item->object_id );
  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 );
  781.  
  782.                 if ( '' === $original_title ) {
  783.                     /* translators: %d: ID of a post */
  784.                     $original_title = sprintf( __( '#%d (no title)' ), $original_object->ID );
  785.                 }
  786.  
  787.                 $menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
  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 );
  803.             } elseif ( 'taxonomy' == $menu_item->type ) {
  804.                 $object = get_taxonomy( $menu_item->object );
  805.                 if ( $object ) {
  806.                     $menu_item->type_label = $object->labels->singular_name;
  807.                 } else {
  808.                     $menu_item->type_label = $menu_item->object;
  809.                     $menu_item->_invalid = true;
  810.                 }
  811.  
  812.                 $term_url = get_term_link( (int) $menu_item->object_id, $menu_item->object );
  813.                 $menu_item->url = !is_wp_error( $term_url ) ? $term_url : '';
  814.  
  815.                 $original_title = get_term_field( 'name', $menu_item->object_id, $menu_item->object, 'raw' );
  816.                 if ( is_wp_error( $original_title ) )
  817.                     $original_title = false;
  818.                 $menu_item->title = '' == $menu_item->post_title ? $original_title : $menu_item->post_title;
  819.  
  820.             } else {
  821.                 $menu_item->type_label = __('Custom Link');
  822.                 $menu_item->title = $menu_item->post_title;
  823.                 $menu_item->url = ! isset( $menu_item->url ) ? get_post_meta( $menu_item->ID, '_menu_item_url', true ) : $menu_item->url;
  824.             }
  825.  
  826.             $menu_item->target = ! isset( $menu_item->target ) ? get_post_meta( $menu_item->ID, '_menu_item_target', true ) : $menu_item->target;
  827.  
  828.             /**
  829.              * Filters a navigation menu item's title attribute.
  830.              *
  831.              * @since 3.0.0
  832.              *
  833.              * @param string $item_title The menu item title attribute.
  834.              */
  835.             $menu_item->attr_title = ! isset( $menu_item->attr_title ) ? apply_filters( 'nav_menu_attr_title', $menu_item->post_excerpt ) : $menu_item->attr_title;
  836.  
  837.             if ( ! isset( $menu_item->description ) ) {
  838.                 /**
  839.                  * Filters a navigation menu item's description.
  840.                  *
  841.                  * @since 3.0.0
  842.                  *
  843.                  * @param string $description The menu item description.
  844.                  */
  845.                 $menu_item->description = apply_filters( 'nav_menu_description', wp_trim_words( $menu_item->post_content, 200 ) );
  846.             }
  847.  
  848.             $menu_item->classes = ! isset( $menu_item->classes ) ? (array) get_post_meta( $menu_item->ID, '_menu_item_classes', true ) : $menu_item->classes;
  849.             $menu_item->xfn = ! isset( $menu_item->xfn ) ? get_post_meta( $menu_item->ID, '_menu_item_xfn', true ) : $menu_item->xfn;
  850.         } else {
  851.             $menu_item->db_id = 0;
  852.             $menu_item->menu_item_parent = 0;
  853.             $menu_item->object_id = (int) $menu_item->ID;
  854.             $menu_item->type = 'post_type';
  855.  
  856.             $object = get_post_type_object( $menu_item->post_type );
  857.             $menu_item->object = $object->name;
  858.             $menu_item->type_label = $object->labels->singular_name;
  859.  
  860.             if ( '' === $menu_item->post_title ) {
  861.                 /* translators: %d: ID of a post */
  862.                 $menu_item->post_title = sprintf( __( '#%d (no title)' ), $menu_item->ID );
  863.             }
  864.  
  865.             $menu_item->title = $menu_item->post_title;
  866.             $menu_item->url = get_permalink( $menu_item->ID );
  867.             $menu_item->target = '';
  868.  
  869.             /** This filter is documented in wp-includes/nav-menu.php */
  870.             $menu_item->attr_title = apply_filters( 'nav_menu_attr_title', '' );
  871.  
  872.             /** This filter is documented in wp-includes/nav-menu.php */
  873.             $menu_item->description = apply_filters( 'nav_menu_description', '' );
  874.             $menu_item->classes = array();
  875.             $menu_item->xfn = '';
  876.         }
  877.     } elseif ( isset( $menu_item->taxonomy ) ) {
  878.         $menu_item->ID = $menu_item->term_id;
  879.         $menu_item->db_id = 0;
  880.         $menu_item->menu_item_parent = 0;
  881.         $menu_item->object_id = (int) $menu_item->term_id;
  882.         $menu_item->post_parent = (int) $menu_item->parent;
  883.         $menu_item->type = 'taxonomy';
  884.  
  885.         $object = get_taxonomy( $menu_item->taxonomy );
  886.         $menu_item->object = $object->name;
  887.         $menu_item->type_label = $object->labels->singular_name;
  888.  
  889.         $menu_item->title = $menu_item->name;
  890.         $menu_item->url = get_term_link( $menu_item, $menu_item->taxonomy );
  891.         $menu_item->target = '';
  892.         $menu_item->attr_title = '';
  893.         $menu_item->description = get_term_field( 'description', $menu_item->term_id, $menu_item->taxonomy );
  894.         $menu_item->classes = array();
  895.         $menu_item->xfn = '';
  896.  
  897.     }
  898.  
  899.     /**
  900.      * Filters a navigation menu item object.
  901.      *
  902.      * @since 3.0.0
  903.      *
  904.      * @param object $menu_item The menu item object.
  905.      */
  906.     return apply_filters( 'wp_setup_nav_menu_item', $menu_item );
  907. }
  908.  
  909. /**
  910.  * Get the menu items associated with a particular object.
  911.  *
  912.  * @since 3.0.0
  913.  *
  914.  * @param int    $object_id   The ID of the original object.
  915.  * @param string $object_type The type of object, such as "taxonomy" or "post_type."
  916.  * @param string $taxonomy    If $object_type is "taxonomy", $taxonomy is the name of the tax that $object_id belongs to
  917.  * @return array The array of menu item IDs; empty array if none;
  918.  */
  919. function wp_get_associated_nav_menu_items( $object_id = 0, $object_type = 'post_type', $taxonomy = '' ) {
  920.     $object_id = (int) $object_id;
  921.     $menu_item_ids = array();
  922.  
  923.     $query = new WP_Query;
  924.     $menu_items = $query->query(
  925.         array(
  926.             'meta_key' => '_menu_item_object_id',
  927.             'meta_value' => $object_id,
  928.             'post_status' => 'any',
  929.             'post_type' => 'nav_menu_item',
  930.             'posts_per_page' => -1,
  931.         )
  932.     );
  933.     foreach ( (array) $menu_items as $menu_item ) {
  934.         if ( isset( $menu_item->ID ) && is_nav_menu_item( $menu_item->ID ) ) {
  935.             $menu_item_type = get_post_meta( $menu_item->ID, '_menu_item_type', true );
  936.             if (
  937.                 'post_type' == $object_type &&
  938.                 'post_type' == $menu_item_type
  939.             ) {
  940.                 $menu_item_ids[] = (int) $menu_item->ID;
  941.             } elseif (
  942.                 'taxonomy' == $object_type &&
  943.                 'taxonomy' == $menu_item_type &&
  944.                 get_post_meta( $menu_item->ID, '_menu_item_object', true ) == $taxonomy
  945.             ) {
  946.                 $menu_item_ids[] = (int) $menu_item->ID;
  947.             }
  948.         }
  949.     }
  950.  
  951.     return array_unique( $menu_item_ids );
  952. }
  953.  
  954. /**
  955.  * Callback for handling a menu item when its original object is deleted.
  956.  *
  957.  * @since 3.0.0
  958.  * @access private
  959.  *
  960.  * @param int $object_id The ID of the original object being trashed.
  961.  *
  962.  */
  963. function _wp_delete_post_menu_item( $object_id = 0 ) {
  964.     $object_id = (int) $object_id;
  965.  
  966.     $menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'post_type' );
  967.  
  968.     foreach ( (array) $menu_item_ids as $menu_item_id ) {
  969.         wp_delete_post( $menu_item_id, true );
  970.     }
  971. }
  972.  
  973. /**
  974.  * Serves as a callback for handling a menu item when its original object is deleted.
  975.  *
  976.  * @since 3.0.0
  977.  * @access private
  978.  *
  979.  * @param int    $object_id Optional. The ID of the original object being trashed. Default 0.
  980.  * @param int    $tt_id     Term taxonomy ID. Unused.
  981.  * @param string $taxonomy  Taxonomy slug.
  982.  */
  983. function _wp_delete_tax_menu_item( $object_id = 0, $tt_id, $taxonomy ) {
  984.     $object_id = (int) $object_id;
  985.  
  986.     $menu_item_ids = wp_get_associated_nav_menu_items( $object_id, 'taxonomy', $taxonomy );
  987.  
  988.     foreach ( (array) $menu_item_ids as $menu_item_id ) {
  989.         wp_delete_post( $menu_item_id, true );
  990.     }
  991. }
  992.  
  993. /**
  994.  * Automatically add newly published page objects to menus with that as an option.
  995.  *
  996.  * @since 3.0.0
  997.  * @access private
  998.  *
  999.  * @param string $new_status The new status of the post object.
  1000.  * @param string $old_status The old status of the post object.
  1001.  * @param object $post       The post object being transitioned from one status to another.
  1002.  */
  1003. function _wp_auto_add_pages_to_menu( $new_status, $old_status, $post ) {
  1004.     if ( 'publish' != $new_status || 'publish' == $old_status || 'page' != $post->post_type )
  1005.         return;
  1006.     if ( ! empty( $post->post_parent ) )
  1007.         return;
  1008.     $auto_add = get_option( 'nav_menu_options' );
  1009.     if ( empty( $auto_add ) || ! is_array( $auto_add ) || ! isset( $auto_add['auto_add'] ) )
  1010.         return;
  1011.     $auto_add = $auto_add['auto_add'];
  1012.     if ( empty( $auto_add ) || ! is_array( $auto_add ) )
  1013.         return;
  1014.  
  1015.     $args = array(
  1016.         'menu-item-object-id' => $post->ID,
  1017.         'menu-item-object' => $post->post_type,
  1018.         'menu-item-type' => 'post_type',
  1019.         'menu-item-status' => 'publish',
  1020.     );
  1021.  
  1022.     foreach ( $auto_add as $menu_id ) {
  1023.         $items = wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );
  1024.         if ( ! is_array( $items ) )
  1025.             continue;
  1026.         foreach ( $items as $item ) {
  1027.             if ( $post->ID == $item->object_id )
  1028.                 continue 2;
  1029.         }
  1030.         wp_update_nav_menu_item( $menu_id, 0, $args );
  1031.     }
  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. }
  1177.