diff --git a/wp-admin/includes/nav-menu.php b/wp-admin/includes/nav-menu.php index db4962845..7ec66664b 100644 --- a/wp-admin/includes/nav-menu.php +++ b/wp-admin/includes/nav-menu.php @@ -15,7 +15,7 @@ class Walker_Nav_Menu_Edit extends Walker_Nav_Menu { * @param string $output Passed by reference. * @param int $depth Depth of page. */ - function start_lvl(&$output, $depth) {} + function start_lvl(&$output) {} /** * @see Walker_Nav_Menu::end_lvl() @@ -24,7 +24,8 @@ class Walker_Nav_Menu_Edit extends Walker_Nav_Menu { * @param string $output Passed by reference. * @param int $depth Depth of page. */ - function end_lvl(&$output, $depth) {} + function end_lvl(&$output) { + } /** * @see Walker::start_el() @@ -79,7 +80,7 @@ class Walker_Nav_Menu_Edit extends Walker_Nav_Menu { ), remove_query_arg($removed_args, admin_url( 'nav-menus.php' ) ) ), - 'move-item' + 'move-menu_item' ); ?>" class="item-move-up"> | @@ -92,7 +93,7 @@ class Walker_Nav_Menu_Edit extends Walker_Nav_Menu { ), remove_query_arg($removed_args, admin_url( 'nav-menus.php' ) ) ), - 'move-item' + 'move-menu_item' ); ?>" class="item-move-down"> @@ -187,7 +188,7 @@ class Walker_Nav_Menu_Edit extends Walker_Nav_Menu { - + diff --git a/wp-admin/nav-menus.php b/wp-admin/nav-menus.php index 06459ed08..d59dee2bf 100644 --- a/wp-admin/nav-menus.php +++ b/wp-admin/nav-menus.php @@ -61,99 +61,165 @@ switch ( $action ) { case 'move-down-menu-item' : // moving down a menu item is the same as moving up the next in order check_admin_referer( 'move-menu_item' ); - $menu_item_id = (int) $_REQUEST['menu-item']; - $next_item_id = 0; + $menu_item_id = isset( $_REQUEST['menu-item'] ) ? (int) $_REQUEST['menu-item'] : 0; if ( is_nav_menu_item( $menu_item_id ) ) { $menus = isset( $_REQUEST['menu'] ) ? array( (int) $_REQUEST['menu'] ) : wp_get_object_terms( $menu_item_id, 'nav_menu', array( 'fields' => 'ids' ) ); - if ( ! is_wp_error( $menus ) ) { - foreach( (array) $menus as $menu_id ) { - $move_down_ordered_menu_items = (array) wp_get_nav_menu_items( $menu_id ); - while ( $next = array_shift( $move_down_ordered_menu_items ) ) { - if ( isset( $next->ID ) && $next->ID == $menu_item_id ) { - break; + if ( ! is_wp_error( $menus ) && ! empty( $menus[0] ) ) { + $menu_id = (int) $menus[0]; + $ordered_menu_items = wp_get_nav_menu_items( $menu_id ); + $menu_item_data = (array) wp_setup_nav_menu_item( get_post( $menu_item_id ) ); + + // setup the data we need in one pass through the array of menu items + $dbids_to_orders = array(); + $orders_to_dbids = array(); + foreach( (array) $ordered_menu_items as $ordered_menu_item_object ) { + if ( isset( $ordered_menu_item_object->ID ) ) { + if ( isset( $ordered_menu_item_object->menu_order ) ) { + $dbids_to_orders[$ordered_menu_item_object->ID] = $ordered_menu_item_object->menu_order; + $orders_to_dbids[$ordered_menu_item_object->menu_order] = $ordered_menu_item_object->ID; } } + } - if ( $following = array_shift( $move_down_ordered_menu_items ) ) { - $next_item_id = (int) $following->ID; + // get next in order + if ( + isset( $orders_to_dbids[$dbids_to_orders[$menu_item_id] + 1] ) + ) { + $next_item_id = $orders_to_dbids[$dbids_to_orders[$menu_item_id] + 1]; + $next_item_data = (array) wp_setup_nav_menu_item( get_post( $next_item_id ) ); + + // if not siblings of same parent, bubble menu item up but keep order + if ( + ! empty( $menu_item_data['menu_item_parent'] ) && + ( + empty( $next_item_data['menu_item_parent'] ) || + $next_item_data['menu_item_parent'] != $menu_item_data['menu_item_parent'] + ) + ) { + + $parent_db_id = in_array( $menu_item_data['menu_item_parent'], $orders_to_dbids ) ? (int) $menu_item_data['menu_item_parent'] : 0; + + $parent_object = wp_setup_nav_menu_item( get_post( $parent_db_id ) ); + + if ( ! is_wp_error( $parent_object ) ) { + $parent_data = (array) $parent_object; + $menu_item_data['menu_item_parent'] = $parent_data['menu_item_parent']; + update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] ); + + } + + // make menu item a child of its next sibling + } else { + $next_item_data['menu_order'] = $next_item_data['menu_order'] - 1; + $menu_item_data['menu_order'] = $menu_item_data['menu_order'] + 1; + + $menu_item_data['menu_item_parent'] = $next_item_data['ID']; + update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] ); + + wp_update_post($menu_item_data); + wp_update_post($next_item_data); } + + + // the item is last but still has a parent, so bubble up + } elseif ( + ! empty( $menu_item_data['menu_item_parent'] ) && + in_array( $menu_item_data['menu_item_parent'], $orders_to_dbids ) + ) { + $menu_item_data['menu_item_parent'] = (int) get_post_meta( $menu_item_data['menu_item_parent'], '_menu_item_menu_item_parent', true); + update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] ); } } } - // fall through to next case + + break; case 'move-up-menu-item' : check_admin_referer( 'move-menu_item' ); - $menu_item_id = empty( $next_item_id ) ? (int) $_REQUEST['menu-item'] : $next_item_id; + $menu_item_id = isset( $_REQUEST['menu-item'] ) ? (int) $_REQUEST['menu-item'] : 0; if ( is_nav_menu_item( $menu_item_id ) ) { $menus = isset( $_REQUEST['menu'] ) ? array( (int) $_REQUEST['menu'] ) : wp_get_object_terms( $menu_item_id, 'nav_menu', array( 'fields' => 'ids' ) ); - if ( ! is_wp_error( $menus ) ) { - foreach( (array) $menus as $menu_id ) { - $ordered_menu_items = wp_get_nav_menu_items( $menu_id ); - $menu_item_data = get_post( $menu_item_id , ARRAY_A ); + if ( ! is_wp_error( $menus ) && ! empty( $menus[0] ) ) { + $menu_id = (int) $menus[0]; + $ordered_menu_items = wp_get_nav_menu_items( $menu_id ); + $menu_item_data = (array) wp_setup_nav_menu_item( get_post( $menu_item_id ) ); - // setup the data we need in one pass through the array of menu items - $dbids_to_orders = array(); - $orders_to_dbids = array(); - $objectids_to_dbids = array(); - $dbids_to_objectids = array(); - foreach( (array) $ordered_menu_items as $ordered_menu_item_object ) { - if ( isset( $ordered_menu_item_object->ID ) ) { - if ( isset( $ordered_menu_item_object->menu_order ) ) { - $dbids_to_orders[$ordered_menu_item_object->ID] = $ordered_menu_item_object->menu_order; - $orders_to_dbids[$ordered_menu_item_object->menu_order] = $ordered_menu_item_object->ID; - } - - $possible_object_id = (int) get_post_meta( $ordered_menu_item_object->ID, '_menu_item_object_id', true ); - if ( ! empty( $possible_object_id ) ) { - $dbids_to_objectids[$ordered_menu_item_object->ID] = $possible_object_id; - $objectids_to_dbids[$possible_object_id] = $ordered_menu_item_object->ID; - } + // setup the data we need in one pass through the array of menu items + $dbids_to_orders = array(); + $orders_to_dbids = array(); + foreach( (array) $ordered_menu_items as $ordered_menu_item_object ) { + if ( isset( $ordered_menu_item_object->ID ) ) { + if ( isset( $ordered_menu_item_object->menu_order ) ) { + $dbids_to_orders[$ordered_menu_item_object->ID] = $ordered_menu_item_object->menu_order; + $orders_to_dbids[$ordered_menu_item_object->menu_order] = $ordered_menu_item_object->ID; } } + } - // if this menu item is not first - if ( ! empty( $dbids_to_orders[$menu_item_id] ) && ! empty( $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) ) { + // if this menu item is not first + if ( ! empty( $dbids_to_orders[$menu_item_id] ) && ! empty( $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) ) { - // if this menu item is a child of the previous - if ( - ! empty( $menu_item_data['post_parent'] ) && - isset( $objectids_to_dbids[$menu_item_data['post_parent']] ) && - isset( $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) && - ( $objectids_to_dbids[$menu_item_data['post_parent']] == $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) - ) { + // if this menu item is a child of the previous + if ( + ! empty( $menu_item_data['menu_item_parent'] ) && + in_array( $menu_item_data['menu_item_parent'], array_keys( $dbids_to_orders ) ) && + isset( $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) && + ( $menu_item_data['menu_item_parent'] == $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) + ) { + $parent_db_id = in_array( $menu_item_data['menu_item_parent'], $orders_to_dbids ) ? (int) $menu_item_data['menu_item_parent'] : 0; + $parent_object = wp_setup_nav_menu_item( get_post( $parent_db_id ) ); - $parent_db_id = $objectids_to_dbids[$menu_item_data['post_parent']]; - $parent_data = get_post( $parent_db_id, ARRAY_A ); + if ( ! is_wp_error( $parent_object ) ) { + $parent_data = (array) $parent_object; - if ( ! is_wp_error( $parent_data ) ) { + // if there is something before the parent and parent a child of it, make menu item a child also of it + if ( + ! empty( $dbids_to_orders[$parent_db_id] ) && + ! empty( $orders_to_dbids[$dbids_to_orders[$parent_db_id] - 1] ) && + ! empty( $parent_data['menu_item_parent'] ) + ) { + $menu_item_data['menu_item_parent'] = $parent_data['menu_item_parent']; - // if there is something before the parent, make menu item a child of the parent's parent - if ( ! empty( $dbids_to_orders[$parent_db_id] ) && ! empty( $orders_to_dbids[$dbids_to_orders[$parent_db_id] - 1] ) ) { - $menu_item_data['post_parent'] = $parent_data['post_parent']; + // else if there is something before parent and parent not a child of it, make menu item a child of that something's parent + } elseif ( + ! empty( $dbids_to_orders[$parent_db_id] ) && + ! empty( $orders_to_dbids[$dbids_to_orders[$parent_db_id] - 1] ) + ) { + $_possible_parent_id = (int) get_post_meta( $orders_to_dbids[$dbids_to_orders[$parent_db_id] - 1], '_menu_item_menu_item_parent', true); + if ( in_array( $_possible_parent_id, array_keys( $dbids_to_orders ) ) ) + $menu_item_data['menu_item_parent'] = $_possible_parent_id; + else + $menu_item_data['menu_item_parent'] = 0; - // else there isn't something before the parent - } else { - $menu_item_data['post_parent'] = 0; - } - - // set former parent's [menu_order] to that of menu-item's - $parent_data['menu_order'] = $parent_data['menu_order'] + 1; - - // set menu-item's [menu_order] to that of former parent - $menu_item_data['menu_order'] = $menu_item_data['menu_order'] - 1; - - // save changes - wp_update_post($menu_item_data); - wp_update_post($parent_data); + // else there isn't something before the parent + } else { + $menu_item_data['menu_item_parent'] = 0; } - // else this menu item is not a child of the previous - } elseif ( isset($dbids_to_objectids[$orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1]] ) ) { - // just make it a child of the previous; keep the order - $menu_item_data['post_parent'] = (int) $dbids_to_objectids[$orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1]]; + // set former parent's [menu_order] to that of menu-item's + $parent_data['menu_order'] = $parent_data['menu_order'] + 1; + + // set menu-item's [menu_order] to that of former parent + $menu_item_data['menu_order'] = $menu_item_data['menu_order'] - 1; + + // save changes + update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] ); wp_update_post($menu_item_data); + wp_update_post($parent_data); } + + // else this menu item is not a child of the previous + } elseif ( + empty( $menu_item_data['menu_order'] ) || + empty( $menu_item_data['menu_item_parent'] ) || + ! in_array( $menu_item_data['menu_item_parent'], array_keys( $dbids_to_orders ) ) || + empty( $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] ) || + $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1] != $menu_item_data['menu_item_parent'] + ) { + // just make it a child of the previous; keep the order + $menu_item_data['menu_item_parent'] = (int) $orders_to_dbids[$dbids_to_orders[$menu_item_id] - 1]; + update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] ); + wp_update_post($menu_item_data); } } } diff --git a/wp-includes/nav-menu-template.php b/wp-includes/nav-menu-template.php index 94d47a75a..b08e66caa 100644 --- a/wp-includes/nav-menu-template.php +++ b/wp-includes/nav-menu-template.php @@ -28,7 +28,7 @@ class Walker_Nav_Menu extends Walker { * @todo Decouple this. * @var array */ - var $db_fields = array( 'parent' => 'post_parent', 'id' => 'object_id' ); + var $db_fields = array( 'parent' => 'menu_item_parent', 'id' => 'db_id' ); /** * @see Walker::start_lvl() @@ -137,7 +137,6 @@ class Walker_Nav_Menu_Checklist extends Walker_Nav_Menu { $_nav_menu_placeholder = ( 0 > $_nav_menu_placeholder ) ? intval($_nav_menu_placeholder) - 1 : -1; $possible_object_id = isset( $item->post_type ) && 'nav_menu_item' == $item->post_type ? $item->object_id : $_nav_menu_placeholder; $possible_db_id = ( ! empty( $item->ID ) ) && ( 0 < $possible_object_id ) ? (int) $item->ID : 0; - $possible_parent_id = ( ! empty( $item->ID ) ) && ( 0 < $possible_object_id ) ? (int) $item->post_parent : 0; $indent = ( $depth ) ? str_repeat( "\t", $depth ) : ''; @@ -157,7 +156,7 @@ class Walker_Nav_Menu_Checklist extends Walker_Nav_Menu { // Menu item hidden fields $output .= ''; $output .= ''; - $output .= ''; + $output .= ''; $output .= ''; $output .= ''; $output .= ''; diff --git a/wp-includes/nav-menu.php b/wp-includes/nav-menu.php index a1215e803..a2f569596 100644 --- a/wp-includes/nav-menu.php +++ b/wp-includes/nav-menu.php @@ -256,6 +256,8 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item } } + $original_parent = 0 < $menu_item_db_id ? get_post_field( 'post_parent', $menu_item_db_id ) : 0; + if ( 'custom' != $args['menu-item-type'] ) { /* if non-custom menu item, then: * use original object's URL @@ -266,10 +268,12 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item $original_title = ''; if ( 'taxonomy' == $args['menu-item-type'] ) { + $original_parent = get_term_field( 'parent', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' ); $original_title = get_term_field( 'name', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' ); } elseif ( 'post_type' == $args['menu-item-type'] ) { $original_object = get_post( $args['menu-item-object-id'] ); + $original_parent = (int) $original_object->post_parent; $original_title = $original_object->post_title; if ( 'trash' == get_post_status( $args['menu-item-object-id'] ) ) { @@ -297,7 +301,7 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item 'ping_status' => 0, 'post_content' => $args['menu-item-description'], 'post_excerpt' => $args['menu-item-attr-title'], - 'post_parent' => $args['menu-item-parent-id'], + 'post_parent' => $original_parent, 'post_title' => $args['menu-item-title'], 'post_type' => 'nav_menu_item', 'tax_input' => array( 'nav_menu' => $menu->name ), @@ -326,6 +330,7 @@ function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item $menu_item_db_id = (int) $menu_item_db_id; update_post_meta( $menu_item_db_id, '_menu_item_type', sanitize_key($args['menu-item-type']) ); + update_post_meta( $menu_item_db_id, '_menu_item_menu_item_parent', (int) $args['menu-item-parent-id'] ); update_post_meta( $menu_item_db_id, '_menu_item_object_id', (int) $args['menu-item-object-id'] ); update_post_meta( $menu_item_db_id, '_menu_item_object', sanitize_key($args['menu-item-object']) ); update_post_meta( $menu_item_db_id, '_menu_item_target', sanitize_key($args['menu-item-target']) ); @@ -439,12 +444,13 @@ function wp_get_nav_menu_items( $menu, $args = array() ) { * Decorates a menu item object with the shared navigation menu item properties. * * Properties: - * - db_id: The DB ID of the this item as a nav_menu_item object, if it exists (0 if it doesn't exist). + * - db_id: The DB ID of this item as a nav_menu_item object, if it exists (0 if it doesn't exist). * - object_id: The DB ID of the original object this menu item represents, e.g. ID for posts and term_id for categories. * - type: The family of objects originally represented, such as "post_type" or "taxonomy." * - object: The type of object originally represented, such as "category," "post", or "attachment." * - append: The singular label used to describe this type of menu item. - * - parent: The DB ID of the original object's parent object, if any (0 otherwise). + * - post_parent: The DB ID of the original object's parent object, if any (0 otherwise). + * - menu_item_parent: The DB ID of the nav_menu_item that is this item's menu parent, if any. 0 otherwise. * - url: The URL to which this menu item points. * - title: The title of this menu item. * - target: The target attribute of the link element for this menu item. @@ -462,6 +468,7 @@ function wp_setup_nav_menu_item( $menu_item ) { if ( isset( $menu_item->post_type ) ) { if ( 'nav_menu_item' == $menu_item->post_type ) { $menu_item->db_id = (int) $menu_item->ID; + $menu_item->menu_item_parent = get_post_meta( $menu_item->ID, '_menu_item_menu_item_parent', true ); $menu_item->object_id = get_post_meta( $menu_item->ID, '_menu_item_object_id', true ); $menu_item->object = get_post_meta( $menu_item->ID, '_menu_item_object', true ); $menu_item->type = get_post_meta( $menu_item->ID, '_menu_item_type', true ); @@ -498,6 +505,7 @@ function wp_setup_nav_menu_item( $menu_item ) { $menu_item->xfn = get_post_meta( $menu_item->ID, '_menu_item_xfn', true ); } else { $menu_item->db_id = 0; + $menu_item->menu_item_parent = 0; $menu_item->object_id = (int) $menu_item->ID; $menu_item->type = 'post_type'; @@ -517,6 +525,7 @@ function wp_setup_nav_menu_item( $menu_item ) { } elseif ( isset( $menu_item->taxonomy ) ) { $menu_item->ID = $menu_item->term_id; $menu_item->db_id = 0; + $menu_item->menu_item_parent = 0; $menu_item->object_id = (int) $menu_item->term_id; $menu_item->post_parent = (int) $menu_item->parent; $menu_item->type = 'taxonomy';