Index: wp-includes/post.php
===================================================================
--- wp-includes/post.php	(revision 15868)
+++ wp-includes/post.php	(working copy)
@@ -20,6 +20,7 @@
 		'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
 		'_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
 		'capability_type' => 'post',
+		'map_meta_cap' => true,
 		'hierarchical' => false,
 		'rewrite' => false,
 		'query_var' => false,
@@ -31,6 +32,7 @@
 		'_builtin' => true, /* internal use only. don't use this when registering your own post type. */
 		'_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
 		'capability_type' => 'page',
+		'map_meta_cap' => true,
 		'hierarchical' => true,
 		'rewrite' => false,
 		'query_var' => false,
@@ -836,7 +838,8 @@
  * - menu_position - The position in the menu order the post type should appear. Defaults to the bottom.
  * - menu_icon - The url to the icon to be used for this menu. Defaults to use the posts icon.
  * - capability_type - The post type to use for checking read, edit, and delete capabilities. Defaults to "post".
- * - capabilities - Array of capabilities for this post type. You can see accepted values in {@link get_post_type_capabilities()}. By default the capability_type is used to construct capabilities.
+ * - capabilities - Array of capabilities for this post type. You can see accepted values in {@link get_post_type_capabilities()}. By default the capability_type is used as a base to construct capabilities.
+ * - map_meta_cap - Whether to use the internal default meta capability handling. Defaults to false.
  * - hierarchical - Whether the post type is hierarchical. Defaults to false.
  * - supports - An alias for calling add_post_type_support() directly. See add_post_type_support() for Documentation. Defaults to none.
  * - register_meta_box_cb - Provide a callback function that will be called when setting up the meta boxes for the edit form.  Do remove_meta_box() and add_meta_box() calls in the callback.
@@ -866,7 +869,8 @@
 	// Args prefixed with an underscore are reserved for internal use.
 	$defaults = array(
 		'labels' => array(), 'description' => '', 'publicly_queryable' => null, 'exclude_from_search' => null,
-		'_builtin' => false, '_edit_link' => 'post.php?post=%d', 'capability_type' => 'post', 'capabilities' => array(), 'hierarchical' => false,
+		'capability_type' => 'post', 'capabilities' => array(), 'map_meta_cap' => false,
+		'_builtin' => false, '_edit_link' => 'post.php?post=%d', 'hierarchical' => false,
 		'public' => false, 'rewrite' => true, 'query_var' => true, 'supports' => array(), 'register_meta_box_cb' => null,
 		'taxonomies' => array(), 'show_ui' => null, 'menu_position' => null, 'menu_icon' => null,
 		'permalink_epmask' => EP_PERMALINK, 'can_export' => true, 'show_in_nav_menus' => null, 'show_in_menu' => null,
@@ -978,25 +982,63 @@
  * - read_private_posts - The capability that controls reading private posts. Defaults to "read_private . $capability_type . s" (read_private_posts).
  * - delete_post - The meta capability that controls deleting a particular object of this post type. Defaults to "delete_ . $capability_type" (delete_post).
  *
+ * @see map_meta_cap()
  * @since 3.0.0
+ *
  * @param object $args
  * @return object object with all the capabilities as member variables
  */
 function get_post_type_capabilities( $args ) {
-	$defaults = array(
+	global $_post_type_meta_capabilities;
+
+	$default_capabilities = array(
+		// Meta capabilities are generally mapped to primitive capabilities depending on the context
+		// (which would be the post being edited/deleted/read), instead of granted to users or roles:
 		'edit_post'          => 'edit_'         . $args->capability_type,
+		'read_post'          => 'read_'         . $args->capability_type,
+		'delete_post'        => 'delete_'       . $args->capability_type,
+		// Primitive capabilities that are used outside of map_meta_cap():
 		'edit_posts'         => 'edit_'         . $args->capability_type . 's',
 		'edit_others_posts'  => 'edit_others_'  . $args->capability_type . 's',
 		'publish_posts'      => 'publish_'      . $args->capability_type . 's',
-		'read_post'          => 'read_'         . $args->capability_type,
 		'read_private_posts' => 'read_private_' . $args->capability_type . 's',
-		'delete_post'        => 'delete_'       . $args->capability_type,
 	);
-	$labels = array_merge( $defaults, $args->capabilities );
-	return (object) $labels;
+	// Primitive capabilities that are used within map_meta_cap():
+	if ( $args->map_meta_cap ) {
+		$default_capabilities_for_mapping = array(
+			'read'                   => 'read',
+			'delete_posts'           => 'delete_'           . $args->capability_type . 's',
+			'delete_private_posts'   => 'delete_private_'   . $args->capability_type . 's',
+			'delete_published_posts' => 'delete_published_' . $args->capability_type . 's', 
+			'delete_others_posts'    => 'delete_others_'    . $args->capability_type . 's',
+			'edit_private_posts'     => 'edit_private_'     . $args->capability_type . 's',
+			'edit_published_posts'   => 'edit_published_'   . $args->capability_type . 's',
+		);
+		$default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );
+	}
+	$capabilities = array_merge( $default_capabilities, $args->capabilities );
+	if ( $args->map_meta_cap )
+		_post_type_meta_capabilities( $capabilities );
+	return (object) $capabilities;
 }
 
 /**
+ * Stores or returns a list of post type meta caps for map_meta_cap().
+ *
+ * @since 3.1.0
+ * @access private
+ */
+function _post_type_meta_capabilities( $capabilities = null ) {
+	static $meta_caps = array();
+	if ( null === $capabilities )
+		return $meta_caps;
+	foreach ( $capabilities as $core => $custom ) {
+		if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) )
+			$meta_caps[ $custom ] = $core;
+	}
+}
+
+/**
  * Builds an object with all post type labels out of a post type object
  *
  * Accepted keys of the label array in the post type object:
Index: wp-includes/capabilities.php
===================================================================
--- wp-includes/capabilities.php	(revision 15868)
+++ wp-includes/capabilities.php	(working copy)
@@ -817,11 +817,12 @@
 			$caps[] = 'edit_users'; // Explicit due to primitive fall through
 		break;
 	case 'delete_post':
+	case 'delete_page':
 		$author_data = get_userdata( $user_id );
 		//echo "post ID: {$args[0]}<br />";
 		$post = get_post( $args[0] );
 		$post_type = get_post_type_object( $post->post_type );
-		if ( $post_type && 'post' != $post_type->capability_type ) {
+		if ( 'delete_post' == $cap && $post_type && 'post' != $post_type->capability_type && ! $post_type->map_meta_cap ) {
 			$args = array_merge( array( $post_type->cap->delete_post, $user_id ), $args );
 			return call_user_func_array( 'map_meta_cap', $args );
 		}
@@ -837,69 +838,34 @@
 		if ( is_object( $post_author_data ) && $user_id == $post_author_data->ID ) {
 			// If the post is published...
 			if ( 'publish' == $post->post_status ) {
-				$caps[] = 'delete_published_posts';
+				$caps[] = $post_type->cap->delete_published_posts;
 			} elseif ( 'trash' == $post->post_status ) {
 				if ('publish' == get_post_meta($post->ID, '_wp_trash_meta_status', true) )
-					$caps[] = 'delete_published_posts';
+					$caps[] = $post_type->cap->delete_published_posts;
 			} else {
 				// If the post is draft...
-				$caps[] = 'delete_posts';
+				$caps[] = $post_type->cap->delete_posts;
 			}
 		} else {
 			// The user is trying to edit someone else's post.
-			$caps[] = 'delete_others_posts';
+			$caps[] = $post_type->cap->delete_others_posts;
 			// The post is published, extra cap required.
 			if ( 'publish' == $post->post_status )
-				$caps[] = 'delete_published_posts';
+				$caps[] = $post_type->cap->delete_published_posts;
 			elseif ( 'private' == $post->post_status )
-				$caps[] = 'delete_private_posts';
+				$caps[] = $post_type->cap->delete_private_posts;
 		}
 		break;
-	case 'delete_page':
-		$author_data = get_userdata( $user_id );
-		//echo "post ID: {$args[0]}<br />";
-		$page = get_page( $args[0] );
-		$page_author_data = get_userdata( $page->post_author );
-		//echo "current user id : $user_id, page author id: " . $page_author_data->ID . "<br />";
-		// If the user is the author...
-
-		if ('' != $page->post_author) {
-			$page_author_data = get_userdata( $page->post_author );
-		} else {
-			//No author set yet so default to current user for cap checks
-			$page_author_data = $author_data;
-		}
-
-		if ( is_object( $page_author_data ) && $user_id == $page_author_data->ID ) {
-			// If the page is published...
-			if ( $page->post_status == 'publish' ) {
-				$caps[] = 'delete_published_pages';
-			} elseif ( 'trash' == $page->post_status ) {
-				if ('publish' == get_post_meta($page->ID, '_wp_trash_meta_status', true) )
-					$caps[] = 'delete_published_pages';
-			} else {
-				// If the page is draft...
-				$caps[] = 'delete_pages';
-			}
-		} else {
-			// The user is trying to edit someone else's page.
-			$caps[] = 'delete_others_pages';
-			// The page is published, extra cap required.
-			if ( $page->post_status == 'publish' )
-				$caps[] = 'delete_published_pages';
-			elseif ( $page->post_status == 'private' )
-				$caps[] = 'delete_private_pages';
-		}
-		break;
 		// edit_post breaks down to edit_posts, edit_published_posts, or
 		// edit_others_posts
 	case 'edit_post':
+	case 'edit_page':
 		$author_data = get_userdata( $user_id );
 		//echo "post ID: {$args[0]}<br />";
 		$post = get_post( $args[0] );
 
 		$post_type = get_post_type_object( $post->post_type );
-		if ( $post_type && 'post' != $post_type->capability_type ) {
+		if ( 'edit_post' == $cap && $post_type && 'post' != $post_type->capability_type && ! $post_type->map_meta_cap ) {
 			$args = array_merge( array( $post_type->cap->edit_post, $user_id ), $args );
 			return call_user_func_array( 'map_meta_cap', $args );
 		}
@@ -909,87 +875,45 @@
 		if ( is_object( $post_author_data ) && $user_id == $post_author_data->ID ) {
 			// If the post is published...
 			if ( 'publish' == $post->post_status ) {
-				$caps[] = 'edit_published_posts';
+				$caps[] = $post_type->cap->edit_published_posts;
 			} elseif ( 'trash' == $post->post_status ) {
 				if ('publish' == get_post_meta($post->ID, '_wp_trash_meta_status', true) )
-					$caps[] = 'edit_published_posts';
+					$caps[] = $post_type->cap->edit_published_posts;
 			} else {
 				// If the post is draft...
-				$caps[] = 'edit_posts';
+				$caps[] = $post_type->cap->edit_posts;
 			}
 		} else {
 			// The user is trying to edit someone else's post.
-			$caps[] = 'edit_others_posts';
+			$caps[] = $post_type->cap->edit_others_posts;
 			// The post is published, extra cap required.
 			if ( 'publish' == $post->post_status )
-				$caps[] = 'edit_published_posts';
+				$caps[] = $post_type->cap->edit_published_posts;
 			elseif ( 'private' == $post->post_status )
-				$caps[] = 'edit_private_posts';
+				$caps[] = $post_type->cap->edit_private_posts;
 		}
 		break;
-	case 'edit_page':
-		$author_data = get_userdata( $user_id );
-		//echo "post ID: {$args[0]}<br />";
-		$page = get_page( $args[0] );
-		$page_author_data = get_userdata( $page->post_author );
-		//echo "current user id : $user_id, page author id: " . $page_author_data->ID . "<br />";
-		// If the user is the author...
-		if ( is_object( $page_author_data ) && $user_id == $page_author_data->ID ) {
-			// If the page is published...
-			if ( 'publish' == $page->post_status ) {
-				$caps[] = 'edit_published_pages';
-			} elseif ( 'trash' == $page->post_status ) {
-				if ('publish' == get_post_meta($page->ID, '_wp_trash_meta_status', true) )
-					$caps[] = 'edit_published_pages';
-			} else {
-				// If the page is draft...
-				$caps[] = 'edit_pages';
-			}
-		} else {
-			// The user is trying to edit someone else's page.
-			$caps[] = 'edit_others_pages';
-			// The page is published, extra cap required.
-			if ( 'publish' == $page->post_status )
-				$caps[] = 'edit_published_pages';
-			elseif ( 'private' == $page->post_status )
-				$caps[] = 'edit_private_pages';
-		}
-		break;
 	case 'read_post':
+	case 'read_page':
 		$post = get_post( $args[0] );
 		$post_type = get_post_type_object( $post->post_type );
-		if ( $post_type && 'post' != $post_type->capability_type ) {
+		if ( 'read_post' == $cap && $post_type && 'post' != $post_type->capability_type && ! $post_type->map_meta_cap ) {
 			$args = array_merge( array( $post_type->cap->read_post, $user_id ), $args );
 			return call_user_func_array( 'map_meta_cap', $args );
 		}
 
 		if ( 'private' != $post->post_status ) {
-			$caps[] = 'read';
+			$caps[] = $post_type->cap->read;
 			break;
 		}
 
 		$author_data = get_userdata( $user_id );
 		$post_author_data = get_userdata( $post->post_author );
 		if ( is_object( $post_author_data ) && $user_id == $post_author_data->ID )
-			$caps[] = 'read';
+			$caps[] = $post_type->cap->read;
 		else
-			$caps[] = 'read_private_posts';
+			$caps[] = $post_type->cap->read_private_posts;
 		break;
-	case 'read_page':
-		$page = get_page( $args[0] );
-
-		if ( 'private' != $page->post_status ) {
-			$caps[] = 'read';
-			break;
-		}
-
-		$author_data = get_userdata( $user_id );
-		$page_author_data = get_userdata( $page->post_author );
-		if ( is_object( $page_author_data ) && $user_id == $page_author_data->ID )
-			$caps[] = 'read';
-		else
-			$caps[] = 'read_private_pages';
-		break;
 	case 'edit_comment':
 		$comment = get_comment( $args[0] );
 		$post = get_post( $comment->comment_post_ID );
@@ -1050,6 +974,13 @@
 			$caps[] = $cap;
 		break;
 	default:
+		// Handle meta capabilities for custom post types.
+		$post_type_meta_caps = _post_type_meta_capabilities();
+		if ( isset( $post_type_meta_caps[ $cap ] ) ) {
+			$args = array_merge( array( $post_type_meta_caps[ $cap ], $user_id ), $args );
+			return call_user_func_array( 'map_meta_cap', $args );
+		}
+
 		// If no meta caps match, return the original cap.
 		$caps[] = $cap;
 	}
