checkbox in post add/edit to add/remove the post from menu

Introduction:

I have created several post type for my theme. To display a post from a post-type I need to go in appearance/menu and then add the post manually.

Read More

My Goal:

I want to add two radio buttons in the post add/edit page to choose whether the current post will be in menu.

Attempts:

So I have created the checkbox in post-type add/edit page. Looks like
enter image description here

Everything works fine, the status is saved, now I want before saving to add/remove it in the menu list as sub-menu with for parent its post-type, how could I achieve that?

Here is what I have done so far:

// create post type
add_action( 'init', 'create_post_type' );
function create_post_type() {

    $conf = array(
        'public' => true,
        'has_archive' => true,
        'menu_position' => 5,
        'show_in_nav_menus' => true,
        'hierarchical' => true,
        'has_archive' => true,
        'supports' => array('title', 'editor', 'content', 'page-attributes'),
        'taxonomies' => array('category', 'post_tag'),

        );

    $conf['labels'] = array(
        'name' => __( 'Architecte Posts' ),
        'singular_name' => __( 'Architecte' )
        );
    $conf['rewrite'] = array('slug' => 'architect');

    register_post_type( 'architect_subpage',$conf);


    $conf['labels'] = array(
        'name' => __( 'Interior Posts' ),
        'singular_name' => __( 'Interior' )
        );
    $conf['rewrite'] = array('slug' => 'interior');
    register_post_type( 'interior_subpage',$conf);
}

// remove posts menu since it's not in use
function remove_menus () {
    global $menu;
    $restricted = array(__('Posts'));
    end ($menu);
    while (prev($menu)){
        $value = explode(' ',$menu[key($menu)][0]);
        if(in_array($value[0] != NULL?$value[0]:"" , $restricted)){unset($menu[key($menu)]);}
    }
}
add_action('admin_menu', 'remove_menus');




/* Define the custom box */
add_action( 'add_meta_boxes', 'wpse_61041_add_custom_box' );

/* Do something with the data entered */
add_action( 'save_post', 'wpse_61041_save_postdata' );

/* Adds a box to the main column on the Post and Page edit screens */
function wpse_61041_add_custom_box() {
    add_meta_box(
        'wpse_61041_sectionid',
        'Publish in Menu',
        'wpse_61041_inner_custom_box',
        'architect_subpage',
        'side',
        'high'
    );
}

/* Prints the box content */
function wpse_61041_inner_custom_box($post)
{
    // Use nonce for verification
    wp_nonce_field( 'wpse_61041_wpse_61041_field_nonce', 'wpse_61041_noncename' );

    // Get saved value, if none exists, "default" is selected
    $saved = get_post_meta( $post->ID, 'in_menu', true);
    if( !$saved )
        $saved = 'no';

    $fields = array(
        'yes'       => __('Yes', 'wpse'),
        'no'     => __('No', 'wpse'),
    );

    foreach($fields as $key => $label)
    {
        printf(
            '<input type="radio" name="in_menu" value="%1$s" id="in_menu[%1$s]" %3$s />'.
            '<label for="in_menu[%1$s]"> %2$s ' .
            '</label><br>',
            esc_attr($key),
            esc_html($label),
            checked($saved, $key, false)
        );
    }
}

/* When the post is saved, saves our custom data */
function wpse_61041_save_postdata( $post_id )
{
      // verify if this is an auto save routine.
      // If it is our form has not been submitted, so we dont want to do anything
      if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
          return;

      // verify this came from the our screen and with proper authorization,
      // because save_post can be triggered at other times
      if ( !wp_verify_nonce( $_POST['wpse_61041_noncename'], 'wpse_61041_wpse_61041_field_nonce' ) )
          return;

      if ( isset($_POST['in_menu']) && $_POST['in_menu'] != "" ){

            update_post_meta( $post_id, 'in_menu', $_POST['in_menu'] );

      }
}

https://gist.github.com/roine/4994447

Related posts

Leave a Reply

2 comments

  1. The following is just a proof of concept and needs to be adapted/improved to work per the Question requirements.

    It creates a meta box with a dropdown listing all available Navigation Menus. On post save, it is added to the selected menu.

    This Q&A was used as starting point: Programmatically add a Navigation menu and menu items. The relevant functions are wp_get_nav_menus and wp_update_nav_menu_item. I’m not sure how to remove a menu item, but probably wp_delete_post must be used.

    add_action( 'add_meta_boxes', 'add_custom_box_wpse_87594' );
    add_action( 'save_post', 'save_postdata_wpse_87594', 10, 2 );
    
    function add_custom_box_wpse_87594() 
    {
        add_meta_box( 
            'section_id_wpse_87594',
            __( 'Available Nav-Menus' ),
            'inner_custom_box_wpse_87594',
            'post',
            'side'
        );
    }
    
    function inner_custom_box_wpse_87594() 
    {
        $navmenus = wp_get_nav_menus( array( 'hide_empty' => false, 'orderby' => 'none' ) );
    
        // DEBUG
        // echo '<pre>' . print_r( $navmenus, true ) . '</pre>';
    
        wp_nonce_field( plugin_basename( __FILE__ ), 'noncename_wpse_87594' );
    
        echo '<select name="nav_menus_dropdown" id="nav_menus_dropdown">
            <option value="">- Select -</option>';
    
        foreach( $navmenus as $m ) 
        {
            printf( 
                '<option value="%s">%s</option>',
                $m->term_id,
                $m->name
            );
        }
    
        echo '</select>';   
    }
    
    function save_postdata_wpse_87594( $post_id, $post_object ) 
    {
        // Auto save?
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )  
            return;
    
        // Security
        if ( 
            !isset( $_POST['noncename_wpse_87594'] ) 
            || !wp_verify_nonce( $_POST['noncename_wpse_87594'], plugin_basename( __FILE__ ) ) 
            )
            return;
    
        // Correct post_type
        if ( 'post' != $post_object->post_type )
            return;
    
        if( !empty( $_POST['nav_menus_dropdown'] ) )
        {
            wp_update_nav_menu_item(
                $_POST['nav_menus_dropdown'], 
                0, 
                array(
                    'menu-item-title' => $_POST['post_title'],
                    'menu-item-object' => 'page',
                    'menu-item-object-id' => $_POST['ID'],
                    'menu-item-type' => 'post_type',
                    'menu-item-status' => 'publish'
                )
            );
        }       
    }
    

    Result:
    navigation menus meta box

  2. I have changed the code of brasofilo, and added the ability to add the post as a child

    Here is the resivion: https://gist.github.com/roine/5105421/revisions

    And here is the full code (also available here):

    <?php
    add_action( 'add_meta_boxes', 'add_custom_box_wpse_87594' );
    add_action( 'save_post', 'save_postdata_wpse_87594', 10, 2 );
    
    function add_custom_box_wpse_87594() 
    {
        add_meta_box( 
            'section_id_wpse_87594',
            __( 'Available Nav-Menus' ),
            'inner_custom_box_wpse_87594',
            'post',
            'side'
        );
    }
    
    function inner_custom_box_wpse_87594() 
    {
        $navmenus = wp_get_nav_menus( array( 'hide_empty' => false, 'orderby' => 'none' ) );
    
        // DEBUG
        // echo '<pre>' . print_r( $navmenus, true ) . '</pre>';
    
        wp_nonce_field( plugin_basename( __FILE__ ), 'noncename_wpse_87594' );
    
        echo '<select name="nav_menus_dropdown" id="nav_menus_dropdown">
            <option value="">- Select -</option>';
    
        foreach( $navmenus as $m ) 
        {
            echo "<optgroup label='$m->name'>";
            echo "<option value={$m->term_id}>&#8593; Add on top</option>";
            $items = wp_get_nav_menu_items( $m->term_id);
            foreach ($items as $key => $menu_item ) {
               if(!$menu_item->menu_item_parent){
                  printf('<option value="%s-%s">%s</option>',
                  $m->term_id,
                  $menu_item->object_id,
                  $menu_item->title);
               }
               echo "</optgroup>";
            }
    
        echo '</select>';   
        }
    }
    
    function save_postdata_wpse_87594( $post_id, $post_object ) 
    {
        // Auto save?
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )  
            return;
    
        // Security
        if ( 
            !isset( $_POST['noncename_wpse_87594'] ) 
            || !wp_verify_nonce( $_POST['noncename_wpse_87594'], plugin_basename( __FILE__ ) ) 
            )
            return;
    
        // Correct post_type
        if ( 'post' != $post_object->post_type )
            return;
    
        if( !empty( $_POST['nav_menus_dropdown'] ) )
        {
            $parents = explode('-', $_POST['nav_menus_dropdown']);
            $item = array(
                'menu-item-title' => $_POST['post_title'],
                'menu-item-object' => 'page',
                'menu-item-object-id' => $_POST['ID'],
                'menu-item-type' => 'post_type',
                'menu-item-status' => 'publish'
            );
            if(count($parents) > 1){
               $item['menu-item-parent-id'] = $parents[1];
            }
            wp_update_nav_menu_item(
                $_POST['nav_menus_dropdown'], 
                0, 
                $item
            );
        }       
    }
    

    It works for me but still could be improved.

    Behavior:

    It will display the top level items of each menu in the dropdown list, also Add on top will add the post as a top level instead of as a child

    enter image description here