I’ve looked for plugins and couldn’t find anything and I believe this should be a core feature.
The Problem:
The current methodology for replacing a custom link, or any menu link is by doing the following process:
- Remove old menu link
- Insert new link
- Drag new link from end of list
- Drop new link in desired location
- repeat steps 3 and 4 until you hit the jackpot
- Enter menu options again (css, label etc)
Why is it a problem
It is very inefficient, especially when: (a) dealing with huge menus (b) menus with many sublevels (c) replacing many menu items which have custom options
Solution requirements
- Retains Menu position / hierarchy
- Retains Options (css class, label, title)
- Choose from Pages / Posts / Categories etc
Demonstration
Should be that simple:
Other possible ideas are duplicating / “add child link to this”/ or even adding new links to top of list instead of bottom.
Any feedback appreciated.
Although this does not directly answer the question, the code on which it is based provides the functionality. The code set is:
function install_menus() {
require_once dirname( __FILE__) . '/data.php';
$menus = get_menus_data();
if ( ! empty ( $menus ) ) foreach ( $menus as $menu ) {
if ( $menu['build'] ) {
$menu_id = create_nav_menu( $menu );
add_items_to_menu( $menu_id, $menu['slug'], $menu['items'] );
}
}
}
function create_nav_menu( $menu ) {
if ( $exists = wp_get_nav_menu_object( $menu['name'] ) ) {
$menu_id = $exists -> term_id;
if ( empty ( $menu_id ) ) {
$menu_id = wp_create_nav_menu( $menu['name'] );
}
}
else {
$menu_id = wp_create_nav_menu( $menu['name'] );
}
return $menu_id;
}
function add_items_to_menu( $menu_id, $slug, $items ) {
if ( $items ) foreach ( $items as $item ) {
if ( $item['build'] ) {
$slug = ( $item['title'] == 'Home' ) ? 'home' : $item['slug'];
if ( ! menu_item_exists( $slug, $menu_id ) ) {
wp_update_nav_menu_item( $menu_id, 0, array (
'menu-item-title' => __( $item['title'] ),
'menu-item-classes' => '',
'menu-item-url' => home_url( $item['slug'] . "https://wordpress.stackexchange.com/" ),
'menu-item-status' => 'publish'
) );
}
}
}
}
function menu_item_exists( $slug, $menu_id ) {
$args = array(
'order' => 'ASC',
'orderby' => 'menu_order',
'post_type' => 'nav_menu_item',
'post_status' => 'publish',
'output' => ARRAY_A,
'output_key' => 'menu_order',
'nopaging' => true,
'update_post_term_cache' => false );
$existing = wp_get_nav_menu_items( $menu_id, $args );
$found = false;
foreach ( $existing as $exists ) {
if( strpos( $exists->post_name, $slug ) !== FALSE ) { //pretty good search (not exact).
$found = true;
break;
}
}
return $found;
}
and the data file is:
function get_menus_data() {
$items = array (
array (
'name' => 'Main Menu', 'slug' => 'main-menu', 'build' => 1,
'items' => array (
array ( 'title' => 'Home', 'slug' => '', 'build' => 1 ), //slug should be empty
array ( 'title' => 'Blog', 'slug' => 'blog', 'build' => 1 ),
array ( 'title' => 'About', 'slug' => 'about', 'build' => 1 ),
array ( 'title' => 'Contact', 'slug' => 'contact', 'build' => 1 ),
),
),
array (
'name' => 'Secondary Menu', 'slug' => 'secondary-menu', 'build' => 0,
'items' => array (
array ( 'title' => 'Home', 'slug' => '', 'build' => 1 ),
array ( 'title' => 'Blog', 'slug' => 'blog', 'build' => 1 ),
array ( 'title' => 'About', 'slug' => 'about', 'build' => 1 ),
array ( 'title' => 'Contact', 'slug' => 'contact', 'build' => 1 ),
),
),
array (
'name' => 'Footer Menu', 'slug' => 'footer-menu', 'build' => 1,
'items' => array (
array ( 'title' => 'Terms', 'slug' => 'terms', 'build' => 1 ),
array ( 'title' => 'Privacy', 'slug' => 'privacy', 'build' => 1 ),
array ( 'title' => 'Contact', 'slug' => 'contact', 'build' => 1 ),
),
)
);
return $items;
}
An interface would need to be built on top of this to allow for the selection that is asked, but this code is working and tested.