cc licensed flickr photo shared by StayRAW

In the previous posts in this series of using the new WordPress 3 Custom Post Types (I keep calling them custom content types, same thing), I overviewed the plans for the MIDEA web site, we set up the places to create the new content types, and diverged into some set up magic using child themes.

That was the easy stuff. Now I get into the parts I had to more or less invent on my own (well, with some good leg ups from others)- how to add all of the form field elements to my new content types so I could add extra information to them. This are fields for say, my Organization content types, to have a field to enter their web site address, latitude/longitude for doing some mapping, etc.

Now I did this all in custom code, and as hopefully I stated earlier, I have no expectation that this is the way it will be dobe going forward. I foresee a raft of new plugins coming in that will take away this manual code layer, and maybe, when WordPress 3.7 or 4.1 comes out, it might be built in. I have no idea. I did this to show the WP platform can do this.

What we are working towards is creating a place like this for my Organization content type when I create or edit new content:

So returning to our functions.php editing, the first thing we need to do is to activate the bits to turn on the lights inside the WordPress dashboard; adding in the top sequence of our code:

// Admin interface init
add_action("admin_init", 'midea_admin_init');

which again, puts a “hook” in the wordpress flow to include the admin functions I want to add. This new function will in turn set up what is needed to add “metaboxes” to the wordpress editing pane- these are my own editing form elements.

function midea_admin_init() {
	// Custom meta boxes for the custom post types
	add_meta_box("org-meta", "MIDEA Organization Details", midea_org_options, 'org', 'normal', 'low');
	add_meta_box("proj-meta", "MIDEA Project Details", midea_proj_options, 'proj', 'normal', 'low');
	add_meta_box("event-meta", "MIDEA Event Details", midea_event_options, 'event', 'normal', 'low');
}

Here we use the add_meta_box function to do the work. The variables I am passing, for say the Organization types are:

  • org-meta a unique id just to identify the metabox, likely used to track in the database. I named mine in line with the content type slugs.
  • MIDEA Organization Details the title that will appear in the top of the box
  • midea_org_options the “callback function”, meaning my own function that will be used to do the set up work.
  • org the internal name I set up previously for this content type.
  • normal the placement of the metabox- normal is the main pane (below the post editing field). I could also put it on the right side if I use the value ‘side’
  • low I’m not sure what this does, the codex says “The priority within the context where the boxes should show”; perhaps if it is set to “high” it might appear above the main post editing field?

As something we will need quite a bit, I came up with this approach to define the meta data fields for my content types (as opposed to tossing around global variables). I call one of these when I need to create a code “container” for each content type’s meta data, and later we will use database calls to put in the actual values. But as a utility, this simply returns an array of the metadata, all set with initial empty string values.

function midea_org_fields() {
	// provide array of names for fields in organization content type;
	// init with empty valyes to fill later
	return array('web' => '', 'facebook' => '', 'twitter' => '' , 'address' => '', 'lat' => '', 'long' => '', 'tagline' => '');
}

function midea_proj_fields() {
	// provide array of names for fields in project content type;
	// init with empty valyes to fill later
	return array('url' => '', 'org' => '', 'rating' => '');
}

function midea_event_fields() {
	// provide array of names for fields in event content type;
	// init with empty valyes to fill later
	return array('start' => '', 'end' => '', 'url' => '', 'location' => '');
}

Now we are ready to create some metaboxes! For our Organization type, this is the callback function that creates the pieces shown in the image above.

function midea_org_options() {
	// add meta data fields for a MIDEA organization content type
	global $post;
	
	// Use nonce for verification
	echo '<input type="hidden" name="midea_noncename" id="midea_noncename" value="' . 
    wp_create_nonce( 'MyNonceCode' ) . '" />';

	$my_fields = midea_org_fields(); // fields for this content type
	
	foreach ($my_fields as $key => $value) {
		// load values into our array
		$my_fields[$key] = get_post_meta($post->ID, 'midea-org-' . $key, true);
	}	
	
	echo '<p><strong>About</strong></p><p><label>Tagline</label> A one sentence blurb that describes what this organization is about:<textarea name="midea-org-tagline" rows="4" cols="55">' . $my_fields['tagline'] . '</textarea></p>';
	
	echo '<p><strong>Online Presence</strong></p><p><label>web site:</label>: <input name="midea-org-web" size="60" value="' . $my_fields['web'] . '" /></p>' . "\n";
	echo '<p><label>facebook url</label>: <input name="midea-org-facebook" size="60"  value="' . $my_fields['facebook'] . '" /></p>' . "\n";
	echo '<p><label>twitter username</label>:<input name="midea-org-twitter" size="30"  value="' . $my_fields['twitter'] . '" /></p>' . "\n";
	
	echo '<p><strong>Location</strong></p><p><label>address</label>:<textarea name="midea-org-address" rows="2" cols="55">' . $my_fields['address'] . '</textarea></p>' . "\n" . '<p><label>latitude</label>: <input name="midea-org-lat" size="15"  value="' . $my_fields['lat'] . '" />' . "\n";
	echo '<label>longitude</label>: <input name="midea-org-long" size="15"  value="' . $my_fields['long'] . '" />' . "\n";
}

So roughly, as a narrative, this is what I am doing. We always have to add this thing I dont fully understand, but is a security to make sure the form data is coming from my own code, a “nonce”, a hidden form element. The value you use “MyNonceCode” should be any unique string (don’t use that string, its just a placeholder). If you do not do this, any data you add will not “stay”in te database, as WordPress does its own internal cleanups. I think.

Next, I use the functions listed above to get my meta data container, and I cycle through them with the get_post_meta function to grab any values that already exist. If you look carefully, I use the array keys to define the meta data, prefixed in this case with “midea-org-” to identify it in the database.

And lastly, I just echo what I need to build the form fields I want. It may be more code clean to use a function approach as described in Genuine Juice’s key guide to creating these elements but I knew I would adding some very specific labels to my form elements, and I wanted full control. Hence the HazMat suit.

I’m not going to march through all the details of my other two content types, but they are included in my sample code below. The extra wrinkles here was for say, my Project content types, as I wanted one meta data element to be a drop down list of organizations I could associate a project with- so this involved writing a function to get all my Organization names and database ids, and build a menu out of that– isn;t this cool- I use one custom content type as an elemnt to create meta data for another.

And the Events content type had to do some extra work to deal with the date/time entry fields. Instead of creating a series series of month, day, year, hour, minute fields or using some Javascript widget, I took a simpler approach of a single field and using PHP’s strtotime function to convert it to a unix time stamp, which is what we store in the database. There is a bit of gymnastics here for events- if it is a single day event, we look at the time values to generate start/end times. If it is a multi-day event, we just want to know the month/day/year values.

So for now, we can create the form fields for entering data, but we have to add more code to have WordPress save it all.

We have to add another “hook” at the top to tell WordPress do do our extra bits when a post/content type is saved.

// Use the save_post action to do something with the data entered
add_action( 'save_post', 'midea_save_post', 1, 2 );

The midea_save_post is the callback function used when we save anything- I honestly lost track of what the extra variables do (I did read up on it and just used values I saw used in the examples).

So here is the function I made to save my meta post data

// When a post is inserted or updated
function midea_save_post()
{
	global $post;
	
	$post_id = $post->ID;

	// 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['midea_noncename'], 'MyNonceCode')) return $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 $post_id;
	
	// flag to set if we need to deal with custom post type
	// not exactly sure if we need this, but it cannot hurt
	
	$post_flag = 'x'; 
	
	if ($post->post_type == "org") {
		$my_fields = midea_org_fields(); // fields for this content type
		$post_flag = 'org';
	} elseif ($post->post_type == "proj") {
		$my_fields = midea_proj_fields(); // fields for this content type
		$post_flag = 'proj';
	}  elseif ($post->post_type == "event") {
		$my_fields = midea_event_fields(); // fields for this content type
		$post_flag = 'event';
	}
	
	if ($post_flag != 'x') {
		// we have MIDEA content to deal with
	
		// Loop through the MIDEA field data
		foreach ($my_fields as $k=>$v)
		
		{	
			$key = 'midea-' . $post_flag . '-' . $k;
			
			$value = @$_POST[$key];
			if (empty($value))
			{
				delete_post_meta($post_id, $key);
				continue;
			}
			
			// special check for event time fields, convert as needed to unix time
			if ($key == 'midea-event-start' OR $key == 'midea-event-end' ) {
				$value = strtotime($value);
			}

			// If value is a string it should be unique
			if (!is_array($value))
			{
				// Update meta
				if (!update_post_meta($post_id, $key, $value))
				{
					// Or add the meta data
					add_post_meta($post_id, $key, $value);
				}
			}
			else
			{
				// If passed along is an array, we should remove all previous data
				delete_post_meta($post_id, $key);
				
				// Loop through the array adding new values to the post meta as different entries with the same name
				foreach ($value as $entry)
					add_post_meta($post_id, $key, $entry);
			}
		}
		
	}
}

This is a bit more complex, and again, I just modified it from examples I found form early HazMat suit wearers. It uses that same “nonce’ thing (make sure your codes match). I am not 100% sure I needed that hack of using $post_flag I thought at one point I needed to make sure I did not run this function for saving any post type.

Basically, I am setting up my post meta data array, and then looking through the submitting form POST values to see if we have a value there. If we have this already in the database, we need to remove the old data first (delete_post_meta), and then add the new data (add_post_meta).

So now at this point, we can add this metadata, and if we have done it right, when we go to edit, the current values, if they exist, are pre-populated in the fields.

Whew. Anyone still breathing?

The last bit is some useful things you can do to customize the layout when you go to the Edit content types area- you can generate your own list of columns relevant to your content types. For example, when I go to edit my Organization content types, I get this interface:

So rather than a blog or page post title, my columns list the organization name, plus (from my own post meta data) the organization’s URL and their address. I can make any columns I want, and can even manipulate the output anyway relevent (like I made the URL a hyperlink).

To do this, we need to modify our content type creator function midea_custom_init() to include 2 new actions added for each content type, in the case of organizations:

	// set up dashboard editing view
	add_action("manage_posts_custom_column", "org_custom_columns");
	add_filter("manage_edit-org_columns", "org_columns");

This creates a callback function org_custom_columns used to generate the view above, and uses the org_columns function to define the columns. These functions look like:

function org_columns($columns)
{
	$columns = array(
		"cb" => "<input type=\"checkbox\" />",
		"title" => "Organization",
		"web" => "Web Site",
		"address" => "Address",	
		"status" => "Status",	
	);	
	return $columns;
}

function org_custom_columns($column) 
{
	global $post;
	
	if ("ID" == $column) echo $post->ID;
	elseif ("title" == $column) echo $post->post_title;
	elseif ("web" == $column) {
		$ourl = get_post_meta($post->ID, 'midea-org-web', true);
		echo '<a href="' . $ourl . '" target="_blank">' . $ourl . '</a>';
	}
	elseif ("address" == $column) echo get_post_meta($post->ID, 'midea-org-address', true);
	elseif ("status" == $column) echo ucfirst($post->post_status) . 'ed' . substr($post->post_date,0,10) ;
	
}

So org_columns merely defineds an array of the columns (“cb” is the built in check boxed for multiple editing, and the org_custom_columns function defines the output for each type; you can see for the URL where I do the extra work to extract my meta data values and build a hyperlink.

My sample code below includes the other functions used for my own other two content types. They have a bit more gymnastics, but follow the same concept as above.

So here is e the functions.php cod I have made up to this point. We have set up everything we need to do to create, edit custom content types, the next step is to do some WordPress theme hacking to set up the output. Easy peasy, eh?

For the other parts of this convoluted series…

You can find all the posts in this series at http://cogdogblog.com/2010/07/23/roundup-wp3/

The post "Put on Your HazMat Suits- Setting Up Metadata For WordPress 3 Custom Content Types" was originally zapped with 10,000 volts and declared "It's ALIVE" by Dr. Frankenstein at CogDogBlog (http://cogdogblog.com/2010/06/put-on-your-hazmat-suits/) on June 6, 2010.

23 Comments

  • Setting up Custom Content Types in WordPress 3.0 - CogDogBlog cogdogblog.com/2010/05/28/setting-up-custom-contenttypes

    […] Adding the meta data field editing boxes (add_post_meta). Hacking the Editing fields. […]

  • First of all I wanted to thank for this amazing tutorial , I am very carefully watching this post , I am trying to create multiwebsite for nightclub owners where everyone can create their own website and this post and child themes are exactly what I need , instead organizations I will have nightclubs , events will remains , projects I dont know yet but I will find solution also for that , for me coding is impossible at this moment because I’m programmer – ignorant yet, but copy , edit , delete and paste I know well and I hope that very soon in future thanks tutorials like this I will discover the way how to understand things well.
    One thing I wanted ask you now : Is it hard to change latitute and longitude with something like one of the plugins in wordpress where you enter the address and output will be exact Google map address of the organization? This plugin after you enter right address gives you exact Google map , it should be nice connection with this plugin because you don’t need to search for latitudes etc , it will grab your info from address fields.
    btw. I use changed default theme from developer pack wordpress 3.0 . Keep up good work ! Daniel

  • Another question wanted to ask you: was studying your mother website of this wonderfully described post type hack (Midea ) and I would like to know whether you are also planning to add some frontpage interface for your website members for adding museums , projects , events theirselves ? At this moment I suppose that everybody has to email you to be included in your database etc and you have to go in your admin dashboard to add it there. I think that it would be better to let the people add their projects , organisations , events themselves in the frontpage and you will only moderate them , it means much less work for you . I am sure that post types will be new hit between wordpress fans and I hope that in wordpress directory they will create new category “post types” (like plugins , widgets etc .)where everybody can get the codes for their functions.php . Only thing I miss now is that somebody should come with some interface to use the post types in frontend.

    • Alan Levine aka CogDog cogdogblog.com

      @Daniel,

      There are a few code bits I’ve not explored that allow you to create some editing privileges on custom content, but I;ve not explored it in depth. The site I was working on is not a giant site where we are collecting a lot of user created content, so its a different scenario of managing it.

      The things you seek will likely emerge as plugins.

  • Christina

    Thank you so much for this series of posts. I’m anxiously awaiting your next installment!

  • Christina

    Are you planning on doing an entry about how to take the data, format it and display it on your public site? I realize that is like asking for a dessert on top of dessert, since you’ve been so helpful already. So far, the only help I’ve found with that is “sure, easy, modify the loop”. Yikes!

  • Alan Levine aka CogDog cogdogblog.com

    @Christina- that part is coming in the next installment (it might be next week), and I’ll show you some loop examples of how to do this, and more.

  • Alan-
    What a wonderful aggregation of meta-box resources and a fine walk through by you! It is much appreciated as this is a more highly technical aspect of WordPress for the advanced user.

    At WordCamp Chicago I asked Jane Wells (core WP developer, UI focused) about the possibility of a built-in UI for custom post types à la Drupal’s CCK/Views/Blocks powerhouse of modules. She referred the audience to the Custom Post Type UI plugin by Brad Williams.

    While this plugin is a “nice” step in a UI direction, it is too simplistic to really do anything with. Like you wrote, we will hopefully see the core team or a dedicated plugin creator create something comprehensive that not only handles the custom post types but also the meta-boxes and the front-facing display of this content.

    I’m creating a thorough research knowledge base in preparation for my PhD work in the fall all with custom post types. There have been plenty of times that I’ve said, “Why am I not doing this with CCK?” In the end, my “allegiance” to WordPress and my desire to learn something new about WP and PHP keeps me chugging along despite the urge to fall back on something easier to use.

    Alas…

    Thanks for your hard work-
    ~Kyle~
    @thecorkboard

  • Mark

    Hi Alan

    thanks for the indepth step-by-step article!

    One question: is there any way to make the columns sortable (with a little arrow at the top of each column) so you could sort by address or website or status?

    looking forward to the next installment :) thanks again

  • Hello Alan , I wanted to ask you , did I miss this somewhere ?
    Christina says:
    June 10, 2010 at 5:48 am
    Are you planning on doing an entry about how to take the data, format it and display it on your public site? I realize that is like asking for a dessert on top of dessert, since you’ve been so helpful already. So far, the only help I’ve found with that is “sure, easy, modify the loop”. Yikes!

    Reply
    Alan Levine aka CogDog says:
    June 10, 2010 at 7:22 am

    @Christina- that part is coming in the next installment (it might be next week), and I’ll show you some loop examples of how to do this, and more.

  • ok Alan , I understand , I have a lot of work too, so I didn’t check it out here very often, I only wanted to know if I miss it somewhere , it should be a pity to do not finish this great series, Thanks for it -:)

  • Building a Site with New WordPress 3.0 Content Types: Part 1 of Several - CogDogBlog cogdogblog.com/2010/05/28/wordpress-3-pt1

    […] Adding the meta data field editing boxes (add_post_meta). Hacking the Editing fields. […]

  • Pinning WordPress 3 Custom Content to the Map (b) - CogDogBlog cogdogblog.com/2010/07/22/wp3-custom-content-map

    […] the places to create the new content types, and introduced the benefits using child themes. Then we dove headfirst into the code to create the interface and data elements to add/edit the custom meta d… for each of the three content (post) […]

  • Those Darn Kids: WordPress Child Themes - CogDogBlog cogdogblog.com/2010/05/29/those-darn-kids

    […] Adding the meta data field editing boxes (add_post_meta). Hacking the Editing fields. […]

  • Dressing up and Displaying WordPress 3 Custom Post Content (a) - CogDogBlog cogdogblog.com/2010/07/21/dressing-up-wp3-content-a

    […] new content types, and introduced the benefits using child themes. Then we took it up a notch to dive into the code to create the interface and data elements to add/edit the custom meta data for each of the three content (post) […]

  • Aziwaan

    Have you checked the update works properly. By properly I mean no duplicates in the DB, pretty sure every time you update a post this will add a whole new lot of meta_key and meta_values to the wp_postmeta table.

    • Alan Levine aka CogDog cogdogblog.com

      I’ll have to spend some time to look back at it- I thought the code is set to check if the sabe is an update- it is supposed to delete old meta data before adding new, but I am seeing some repeats. Thanks

      • Aziwaan

        Im not sure how elegant this is but it seems to work.

        // If value is a string it should be unique
        if (!is_array($value))
        {
        // Update meta
        if (!update_post_meta($post_id, $key, $value))
        {
        // Or add the meta data
        update_post_meta($post_id, $key, $value);
        }
        }
        else
        {
        // If passed along is an array, we should remove all previous data
        delete_post_meta($post_id, $key);

        // Loop through the array adding new values to the post meta as different entries with the same name
        foreach ($value as $entry)
        add_post_meta($post_id, $key, $entry);
        }

  • Custom Post Types in Wordpress 3.0 Links | Jason Lawton's Blog jasonlawton.com/blog/custom-post-types-in-wordpress-3-0-links

    […] post helped me figure out how to get meta data onto the manage post page. Hint, it’s […]

Leave a Comment

All fields are required. Your email address will not be published.