How to add a section to the menus configuration, offering hard coded items?

Menu items are a post type, and the menus to which they belong are taxonomy terms. So to create a post type in order to get a metabox from which you can add menu items seems a bit redundant (effectively duplicating every ‘special item’ in the database). In any case, setting the post type’s visibilty so that its only seen on the menus screen might be tricky.

Regardless this is quite an involved task, so I’ll just outline here how I would do this. You can see a ‘working example’ of this in this plug-in I built with Kaiser and Ryan Urban. That plug-in adds a post-type archive metabox for links to a post type’s archive page.

Adding the metabox

You’ll want to hook onto the admin_init hook and add_meta_box() (see codex). The screen ID for that page is nav-menus

add_action( 'admin_init', 'wpse102735_add_meta_boxe' );
public function wpse102735_add_meta_boxe() {
    add_meta_box(
        'wpse102735-metabox-id,
        'My Custom Section'
        'wpse102735_metabox_callback',
        'nav-menus',
        'side',
        'low'
    );
}

The function wpse102735_metabox_callback() is responsible for the content of the metabox. And should produce a checkbox-list of the ‘special items’ – all with a value/ID with which you can identify that item. (See metabox() method in the above mentioned plug-in, or this function in core which produces the HTML mark-up for the standard post/page metaboxes.

Javascript

In the previous step you should have added a ‘submit’ button. You’ll need to listen for when that button is clicked and then send a custom ajax request to ajaxurl (a global javascript variable pointing to WordPress’ ajax handler). The following is skeleton code:

jQuery( document ).ready( function($) {
     $( '#my-submit-id' ).click( function( event ) {
        event.preventDefault();

        var item_identifiers = []; 

        //Collect the IDs of the selected items and put them in $item_identifiers

        // Show spinner - you need to add this in the mark-up.
        $( '#wpse102735-metabox-id' ).find('.spinner').show();

        // Disable button
        $(this).prop( 'disabled', true );

        // Send checked post types with our action, and nonce
        $.post( ajaxurl, {
                action: wpse102735_add_menu_items,
                items: item_identifiers,
            },

            // AJAX returns html to add to the menu, hide spinner
            function( response ) {
                $( '#menu-to-edit' ).append( response );
                $( '#wpse102735-metabox-id').find('.spinner').hide();
                $(this).prop( 'disabled', false );
                            //Uncheck checkboxes too
            }
        );
    } );
} );

Note I’ve omitted nonces from the above (and below). You’ll need to add those in. Also you’ll need to properly register/enqueue the above script. See the referenced plug-in for details.

Handle the ajax

In the above we set the action to wpse102735_add_menu_items, we need that the ajax handler hook.

 add_action( 'wp_ajax_wpse102735_add_menu_items', 'wpse102735_ajax_handler' );

Our ajax handler will do two things:

  1. Create the menu items with wp_update_nav_menu_item() (best look at source / above plug-in)
  2. From the returned IDs, retrieve the menu objects and generate the HTML for the admin screen. (We appended the returned HTML to the menu in the previous step)

The ajax handler:

add_action( 'wp_ajax_wpse102735_add_menu_items', 'wpse102735_ajax_handler' );
function wpse102735_ajax_handler(){

   //Perform any necessary nonce/permission checks

   $items = $_POST['items']

   //For each item, set up the details for the menu item:
   foreach( $items as $item ){
      $menu_item_data= array(
         'menu-item-title'  => 'Item title',
         ...
      );

      // Collect the items' IDs.
      $item_ids[] = wp_update_nav_menu_item( 0, 0, $menu_item_data );
   }

   // If there was an error die here
   if( is_wp_error( $item_ids ) )
        die( '-1' );

   //Finally we want to generate mark-up to be added to the menu admin screen
   //First we set up the menu items we've just created

   // Set up menu items
   foreach ( (array) $item_ids as $menu_item_id ) {
        $menu_obj = get_post( $menu_item_id );
        if ( ! empty( $menu_obj->ID ) ) {
             $menu_obj = wp_setup_nav_menu_item( $menu_obj );
             // don't show "(pending)" in ajax-added items
             $menu_obj->label = $menu_obj->title;
             $menu_items[] = $menu_obj;
         }
    }

    //Now generate HTML using Walker_Nav_Menu_Edit walker

    // Needed to get the Walker up and running
    require_once ABSPATH.'wp-admin/includes/nav-menu.php';

    // This gets the HTML to returns it to the menu
    if ( ! empty( $menu_items ) ) {
         $args = array(
              'after'       => '',
              'before'      => '',
              'link_after'  => '',
              'link_before' => '',
              'walker'      => new Walker_Nav_Menu_Edit
         );

          echo walk_nav_menu_tree( $menu_items, 0, (object) $args );
     }

     exit();
}

I hope that gets you on the right track 🙂

Leave a Comment