Add a Standard WordPress Meta-Box

Meta-boxes are interface objects that allow you to easily associate additional information with your WordPress posts, pages, or links. If you go into your WordPress Edit Post screen, you will see a variety of standard WordPress meta-boxes, including those for entering tags, categories, post thumbnails, and setting post status.

You can create your own meta-box if you want, or you can add standard WordPress meta-boxes to your plugin and theme interface. In this article, we focus on how to add an existing WordPress meta-box.

1. Add Standard WordPress Meta-Box

We start out by adding a simple WordPress tag meta-box to our plugin interface. To do this, include the code below into your plugin menu page.

For an example of how to create a plugin menu page, refer to the Shiba Example Plugin.

if (is_file('includes/meta-boxes.php'))
	require_once('includes/meta-boxes.php');
else
	require_once(SHIBA_TEST_DIR.'/meta-boxes.php');	

add_meta_box(	'tagsdiv-post_tag', __('Test Tags'), 'post_tags_meta_box', 
		'settings_page_shiba_test', 'side', 'core');

Lines 1-4 loads the standard WordPress meta-box functions. As of WordPress 2.9, these meta-box functions are conveniently grouped in the includes/meta-boxes.php file in your WordPress installation. For previous versions of WordPress, you will have to include this file in your plugin environment.

Lines 6-7 registers the standard WordPress tag meta-box for our plugin page which is called ‘settings_page_shiba_test’ in this example.

This is the name that is returned by add_submenu_page or any of the other add_*_page functions.

Note – This name contains not just the page handle, e.g. shiba_test, but also the parent page handle, e.g. settings_page. That is why in the example above we use the name settings_page_shiba_test.

Below are the other standard WordPress meta-box functions available and how you must name them while using the add_meta_box command (i.e. argument 1 = meta-box name/div name, argument 3 = function name) -

  1. post_submit_meta_box – submitdiv
  2. post_tags_meta_box – tagsdiv-post_tag
  3. post_categories_meta_box – categorydiv
  4. post_excerpt_meta_box – postexcerpt
  5. post_trackback_meta_box – trackbacksdiv
  6. post_custom_meta_box – postcustom
  7. post_comment_status_meta_box – commentstatusdiv
  8. post_comment_meta_box – commentsdiv
  9. post_slug_meta_box – slugdiv
  10. post_author_meta_box
  11. post_revisions_meta_box – revisionsdiv
  12. page_attributes_meta_box – pageparentdiv
  13. link_submit_meta_box – linksubmitdiv
  14. link_categories_meta_box – linkcategorydiv
  15. link_target_meta_box – linktargetdiv
  16. link_xfn_meta_box – linkxfndiv
  17. link_advanced_meta_box – linkadvanceddiv
  18. post_thumbnail_meta_box – postimagediv

2. Render Standard WordPress Meta-Box

The code above just registers the meta-box, it does not render it. To render the meta-box onto your plugin page, you must use the do_meta_boxes function. Add do_meta_boxes to the HTML div where you want your meta-boxes to appear.

    <?php $meta_boxes = do_meta_boxes('settings_page_shiba_test', 'side', $test_object); ?>

The first argument specifies the page where you are rendering the meta-boxes to, for example posts, pages, links, or your own plugin page. In the example above we are rendering to our own plugin test page called settings_page_shiba_test. This name should exactly match the page name used above in your add_meta_box function.

Adding do_meta_boxes will render the standard WordPress tag meta-box in your plugin page.

3. Enable Meta-Box Interface

You will quickly notice that even though the meta-box is rendered, the interface does not work. Adding tags do not work, and clicking on the buttons or links does nothing.

To enable the tag meta-box interface, you will need to include the Javascript associated with it, in this case post.js. If you are adding link meta-boxes instead of post meta-boxes then you want to include link.js. However, you can only include one of them at a time.

add_action("admin_print_scripts-settings_page_shiba_test", array(&$this,'add_shiba_example_admin_scripts'));
	
function add_shiba_example_admin_scripts() {
	wp_enqueue_script('post');
}

Line 1 uses the add_action function to associate shiba_example_admin_scripts to our plugin page and only to our plugin page.

Line 4 loads the post.js javascript which now makes the tag meta-box active.

4. Collapse the Meta-Box Interface

Now the meta-box interface is active but we still cannot close or collapse the meta-box, and we cannot drag and drop our meta-boxes.

This article and example plugin from Code Styling explains very well what you must do to achieve this.

In essence, you must run the add_meta_box function from the ‘load-$pagename’ action hook, which in our example is ‘load-settings_page_shiba_test’. If you use the add_meta_box function within the page itself, then you will not be able to close the meta-boxes or drag and drop them.

Therefore, instead of calling our add_meta_box function in the body of our plugin page file as was done above, we do it based on the load-settings_page_shiba_test action hook.

add_action("load-settings_page_shiba_test", array(&$this, 'on_load_shiba_test_page'));

function on_load_shiba_test_page() {
add_meta_box(	'tagsdiv-post_tag', __('Test Tags'), 
				'post_tags_meta_box', 
				settings_page_shiba_test, 'side', 'core');
	}

In addition, you will need to run the postboxes.add_postbox_toggles Javascript function for the collapsible meta-boxes to work.

<script type="text/javascript">
	//<![CDATA[
	jQuery(document).ready( function($) { 
             postboxes.add_postbox_toggles('settings_page_shiba_test');
        });
	//]]>
</script>

5. Meta-Box Layout

Thus far our meta-box has no layout and is merely rendered to fill the entire width of our page. Using a simplified example from Code Styling we can set our plugin menu to have a two column layout similar to the post, page, and link menus.

    <?php global $screen_layout_columns;?>
    <?php wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false ); ?>
    <?php wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false ); ?>
    <div id="poststuff" class="metabox-holder<?php echo 2 == $screen_layout_columns ? ' has-right-sidebar' : ''; ?>">
        <div id="side-info-column" class="inner-sidebar">
		<?php $meta_boxes = do_meta_boxes('settings_page_shiba_test', 'side', $test_object); ?>
        </div>
        <div id="post-body" class="has-sidebar">
            <div id="post-body-content" class="has-sidebar-content">
			<?php $meta_boxes = do_meta_boxes('settings_page_shiba_test', 'normal', $test_object); ?>
                <?php $meta_boxes = do_meta_boxes('settings_page_shiba_test', 'additional', $test_object); ?>
           </div>
        </div>
        <br class="clear"/>
                        
    </div>	<!-- end poststuff -->

Note – The closedpostboxes and meta-box-order nonce fields are both needed for proper operation of the Screen Options settings and the drag and drop operations.

To enable number-of-columns selection under Screen Options, you will need to add in the screen_layout_columns filter. You can then set the number of columns for your plugin page in your filter function.

add_filter('screen_layout_columns', array(&$this, 'on_screen_shiba_test_columns'), 10, 2);

function on_screen_shiba_test_columns($columns, $screen) {
	if ($screen == 'settings_page_shiba_test') {
		$columns['settings_page_shiba_test'] = 2;
	}
	return $columns;
}

6. Getting Your Meta-Box Values

At some point, you will want to retrieve the values from your WordPress meta-box and save them or use them for further processing. To do this you must identify where those values are stored in the $_GET or $_POST arrays.

For example, to get the values from the standard tag meta-box, we can use the code below -

if (!isset($_GET['tax_input']['post_tag'])) return;
$tag_arr = explode(',',esc_attr($_GET['tax_input']['post_tag']));

The simplest way to find out where the meta-box results are stored is to add a print_r statement to the beginning of your plugin page. In this way, we discover that -

  • ['tax_input']['post_tag'] contains our tag values,
  • ['newtag']['post_tag'] contains any new tags added,
  • ['post_author_override'] contains the author meta-box id,
  • ['post_category'] contains an array of category ids,
  • ['newcat'] contains any new categories added,
  • ['newcat_parent] contains the parent of any new categories added.

Once you identify the location of your meta-box values, you can easily extract and process them in your form processing function.

7. Congratulations You Are Done!

Whew – and we are done!

Many thanks to Heiko Rabe (Code Styling Project) for his excellent tutorial on enabling drag and drop as well as screen option capabilities for meta-boxes.

Related Articles

Comments

  1. says

    Reason why I need this: I want my word-press post be in specific category like Country – State – City. But my list is toooooo big so it causes the loading time so higher.

    I want to modify this category section like.

    On the new post it only shows the country list when I click on the country then it loads the states for that parent country. And when I clicked on the state then it loads the city list.

    I search for the plugin which suits my requirement but I fail If you know any plugin which help me then please recommend me.

    Or give me some suggestion how I make this king of plugin from start.

    Thanks guys,
    Love WordPress.

  2. says

    What a great post.
    I’m trying to put a Category selection list in my plugin’s options page.
    I have succeeded in getting the list there, but now I need to:
    a. Check the boxes of any categories that have been checked and saved for my options.
    b. Get the boxes that have been checked so I can save them.

    You explained well in step 6: Getting your meta box values. But, I am using the Settings API, and don’t see anywhere that I can “intervene” and munge out those settings to have the SAPI save away.

    I hope this makes sense. Anyway, great stuff on your site, I appreciate it.

    • ShibaShake says

      Hello Paul,
      Do you mean you have added your own plugin page to the Settings menu? Or are you trying to expand an existing Settings page?

      I usually just add my own options page. In that case, I just set my form action to “”, and do the $_POST argument extraction and saving at the beginning of the page.

  3. says

    Hi, great tutorial! I have only one question – is it possible to modify default “Publish” metabox for custom posts type? This metabox can not be hidden, but I would like to change buttons text, some styling and remove visibility option. I tried to find solution, but with no success… Thank you very much for your help…

  4. Burner says

    Hello. I have two questions:
    1. How can you toggle a metabox by default? In your example, the metabox is open. I want to close 2-3 metaboxes from 10.
    2. How can you start with 1 column layout selected? I read the tutorial from code-styling.de and he used a two layout columns. I want to start with 1 layout column and let the user to choose the 2 layout column.

    • ShibaShake says

      1. How can you toggle a metabox by default?

      You can do this by adding the class ‘closed’ to the metabox div. For example I added the Javascript addClass command in my ready function below -

      %MINIFYHTML446cda3c37f8b0594512474d6fe0454e14%

      Make sure to use the div names of the metaboxes you want to close.

      2. How can you start with 1 column layout selected?

      This information is stored in a user option field called screen_layout_$screenID where $screenID is the ID used in your add_meta_box call.

      One way to set a default, is by adding the following to my admin_init function

      		$user_col = get_user_option("screen_layout_settings_page_shiba_test");
      		if (!$user_col) {
      			$user = wp_get_current_user();
       			update_user_option($user->ID, "screen_layout_settings_page_shiba_test",1, TRUE);
      		}	
      
  5. Jessica says

    Hi Shibashake,

    I’m wondering if you’ve ever actually used post_submit_meta_box. I’ve added it to one of my plugin admin screens, but it displays as if I’m logged in as a contributor (even though I’m logged in as admin).

    For example, the button reads “Submit for Review,” not “Publish.”

    What’s particularly strange, though, is that it still publishes my post instead of submitting is as pending. So I guess you could say that my post_submit_meta_box is displaying as if it thinks I’m a contributor, but it’s still behaving as if it knows I’m an admin.

    I’m just wondering if you’ve ever run into a similar issue when using post_submit_meta_box.

    Thx,
    Jessica

    • ShibaShake says

      Hi Jessica,
      I have never actually used post_submit_meta_box, but I did look briefly at the code. It seems that it decides which Text to display based on this condition -

      current_user_can('publish_posts')

      or

      current_user_can($post_type_object->cap->publish_posts);

      in 3.0.

      current_user_can should retrieve the proper user data, so it is strange that it is not returning the right values. I would try to call this function in the same page, but outside of the metabox and see what you get back.

      Anyway, it would be great if you can post your results, in case someone else runs into similar issues. Thanks!

      • Jessica says

        Yeah, current_user_can(‘publish_posts’) was one of the things I checked. I even created a fresh installation just to ensure that a plugin hadn’t meddled with role capabilities in some way.

        If I ever figure this out, I will definitely report back. Thanks.

  6. ShibaShake says

    Thanks for posting your solution Doe. I think it will be helpful to other plugin developers.

  7. Doe says

    I have just a question. I finished to implement the metaboxes inside my custom plugin but i can’t restructure the columns in two equal columns (50-50, not postbos-sidebar). I searched over the WordPress code for an substitute for “has-right-sidebar” but i can’t find anything. Could you help me with a solution? Thanks.

    • ShibaShake says

      You could try something like this –

          
      	#side-info-column {
      		width:370px;
      		border:solid;
      	}	
      	.inner-sidebar #side-sortables {
      		width:100%;
      		}
      	.has-right-sidebar #post-body-content {
      		width:370px; 
      		border:solid;
      		margin-right:390px;	
      	}	
      	.has-right-sidebar #post-body {
      		margin-right:-410px;
      	}	
      	#post-body #normal-sortables {
      		width:100%;
      	}		
      		        
      
      • Doe says

        Thank you very much. Here is my actual CSS code (based on yours):

        #side-info-column {
        width:48%;
        }
        .inner-sidebar #side-sortables {
        width:100%;
        }
        .has-right-sidebar #post-body-content {
        width:50%;
        margin-right:390px;
        }
        .has-right-sidebar #post-body {
        margin-right:-50%;
        }
        #post-body #normal-sortables {
        width:100%;
        }
        I used percents to keep the metaboxes flexible if the resolution is changed.
        Thanks again.

        • Doe says

          Sorry for repost but, actually, this code is making the columns equal:

          #side-info-column {
          width:49%;
          }
          .inner-sidebar #side-sortables {
          width:100%;
          }
          .has-right-sidebar #post-body-content {
          width:49%;
          margin-right:390px;
          }
          .has-right-sidebar #post-body {
          margin-right:-50%;
          }
          #post-body #normal-sortables {
          width:100%;
          }

  8. Jessica says

    Thanks, Shibashake. I’ve figured it out.

    Yes, I was calling load-(page) from the admin_menu action as you noted, but I must have been doing it improperly.

    My main plugin page runs the admin_menu action (to add my plugin pages to the admin menu), and then I was trying to use admin_menu again on another page. I just transferred all my code code to the admin_menu action function on the main plugin page, and it worked perfectly. (I’m still a novice at PHP and WordPress, so maybe that’s a pretty obvious thing to do. :)

    Another problem was that I was trying to use the page $handle in the load-(page) action instead of the full pagename.

    Thanks again for your great tutorial.

    • ShibaShake says

      Thanks for letting us know Jessica.

      Another problem was that I was trying to use the page $handle in the load-(page) action instead of the full pagename.

      I did that as well. I should probably add something about it in the article. Thanks!

  9. ShibaShake says

    Hi Jessica,
    Where are you adding in the add_action and add_filter hooks?

    I call mine from the ‘admin_menu’ hook function after I add my pages.

    I have also tried adding in the add_meta_box commands in the ‘admin_menu’ hook function and that has also worked. It is not ideal because you only want the meta_boxes to load on a particular page, but may help with debugging.

    Another thing that may help is to load the example plugin on the Code Styling site. If the plugin metaboxes work on your environment, you can just backtrack from there.

    Hope this helps. Let us know what happens.

  10. Jessica says

    Thanks so much for this tutorial! It has been a life saver.

    I’m running into a problem, though, in that I’m unable to run the add_meta_box functions from within the load-$pagename action hook. (I end up with a mostly blank page.) If I ever figure out what is going wrong, I’ll come back here with an update to hopefully help others avoid the same problem.

Trackbacks

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>